diff --git a/surfsense_backend/alembic/versions/49_migrate_old_chats_to_new_chat.py b/surfsense_backend/alembic/versions/49_migrate_old_chats_to_new_chat.py index 2d188b4e1..bacb33f05 100644 --- a/surfsense_backend/alembic/versions/49_migrate_old_chats_to_new_chat.py +++ b/surfsense_backend/alembic/versions/49_migrate_old_chats_to_new_chat.py @@ -13,7 +13,7 @@ This migration: import json from collections.abc import Sequence -from datetime import datetime, timezone +from datetime import datetime import sqlalchemy as sa @@ -116,7 +116,7 @@ def upgrade() -> None: # Only migrate user and assistant messages if role_lower not in ("user", "assistant"): continue - + # Convert to uppercase for database enum role = role_lower.upper() @@ -195,8 +195,18 @@ def downgrade() -> None: sa.Column("initial_connectors", sa.ARRAY(sa.String()), nullable=True), sa.Column("messages", sa.JSON(), nullable=False), sa.Column("state_version", sa.BigInteger(), nullable=False, default=1), - sa.Column("search_space_id", sa.Integer(), sa.ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False), - sa.Column("created_at", sa.TIMESTAMP(timezone=True), nullable=False, server_default=sa.func.now()), + sa.Column( + "search_space_id", + sa.Integer(), + sa.ForeignKey("searchspaces.id", ondelete="CASCADE"), + nullable=False, + ), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), ) # Recreate podcasts table @@ -206,11 +216,25 @@ def downgrade() -> None: sa.Column("title", sa.String(), nullable=False, index=True), sa.Column("podcast_transcript", sa.JSON(), nullable=False, server_default="{}"), sa.Column("file_location", sa.String(500), nullable=False, server_default=""), - sa.Column("chat_id", sa.Integer(), sa.ForeignKey("chats.id", ondelete="CASCADE"), nullable=True), + sa.Column( + "chat_id", + sa.Integer(), + sa.ForeignKey("chats.id", ondelete="CASCADE"), + nullable=True, + ), sa.Column("chat_state_version", sa.BigInteger(), nullable=True), - sa.Column("search_space_id", sa.Integer(), sa.ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False), - sa.Column("created_at", sa.TIMESTAMP(timezone=True), nullable=False, server_default=sa.func.now()), + sa.Column( + "search_space_id", + sa.Integer(), + sa.ForeignKey("searchspaces.id", ondelete="CASCADE"), + nullable=False, + ), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), ) print("[Migration 49 Downgrade] Tables recreated (data not restored)") - diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index 8a3a7e30e..b563cd594 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -9,7 +9,6 @@ from sqlalchemy import ( ARRAY, JSON, TIMESTAMP, - BigInteger, Boolean, Column, Enum as SQLAlchemyEnum, @@ -423,6 +422,25 @@ class Chunk(BaseModel, TimestampMixin): document = relationship("Document", back_populates="chunks") +class Podcast(BaseModel, TimestampMixin): + """Podcast model for storing generated podcasts.""" + + __tablename__ = "podcasts" + + title = Column(String(500), nullable=False) + podcast_transcript = Column(JSONB, nullable=True) # List of transcript entries + file_location = Column(Text, nullable=True) # Path to the audio file + + search_space_id = Column( + Integer, ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False + ) + search_space = relationship("SearchSpace", back_populates="podcasts") + + # Optional: link to chat thread (null for content-based podcasts from new-chat) + chat_id = Column(Integer, nullable=True) + chat_state_version = Column(String(100), nullable=True) + + class SearchSpace(BaseModel, TimestampMixin): __tablename__ = "searchspaces" @@ -459,6 +477,12 @@ class SearchSpace(BaseModel, TimestampMixin): order_by="NewChatThread.updated_at.desc()", cascade="all, delete-orphan", ) + podcasts = relationship( + "Podcast", + back_populates="search_space", + order_by="Podcast.id.desc()", + cascade="all, delete-orphan", + ) logs = relationship( "Log", back_populates="search_space", diff --git a/surfsense_backend/app/routes/__init__.py b/surfsense_backend/app/routes/__init__.py index 732693eb5..6ad87d975 100644 --- a/surfsense_backend/app/routes/__init__.py +++ b/surfsense_backend/app/routes/__init__.py @@ -16,6 +16,7 @@ from .logs_routes import router as logs_router from .luma_add_connector_route import router as luma_add_connector_router from .new_chat_routes import router as new_chat_router from .notes_routes import router as notes_router +from .podcasts_routes import router as podcasts_router from .rbac_routes import router as rbac_router from .search_source_connectors_routes import router as search_source_connectors_router from .search_spaces_routes import router as search_spaces_router @@ -28,6 +29,7 @@ router.include_router(editor_router) router.include_router(documents_router) router.include_router(notes_router) router.include_router(new_chat_router) # Chat with assistant-ui persistence +router.include_router(podcasts_router) # Podcast task status and audio router.include_router(search_source_connectors_router) router.include_router(google_calendar_add_connector_router) router.include_router(google_gmail_add_connector_router) diff --git a/surfsense_backend/app/routes/new_chat_routes.py b/surfsense_backend/app/routes/new_chat_routes.py index 8d2734808..cc9c94eea 100644 --- a/surfsense_backend/app/routes/new_chat_routes.py +++ b/surfsense_backend/app/routes/new_chat_routes.py @@ -8,11 +8,16 @@ These endpoints support the ThreadHistoryAdapter pattern from assistant-ui: - PUT /threads/{thread_id} - Update thread (rename, archive) - DELETE /threads/{thread_id} - Delete thread - POST /threads/{thread_id}/messages - Append message +- POST /attachments/process - Process attachments for chat context """ +import contextlib +import os +import tempfile +import uuid from datetime import UTC, datetime -from fastapi import APIRouter, Depends, HTTPException, Request +from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile from fastapi.responses import StreamingResponse from sqlalchemy.exc import IntegrityError, OperationalError from sqlalchemy.ext.asyncio import AsyncSession @@ -650,10 +655,10 @@ async def handle_new_chat( ): """ Stream chat responses from the deep agent. - + This endpoint handles the new chat functionality with streaming responses using Server-Sent Events (SSE) format compatible with Vercel AI SDK. - + Requires CHATS_CREATE permission. """ try: @@ -695,6 +700,7 @@ async def handle_new_chat( session=session, llm_config_id=llm_config_id, messages=request.messages, + attachments=request.attachments, ), media_type="text/event-stream", headers={ @@ -711,3 +717,185 @@ async def handle_new_chat( status_code=500, detail=f"An unexpected error occurred: {e!s}", ) from None + + +# ============================================================================= +# Attachment Processing Endpoint +# ============================================================================= + + +@router.post("/attachments/process") +async def process_attachment( + file: UploadFile = File(...), + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Process an attachment file and extract its content as markdown. + + This endpoint uses the configured ETL service to parse files and return + the extracted content that can be used as context in chat messages. + + Supported file types depend on the configured ETL_SERVICE: + - Markdown/Text files: .md, .markdown, .txt (always supported) + - Audio files: .mp3, .mp4, .mpeg, .mpga, .m4a, .wav, .webm (if STT configured) + - Documents: .pdf, .docx, .doc, .pptx, .xlsx (depends on ETL service) + + Returns: + JSON with attachment id, name, type, and extracted content + """ + from app.config import config as app_config + + if not file.filename: + raise HTTPException(status_code=400, detail="No filename provided") + + filename = file.filename + attachment_id = str(uuid.uuid4()) + + try: + # Save file to a temporary location + file_ext = os.path.splitext(filename)[1].lower() + with tempfile.NamedTemporaryFile(delete=False, suffix=file_ext) as temp_file: + temp_path = temp_file.name + content = await file.read() + temp_file.write(content) + + extracted_content = "" + + # Process based on file type + if file_ext in (".md", ".markdown", ".txt"): + # For text/markdown files, read content directly + with open(temp_path, encoding="utf-8") as f: + extracted_content = f.read() + + elif file_ext in (".mp3", ".mp4", ".mpeg", ".mpga", ".m4a", ".wav", ".webm"): + # Audio files - transcribe if STT service is configured + if not app_config.STT_SERVICE: + raise HTTPException( + status_code=422, + detail="Audio transcription is not configured. Please set STT_SERVICE.", + ) + + stt_service_type = ( + "local" if app_config.STT_SERVICE.startswith("local/") else "external" + ) + + if stt_service_type == "local": + from app.services.stt_service import stt_service + + result = stt_service.transcribe_file(temp_path) + extracted_content = result.get("text", "") + else: + from litellm import atranscription + + with open(temp_path, "rb") as audio_file: + transcription_kwargs = { + "model": app_config.STT_SERVICE, + "file": audio_file, + "api_key": app_config.STT_SERVICE_API_KEY, + } + if app_config.STT_SERVICE_API_BASE: + transcription_kwargs["api_base"] = ( + app_config.STT_SERVICE_API_BASE + ) + + transcription_response = await atranscription( + **transcription_kwargs + ) + extracted_content = transcription_response.get("text", "") + + if extracted_content: + extracted_content = ( + f"# Transcription of {filename}\n\n{extracted_content}" + ) + + else: + # Document files - use configured ETL service + if app_config.ETL_SERVICE == "UNSTRUCTURED": + from langchain_unstructured import UnstructuredLoader + + from app.utils.document_converters import convert_document_to_markdown + + loader = UnstructuredLoader( + temp_path, + mode="elements", + post_processors=[], + languages=["eng"], + include_orig_elements=False, + include_metadata=False, + strategy="auto", + ) + docs = await loader.aload() + extracted_content = await convert_document_to_markdown(docs) + + elif app_config.ETL_SERVICE == "LLAMACLOUD": + from llama_cloud_services import LlamaParse + from llama_cloud_services.parse.utils import ResultType + + parser = LlamaParse( + api_key=app_config.LLAMA_CLOUD_API_KEY, + num_workers=1, + verbose=False, + language="en", + result_type=ResultType.MD, + ) + result = await parser.aparse(temp_path) + markdown_documents = await result.aget_markdown_documents( + split_by_page=False + ) + + if markdown_documents: + extracted_content = "\n\n".join( + doc.text for doc in markdown_documents + ) + + elif app_config.ETL_SERVICE == "DOCLING": + from app.services.docling_service import create_docling_service + + docling_service = create_docling_service() + result = await docling_service.process_document(temp_path, filename) + extracted_content = result.get("content", "") + + else: + raise HTTPException( + status_code=422, + detail=f"ETL service not configured or unsupported file type: {file_ext}", + ) + + # Clean up temp file + with contextlib.suppress(Exception): + os.unlink(temp_path) + + if not extracted_content: + raise HTTPException( + status_code=422, + detail=f"Could not extract content from file: {filename}", + ) + + # Determine attachment type (must be one of: "image", "document", "file") + # assistant-ui only supports these three types + if file_ext in (".png", ".jpg", ".jpeg", ".gif", ".webp"): + attachment_type = "image" + else: + # All other files (including audio, documents, text) are treated as "document" + attachment_type = "document" + + return { + "id": attachment_id, + "name": filename, + "type": attachment_type, + "content": extracted_content, + "contentLength": len(extracted_content), + } + + except HTTPException: + raise + except Exception as e: + # Clean up temp file on error + with contextlib.suppress(Exception): + os.unlink(temp_path) + + raise HTTPException( + status_code=500, + detail=f"Failed to process attachment: {e!s}", + ) from e diff --git a/surfsense_backend/app/routes/podcasts_routes.py b/surfsense_backend/app/routes/podcasts_routes.py new file mode 100644 index 000000000..ef362edb5 --- /dev/null +++ b/surfsense_backend/app/routes/podcasts_routes.py @@ -0,0 +1,277 @@ +""" +Podcast routes for task status polling and audio retrieval. + +These routes support the podcast generation feature in new-chat. +Note: The old Chat-based podcast generation has been removed. +""" + +import os +from pathlib import Path + +from celery.result import AsyncResult +from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import StreamingResponse +from sqlalchemy import select +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession + +from app.celery_app import celery_app +from app.db import ( + Permission, + Podcast, + SearchSpace, + SearchSpaceMembership, + User, + get_async_session, +) +from app.schemas import PodcastRead +from app.users import current_active_user +from app.utils.rbac import check_permission + +router = APIRouter() + + +@router.get("/podcasts", response_model=list[PodcastRead]) +async def read_podcasts( + skip: int = 0, + limit: int = 100, + search_space_id: int | None = None, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + List podcasts the user has access to. + Requires PODCASTS_READ permission for the search space(s). + """ + if skip < 0 or limit < 1: + raise HTTPException(status_code=400, detail="Invalid pagination parameters") + try: + if search_space_id is not None: + # Check permission for specific search space + await check_permission( + session, + user, + search_space_id, + Permission.PODCASTS_READ.value, + "You don't have permission to read podcasts in this search space", + ) + result = await session.execute( + select(Podcast) + .filter(Podcast.search_space_id == search_space_id) + .offset(skip) + .limit(limit) + ) + else: + # Get podcasts from all search spaces user has membership in + result = await session.execute( + select(Podcast) + .join(SearchSpace) + .join(SearchSpaceMembership) + .filter(SearchSpaceMembership.user_id == user.id) + .offset(skip) + .limit(limit) + ) + return result.scalars().all() + except HTTPException: + raise + except SQLAlchemyError: + raise HTTPException( + status_code=500, detail="Database error occurred while fetching podcasts" + ) from None + + +@router.get("/podcasts/{podcast_id}", response_model=PodcastRead) +async def read_podcast( + podcast_id: int, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Get a specific podcast by ID. + Requires PODCASTS_READ permission for the search space. + """ + try: + result = await session.execute(select(Podcast).filter(Podcast.id == podcast_id)) + podcast = result.scalars().first() + + if not podcast: + raise HTTPException( + status_code=404, + detail="Podcast not found", + ) + + # Check permission for the search space + await check_permission( + session, + user, + podcast.search_space_id, + Permission.PODCASTS_READ.value, + "You don't have permission to read podcasts in this search space", + ) + + return podcast + except HTTPException as he: + raise he + except SQLAlchemyError: + raise HTTPException( + status_code=500, detail="Database error occurred while fetching podcast" + ) from None + + +@router.delete("/podcasts/{podcast_id}", response_model=dict) +async def delete_podcast( + podcast_id: int, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Delete a podcast. + Requires PODCASTS_DELETE permission for the search space. + """ + try: + result = await session.execute(select(Podcast).filter(Podcast.id == podcast_id)) + db_podcast = result.scalars().first() + + if not db_podcast: + raise HTTPException(status_code=404, detail="Podcast not found") + + # Check permission for the search space + await check_permission( + session, + user, + db_podcast.search_space_id, + Permission.PODCASTS_DELETE.value, + "You don't have permission to delete podcasts in this search space", + ) + + await session.delete(db_podcast) + await session.commit() + return {"message": "Podcast deleted successfully"} + except HTTPException as he: + raise he + except SQLAlchemyError: + await session.rollback() + raise HTTPException( + status_code=500, detail="Database error occurred while deleting podcast" + ) from None + + +@router.get("/podcasts/{podcast_id}/stream") +@router.get("/podcasts/{podcast_id}/audio") +async def stream_podcast( + podcast_id: int, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Stream a podcast audio file. + Requires PODCASTS_READ permission for the search space. + + Note: Both /stream and /audio endpoints are supported for compatibility. + """ + try: + result = await session.execute(select(Podcast).filter(Podcast.id == podcast_id)) + podcast = result.scalars().first() + + if not podcast: + raise HTTPException( + status_code=404, + detail="Podcast not found", + ) + + # Check permission for the search space + await check_permission( + session, + user, + podcast.search_space_id, + Permission.PODCASTS_READ.value, + "You don't have permission to access podcasts in this search space", + ) + + # Get the file path + file_path = podcast.file_location + + # Check if the file exists + if not file_path or not os.path.isfile(file_path): + raise HTTPException(status_code=404, detail="Podcast audio file not found") + + # Define a generator function to stream the file + def iterfile(): + with open(file_path, mode="rb") as file_like: + yield from file_like + + # Return a streaming response with appropriate headers + return StreamingResponse( + iterfile(), + media_type="audio/mpeg", + headers={ + "Accept-Ranges": "bytes", + "Content-Disposition": f"inline; filename={Path(file_path).name}", + }, + ) + + except HTTPException as he: + raise he + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Error streaming podcast: {e!s}" + ) from e + + +@router.get("/podcasts/task/{task_id}/status") +async def get_podcast_task_status( + task_id: str, + user: User = Depends(current_active_user), +): + """ + Get the status of a podcast generation task. + Used by new-chat frontend to poll for completion. + + Returns: + - status: "processing" | "success" | "error" + - podcast_id: (only if status == "success") + - title: (only if status == "success") + - error: (only if status == "error") + """ + try: + result = AsyncResult(task_id, app=celery_app) + + if result.ready(): + # Task completed + if result.successful(): + task_result = result.result + if isinstance(task_result, dict): + if task_result.get("status") == "success": + return { + "status": "success", + "podcast_id": task_result.get("podcast_id"), + "title": task_result.get("title"), + "transcript_entries": task_result.get("transcript_entries"), + } + else: + return { + "status": "error", + "error": task_result.get("error", "Unknown error"), + } + else: + return { + "status": "error", + "error": "Unexpected task result format", + } + else: + # Task failed + return { + "status": "error", + "error": str(result.result) if result.result else "Task failed", + } + else: + # Task still processing + return { + "status": "processing", + "state": result.state, + } + + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Error checking task status: {e!s}" + ) from e diff --git a/surfsense_backend/app/schemas/__init__.py b/surfsense_backend/app/schemas/__init__.py index 4d0eb3595..9ec639e7b 100644 --- a/surfsense_backend/app/schemas/__init__.py +++ b/surfsense_backend/app/schemas/__init__.py @@ -26,6 +26,7 @@ from .new_chat import ( ThreadListItem, ThreadListResponse, ) +from .podcasts import PodcastBase, PodcastCreate, PodcastRead, PodcastUpdate from .rbac_schemas import ( InviteAcceptRequest, InviteAcceptResponse, @@ -61,17 +62,6 @@ from .users import UserCreate, UserRead, UserUpdate __all__ = [ # Chat schemas (assistant-ui integration) "ChatMessage", - "NewChatMessageAppend", - "NewChatMessageCreate", - "NewChatMessageRead", - "NewChatRequest", - "NewChatThreadCreate", - "NewChatThreadRead", - "NewChatThreadUpdate", - "NewChatThreadWithMessages", - "ThreadHistoryLoadResponse", - "ThreadListItem", - "ThreadListResponse", # Chunk schemas "ChunkBase", "ChunkCreate", @@ -85,10 +75,15 @@ __all__ = [ "DocumentsCreate", "ExtensionDocumentContent", "ExtensionDocumentMetadata", - "PaginatedResponse", # Base schemas "IDModel", - "TimestampModel", + # RBAC schemas + "InviteAcceptRequest", + "InviteAcceptResponse", + "InviteCreate", + "InviteInfoResponse", + "InviteRead", + "InviteUpdate", # LLM Config schemas "LLMConfigBase", "LLMConfigCreate", @@ -100,18 +95,25 @@ __all__ = [ "LogFilter", "LogRead", "LogUpdate", - # RBAC schemas - "InviteAcceptRequest", - "InviteAcceptResponse", - "InviteCreate", - "InviteInfoResponse", - "InviteRead", - "InviteUpdate", "MembershipRead", "MembershipReadWithUser", "MembershipUpdate", + "NewChatMessageAppend", + "NewChatMessageCreate", + "NewChatMessageRead", + "NewChatRequest", + "NewChatThreadCreate", + "NewChatThreadRead", + "NewChatThreadUpdate", + "NewChatThreadWithMessages", + "PaginatedResponse", "PermissionInfo", "PermissionsListResponse", + # Podcast schemas + "PodcastBase", + "PodcastCreate", + "PodcastRead", + "PodcastUpdate", "RoleCreate", "RoleRead", "RoleUpdate", @@ -126,6 +128,10 @@ __all__ = [ "SearchSpaceRead", "SearchSpaceUpdate", "SearchSpaceWithStats", + "ThreadHistoryLoadResponse", + "ThreadListItem", + "ThreadListResponse", + "TimestampModel", # User schemas "UserCreate", "UserRead", diff --git a/surfsense_backend/app/schemas/new_chat.py b/surfsense_backend/app/schemas/new_chat.py index 1fdb50777..ffaf85554 100644 --- a/surfsense_backend/app/schemas/new_chat.py +++ b/surfsense_backend/app/schemas/new_chat.py @@ -141,6 +141,15 @@ class ChatMessage(BaseModel): content: str +class ChatAttachment(BaseModel): + """An attachment with its extracted content for chat context.""" + + id: str # Unique attachment ID + name: str # Original filename + type: str # Attachment type: document, image, audio + content: str # Extracted markdown content from the file + + class NewChatRequest(BaseModel): """Request schema for the deep agent chat endpoint.""" @@ -148,3 +157,6 @@ class NewChatRequest(BaseModel): user_query: str search_space_id: int messages: list[ChatMessage] | None = None # Optional chat history from frontend + attachments: list[ChatAttachment] | None = ( + None # Optional attachments with extracted content + ) diff --git a/surfsense_backend/app/schemas/podcasts.py b/surfsense_backend/app/schemas/podcasts.py new file mode 100644 index 000000000..bcf50a710 --- /dev/null +++ b/surfsense_backend/app/schemas/podcasts.py @@ -0,0 +1,41 @@ +"""Podcast schemas for API responses.""" + +from datetime import datetime +from typing import Any + +from pydantic import BaseModel + + +class PodcastBase(BaseModel): + """Base podcast schema.""" + + title: str + podcast_transcript: list[dict[str, Any]] | None = None + file_location: str | None = None + search_space_id: int + chat_id: int | None = None + chat_state_version: str | None = None + + +class PodcastCreate(PodcastBase): + """Schema for creating a podcast.""" + + pass + + +class PodcastUpdate(BaseModel): + """Schema for updating a podcast.""" + + title: str | None = None + podcast_transcript: list[dict[str, Any]] | None = None + file_location: str | None = None + + +class PodcastRead(PodcastBase): + """Schema for reading a podcast.""" + + id: int + created_at: datetime + + class Config: + from_attributes = True diff --git a/surfsense_backend/app/tasks/celery_tasks/podcast_tasks.py b/surfsense_backend/app/tasks/celery_tasks/podcast_tasks.py index aa28259ab..7f67ba599 100644 --- a/surfsense_backend/app/tasks/celery_tasks/podcast_tasks.py +++ b/surfsense_backend/app/tasks/celery_tasks/podcast_tasks.py @@ -13,7 +13,6 @@ from app.agents.podcaster.state import State as PodcasterState from app.celery_app import celery_app from app.config import config from app.db import Podcast -from app.tasks.podcast_tasks import generate_chat_podcast logger = logging.getLogger(__name__) @@ -40,58 +39,6 @@ def get_celery_session_maker(): return async_sessionmaker(engine, expire_on_commit=False) -@celery_app.task(name="generate_chat_podcast", bind=True) -def generate_chat_podcast_task( - self, - chat_id: int, - search_space_id: int, - user_id: int, - podcast_title: str | None = None, - user_prompt: str | None = None, -): - """ - Celery task to generate podcast from chat. - - Args: - chat_id: ID of the chat to generate podcast from - search_space_id: ID of the search space - user_id: ID of the user, - podcast_title: Title for the podcast - user_prompt: Optional prompt from the user to guide the podcast generation - """ - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - try: - loop.run_until_complete( - _generate_chat_podcast( - chat_id, search_space_id, user_id, podcast_title, user_prompt - ) - ) - loop.run_until_complete(loop.shutdown_asyncgens()) - finally: - asyncio.set_event_loop(None) - loop.close() - - -async def _generate_chat_podcast( - chat_id: int, - search_space_id: int, - user_id: int, - podcast_title: str | None = None, - user_prompt: str | None = None, -): - """Generate chat podcast with new session.""" - async with get_celery_session_maker()() as session: - try: - await generate_chat_podcast( - session, chat_id, search_space_id, user_id, podcast_title, user_prompt - ) - except Exception as e: - logger.error(f"Error generating podcast from chat: {e!s}") - raise - - # ============================================================================= # Content-based podcast generation (for new-chat) # ============================================================================= diff --git a/surfsense_backend/app/tasks/chat/stream_new_chat.py b/surfsense_backend/app/tasks/chat/stream_new_chat.py index e1061e745..9711445aa 100644 --- a/surfsense_backend/app/tasks/chat/stream_new_chat.py +++ b/surfsense_backend/app/tasks/chat/stream_new_chat.py @@ -18,11 +18,28 @@ from app.agents.new_chat.llm_config import ( create_chat_litellm_from_config, load_llm_config_from_yaml, ) -from app.schemas.new_chat import ChatMessage +from app.schemas.new_chat import ChatAttachment, ChatMessage from app.services.connector_service import ConnectorService from app.services.new_streaming_service import VercelStreamingService +def format_attachments_as_context(attachments: list[ChatAttachment]) -> str: + """Format attachments as context for the agent.""" + if not attachments: + return "" + + context_parts = [""] + for i, attachment in enumerate(attachments, 1): + context_parts.append( + f"" + ) + context_parts.append(f"") + context_parts.append("") + context_parts.append("") + + return "\n".join(context_parts) + + async def stream_new_chat( user_query: str, user_id: str | UUID, @@ -31,6 +48,7 @@ async def stream_new_chat( session: AsyncSession, llm_config_id: int = -1, messages: list[ChatMessage] | None = None, + attachments: list[ChatAttachment] | None = None, ) -> AsyncGenerator[str, None]: """ Stream chat responses from the new SurfSense deep agent. @@ -96,6 +114,14 @@ async def stream_new_chat( # Build input with message history from frontend langchain_messages = [] + # Format the user query with attachment context if any + final_query = user_query + if attachments: + attachment_context = format_attachments_as_context(attachments) + final_query = ( + f"{attachment_context}\n\n{user_query}" + ) + # if messages: # # Convert frontend messages to LangChain format # for msg in messages: @@ -104,8 +130,8 @@ async def stream_new_chat( # elif msg.role == "assistant": # langchain_messages.append(AIMessage(content=msg.content)) # else: - # Fallback: just use the current user query - langchain_messages.append(HumanMessage(content=user_query)) + # Fallback: just use the current user query with attachment context + langchain_messages.append(HumanMessage(content=final_query)) input_state = { # Lets not pass this message atm because we are using the checkpointer to manage the conversation history diff --git a/surfsense_backend/app/tasks/podcast_tasks.py b/surfsense_backend/app/tasks/podcast_tasks.py index 2285c5d09..21cd0d947 100644 --- a/surfsense_backend/app/tasks/podcast_tasks.py +++ b/surfsense_backend/app/tasks/podcast_tasks.py @@ -1,11 +1,15 @@ -from sqlalchemy import select -from sqlalchemy.exc import SQLAlchemyError +""" +Legacy podcast task for old chat system. + +NOTE: The old Chat model has been removed. This module is kept for backwards +compatibility but the generate_chat_podcast function will raise an error +if called. Use generate_content_podcast_task in celery_tasks/podcast_tasks.py +for new-chat podcast generation instead. +""" + from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.podcaster.graph import graph as podcaster_graph -from app.agents.podcaster.state import State -from app.db import Chat, Podcast -from app.services.task_logging_service import TaskLoggingService +from app.db import Podcast # noqa: F401 - imported for backwards compatibility async def generate_chat_podcast( @@ -16,196 +20,13 @@ async def generate_chat_podcast( podcast_title: str | None = None, user_prompt: str | None = None, ): - task_logger = TaskLoggingService(session, search_space_id) + """ + Legacy function for generating podcasts from old chat system. - # Log task start - log_entry = await task_logger.log_task_start( - task_name="generate_chat_podcast", - source="podcast_task", - message=f"Starting podcast generation for chat {chat_id}", - metadata={ - "chat_id": chat_id, - "search_space_id": search_space_id, - "podcast_title": podcast_title, - "user_id": str(user_id), - "user_prompt": user_prompt, - }, + This function is deprecated as the old Chat model has been removed. + Use generate_content_podcast_task for new-chat podcast generation. + """ + raise NotImplementedError( + "generate_chat_podcast is deprecated. The old Chat model has been removed. " + "Use generate_content_podcast_task for podcast generation from new-chat." ) - - try: - # Fetch the chat with the specified ID - await task_logger.log_task_progress( - log_entry, f"Fetching chat {chat_id} from database", {"stage": "fetch_chat"} - ) - - query = select(Chat).filter( - Chat.id == chat_id, Chat.search_space_id == search_space_id - ) - - result = await session.execute(query) - chat = result.scalars().first() - - if not chat: - await task_logger.log_task_failure( - log_entry, - f"Chat with id {chat_id} not found in search space {search_space_id}", - "Chat not found", - {"error_type": "ChatNotFound"}, - ) - raise ValueError( - f"Chat with id {chat_id} not found in search space {search_space_id}" - ) - - # Create chat history structure - await task_logger.log_task_progress( - log_entry, - f"Processing chat history for chat {chat_id}", - {"stage": "process_chat_history", "message_count": len(chat.messages)}, - ) - - chat_history_str = "" - - processed_messages = 0 - for message in chat.messages: - if message["role"] == "user": - chat_history_str += f"{message['content']}" - processed_messages += 1 - elif message["role"] == "assistant": - chat_history_str += ( - f"{message['content']}" - ) - processed_messages += 1 - - chat_history_str += "" - - # Pass it to the SurfSense Podcaster - await task_logger.log_task_progress( - log_entry, - f"Initializing podcast generation for chat {chat_id}", - { - "stage": "initialize_podcast_generation", - "processed_messages": processed_messages, - "content_length": len(chat_history_str), - }, - ) - - config = { - "configurable": { - "podcast_title": podcast_title or "SurfSense Podcast", - "user_id": str(user_id), - "search_space_id": search_space_id, - "user_prompt": user_prompt, - } - } - # Initialize state with database session and streaming service - initial_state = State(source_content=chat_history_str, db_session=session) - - # Run the graph directly - await task_logger.log_task_progress( - log_entry, - f"Running podcast generation graph for chat {chat_id}", - {"stage": "run_podcast_graph"}, - ) - - result = await podcaster_graph.ainvoke(initial_state, config=config) - - # Convert podcast transcript entries to serializable format - await task_logger.log_task_progress( - log_entry, - f"Processing podcast transcript for chat {chat_id}", - { - "stage": "process_transcript", - "transcript_entries": len(result["podcast_transcript"]), - }, - ) - - serializable_transcript = [] - for entry in result["podcast_transcript"]: - serializable_transcript.append( - {"speaker_id": entry.speaker_id, "dialog": entry.dialog} - ) - - # Create a new podcast entry - await task_logger.log_task_progress( - log_entry, - f"Creating podcast database entry for chat {chat_id}", - { - "stage": "create_podcast_entry", - "file_location": result.get("final_podcast_file_path"), - }, - ) - - # check if podcast already exists for this chat (re-generation) - existing_podcast = await session.execute( - select(Podcast).filter(Podcast.chat_id == chat_id) - ) - existing_podcast = existing_podcast.scalars().first() - - if existing_podcast: - existing_podcast.podcast_transcript = serializable_transcript - existing_podcast.file_location = result["final_podcast_file_path"] - existing_podcast.chat_state_version = chat.state_version - await session.commit() - await session.refresh(existing_podcast) - return existing_podcast - else: - podcast = Podcast( - title=f"{podcast_title}", - podcast_transcript=serializable_transcript, - file_location=result["final_podcast_file_path"], - search_space_id=search_space_id, - chat_state_version=chat.state_version, - chat_id=chat.id, - ) - - # Add to session and commit - session.add(podcast) - await session.commit() - await session.refresh(podcast) - - # Log success - await task_logger.log_task_success( - log_entry, - f"Successfully generated podcast for chat {chat_id}", - { - "podcast_id": podcast.id, - "podcast_title": podcast_title, - "transcript_entries": len(serializable_transcript), - "file_location": result.get("final_podcast_file_path"), - "processed_messages": processed_messages, - "content_length": len(chat_history_str), - }, - ) - - return podcast - - except ValueError as ve: - # ValueError is already logged above for chat not found - if "not found" not in str(ve): - await task_logger.log_task_failure( - log_entry, - f"Value error during podcast generation for chat {chat_id}", - str(ve), - {"error_type": "ValueError"}, - ) - raise ve - except SQLAlchemyError as db_error: - await session.rollback() - await task_logger.log_task_failure( - log_entry, - f"Database error during podcast generation for chat {chat_id}", - str(db_error), - {"error_type": "SQLAlchemyError"}, - ) - raise db_error - except Exception as e: - await session.rollback() - await task_logger.log_task_failure( - log_entry, - f"Unexpected error during podcast generation for chat {chat_id}", - str(e), - {"error_type": type(e).__name__}, - ) - raise RuntimeError( - f"Failed to generate podcast for chat {chat_id}: {e!s}" - ) from e diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index 5583af64d..0e0f32d2b 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -1,24 +1,20 @@ "use client"; -import { useAtom, useAtomValue, useSetAtom } from "jotai"; -import { Loader2, PanelRight } from "lucide-react"; -import { AnimatePresence, motion } from "motion/react"; +import { useAtomValue, useSetAtom } from "jotai"; +import { Loader2 } from "lucide-react"; import { useParams, usePathname, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import type React from "react"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms"; import { llmPreferencesAtom } from "@/atoms/llm-config/llm-config-query.atoms"; import { myAccessAtom } from "@/atoms/members/members-query.atoms"; import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; -import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer"; import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb"; import { LanguageSwitcher } from "@/components/LanguageSwitcher"; import { AppSidebarProvider } from "@/components/sidebar/AppSidebarProvider"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; -import { cn } from "@/lib/utils"; export function DashboardClientLayout({ children, @@ -34,33 +30,8 @@ export function DashboardClientLayout({ const t = useTranslations("dashboard"); const router = useRouter(); const pathname = usePathname(); - const searchSpaceIdNum = Number(searchSpaceId); - const { search_space_id, chat_id } = useParams(); - const [chatUIState, setChatUIState] = useAtom(activeChathatUIAtom); - const activeChatId = useAtomValue(activeChatIdAtom); + const { search_space_id } = useParams(); const setActiveSearchSpaceIdState = useSetAtom(activeSearchSpaceIdAtom); - const setActiveChatIdState = useSetAtom(activeChatIdAtom); - const [showIndicator, setShowIndicator] = useState(false); - - const { isChatPannelOpen } = chatUIState; - - // Check if we're on the researcher page - const isResearcherPage = pathname?.includes("/researcher"); - - // Check if we're on the new-chat page (uses separate thread persistence) - const isNewChatPage = pathname?.includes("/new-chat"); - - // Show indicator when chat becomes active and panel is closed - useEffect(() => { - if (activeChatId && !isChatPannelOpen) { - setShowIndicator(true); - // Hide indicator after 5 seconds - const timer = setTimeout(() => setShowIndicator(false), 5000); - return () => clearTimeout(timer); - } else { - setShowIndicator(false); - } - }, [activeChatId, isChatPannelOpen]); const { data: preferences = {}, isFetching: loading, error } = useAtomValue(llmPreferencesAtom); @@ -151,24 +122,7 @@ export function DashboardClientLayout({ : ""; if (!activeSeacrhSpaceId) return; setActiveSearchSpaceIdState(activeSeacrhSpaceId); - }, [search_space_id]); - - useEffect(() => { - // Skip setting activeChatIdAtom on new-chat page (uses separate thread persistence) - if (isNewChatPage) { - setActiveChatIdState(null); - return; - } - - const activeChatId = - typeof chat_id === "string" - ? chat_id - : Array.isArray(chat_id) && chat_id.length > 0 - ? chat_id[0] - : ""; - if (!activeChatId) return; - setActiveChatIdState(activeChatId); - }, [chat_id, search_space_id, isNewChatPage]); + }, [search_space_id, setActiveSearchSpaceIdState]); // Show loading screen while checking onboarding status (only on first load) if (!hasCheckedOnboarding && (loading || accessLoading) && !isOnboardingPage) { @@ -221,123 +175,20 @@ export function DashboardClientLayout({ navMain={translatedNavMain} /> -
-
-
-
-
- - - -
-
- - {/* Only show artifacts toggle on researcher page */} - {isResearcherPage && ( - - { - setChatUIState((prev) => ({ - ...prev, - isChatPannelOpen: !isChatPannelOpen, - })); - setShowIndicator(false); - }} - className={cn( - "shrink-0 rounded-full p-2 transition-all duration-300 relative", - showIndicator - ? "bg-primary/20 hover:bg-primary/30 shadow-lg shadow-primary/25" - : "hover:bg-muted", - activeChatId && !showIndicator && "hover:bg-primary/10" - )} - title="Toggle Artifacts Panel" - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - - - - - {/* Pulsing indicator badge */} - - {showIndicator && ( - - -
- - - - )} - - - )} -
+
+
+
+
+ + +
-
-
{children}
-
- {/* Only render chat panel on researcher page */} - {isResearcherPage && } +
+ +
+
+
+
{children}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index 317209429..1426ecea1 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -1,6 +1,7 @@ "use client"; import { + type AppendMessage, AssistantRuntimeProvider, type ThreadMessageLike, useExternalStoreRuntime, @@ -11,6 +12,7 @@ import { toast } from "sonner"; import { Thread } from "@/components/assistant-ui/thread"; import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; import { getBearerToken } from "@/lib/auth-utils"; +import { createAttachmentAdapter, extractAttachmentContent } from "@/lib/chat/attachment-adapter"; import { isPodcastGenerating, looksLikePodcastRequest, @@ -59,6 +61,9 @@ export default function NewChatPage() { const [isRunning, setIsRunning] = useState(false); const abortControllerRef = useRef(null); + // Create the attachment adapter for file processing + const attachmentAdapter = useMemo(() => createAttachmentAdapter(), []); + // Extract search_space_id from URL params const searchSpaceId = useMemo(() => { const id = params.search_space_id; @@ -99,7 +104,10 @@ export default function NewChatPage() { } } catch (error) { console.error("[NewChatPage] Failed to initialize thread:", error); - setThreadId(Date.now()); + // Keep threadId as null - don't use Date.now() as it creates an invalid ID + // that will cause 404 errors on subsequent API calls + setThreadId(null); + toast.error("Failed to initialize chat. Please try again."); } finally { setIsInitializing(false); } @@ -121,18 +129,27 @@ export default function NewChatPage() { // Handle new message from user const onNew = useCallback( - async (message: ThreadMessageLike) => { + async (message: AppendMessage) => { if (!threadId) return; - // Extract user query text + // Extract user query text from content parts let userQuery = ""; for (const part of message.content) { - if (typeof part === "object" && part.type === "text" && "text" in part) { + if (part.type === "text") { userQuery += part.text; } } - if (!userQuery.trim()) return; + // Extract attachments from message + // AppendMessage.attachments contains the processed attachment objects (from adapter.send()) + const messageAttachments: Array> = []; + if (message.attachments && message.attachments.length > 0) { + for (const att of message.attachments) { + messageAttachments.push(att as unknown as Record); + } + } + + if (!userQuery.trim() && messageAttachments.length === 0) return; // Check if podcast is already generating if (isPodcastGenerating() && looksLikePodcastRequest(userQuery)) { @@ -239,6 +256,9 @@ export default function NewChatPage() { }) .filter((m) => m.content.length > 0); + // Extract attachment content to send with the request + const attachments = extractAttachmentContent(messageAttachments); + const response = await fetch(`${backendUrl}/api/v1/new_chat`, { method: "POST", headers: { @@ -250,6 +270,7 @@ export default function NewChatPage() { user_query: userQuery.trim(), search_space_id: searchSpaceId, messages: messageHistory, + attachments: attachments.length > 0 ? attachments : undefined, }), signal: controller.signal, }); @@ -405,13 +426,29 @@ export default function NewChatPage() { [] ); - // Create external store runtime + // Handle editing a message - removes messages after the edited one and sends as new + const onEdit = useCallback( + async (message: AppendMessage) => { + // Find the message being edited by looking at the parentId + // The parentId tells us which message's response we're editing + // For now, we'll just treat edits like new messages + // A more sophisticated implementation would truncate the history + await onNew(message); + }, + [onNew] + ); + + // Create external store runtime with attachment support const runtime = useExternalStoreRuntime({ messages, isRunning, onNew, + onEdit, convertMessage, onCancel: cancelRun, + adapters: { + attachments: attachmentAdapter, + }, }); // Show loading state @@ -423,6 +460,25 @@ export default function NewChatPage() { ); } + // Show error state if thread initialization failed + if (!threadId) { + return ( +
+
Failed to initialize chat
+ +
+ ); + } + return ( diff --git a/surfsense_web/app/dashboard/[search_space_id]/podcasts/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/podcasts/page.tsx deleted file mode 100644 index c8c724ee2..000000000 --- a/surfsense_web/app/dashboard/[search_space_id]/podcasts/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Suspense } from "react"; -import PodcastsPageClient from "./podcasts-client"; - -interface PageProps { - params: { - search_space_id: string; - }; -} - -export default async function PodcastsPage({ params }: PageProps) { - const { search_space_id: searchSpaceId } = await Promise.resolve(params); - - return ( - -
- - } - > - -
- ); -} diff --git a/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx deleted file mode 100644 index 730defae8..000000000 --- a/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx +++ /dev/null @@ -1,957 +0,0 @@ -"use client"; - -import { format } from "date-fns"; -import { useAtom, useAtomValue } from "jotai"; -import { - Calendar, - MoreHorizontal, - Pause, - Play, - Podcast as PodcastIcon, - Search, - SkipBack, - SkipForward, - Trash2, - Volume2, - VolumeX, - X, -} from "lucide-react"; -import { AnimatePresence, motion, type Variants } from "motion/react"; -import Image from "next/image"; -import { useEffect, useRef, useState } from "react"; -import { toast } from "sonner"; -import { deletePodcastMutationAtom } from "@/atoms/podcasts/podcast-mutation.atoms"; -import { podcastsAtom } from "@/atoms/podcasts/podcast-query.atoms"; -// UI Components -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Slider } from "@/components/ui/slider"; -import type { Podcast } from "@/contracts/types/podcast.types"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; - -interface PodcastsPageClientProps { - searchSpaceId: string; -} - -const pageVariants: Variants = { - initial: { opacity: 0 }, - enter: { - opacity: 1, - transition: { duration: 0.4, ease: "easeInOut", staggerChildren: 0.1 }, - }, - exit: { opacity: 0, transition: { duration: 0.3, ease: "easeInOut" } }, -}; - -const podcastCardVariants: Variants = { - initial: { scale: 0.95, y: 20, opacity: 0 }, - animate: { - scale: 1, - y: 0, - opacity: 1, - transition: { type: "spring", stiffness: 300, damping: 25 }, - }, - exit: { scale: 0.95, y: -20, opacity: 0 }, - hover: { y: -5, scale: 1.02, transition: { duration: 0.2 } }, -}; - -const MotionCard = motion(Card); - -export default function PodcastsPageClient({ searchSpaceId }: PodcastsPageClientProps) { - const [filteredPodcasts, setFilteredPodcasts] = useState([]); - const [searchQuery, setSearchQuery] = useState(""); - const [sortOrder, setSortOrder] = useState("newest"); - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); - const [podcastToDelete, setPodcastToDelete] = useState<{ - id: number; - title: string; - } | null>(null); - - // Audio player state - const [currentPodcast, setCurrentPodcast] = useState(null); - const [audioSrc, setAudioSrc] = useState(undefined); - const [isAudioLoading, setIsAudioLoading] = useState(false); - const [isPlaying, setIsPlaying] = useState(false); - const [currentTime, setCurrentTime] = useState(0); - const [duration, setDuration] = useState(0); - const [volume, setVolume] = useState(0.7); - const [isMuted, setIsMuted] = useState(false); - const audioRef = useRef(null); - const currentObjectUrlRef = useRef(null); - const [{ isPending: isDeletingPodcast, mutateAsync: deletePodcast, error: deleteError }] = - useAtom(deletePodcastMutationAtom); - const { - data: podcasts, - isLoading: isFetchingPodcasts, - error: fetchError, - } = useAtomValue(podcastsAtom); - - // Add podcast image URL constant - const PODCAST_IMAGE_URL = - "https://static.vecteezy.com/system/resources/thumbnails/002/157/611/small_2x/illustrations-concept-design-podcast-channel-free-vector.jpg"; - - useEffect(() => { - if (isFetchingPodcasts) return; - - if (fetchError) { - console.error("Error fetching podcasts:", fetchError); - setFilteredPodcasts([]); - return; - } - - if (!podcasts) { - setFilteredPodcasts([]); - return; - } - - setFilteredPodcasts(podcasts); - }, []); - - // Filter and sort podcasts based on search query and sort order - useEffect(() => { - if (!podcasts) return; - - let result = [...podcasts]; - - // Filter by search term - if (searchQuery) { - const query = searchQuery.toLowerCase(); - result = result.filter((podcast) => podcast.title.toLowerCase().includes(query)); - } - - // Filter by search space - result = result.filter((podcast) => podcast.search_space_id === parseInt(searchSpaceId)); - - // Sort podcasts - result.sort((a, b) => { - const dateA = new Date(a.created_at).getTime(); - const dateB = new Date(b.created_at).getTime(); - - return sortOrder === "newest" ? dateB - dateA : dateA - dateB; - }); - - setFilteredPodcasts(result); - }, [podcasts, searchQuery, sortOrder, searchSpaceId]); - - // Cleanup object URL on unmount or when currentPodcast changes - useEffect(() => { - return () => { - if (currentObjectUrlRef.current) { - URL.revokeObjectURL(currentObjectUrlRef.current); - currentObjectUrlRef.current = null; - } - }; - }, []); - - // Audio player time update handler - const handleTimeUpdate = () => { - if (audioRef.current) { - setCurrentTime(audioRef.current.currentTime); - } - }; - - // Audio player metadata loaded handler - const handleMetadataLoaded = () => { - if (audioRef.current) { - setDuration(audioRef.current.duration); - } - }; - - // Play/pause toggle - const togglePlayPause = () => { - if (audioRef.current) { - if (isPlaying) { - audioRef.current.pause(); - } else { - audioRef.current.play(); - } - setIsPlaying(!isPlaying); - } - }; - - // To close player - const closePlayer = () => { - if (isPlaying) { - audioRef.current?.pause(); - } - setIsPlaying(false); - setAudioSrc(undefined); - setCurrentTime(0); - setCurrentPodcast(null); - }; - - // Seek to position - const handleSeek = (value: number[]) => { - if (audioRef.current) { - audioRef.current.currentTime = value[0]; - setCurrentTime(value[0]); - } - }; - - // Volume change - const handleVolumeChange = (value: number[]) => { - if (audioRef.current) { - const newVolume = value[0]; - - // Set volume - audioRef.current.volume = newVolume; - setVolume(newVolume); - - // Handle mute state based on volume - if (newVolume === 0) { - audioRef.current.muted = true; - setIsMuted(true); - } else { - audioRef.current.muted = false; - setIsMuted(false); - } - } - }; - - // Toggle mute - const toggleMute = () => { - if (audioRef.current) { - const newMutedState = !isMuted; - audioRef.current.muted = newMutedState; - setIsMuted(newMutedState); - - // If unmuting, restore previous volume if it was 0 - if (!newMutedState && volume === 0) { - const restoredVolume = 0.5; - audioRef.current.volume = restoredVolume; - setVolume(restoredVolume); - } - } - }; - - // Skip forward 10 seconds - const skipForward = () => { - if (audioRef.current) { - audioRef.current.currentTime = Math.min( - audioRef.current.duration, - audioRef.current.currentTime + 10 - ); - } - }; - - // Skip backward 10 seconds - const skipBackward = () => { - if (audioRef.current) { - audioRef.current.currentTime = Math.max(0, audioRef.current.currentTime - 10); - } - }; - - // Format time in MM:SS - const formatTime = (time: number) => { - const minutes = Math.floor(time / 60); - const seconds = Math.floor(time % 60); - return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; - }; - - // Play podcast - Fetch blob and set object URL - const playPodcast = async (podcast: Podcast) => { - // If the same podcast is selected, just toggle play/pause - if (currentPodcast && currentPodcast.id === podcast.id) { - togglePlayPause(); - return; - } - - // Prevent multiple simultaneous loading requests - if (isAudioLoading) { - return; - } - - try { - // Reset player state and show loading - setCurrentPodcast(podcast); - setAudioSrc(undefined); - setCurrentTime(0); - setDuration(0); - setIsPlaying(false); - setIsAudioLoading(true); - - // Revoke previous object URL if exists (only after we've started the new request) - if (currentObjectUrlRef.current) { - URL.revokeObjectURL(currentObjectUrlRef.current); - currentObjectUrlRef.current = null; - } - - // Use AbortController to handle timeout or cancellation - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout - - try { - const response = await podcastsApiService.loadPodcast({ - request: { id: podcast.id }, - controller, - }); - const objectUrl = URL.createObjectURL(response); - currentObjectUrlRef.current = objectUrl; - - // Set audio source - setAudioSrc(objectUrl); - - // Wait for the audio to be ready before playing - // We'll handle actual playback in the onLoadedData event instead of here - } catch (error) { - if (error instanceof DOMException && error.name === "AbortError") { - throw new Error("Request timed out. Please try again."); - } - throw error; - } finally { - clearTimeout(timeoutId); - } - } catch (error) { - console.error("Error fetching or playing podcast:", error); - toast.error(error instanceof Error ? error.message : "Failed to load podcast audio."); - // Reset state on error - setCurrentPodcast(null); - setAudioSrc(undefined); - } finally { - setIsAudioLoading(false); - } - }; - - // Function to handle podcast deletion - const handleDeletePodcast = async () => { - if (!podcastToDelete) return; - - try { - await deletePodcast({ id: podcastToDelete.id }); - - // Close dialog - setDeleteDialogOpen(false); - setPodcastToDelete(null); - - // If the current playing podcast is deleted, stop playback - if (currentPodcast && currentPodcast.id === podcastToDelete.id) { - if (audioRef.current) { - audioRef.current.pause(); - } - setCurrentPodcast(null); - setIsPlaying(false); - } - } catch (error) { - console.error("Error deleting podcast:", error); - toast.error(error instanceof Error ? error.message : "Failed to delete podcast"); - } - }; - - return ( - -
-
-

Podcasts

-

Listen to generated podcasts.

-
- - {/* Filter and Search Bar */} -
-
-
- - setSearchQuery(e.target.value)} - /> -
-
- -
- -
-
- - {/* Status Messages */} - {isFetchingPodcasts && ( -
-
-
-

Loading podcasts...

-
-
- )} - - {fetchError && !isFetchingPodcasts && ( -
-

Error loading podcasts

-

{fetchError.message ?? "Failed to load podcasts"}

-
- )} - - {!isFetchingPodcasts && !fetchError && filteredPodcasts.length === 0 && ( -
- -

No podcasts found

-

- {searchQuery - ? "Try adjusting your search filters" - : "Generate podcasts from your chats to get started"} -

-
- )} - - {/* Podcast Grid */} - {!isFetchingPodcasts && !fetchError && filteredPodcasts.length > 0 && ( - - - {filteredPodcasts.map((podcast, index) => ( - playPodcast(podcast)} - > -
- {/* Podcast image with gradient overlay */} - Podcast illustration - - {/* Better overlay with gradient for improved text legibility */} -
- - {/* Loading indicator with improved animation */} - {currentPodcast?.id === podcast.id && isAudioLoading && ( - - -
-

Loading podcast...

-
-
- )} - - {/* Play button with animations */} - {!(currentPodcast?.id === podcast.id && (isPlaying || isAudioLoading)) && ( - - - - )} - - {/* Pause button with animations */} - {currentPodcast?.id === podcast.id && isPlaying && !isAudioLoading && ( - - - - )} - - {/* Now playing indicator */} - {currentPodcast?.id === podcast.id && !isAudioLoading && ( -
- - - - - Now Playing -
- )} -
- -
-

- {podcast.title || "Untitled Podcast"} -

-

- - {format(new Date(podcast.created_at), "MMM d, yyyy")} -

-
- - {currentPodcast?.id === podcast.id && !isAudioLoading && ( - - -
- {formatTime(currentTime)} - {formatTime(duration)} -
-
- )} - - {currentPodcast?.id === podcast.id && !isAudioLoading && ( - - - - - - - - - - - - )} - -
- - - - - - { - e.stopPropagation(); - setPodcastToDelete({ - id: podcast.id, - title: podcast.title, - }); - setDeleteDialogOpen(true); - }} - > - - Delete Podcast - - - -
-
- ))} -
-
- )} - - {/* Current Podcast Player (Fixed at bottom) */} - {currentPodcast && !isAudioLoading && audioSrc && ( - -
-
-
- - - -
- -
-

{currentPodcast.title}

- -
-
- - -
-
- {formatTime(currentTime)} / {formatTime(duration)} -
-
-
- -
- - - - - - - - - - - - -
- - - - -
- - -
-
- - - - -
-
-
-
- )} -
- - {/* Delete Confirmation Dialog */} - - - - - - Delete Podcast - - - Are you sure you want to delete{" "} - {podcastToDelete?.title}? This action cannot be - undone. - - - - - - - - - - {/* Hidden audio element for playback */} - -
- ); -} diff --git a/surfsense_web/app/dashboard/[search_space_id]/researcher/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/researcher/[[...chat_id]]/page.tsx deleted file mode 100644 index 60980f034..000000000 --- a/surfsense_web/app/dashboard/[search_space_id]/researcher/[[...chat_id]]/page.tsx +++ /dev/null @@ -1,291 +0,0 @@ -"use client"; - -import { type CreateMessage, type Message, useChat } from "@ai-sdk/react"; -import { useAtom, useAtomValue } from "jotai"; -import { useParams, useRouter } from "next/navigation"; -import { useEffect, useMemo, useRef } from "react"; -import { createChatMutationAtom, updateChatMutationAtom } from "@/atoms/chats/chat-mutation.atoms"; -import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; -import { activeChatIdAtom } from "@/atoms/chats/ui.atoms"; -import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; -import ChatInterface from "@/components/chat/ChatInterface"; -import type { Document } from "@/contracts/types/document.types"; -import { useChatState } from "@/hooks/use-chat"; -import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors"; - -export default function ResearcherPage() { - const { search_space_id } = useParams(); - const router = useRouter(); - const hasSetInitialConnectors = useRef(false); - const hasInitiatedResponse = useRef(null); - const activeChatId = useAtomValue(activeChatIdAtom); - const { data: activeChatState, isFetching: isChatLoading } = useAtomValue(activeChatAtom); - const { mutateAsync: createChat } = useAtomValue(createChatMutationAtom); - const { mutateAsync: updateChat } = useAtomValue(updateChatMutationAtom); - const isNewChat = !activeChatId; - - // Reset the flag when chat ID changes (but not hasInitiatedResponse - we need to remember if we already initiated) - useEffect(() => { - hasSetInitialConnectors.current = false; - }, [activeChatId]); - - const { - token, - researchMode, - selectedConnectors, - setSelectedConnectors, - selectedDocuments, - setSelectedDocuments, - topK, - setTopK, - } = useChatState({ - search_space_id: search_space_id as string, - chat_id: activeChatId ?? undefined, - }); - - // Fetch all available sources (document types + live search connectors) - // Use the documentTypeCountsAtom for fetching document types - const [documentTypeCountsQuery] = useAtom(documentTypeCountsAtom); - const { data: documentTypeCountsData } = documentTypeCountsQuery; - - // Transform the response into the expected format - const documentTypes = useMemo(() => { - if (!documentTypeCountsData) return []; - return Object.entries(documentTypeCountsData).map(([type, count]) => ({ - type, - count, - })); - }, [documentTypeCountsData]); - - const { connectors: searchConnectors } = useSearchSourceConnectors( - false, - Number(search_space_id) - ); - - // Filter for non-indexable connectors (live search) - const liveSearchConnectors = useMemo( - () => searchConnectors.filter((connector) => !connector.is_indexable), - [searchConnectors] - ); - - // Memoize document IDs to prevent infinite re-renders - const documentIds = useMemo(() => { - return selectedDocuments.map((doc) => doc.id); - }, [selectedDocuments]); - - // Memoize connector types to prevent infinite re-renders - const connectorTypes = useMemo(() => { - return selectedConnectors; - }, [selectedConnectors]); - - // Unified localStorage management for chat state - interface ChatState { - selectedDocuments: Document[]; - selectedConnectors: string[]; - researchMode: "QNA"; // Always QNA mode - topK: number; - } - - const getChatStateStorageKey = (searchSpaceId: string, chatId: string) => - `surfsense_chat_state_${searchSpaceId}_${chatId}`; - - const storeChatState = (searchSpaceId: string, chatId: string, state: ChatState) => { - const key = getChatStateStorageKey(searchSpaceId, chatId); - localStorage.setItem(key, JSON.stringify(state)); - }; - - const restoreChatState = (searchSpaceId: string, chatId: string): ChatState | null => { - const key = getChatStateStorageKey(searchSpaceId, chatId); - const stored = localStorage.getItem(key); - if (stored) { - localStorage.removeItem(key); // Clean up after restoration - try { - return JSON.parse(stored); - } catch (error) { - console.error("Error parsing stored chat state:", error); - return null; - } - } - return null; - }; - - const handler = useChat({ - api: `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chat`, - streamProtocol: "data", - initialMessages: [], - headers: { - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: { - data: { - search_space_id: search_space_id, - selected_connectors: connectorTypes, - research_mode: researchMode, - document_ids_to_add_in_context: documentIds, - top_k: topK, - }, - }, - onError: (error) => { - console.error("Chat error:", error); - }, - }); - - const customHandlerAppend = async ( - message: Message | CreateMessage, - chatRequestOptions?: { data?: any } - ) => { - // Use the first message content as the chat title (truncated to 100 chars) - const messageContent = typeof message.content === "string" ? message.content : ""; - const chatTitle = messageContent.slice(0, 100) || "Untitled Chat"; - - const newChat = await createChat({ - type: researchMode, - title: chatTitle, - initial_connectors: selectedConnectors, - messages: [ - { - role: "user", - content: message.content, - }, - ], - search_space_id: Number(search_space_id), - }); - if (newChat) { - // Store chat state before navigation - storeChatState(search_space_id as string, String(newChat.id), { - selectedDocuments, - selectedConnectors, - researchMode, - topK, - }); - router.replace(`/dashboard/${search_space_id}/researcher/${newChat.id}`); - } - return String(newChat.id); - }; - - useEffect(() => { - if (token && !isNewChat && activeChatId) { - const chatData = activeChatState?.chatDetails; - if (!chatData) return; - - // Update configuration from chat data - // researchMode is always "QNA", no need to set from chat data - - if (chatData.initial_connectors && Array.isArray(chatData.initial_connectors)) { - setSelectedConnectors(chatData.initial_connectors); - } - - // Load existing messages - if (chatData.messages && Array.isArray(chatData.messages)) { - if (chatData.messages.length === 1 && chatData.messages[0].role === "user") { - // Single user message - append to trigger LLM response - // Only if we haven't already initiated for this chat and handler doesn't have messages yet - if (hasInitiatedResponse.current !== activeChatId && handler.messages.length === 0) { - hasInitiatedResponse.current = activeChatId; - handler.append({ - role: "user", - content: chatData.messages[0].content, - }); - } - } else if (chatData.messages.length > 1) { - // Multiple messages - set them all - handler.setMessages(chatData.messages); - } - } - } - }, [token, isNewChat, activeChatId, isChatLoading]); - - // Restore chat state from localStorage on page load - useEffect(() => { - if (activeChatId && search_space_id) { - const restoredState = restoreChatState(search_space_id as string, activeChatId); - if (restoredState) { - setSelectedDocuments(restoredState.selectedDocuments); - setSelectedConnectors(restoredState.selectedConnectors); - setTopK(restoredState.topK); - // researchMode is always "QNA", no need to restore - } - } - }, [ - activeChatId, - isChatLoading, - search_space_id, - setSelectedDocuments, - setSelectedConnectors, - setTopK, - ]); - - // Set all sources as default for new chats (only once on initial mount) - useEffect(() => { - if ( - isNewChat && - !hasSetInitialConnectors.current && - selectedConnectors.length === 0 && - documentTypes.length > 0 - ) { - // Combine all document types and live search connectors - const allSourceTypes = [ - ...documentTypes.map((dt) => dt.type), - ...liveSearchConnectors.map((c) => c.connector_type), - ]; - - if (allSourceTypes.length > 0) { - setSelectedConnectors(allSourceTypes); - hasSetInitialConnectors.current = true; - } - } - }, [ - isNewChat, - documentTypes, - liveSearchConnectors, - selectedConnectors.length, - setSelectedConnectors, - ]); - - // Auto-update chat when messages change (only for existing chats) - useEffect(() => { - if ( - !isNewChat && - activeChatId && - handler.status === "ready" && - handler.messages.length > 0 && - handler.messages[handler.messages.length - 1]?.role === "assistant" - ) { - const userMessages = handler.messages.filter((msg) => msg.role === "user"); - if (userMessages.length === 0) return; - const title = userMessages[0].content; - - updateChat({ - type: researchMode, - title: title, - initial_connectors: selectedConnectors, - messages: handler.messages, - search_space_id: Number(search_space_id), - id: Number(activeChatId), - }); - } - }, [handler.messages, handler.status, activeChatId, isNewChat, isChatLoading]); - - if (isChatLoading) { - return ( -
-
Loading...
-
- ); - } - - return ( - - ); -} diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index dbf5b7155..f133d0c9c 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -244,7 +244,7 @@ const DashboardPage = () => { />
- + {space.name} {
diff --git a/surfsense_web/atoms/chats/chat-mutation.atoms.ts b/surfsense_web/atoms/chats/chat-mutation.atoms.ts deleted file mode 100644 index c761a706f..000000000 --- a/surfsense_web/atoms/chats/chat-mutation.atoms.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { atomWithMutation } from "jotai-tanstack-query"; -import { toast } from "sonner"; -import type { - ChatSummary, - CreateChatRequest, - DeleteChatRequest, - UpdateChatRequest, -} from "@/contracts/types/chat.types"; -import { chatsApiService } from "@/lib/apis/chats-api.service"; -import { getBearerToken } from "@/lib/auth-utils"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { queryClient } from "@/lib/query-client/client"; -import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; -import { globalChatsQueryParamsAtom } from "./ui.atoms"; - -export const deleteChatMutationAtom = atomWithMutation((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const chatsQueryParams = get(globalChatsQueryParamsAtom); - - return { - mutationKey: cacheKeys.chats.globalQueryParams(chatsQueryParams), - enabled: !!searchSpaceId && !!authToken, - mutationFn: async (request: DeleteChatRequest) => { - return chatsApiService.deleteChat(request); - }, - - onSuccess: (_, request: DeleteChatRequest) => { - toast.success("Chat deleted successfully"); - // Optimistically update the current query - queryClient.setQueryData( - cacheKeys.chats.globalQueryParams(chatsQueryParams), - (oldData: ChatSummary[]) => { - return oldData?.filter((chat) => chat.id !== request.id) ?? []; - } - ); - // Invalidate all chat queries to ensure consistency across components - queryClient.invalidateQueries({ - queryKey: ["chats"], - }); - // Also invalidate the "all-chats" query used by AllChatsSidebar - queryClient.invalidateQueries({ - queryKey: ["all-chats"], - }); - }, - }; -}); - -export const createChatMutationAtom = atomWithMutation((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const chatsQueryParams = get(globalChatsQueryParamsAtom); - - return { - mutationKey: cacheKeys.chats.globalQueryParams(chatsQueryParams), - enabled: !!searchSpaceId && !!authToken, - mutationFn: async (request: CreateChatRequest) => { - return chatsApiService.createChat(request); - }, - - onSuccess: () => { - // Invalidate ALL chat queries to ensure sidebar and other components refresh - // Using a partial key match to avoid stale closure issues with specific query params - queryClient.invalidateQueries({ - queryKey: ["chats"], - }); - // Also invalidate the "all-chats" query used by AllChatsSidebar - queryClient.invalidateQueries({ - queryKey: ["all-chats"], - }); - }, - }; -}); - -export const updateChatMutationAtom = atomWithMutation((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const chatsQueryParams = get(globalChatsQueryParamsAtom); - - return { - mutationKey: cacheKeys.chats.globalQueryParams(chatsQueryParams), - enabled: !!searchSpaceId && !!authToken, - mutationFn: async (request: UpdateChatRequest) => { - return chatsApiService.updateChat(request); - }, - - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: cacheKeys.chats.globalQueryParams(chatsQueryParams), - }); - }, - }; -}); diff --git a/surfsense_web/atoms/chats/chat-query.atoms.ts b/surfsense_web/atoms/chats/chat-query.atoms.ts deleted file mode 100644 index 5a1242ded..000000000 --- a/surfsense_web/atoms/chats/chat-query.atoms.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; -import { chatsApiService } from "@/lib/apis/chats-api.service"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; -import { getBearerToken } from "@/lib/auth-utils"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { activeChatIdAtom, globalChatsQueryParamsAtom } from "./ui.atoms"; - -export const activeChatAtom = atomWithQuery((get) => { - const activeChatId = get(activeChatIdAtom); - const authToken = getBearerToken(); - - return { - queryKey: cacheKeys.chats.activeChat(activeChatId ?? ""), - enabled: !!activeChatId && !!authToken, - queryFn: async () => { - if (!authToken) { - throw new Error("No authentication token found"); - } - if (!activeChatId) { - throw new Error("No active chat id found"); - } - - const [podcast, chatDetails] = await Promise.all([ - podcastsApiService.getPodcastByChatId({ chat_id: Number(activeChatId) }), - chatsApiService.getChatDetails({ id: Number(activeChatId) }), - ]); - - return { chatId: activeChatId, chatDetails, podcast }; - }, - }; -}); - -export const chatsAtom = atomWithQuery((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const queryParams = get(globalChatsQueryParamsAtom); - - return { - queryKey: cacheKeys.chats.globalQueryParams(queryParams), - enabled: !!searchSpaceId && !!authToken, - queryFn: async () => { - return chatsApiService.getChats({ - queryParams: queryParams, - }); - }, - }; -}); diff --git a/surfsense_web/atoms/chats/ui.atoms.ts b/surfsense_web/atoms/chats/ui.atoms.ts deleted file mode 100644 index c92365aef..000000000 --- a/surfsense_web/atoms/chats/ui.atoms.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { atom } from "jotai"; -import type { GetChatsRequest } from "@/contracts/types/chat.types"; - -type ActiveChathatUIState = { - isChatPannelOpen: boolean; -}; - -export const activeChathatUIAtom = atom({ - isChatPannelOpen: false, -}); - -export const activeChatIdAtom = atom(null); - -export const globalChatsQueryParamsAtom = atom({ - limit: 5, - skip: 0, -}); diff --git a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts b/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts deleted file mode 100644 index cdb28ceb2..000000000 --- a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { atomWithMutation } from "jotai-tanstack-query"; -import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; -import type { - DeletePodcastRequest, - GeneratePodcastRequest, - Podcast, -} from "@/contracts/types/podcast.types"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; -import { getBearerToken } from "@/lib/auth-utils"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { queryClient } from "@/lib/query-client/client"; -import { globalPodcastsQueryParamsAtom } from "./ui.atoms"; - -export const deletePodcastMutationAtom = atomWithMutation((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const podcastsQueryParams = get(globalPodcastsQueryParamsAtom); - - return { - mutationKey: cacheKeys.podcasts.globalQueryParams(podcastsQueryParams), - enabled: !!searchSpaceId && !!authToken, - mutationFn: async (request: DeletePodcastRequest) => { - return podcastsApiService.deletePodcast(request); - }, - - onSuccess: (_, request: DeletePodcastRequest) => { - toast.success("Podcast deleted successfully"); - queryClient.setQueryData( - cacheKeys.podcasts.globalQueryParams(podcastsQueryParams), - (oldData: Podcast[]) => { - return oldData.filter((podcast) => podcast.id !== request.id); - } - ); - }, - }; -}); - -export const generatePodcastMutationAtom = atomWithMutation((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - const authToken = getBearerToken(); - const podcastsQueryParams = get(globalPodcastsQueryParamsAtom); - - return { - mutationKey: cacheKeys.podcasts.globalQueryParams(podcastsQueryParams), - enabled: !!searchSpaceId && !!authToken, - mutationFn: async (request: GeneratePodcastRequest) => { - return podcastsApiService.generatePodcast(request); - }, - }; -}); diff --git a/surfsense_web/atoms/podcasts/podcast-query.atoms.ts b/surfsense_web/atoms/podcasts/podcast-query.atoms.ts deleted file mode 100644 index ea5c1c104..000000000 --- a/surfsense_web/atoms/podcasts/podcast-query.atoms.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { atomWithQuery } from "jotai-tanstack-query"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { globalPodcastsQueryParamsAtom } from "./ui.atoms"; - -export const podcastsAtom = atomWithQuery((get) => { - const queryParams = get(globalPodcastsQueryParamsAtom); - - return { - queryKey: cacheKeys.podcasts.globalQueryParams(queryParams), - queryFn: async () => { - return podcastsApiService.getPodcasts({ - queryParams: queryParams, - }); - }, - }; -}); diff --git a/surfsense_web/atoms/podcasts/ui.atoms.ts b/surfsense_web/atoms/podcasts/ui.atoms.ts deleted file mode 100644 index 0f2701375..000000000 --- a/surfsense_web/atoms/podcasts/ui.atoms.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { atom } from "jotai"; -import type { GetPodcastsRequest } from "@/contracts/types/podcast.types"; - -export const globalPodcastsQueryParamsAtom = atom({ - limit: 5, - skip: 0, -}); diff --git a/surfsense_web/components/assistant-ui/attachment.tsx b/surfsense_web/components/assistant-ui/attachment.tsx index a51c13cb0..dfb63cbf3 100644 --- a/surfsense_web/components/assistant-ui/attachment.tsx +++ b/surfsense_web/components/assistant-ui/attachment.tsx @@ -7,7 +7,7 @@ import { useAssistantApi, useAssistantState, } from "@assistant-ui/react"; -import { FileText, PlusIcon, XIcon } from "lucide-react"; +import { FileText, Loader2, PlusIcon, XIcon } from "lucide-react"; import Image from "next/image"; import { type FC, type PropsWithChildren, useEffect, useState } from "react"; import { useShallow } from "zustand/shallow"; @@ -40,11 +40,15 @@ const useFileSrc = (file: File | undefined) => { const useAttachmentSrc = () => { const { file, src } = useAssistantState( useShallow(({ attachment }): { file?: File; src?: string } => { - if (attachment.type !== "image") return {}; + if (!attachment || attachment.type !== "image") return {}; if (attachment.file) return { file: attachment.file }; - const src = attachment.content?.filter((c) => c.type === "image")[0]?.image; - if (!src) return {}; - return { src }; + // Only try to filter if content is an array (standard assistant-ui format) + // Our custom ChatAttachment has content as a string, so skip this + if (Array.isArray(attachment.content)) { + const src = attachment.content.filter((c) => c.type === "image")[0]?.image; + if (src) return { src }; + } + return {}; }) ); @@ -98,9 +102,27 @@ const AttachmentPreviewDialog: FC = ({ children }) => { }; const AttachmentThumb: FC = () => { - const isImage = useAssistantState(({ attachment }) => attachment.type === "image"); + const isImage = useAssistantState(({ attachment }) => attachment?.type === "image"); + // Check if actively processing (running AND progress < 100) + // When progress is 100, processing is done but waiting for send() + const isProcessing = useAssistantState(({ attachment }) => { + const status = attachment?.status; + if (status?.type !== "running") return false; + // If progress is defined and equals 100, processing is complete + const progress = (status as { type: "running"; progress?: number }).progress; + return progress === undefined || progress < 100; + }); const src = useAttachmentSrc(); + // Show loading spinner only when actively processing (not when done and waiting for send) + if (isProcessing) { + return ( +
+ +
+ ); + } + return ( { const api = useAssistantApi(); const isComposer = api.attachment.source === "composer"; - const isImage = useAssistantState(({ attachment }) => attachment.type === "image"); + const isImage = useAssistantState(({ attachment }) => attachment?.type === "image"); + // Check if actively processing (running AND progress < 100) + // When progress is 100, processing is done but waiting for send() + const isProcessing = useAssistantState(({ attachment }) => { + const status = attachment?.status; + if (status?.type !== "running") return false; + const progress = (status as { type: "running"; progress?: number }).progress; + return progress === undefined || progress < 100; + }); const typeLabel = useAssistantState(({ attachment }) => { - const type = attachment.type; + const type = attachment?.type; switch (type) { case "image": return "Image"; @@ -129,10 +159,8 @@ const AttachmentUI: FC = () => { return "Document"; case "file": return "File"; - default: { - const _exhaustiveCheck: never = type; - throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`); - } + default: + return "File"; // Default fallback for unknown types } }); @@ -149,20 +177,28 @@ const AttachmentUI: FC = () => {
- {isComposer && } + {isComposer && !isProcessing && } - + {isProcessing ? ( + + + Processing... + + ) : ( + + )} ); diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index c2dab7b3c..a41d2e143 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -6,6 +6,7 @@ import { ErrorPrimitive, MessagePrimitive, ThreadPrimitive, + useAssistantState, } from "@assistant-ui/react"; import { ArrowDownIcon, @@ -15,6 +16,7 @@ import { ChevronRightIcon, CopyIcon, DownloadIcon, + Loader2, PencilIcon, RefreshCwIcon, SquareIcon, @@ -157,20 +159,43 @@ const Composer: FC = () => { }; const ComposerAction: FC = () => { + // Check if any attachments are still being processed (running AND progress < 100) + // When progress is 100, processing is done but waiting for send() + const hasProcessingAttachments = useAssistantState(({ composer }) => + composer.attachments?.some((att) => { + const status = att.status; + if (status?.type !== "running") return false; + const progress = (status as { type: "running"; progress?: number }).progress; + return progress === undefined || progress < 100; + }) + ); + return (
+ {/* Show processing indicator when attachments are being processed */} + {hasProcessingAttachments && ( +
+ + Processing... +
+ )} + !thread.isRunning}> - + diff --git a/surfsense_web/components/chat/AnimatedEmptyState.tsx b/surfsense_web/components/chat/AnimatedEmptyState.tsx deleted file mode 100644 index d04708aa5..000000000 --- a/surfsense_web/components/chat/AnimatedEmptyState.tsx +++ /dev/null @@ -1,151 +0,0 @@ -"use client"; - -import { useInView } from "motion/react"; -import { Manrope } from "next/font/google"; -import { useEffect, useMemo, useReducer, useRef } from "react"; -import { RoughNotation, RoughNotationGroup } from "react-rough-notation"; -import { useSidebar } from "@/components/ui/sidebar"; -import { cn } from "@/lib/utils"; - -// Font configuration - could be moved to a global font config file -const manrope = Manrope({ - subsets: ["latin"], - weight: ["400", "700"], - display: "swap", // Optimize font loading - variable: "--font-manrope", -}); - -// Constants for timing - makes it easier to adjust and more maintainable -const TIMING = { - SIDEBAR_TRANSITION: 300, // Wait for sidebar transition + buffer - LAYOUT_SETTLE: 100, // Small delay to ensure layout is fully settled -} as const; - -// Animation configuration -const ANIMATION_CONFIG = { - HIGHLIGHT: { - type: "highlight" as const, - animationDuration: 2000, - iterations: 3, - color: "#3b82f680", - multiline: true, - }, - UNDERLINE: { - type: "underline" as const, - animationDuration: 2000, - iterations: 3, - color: "#10b981", - }, -} as const; - -// State management with useReducer for better organization -interface HighlightState { - shouldShowHighlight: boolean; - layoutStable: boolean; -} - -type HighlightAction = - | { type: "SIDEBAR_CHANGED" } - | { type: "LAYOUT_STABILIZED" } - | { type: "SHOW_HIGHLIGHT" } - | { type: "HIDE_HIGHLIGHT" }; - -const highlightReducer = (state: HighlightState, action: HighlightAction): HighlightState => { - switch (action.type) { - case "SIDEBAR_CHANGED": - return { - shouldShowHighlight: false, - layoutStable: false, - }; - case "LAYOUT_STABILIZED": - return { - ...state, - layoutStable: true, - }; - case "SHOW_HIGHLIGHT": - return { - ...state, - shouldShowHighlight: true, - }; - case "HIDE_HIGHLIGHT": - return { - ...state, - shouldShowHighlight: false, - }; - default: - return state; - } -}; - -const initialState: HighlightState = { - shouldShowHighlight: false, - layoutStable: true, -}; - -export function AnimatedEmptyState() { - const ref = useRef(null); - const isInView = useInView(ref); - const [{ shouldShowHighlight, layoutStable }, dispatch] = useReducer( - highlightReducer, - initialState - ); - - // Memoize class names to prevent unnecessary recalculations - const headingClassName = useMemo( - () => - cn( - "text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight text-neutral-900 dark:text-neutral-50 mb-6", - manrope.className - ), - [] - ); - - const paragraphClassName = useMemo( - () => "text-lg sm:text-xl text-neutral-600 dark:text-neutral-300 mb-8 max-w-2xl mx-auto", - [] - ); - - // Handle sidebar state changes - useEffect(() => { - dispatch({ type: "SIDEBAR_CHANGED" }); - - const stabilizeTimer = setTimeout(() => { - dispatch({ type: "LAYOUT_STABILIZED" }); - }, TIMING.SIDEBAR_TRANSITION); - - return () => clearTimeout(stabilizeTimer); - }, []); - - // Handle highlight visibility based on layout stability and viewport visibility - useEffect(() => { - if (!layoutStable || !isInView) { - dispatch({ type: "HIDE_HIGHLIGHT" }); - return; - } - - const showTimer = setTimeout(() => { - dispatch({ type: "SHOW_HIGHLIGHT" }); - }, TIMING.LAYOUT_SETTLE); - - return () => clearTimeout(showTimer); - }, [layoutStable, isInView]); - - return ( -
-
- -

- - SurfSense - -

- -

- Let's Start Surfing{" "} - through your knowledge base. -

-
-
-
- ); -} diff --git a/surfsense_web/components/chat/ChatCitation.tsx b/surfsense_web/components/chat/ChatCitation.tsx deleted file mode 100644 index d8c681781..000000000 --- a/surfsense_web/components/chat/ChatCitation.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; - -import type React from "react"; -import { useState } from "react"; -import { SheetTrigger } from "@/components/ui/sheet"; -import { SourceDetailSheet } from "./SourceDetailSheet"; - -export const CitationDisplay: React.FC<{ index: number; node: any }> = ({ index, node }) => { - const chunkId = Number(node?.id); - const sourceType = node?.metadata?.source_type; - const [isOpen, setIsOpen] = useState(false); - - return ( - - - - {index + 1} - - - - ); -}; diff --git a/surfsense_web/components/chat/ChatFurtherQuestions.tsx b/surfsense_web/components/chat/ChatFurtherQuestions.tsx deleted file mode 100644 index fbdc9edab..000000000 --- a/surfsense_web/components/chat/ChatFurtherQuestions.tsx +++ /dev/null @@ -1,36 +0,0 @@ -"use client"; - -import { getAnnotationData, type Message, useChatUI } from "@llamaindex/chat-ui"; -import { SuggestedQuestions } from "@llamaindex/chat-ui/widgets"; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from "@/components/ui/accordion"; - -export const ChatFurtherQuestions: React.FC<{ message: Message }> = ({ message }) => { - const annotations: string[][] = getAnnotationData(message, "FURTHER_QUESTIONS"); - const { append, requestData } = useChatUI(); - - if (annotations.length !== 1 || annotations[0].length === 0) { - return null; - } - - return ( - - - - Further Suggested Questions - - - - - - - ); -}; diff --git a/surfsense_web/components/chat/ChatInputGroup.tsx b/surfsense_web/components/chat/ChatInputGroup.tsx deleted file mode 100644 index 080bd65d4..000000000 --- a/surfsense_web/components/chat/ChatInputGroup.tsx +++ /dev/null @@ -1,851 +0,0 @@ -"use client"; - -import { ChatInput } from "@llamaindex/chat-ui"; -import { useAtom, useAtomValue } from "jotai"; -import { Brain, Check, FolderOpen, Minus, Plus, PlusCircle, Zap } from "lucide-react"; -import { useParams, useRouter } from "next/navigation"; -import React, { Suspense, useCallback, useMemo, useState } from "react"; -import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; -import { updateLLMPreferencesMutationAtom } from "@/atoms/llm-config/llm-config-mutation.atoms"; -import { - globalLLMConfigsAtom, - llmConfigsAtom, - llmPreferencesAtom, -} from "@/atoms/llm-config/llm-config-query.atoms"; -import { DocumentsDataTable } from "@/components/chat/DocumentsDataTable"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; -import type { Document } from "@/contracts/types/document.types"; -import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors"; - -const DocumentSelector = React.memo( - ({ - onSelectionChange, - selectedDocuments = [], - }: { - onSelectionChange?: (documents: Document[]) => void; - selectedDocuments?: Document[]; - }) => { - const { search_space_id } = useParams(); - const [isOpen, setIsOpen] = useState(false); - - const handleOpenChange = useCallback((open: boolean) => { - setIsOpen(open); - }, []); - - const handleSelectionChange = useCallback( - (documents: Document[]) => { - onSelectionChange?.(documents); - }, - [onSelectionChange] - ); - - const handleDone = useCallback(() => { - setIsOpen(false); - }, []); - - const selectedCount = React.useMemo(() => selectedDocuments.length, [selectedDocuments.length]); - - return ( - - - - - - -
-
- - Select Documents - - - Choose specific documents to include in your research context - -
- -
- -
-
-
-
- ); - } -); - -DocumentSelector.displayName = "DocumentSelector"; - -const ConnectorSelector = React.memo( - ({ - onSelectionChange, - selectedConnectors = [], - }: { - onSelectionChange?: (connectorTypes: string[]) => void; - selectedConnectors?: string[]; - }) => { - const { search_space_id } = useParams(); - const router = useRouter(); - const [isOpen, setIsOpen] = useState(false); - - // Use the documentTypeCountsAtom for fetching document types - const [documentTypeCountsQuery] = useAtom(documentTypeCountsAtom); - const { - data: documentTypeCountsData, - isLoading, - refetch: fetchDocumentTypes, - } = documentTypeCountsQuery; - - // Transform the response into the expected format - const documentTypes = useMemo(() => { - if (!documentTypeCountsData) return []; - return Object.entries(documentTypeCountsData).map(([type, count]) => ({ - type, - count, - })); - }, [documentTypeCountsData]); - - const isLoaded = !!documentTypeCountsData; - - // Fetch live search connectors immediately (non-indexable) - const { - connectors: searchConnectors, - isLoading: connectorsLoading, - isLoaded: connectorsLoaded, - fetchConnectors, - } = useSearchSourceConnectors(false, Number(search_space_id)); - - // Filter for non-indexable connectors (live search) - const liveSearchConnectors = React.useMemo( - () => searchConnectors.filter((connector) => !connector.is_indexable), - [searchConnectors] - ); - - const handleOpenChange = useCallback((open: boolean) => { - setIsOpen(open); - // Data is already loaded on mount, no need to fetch again - }, []); - - const handleConnectorToggle = useCallback( - (connectorType: string) => { - const isSelected = selectedConnectors.includes(connectorType); - const newSelection = isSelected - ? selectedConnectors.filter((type) => type !== connectorType) - : [...selectedConnectors, connectorType]; - onSelectionChange?.(newSelection); - }, - [selectedConnectors, onSelectionChange] - ); - - const handleSelectAll = useCallback(() => { - const allTypes = [ - ...documentTypes.map((dt) => dt.type), - ...liveSearchConnectors.map((c) => c.connector_type), - ]; - onSelectionChange?.(allTypes); - }, [documentTypes, liveSearchConnectors, onSelectionChange]); - - const handleClearAll = useCallback(() => { - onSelectionChange?.([]); - }, [onSelectionChange]); - - // Get display name for connector type - const getDisplayName = (type: string) => { - return type - .split("_") - .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) - .join(" "); - }; - - // Get selected document types with their counts - const selectedDocTypes = documentTypes.filter((dt) => selectedConnectors.includes(dt.type)); - const selectedLiveConnectors = liveSearchConnectors.filter((c) => - selectedConnectors.includes(c.connector_type) - ); - - // Total selected count - const totalSelectedCount = selectedDocTypes.length + selectedLiveConnectors.length; - const totalAvailableCount = documentTypes.length + liveSearchConnectors.length; - - return ( - - - - - - -
-
- Select Sources - - Choose indexed document types and live search connectors to include in your search - -
- - {isLoading || connectorsLoading ? ( -
-
-
- ) : totalAvailableCount === 0 ? ( -
-
- -
-

No sources found

-

- Add documents or configure search connectors for this search space -

- -
- ) : ( - <> - {/* Live Search Connectors Section */} - {liveSearchConnectors.length > 0 && ( -
-
- -

Live Search Connectors

- - Real-time - -
-
- {liveSearchConnectors.map((connector) => { - const isSelected = selectedConnectors.includes(connector.connector_type); - - return ( - - ); - })} -
-
- )} - - {/* Document Types Section */} - {documentTypes.length > 0 && ( -
-
- -

Indexed Document Types

- - Stored - -
-
- {documentTypes.map((docType) => { - const isSelected = selectedConnectors.includes(docType.type); - - return ( - - ); - })} -
-
- )} - - )} -
- - {totalAvailableCount > 0 && ( - - - - - )} - -
- ); - } -); - -ConnectorSelector.displayName = "ConnectorSelector"; - -const TopKSelector = React.memo( - ({ topK = 10, onTopKChange }: { topK?: number; onTopKChange?: (topK: number) => void }) => { - const MIN_VALUE = 1; - const MAX_VALUE = 100; - - const handleIncrement = React.useCallback(() => { - if (topK < MAX_VALUE) { - onTopKChange?.(topK + 1); - } - }, [topK, onTopKChange]); - - const handleDecrement = React.useCallback(() => { - if (topK > MIN_VALUE) { - onTopKChange?.(topK - 1); - } - }, [topK, onTopKChange]); - - const handleInputChange = React.useCallback( - (e: React.ChangeEvent) => { - const value = e.target.value; - // Allow empty input for editing - if (value === "") { - return; - } - const numValue = parseInt(value, 10); - if (!isNaN(numValue) && numValue >= MIN_VALUE && numValue <= MAX_VALUE) { - onTopKChange?.(numValue); - } - }, - [onTopKChange] - ); - - const handleInputBlur = React.useCallback( - (e: React.FocusEvent) => { - const value = e.target.value; - if (value === "") { - // Reset to default if empty - onTopKChange?.(10); - return; - } - const numValue = parseInt(value, 10); - if (isNaN(numValue) || numValue < MIN_VALUE) { - onTopKChange?.(MIN_VALUE); - } else if (numValue > MAX_VALUE) { - onTopKChange?.(MAX_VALUE); - } - }, - [onTopKChange] - ); - - return ( - - - -
- -
- - Results -
- -
-
- -
-

Results per Source

-

- Control how many results to fetch from each data source. Set a higher number to get - more information, or a lower number for faster, more focused results. -

-
- Recommended: 5-20 - - - Range: {MIN_VALUE}-{MAX_VALUE} - -
-
-
-
-
- ); - } -); - -TopKSelector.displayName = "TopKSelector"; - -const LLMSelector = React.memo(() => { - const { search_space_id } = useParams(); - const searchSpaceId = Number(search_space_id); - - const { - data: llmConfigs = [], - isFetching: llmLoading, - isError: error, - } = useAtomValue(llmConfigsAtom); - const { - data: globalConfigs = [], - isFetching: globalConfigsLoading, - isError: globalConfigsError, - } = useAtomValue(globalLLMConfigsAtom); - - // Replace useLLMPreferences with jotai atoms - const { data: preferences = {}, isFetching: preferencesLoading } = - useAtomValue(llmPreferencesAtom); - const { mutateAsync: updatePreferences } = useAtomValue(updateLLMPreferencesMutationAtom); - - const isLoading = llmLoading || preferencesLoading || globalConfigsLoading; - - // Combine global and custom configs - const allConfigs = React.useMemo(() => { - return [...globalConfigs.map((config) => ({ ...config, is_global: true })), ...llmConfigs]; - }, [globalConfigs, llmConfigs]); - - // Memoize the selected config to avoid repeated lookups - const selectedConfig = React.useMemo(() => { - if (!preferences.fast_llm_id || !allConfigs.length) return null; - return allConfigs.find((config) => config.id === preferences.fast_llm_id) || null; - }, [preferences.fast_llm_id, allConfigs]); - - // Memoize the display value for the trigger - const displayValue = React.useMemo(() => { - if (!selectedConfig) return null; - return ( -
- {selectedConfig.provider} - - - {selectedConfig.name} - - {"is_global" in selectedConfig && selectedConfig.is_global && ( - 🌐 - )} -
- ); - }, [selectedConfig]); - - const handleValueChange = React.useCallback( - (value: string) => { - const llmId = value ? parseInt(value, 10) : undefined; - updatePreferences({ - search_space_id: searchSpaceId, - data: { fast_llm_id: llmId }, - }); - }, - [updatePreferences, searchSpaceId] - ); - - // Loading skeleton - if (isLoading) { - return ( -
-
-
-
-
-
- ); - } - - // Error state - if (error || globalConfigsError) { - return ( -
- -
- ); - } - - return ( -
- -
- ); -}); - -LLMSelector.displayName = "LLMSelector"; - -const CustomChatInputOptions = React.memo( - ({ - onDocumentSelectionChange, - selectedDocuments, - onConnectorSelectionChange, - selectedConnectors, - topK, - onTopKChange, - }: { - onDocumentSelectionChange?: (documents: Document[]) => void; - selectedDocuments?: Document[]; - onConnectorSelectionChange?: (connectorTypes: string[]) => void; - selectedConnectors?: string[]; - topK?: number; - onTopKChange?: (topK: number) => void; - }) => { - // Memoize the loading fallback to prevent recreation - const loadingFallback = React.useMemo( - () =>
, - [] - ); - - return ( -
-
- - - - - - -
-
- -
- -
- ); - } -); - -CustomChatInputOptions.displayName = "CustomChatInputOptions"; - -export const ChatInputUI = React.memo( - ({ - onDocumentSelectionChange, - selectedDocuments, - onConnectorSelectionChange, - selectedConnectors, - topK, - onTopKChange, - }: { - onDocumentSelectionChange?: (documents: Document[]) => void; - selectedDocuments?: Document[]; - onConnectorSelectionChange?: (connectorTypes: string[]) => void; - selectedConnectors?: string[]; - topK?: number; - onTopKChange?: (topK: number) => void; - }) => { - return ( - - - - - - - - ); - } -); - -ChatInputUI.displayName = "ChatInputUI"; diff --git a/surfsense_web/components/chat/ChatInterface.tsx b/surfsense_web/components/chat/ChatInterface.tsx deleted file mode 100644 index 64852fa15..000000000 --- a/surfsense_web/components/chat/ChatInterface.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client"; - -import { type ChatHandler, ChatSection as LlamaIndexChatSection } from "@llamaindex/chat-ui"; -import { useParams } from "next/navigation"; -import { ChatInputUI } from "@/components/chat/ChatInputGroup"; -import { ChatMessagesUI } from "@/components/chat/ChatMessages"; -import type { Document } from "@/contracts/types/document.types"; - -interface ChatInterfaceProps { - handler: ChatHandler; - onDocumentSelectionChange?: (documents: Document[]) => void; - selectedDocuments?: Document[]; - onConnectorSelectionChange?: (connectorTypes: string[]) => void; - selectedConnectors?: string[]; - topK?: number; - onTopKChange?: (topK: number) => void; -} - -export default function ChatInterface({ - handler, - onDocumentSelectionChange, - selectedDocuments = [], - onConnectorSelectionChange, - selectedConnectors = [], - topK = 10, - onTopKChange, -}: ChatInterfaceProps) { - const { chat_id, search_space_id } = useParams(); - - return ( - -
- -
- -
-
-
- ); -} diff --git a/surfsense_web/components/chat/ChatMessages.tsx b/surfsense_web/components/chat/ChatMessages.tsx deleted file mode 100644 index 77c4b3e06..000000000 --- a/surfsense_web/components/chat/ChatMessages.tsx +++ /dev/null @@ -1,73 +0,0 @@ -"use client"; - -import { - ChatMessage as LlamaIndexChatMessage, - ChatMessages as LlamaIndexChatMessages, - type Message, - useChatUI, -} from "@llamaindex/chat-ui"; -import { useEffect, useRef } from "react"; -import { AnimatedEmptyState } from "@/components/chat/AnimatedEmptyState"; -import { CitationDisplay } from "@/components/chat/ChatCitation"; -import { ChatFurtherQuestions } from "@/components/chat/ChatFurtherQuestions"; -import ChatSourcesDisplay from "@/components/chat/ChatSources"; -import TerminalDisplay from "@/components/chat/ChatTerminal"; -import { languageRenderers } from "@/components/chat/CodeBlock"; - -export function ChatMessagesUI() { - const { messages } = useChatUI(); - - return ( - - - - - - {messages.map((message, index) => ( - - ))} - - - - ); -} - -function ChatMessageUI({ message, isLast }: { message: Message; isLast: boolean }) { - const bottomRef = useRef(null); - - useEffect(() => { - if (isLast && bottomRef.current) { - bottomRef.current.scrollIntoView({ behavior: "smooth" }); - } - }, [isLast]); - - return ( - - {message.role === "assistant" ? ( -
- - - - - -
-
- {isLast && } - -
-
- ) : ( - - - - )} - - ); -} diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx deleted file mode 100644 index cb0fcb33e..000000000 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; -import { useAtomValue } from "jotai"; -import { LoaderIcon, TriangleAlert } from "lucide-react"; -import { toast } from "sonner"; -import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; -import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms"; -import { generatePodcastMutationAtom } from "@/atoms/podcasts/podcast-mutation.atoms"; -import type { GeneratePodcastRequest } from "@/contracts/types/podcast.types"; -import { cn } from "@/lib/utils"; -import { ChatPanelView } from "./ChatPanelView"; - -export function ChatPanelContainer() { - const { - data: activeChatState, - isLoading: isChatLoading, - error: chatError, - } = useAtomValue(activeChatAtom); - const activeChatIdState = useAtomValue(activeChatIdAtom); - const { isChatPannelOpen } = useAtomValue(activeChathatUIAtom); - const { mutateAsync: generatePodcast, error: generatePodcastError } = useAtomValue( - generatePodcastMutationAtom - ); - - const handleGeneratePodcast = async (request: GeneratePodcastRequest) => { - try { - generatePodcast(request); - toast.success(`Podcast generation started!`); - } catch (error) { - toast.error("Error generating podcast. Please try again later."); - console.error("Error generating podcast:", JSON.stringify(generatePodcastError)); - } - }; - - return activeChatIdState ? ( -
- {isChatLoading || chatError ? ( -
- {isChatLoading ? ( -
- -
- ) : chatError ? ( -
- -
- ) : null} -
- ) : null} - - {!isChatLoading && !chatError && activeChatState?.chatDetails && ( - - )} -
- ) : null; -} diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx deleted file mode 100644 index 5eb428946..000000000 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx +++ /dev/null @@ -1,207 +0,0 @@ -"use client"; - -import { useAtom, useAtomValue } from "jotai"; -import { AlertCircle, Play, RefreshCw, Sparkles } from "lucide-react"; -import { motion } from "motion/react"; -import { useCallback } from "react"; -import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; -import { activeChathatUIAtom } from "@/atoms/chats/ui.atoms"; -import { cn } from "@/lib/utils"; -import { getPodcastStalenessMessage, isPodcastStale } from "../PodcastUtils"; -import type { GeneratePodcastRequest } from "./ChatPanelContainer"; -import { ConfigModal } from "./ConfigModal"; -import { PodcastPlayer } from "./PodcastPlayer"; - -interface ChatPanelViewProps { - generatePodcast: (request: GeneratePodcastRequest) => Promise; -} - -export function ChatPanelView(props: ChatPanelViewProps) { - const [chatUIState, setChatUIState] = useAtom(activeChathatUIAtom); - const { data: activeChatState } = useAtomValue(activeChatAtom); - - const { isChatPannelOpen } = chatUIState; - const podcast = activeChatState?.podcast; - const chatDetails = activeChatState?.chatDetails; - - const { generatePodcast } = props; - - // Check if podcast is stale - const podcastIsStale = - podcast && chatDetails && isPodcastStale(chatDetails.state_version, podcast.chat_state_version); - - const handleGeneratePost = useCallback(async () => { - if (!chatDetails) return; - await generatePodcast({ - type: "CHAT", - ids: [chatDetails.id], - search_space_id: chatDetails.search_space_id, - podcast_title: chatDetails.title, - }); - }, [chatDetails, generatePodcast]); - - // biome-ignore-start lint/a11y/useSemanticElements: using div for custom layout — will convert later - return ( -
-
- {isChatPannelOpen ? ( -
- {/* Show stale podcast warning if applicable */} - {podcastIsStale && ( - -
- - - -
-

Podcast Outdated

-

- {getPodcastStalenessMessage( - chatDetails?.state_version || 0, - podcast?.chat_state_version - )} -

-
-
-
- )} - - - - {/* ConfigModal positioned absolutely to avoid nesting buttons */} -
- -
-
-
- ) : ( - - setChatUIState((prev) => ({ - ...prev, - isChatPannelOpen: !isChatPannelOpen, - })) - } - whileHover={{ scale: 1.1 }} - whileTap={{ scale: 0.9 }} - className={cn( - "p-2.5 rounded-full transition-colors shadow-sm", - podcastIsStale - ? "bg-amber-500/20 hover:bg-amber-500/30 text-amber-600 dark:text-amber-400" - : "bg-primary/20 hover:bg-primary/30 text-primary" - )} - > - {podcastIsStale ? : } - - )} -
- {podcast ? ( -
- {isChatPannelOpen ? ( - - ) : podcast ? ( - setChatUIState((prev) => ({ ...prev, isChatPannelOpen: true }))} - whileHover={{ scale: 1.1 }} - whileTap={{ scale: 0.9 }} - className="p-2.5 rounded-full bg-green-500/20 hover:bg-green-500/30 text-green-600 dark:text-green-400 transition-colors shadow-sm" - > - - - ) : null} -
- ) : null} -
- ); - // biome-ignore-end lint/a11y/useSemanticElements : using div for custom layout — will convert later -} diff --git a/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx b/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx deleted file mode 100644 index 7c1497df9..000000000 --- a/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx +++ /dev/null @@ -1,84 +0,0 @@ -"use client"; - -import { useAtomValue } from "jotai"; -import { Pencil } from "lucide-react"; -import { useCallback, useContext, useState } from "react"; -import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import type { GeneratePodcastRequest } from "./ChatPanelContainer"; - -interface ConfigModalProps { - generatePodcast: (request: GeneratePodcastRequest) => Promise; -} - -export function ConfigModal(props: ConfigModalProps) { - const { data: activeChatState } = useAtomValue(activeChatAtom); - - const chatDetails = activeChatState?.chatDetails; - const podcast = activeChatState?.podcast; - - const { generatePodcast } = props; - - const [userPromt, setUserPrompt] = useState(""); - - const handleGeneratePost = useCallback(async () => { - if (!chatDetails) return; - await generatePodcast({ - type: "CHAT", - ids: [chatDetails.id], - search_space_id: chatDetails.search_space_id, - podcast_title: podcast?.title || chatDetails.title, - user_prompt: userPromt, - }); - }, [chatDetails, userPromt]); - - return ( - - e.stopPropagation()} - > - - - e.stopPropagation()} align="end" className="bg-sidebar w-96 "> -
- -

- Leave empty to use the default prompt -

-
-

Examples:

-
    -
  • Make hosts speak in London street language
  • -
  • Use real-world analogies and metaphors
  • -
  • Add dramatic pauses like a late-night radio show
  • -
  • Include 90s pop culture references
  • -
-
- - - - -
-
-
- ); -} diff --git a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayer.tsx b/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayer.tsx deleted file mode 100644 index 63bd22c37..000000000 --- a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayer.tsx +++ /dev/null @@ -1,329 +0,0 @@ -"use client"; - -import { Pause, Play, SkipBack, SkipForward, Volume2, VolumeX, X } from "lucide-react"; -import { motion } from "motion/react"; -import { useEffect, useRef, useState } from "react"; -import { toast } from "sonner"; -import { Button } from "@/components/ui/button"; -import { Slider } from "@/components/ui/slider"; -import type { Podcast } from "@/contracts/types/podcast.types"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; -import { PodcastPlayerCompactSkeleton } from "./PodcastPlayerCompactSkeleton"; - -interface PodcastPlayerProps { - podcast: Podcast | null; - isLoading?: boolean; - onClose?: () => void; - compact?: boolean; -} - -export function PodcastPlayer({ - podcast, - isLoading = false, - onClose, - compact = false, -}: PodcastPlayerProps) { - const [audioSrc, setAudioSrc] = useState(undefined); - const [isPlaying, setIsPlaying] = useState(false); - const [currentTime, setCurrentTime] = useState(0); - const [duration, setDuration] = useState(0); - const [volume, setVolume] = useState(0.7); - const [isMuted, setIsMuted] = useState(false); - const [isFetching, setIsFetching] = useState(false); - const audioRef = useRef(null); - const currentObjectUrlRef = useRef(null); - - // Cleanup object URL on unmount - useEffect(() => { - return () => { - if (currentObjectUrlRef.current) { - URL.revokeObjectURL(currentObjectUrlRef.current); - currentObjectUrlRef.current = null; - } - }; - }, []); - - // Load podcast audio when podcast changes - useEffect(() => { - if (!podcast) { - setAudioSrc(undefined); - setCurrentTime(0); - setDuration(0); - setIsPlaying(false); - setIsFetching(false); - return; - } - - const loadPodcast = async () => { - setIsFetching(true); - try { - // Revoke previous object URL if exists - if (currentObjectUrlRef.current) { - URL.revokeObjectURL(currentObjectUrlRef.current); - currentObjectUrlRef.current = null; - } - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 30000); - - try { - const response = await podcastsApiService.loadPodcast({ - request: { id: podcast.id }, - controller, - }); - - const objectUrl = URL.createObjectURL(response); - currentObjectUrlRef.current = objectUrl; - setAudioSrc(objectUrl); - } catch (error) { - if (error instanceof DOMException && error.name === "AbortError") { - throw new Error("Request timed out. Please try again."); - } - throw error; - } finally { - clearTimeout(timeoutId); - } - } catch (error) { - console.error("Error fetching podcast:", error); - toast.error(error instanceof Error ? error.message : "Failed to load podcast audio."); - setAudioSrc(undefined); - } finally { - setIsFetching(false); - } - }; - - loadPodcast(); - }, [podcast]); - - const handleTimeUpdate = () => { - if (audioRef.current) { - setCurrentTime(audioRef.current.currentTime); - } - }; - - const handleMetadataLoaded = () => { - if (audioRef.current) { - setDuration(audioRef.current.duration); - } - }; - - const togglePlayPause = () => { - if (audioRef.current) { - if (isPlaying) { - audioRef.current.pause(); - } else { - audioRef.current.play(); - } - setIsPlaying(!isPlaying); - } - }; - - const handleSeek = (value: number[]) => { - if (audioRef.current) { - audioRef.current.currentTime = value[0]; - setCurrentTime(value[0]); - } - }; - - const handleVolumeChange = (value: number[]) => { - if (audioRef.current) { - const newVolume = value[0]; - audioRef.current.volume = newVolume; - setVolume(newVolume); - - if (newVolume === 0) { - audioRef.current.muted = true; - setIsMuted(true); - } else { - audioRef.current.muted = false; - setIsMuted(false); - } - } - }; - - const toggleMute = () => { - if (audioRef.current) { - const newMutedState = !isMuted; - audioRef.current.muted = newMutedState; - setIsMuted(newMutedState); - - if (!newMutedState && volume === 0) { - const restoredVolume = 0.5; - audioRef.current.volume = restoredVolume; - setVolume(restoredVolume); - } - } - }; - - const skipForward = () => { - if (audioRef.current) { - audioRef.current.currentTime = Math.min( - audioRef.current.duration, - audioRef.current.currentTime + 10 - ); - } - }; - - const skipBackward = () => { - if (audioRef.current) { - audioRef.current.currentTime = Math.max(0, audioRef.current.currentTime - 10); - } - }; - - const formatTime = (time: number) => { - const minutes = Math.floor(time / 60); - const seconds = Math.floor(time % 60); - return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; - }; - - // Show skeleton while fetching - if (isFetching && compact) { - return ; - } - - if (!podcast || !audioSrc) { - return null; - } - - if (compact) { - return ( - <> -
- {/* Audio Visualizer */} - - {isPlaying && ( - - )} - - - {/* Progress Bar with Time */} -
- -
- {formatTime(currentTime)} - {formatTime(duration)} -
-
- - {/* Controls */} -
- {/* Left: Volume */} -
- - - -
- - {/* Center: Playback Controls */} -
- - - - - - - - - - - -
- - {/* Right: Placeholder for symmetry */} -
-
-
- - - - ); - } - - return null; -} diff --git a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayerCompactSkeleton.tsx b/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayerCompactSkeleton.tsx deleted file mode 100644 index d7007dadd..000000000 --- a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/PodcastPlayerCompactSkeleton.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client"; - -import { Podcast } from "lucide-react"; -import { motion } from "motion/react"; - -export function PodcastPlayerCompactSkeleton() { - return ( -
- {/* Header with icon and title */} -
- - - - {/* Title skeleton */} -
-
- - {/* Progress bar skeleton */} -
-
-
-
- - {/* Controls skeleton */} -
-
-
-
-
-
-
- ); -} diff --git a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/index.ts b/surfsense_web/components/chat/ChatPanel/PodcastPlayer/index.ts deleted file mode 100644 index 55c19f934..000000000 --- a/surfsense_web/components/chat/ChatPanel/PodcastPlayer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { PodcastPlayer } from "./PodcastPlayer"; -export { PodcastPlayerCompactSkeleton } from "./PodcastPlayerCompactSkeleton"; diff --git a/surfsense_web/components/chat/ChatSources.tsx b/surfsense_web/components/chat/ChatSources.tsx deleted file mode 100644 index 5f205d005..000000000 --- a/surfsense_web/components/chat/ChatSources.tsx +++ /dev/null @@ -1,226 +0,0 @@ -"use client"; - -import { getAnnotationData, type Message } from "@llamaindex/chat-ui"; -import { ExternalLink, FileText } from "lucide-react"; -import { useState } from "react"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; -import { SourceDetailSheet } from "./SourceDetailSheet"; - -interface Source { - id: string; - title: string; - description: string; - url: string; - sourceType: string; -} - -interface SourceGroup { - id: number; - name: string; - type: string; - sources: Source[]; -} - -// New interfaces for the updated data format -interface NodeMetadata { - title: string; - source_type: string; - group_name: string; -} - -interface SourceNode { - id: string; - text: string; - url: string; - metadata: NodeMetadata; -} - -function getSourceIcon(type: string) { - // Handle USER_SELECTED_ prefix - const normalizedType = type.startsWith("USER_SELECTED_") - ? type.replace("USER_SELECTED_", "") - : type; - return getConnectorIcon(normalizedType, "h-4 w-4"); -} - -function SourceCard({ source }: { source: Source }) { - const hasUrl = source.url && source.url.trim() !== ""; - const chunkId = Number(source.id); - const sourceType = source.sourceType; - const [isOpen, setIsOpen] = useState(false); - - // Clean up the description for better display - const cleanDescription = source.description - .replace(/## Metadata\n\n/g, "") - .replace(/\n+/g, " ") - .trim(); - - const handleUrlClick = (e: React.MouseEvent, url: string) => { - e.preventDefault(); - e.stopPropagation(); - window.open(url, "_blank", "noopener,noreferrer"); - }; - - return ( - - - - -
- - {source.title} - -
- - #{chunkId} - - {hasUrl && ( - - )} -
-
-
- - - {cleanDescription} - - -
-
-
- ); -} - -export default function ChatSourcesDisplay({ message }: { message: Message }) { - const [open, setOpen] = useState(false); - const annotations = getAnnotationData(message, "sources"); - - // Transform the new data format to the expected SourceGroup format - const sourceGroups: SourceGroup[] = []; - - if (Array.isArray(annotations) && annotations.length > 0) { - // Extract all nodes from the response - const allNodes: SourceNode[] = []; - - annotations.forEach((item) => { - if (item && typeof item === "object" && "nodes" in item && Array.isArray(item.nodes)) { - allNodes.push(...item.nodes); - } - }); - - // Group nodes by source_type - const groupedByType = allNodes.reduce( - (acc, node) => { - const sourceType = node.metadata.source_type; - if (!acc[sourceType]) { - acc[sourceType] = []; - } - acc[sourceType].push(node); - return acc; - }, - {} as Record - ); - - // Convert grouped nodes to SourceGroup format - Object.entries(groupedByType).forEach(([sourceType, nodes], index) => { - if (nodes.length > 0) { - const firstNode = nodes[0]; - sourceGroups.push({ - id: index + 100, // Generate unique ID - name: firstNode.metadata.group_name, - type: sourceType, - sources: nodes.map((node) => ({ - id: node.id, - title: node.metadata.title, - description: node.text, - url: node.url || "", - sourceType: sourceType, - })), - }); - } - }); - } - - if (sourceGroups.length === 0) { - return null; - } - - const totalSources = sourceGroups.reduce((acc, group) => acc + group.sources.length, 0); - - return ( - - - - - - -
- Sources - - {totalSources} {totalSources === 1 ? "source" : "sources"} - -
-
- -
- - {sourceGroups.map((group) => ( - - {getSourceIcon(group.type)} - - {group.name} - - - {group.sources.length} - - - ))} - -
- {sourceGroups.map((group) => ( - -
-
- {group.sources.map((source) => ( - - ))} -
-
-
- ))} -
-
-
- ); -} diff --git a/surfsense_web/components/chat/ChatTerminal.tsx b/surfsense_web/components/chat/ChatTerminal.tsx deleted file mode 100644 index 07415eaf9..000000000 --- a/surfsense_web/components/chat/ChatTerminal.tsx +++ /dev/null @@ -1,105 +0,0 @@ -"use client"; - -import { getAnnotationData, type Message } from "@llamaindex/chat-ui"; -import { useEffect, useRef, useState } from "react"; -import { Button } from "@/components/ui/button"; - -export default function TerminalDisplay({ message, open }: { message: Message; open: boolean }) { - const [isCollapsed, setIsCollapsed] = useState(!open); - - const bottomRef = useRef(null); - - useEffect(() => { - if (bottomRef.current) { - bottomRef.current.scrollTo({ - top: bottomRef.current.scrollHeight, - behavior: "smooth", - }); - } - }, []); - - // Get the last assistant message that's not being typed - if (!message) { - return null; - } - - interface TerminalInfo { - id: number; - text: string; - type: string; - } - - const events = getAnnotationData(message, "TERMINAL_INFO") as TerminalInfo[]; - - if (events.length === 0) { - return null; - } - - return ( -
- {/* Terminal Header */} - - - {/* Terminal Content (animated expand/collapse) */} -
-
- {events.map((event, index) => ( -
- $ - [{event.type || ""}] - - {event.text || ""}... - -
- ))} - {events.length === 0 && ( -
No agent events to display...
- )} -
-
-
- ); -} diff --git a/surfsense_web/components/chat/Citation.tsx b/surfsense_web/components/chat/Citation.tsx deleted file mode 100644 index 27b352e96..000000000 --- a/surfsense_web/components/chat/Citation.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { ExternalLink } from "lucide-react"; -import { memo, useState } from "react"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { getConnectorIcon } from "./ConnectorComponents"; -import type { Source } from "./types"; - -type CitationProps = { - citationId: number; - citationText: string; - position: number; - source: Source | null; -}; - -/** - * Citation component to handle individual citations - */ -export const Citation = memo(({ citationId, citationText, position, source }: CitationProps) => { - const [open, setOpen] = useState(false); - const citationKey = `citation-${citationId}-${position}`; - - if (!source) return <>{citationText}; - - return ( - - - - - - {citationId} - - - - {open && ( - - -
-
- {getConnectorIcon(source.connectorType || "")} -
-
-
-

{source.title}

-
-

{source.description}

-
- {source.url} -
-
- -
-
-
- )} -
-
- ); -}); - -Citation.displayName = "Citation"; - -/** - * Function to render text with citations - */ -export const renderTextWithCitations = ( - text: string, - getCitationSource: (id: number) => Source | null -) => { - // Regular expression to find citation patterns like [1], [2], etc. - const citationRegex = /\[(\d+)\]/g; - const parts = []; - let lastIndex = 0; - let match: RegExpExecArray | null = citationRegex.exec(text); - let position = 0; - - while (match !== null) { - // Add text before the citation - if (match.index > lastIndex) { - parts.push(text.substring(lastIndex, match.index)); - } - - // Add the citation component - const citationId = parseInt(match[1], 10); - parts.push( - - ); - - lastIndex = match.index + match[0].length; - position++; - match = citationRegex.exec(text); - } - - // Add any remaining text after the last citation - if (lastIndex < text.length) { - parts.push(text.substring(lastIndex)); - } - - return parts; -}; diff --git a/surfsense_web/components/chat/CodeBlock.tsx b/surfsense_web/components/chat/CodeBlock.tsx deleted file mode 100644 index 7641a8b82..000000000 --- a/surfsense_web/components/chat/CodeBlock.tsx +++ /dev/null @@ -1,211 +0,0 @@ -"use client"; - -import { Check, Copy } from "lucide-react"; -import { useTheme } from "next-themes"; -import { memo, useCallback, useEffect, useMemo, useState } from "react"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { oneDark, oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism"; - -// Constants for styling and configuration -const COPY_TIMEOUT = 2000; - -const BASE_CUSTOM_STYLE = { - margin: 0, - borderRadius: "0.375rem", - fontSize: "0.75rem", - lineHeight: "1.5rem", - border: "none", -} as const; - -const LINE_PROPS_STYLE = { - wordBreak: "break-all" as const, - whiteSpace: "pre-wrap" as const, - border: "none", - borderBottom: "none", - paddingLeft: 0, - paddingRight: 0, - margin: "0.25rem 0", -} as const; - -const CODE_TAG_PROPS = { - className: "font-mono", - style: { - border: "none", - background: "var(--syntax-bg)", - }, -} as const; - -// TypeScript interfaces -interface CodeBlockProps { - children: string; - language: string; -} - -type LanguageRenderer = (props: { code: string }) => React.JSX.Element; - -interface SyntaxStyle { - [key: string]: React.CSSProperties; -} - -// Memoized fallback component for SSR/hydration -const FallbackCodeBlock = memo(({ children }: { children: string }) => ( -
-
-			{children}
-		
-
-)); - -FallbackCodeBlock.displayName = "FallbackCodeBlock"; - -// Code block component with syntax highlighting and copy functionality -export const CodeBlock = memo(({ children, language }) => { - const [copied, setCopied] = useState(false); - const { resolvedTheme, theme } = useTheme(); - const [mounted, setMounted] = useState(false); - - // Prevent hydration issues - useEffect(() => { - setMounted(true); - }, []); - - // Memoize theme detection - const isDarkTheme = useMemo( - () => mounted && (resolvedTheme === "dark" || theme === "dark"), - [mounted, resolvedTheme, theme] - ); - - // Memoize syntax theme selection - const syntaxTheme = useMemo(() => (isDarkTheme ? oneDark : oneLight), [isDarkTheme]); - - // Memoize enhanced style with theme-specific modifications - const enhancedStyle = useMemo( - () => ({ - ...syntaxTheme, - 'pre[class*="language-"]': { - ...syntaxTheme['pre[class*="language-"]'], - margin: 0, - border: "none", - borderRadius: "0.375rem", - background: "var(--syntax-bg)", - }, - 'code[class*="language-"]': { - ...syntaxTheme['code[class*="language-"]'], - border: "none", - background: "var(--syntax-bg)", - }, - }), - [syntaxTheme] - ); - - // Memoize custom style with background - const customStyle = useMemo( - () => ({ - ...BASE_CUSTOM_STYLE, - backgroundColor: "var(--syntax-bg)", - }), - [] - ); - - // Memoized copy handler - const handleCopy = useCallback(async () => { - try { - await navigator.clipboard.writeText(children); - setCopied(true); - const timeoutId = setTimeout(() => setCopied(false), COPY_TIMEOUT); - return () => clearTimeout(timeoutId); - } catch (error) { - console.warn("Failed to copy code to clipboard:", error); - } - }, [children]); - - // Memoized line props with style - const lineProps = useMemo( - () => ({ - style: LINE_PROPS_STYLE, - }), - [] - ); - - // Early return for non-mounted state - if (!mounted) { - return {children}; - } - - return ( -
-
- -
- - {children} - -
- ); -}); - -CodeBlock.displayName = "CodeBlock"; - -// Optimized language renderer factory with memoization -const createLanguageRenderer = (lang: string): LanguageRenderer => { - const renderer = ({ code }: { code: string }) => {code}; - renderer.displayName = `LanguageRenderer(${lang})`; - return renderer; -}; - -// Pre-defined supported languages for better maintainability -const SUPPORTED_LANGUAGES = [ - "javascript", - "typescript", - "python", - "java", - "csharp", - "cpp", - "c", - "php", - "ruby", - "go", - "rust", - "swift", - "kotlin", - "scala", - "sql", - "json", - "xml", - "yaml", - "bash", - "shell", - "powershell", - "dockerfile", - "html", - "css", - "scss", - "less", - "markdown", - "text", -] as const; - -// Generate language renderers efficiently -export const languageRenderers: Record = Object.fromEntries( - SUPPORTED_LANGUAGES.map((lang) => [lang, createLanguageRenderer(lang)]) -); diff --git a/surfsense_web/components/chat/ConnectorComponents.tsx b/surfsense_web/components/chat/ConnectorComponents.tsx deleted file mode 100644 index 3866d055e..000000000 --- a/surfsense_web/components/chat/ConnectorComponents.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { ChevronDown, Plus } from "lucide-react"; -import type React from "react"; -import { Button } from "@/components/ui/button"; -import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; -import type { Connector } from "./types"; - -/** - * Displays a small icon for a connector type - */ -export const ConnectorIcon = ({ type, index = 0 }: { type: string; index?: number }) => ( -
- {getConnectorIcon(type)} -
-); - -/** - * Displays a count indicator for additional connectors - */ -export const ConnectorCountBadge = ({ count }: { count: number }) => ( -
- +{count} -
-); - -type ConnectorButtonProps = { - selectedConnectors: string[]; - onClick: () => void; - connectorSources: Connector[]; -}; - -/** - * Button that displays selected connectors and opens connector selection dialog - */ -export const ConnectorButton = ({ - selectedConnectors, - onClick, - connectorSources, -}: ConnectorButtonProps) => { - const totalConnectors = connectorSources.length; - const selectedCount = selectedConnectors.length; - const progressPercentage = (selectedCount / totalConnectors) * 100; - - // Get the name of a single selected connector - const getSingleConnectorName = () => { - const connector = connectorSources.find((c) => c.type === selectedConnectors[0]); - return connector?.name || ""; - }; - - // Get display text based on selection count - const getDisplayText = () => { - if (selectedCount === totalConnectors) return "All Connectors"; - if (selectedCount === 1) return getSingleConnectorName(); - return `${selectedCount} Connectors`; - }; - - // Render the empty state (no connectors selected) - const renderEmptyState = () => ( - <> - - Select Connectors - - ); - - // Render the selected connectors preview - const renderSelectedConnectors = () => ( - <> -
- {/* Show up to 3 connector icons */} - {selectedConnectors.slice(0, 3).map((type, index) => ( - - ))} - - {/* Show count indicator if more than 3 connectors are selected */} - {selectedCount > 3 && } -
- - {/* Display text */} - {getDisplayText()} - - ); - - return ( - - ); -}; diff --git a/surfsense_web/components/chat/DocumentsDataTable.tsx b/surfsense_web/components/chat/DocumentsDataTable.tsx deleted file mode 100644 index 77f1a05bd..000000000 --- a/surfsense_web/components/chat/DocumentsDataTable.tsx +++ /dev/null @@ -1,604 +0,0 @@ -"use client"; - -import { useQuery } from "@tanstack/react-query"; -import { - type ColumnDef, - flexRender, - getCoreRowModel, - type SortingState, - useReactTable, -} from "@tanstack/react-table"; -import { useAtomValue } from "jotai"; -import { ArrowUpDown, Calendar, FileText, Filter, Plus, Search } from "lucide-react"; -import { useRouter } from "next/navigation"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; -import { Button } from "@/components/ui/button"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; -import type { Document, DocumentTypeEnum } from "@/contracts/types/document.types"; -import { documentsApiService } from "@/lib/apis/documents-api.service"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; - -interface DocumentsDataTableProps { - searchSpaceId: number; - onSelectionChange: (documents: Document[]) => void; - onDone: () => void; - initialSelectedDocuments?: Document[]; -} - -function useDebounced(value: T, delay = 300) { - const [debounced, setDebounced] = useState(value); - useEffect(() => { - const t = setTimeout(() => setDebounced(value), delay); - return () => clearTimeout(t); - }, [value, delay]); - return debounced; -} - -const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - size: 40, - }, - { - accessorKey: "title", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const title = row.getValue("title") as string; - return ( -
- {title} -
- ); - }, - }, - { - accessorKey: "document_type", - header: "Type", - cell: ({ row }) => { - const type = row.getValue("document_type") as DocumentType; - return ( -
- {getConnectorIcon(String(type))} -
- ); - }, - size: 80, - meta: { - className: "hidden sm:table-cell", - }, - }, - { - accessorKey: "content", - header: "Preview", - cell: ({ row }) => { - const content = row.getValue("content") as string; - return ( -
- {content.substring(0, 30)}... - {content.substring(0, 100)}... -
- ); - }, - enableSorting: false, - meta: { - className: "hidden md:table-cell", - }, - }, - { - accessorKey: "created_at", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const date = new Date(row.getValue("created_at")); - return ( -
- - {date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - })} - - - {date.toLocaleDateString("en-US", { - month: "numeric", - day: "numeric", - })} - -
- ); - }, - size: 80, - }, -]; - -export function DocumentsDataTable({ - searchSpaceId, - onSelectionChange, - onDone, - initialSelectedDocuments = [], -}: DocumentsDataTableProps) { - const router = useRouter(); - const [sorting, setSorting] = useState([]); - const [search, setSearch] = useState(""); - const debouncedSearch = useDebounced(search, 300); - const [documentTypeFilter, setDocumentTypeFilter] = useState([]); - const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(10); - const { data: typeCounts } = useAtomValue(documentTypeCountsAtom); - - const fetchQueryParams = useMemo( - () => ({ - search_space_id: searchSpaceId, - page: pageIndex, - page_size: pageSize, - ...(documentTypeFilter.length > 0 && { document_types: documentTypeFilter }), - }), - [searchSpaceId, pageIndex, pageSize, documentTypeFilter, debouncedSearch] - ); - - const searchQueryParams = useMemo(() => { - return { - search_space_id: searchSpaceId, - page: pageIndex, - page_size: pageSize, - ...(documentTypeFilter.length > 0 && { document_types: documentTypeFilter }), - title: debouncedSearch, - }; - }, [debouncedSearch, searchSpaceId, pageIndex, pageSize, documentTypeFilter, debouncedSearch]); - - // Use query for fetching documents - const { data: documents, isLoading: isDocumentsLoading } = useQuery({ - queryKey: cacheKeys.documents.withQueryParams(fetchQueryParams), - queryFn: () => documentsApiService.getDocuments({ queryParams: fetchQueryParams }), - staleTime: 3 * 60 * 1000, // 3 minutes - enabled: !!searchSpaceId && !debouncedSearch.trim(), - }); - - // Seaching - const { data: searchedDocuments, isLoading: isSearchedDocumentsLoading } = useQuery({ - queryKey: cacheKeys.documents.withQueryParams(searchQueryParams), - queryFn: () => documentsApiService.searchDocuments({ queryParams: searchQueryParams }), - staleTime: 3 * 60 * 1000, // 3 minutes - enabled: !!searchSpaceId && !!debouncedSearch.trim(), - }); - - // Use query data when not searching, otherwise use hook data - const actualDocuments = debouncedSearch.trim() - ? searchedDocuments?.items || [] - : documents?.items || []; - const actualTotal = debouncedSearch.trim() - ? searchedDocuments?.total || 0 - : documents?.total || 0; - const actualLoading = debouncedSearch.trim() ? isSearchedDocumentsLoading : isDocumentsLoading; - - // Memoize initial row selection to prevent infinite loops - const initialRowSelection = useMemo(() => { - if (!initialSelectedDocuments.length) return {}; - - const selection: Record = {}; - initialSelectedDocuments.forEach((selectedDoc) => { - selection[selectedDoc.id] = true; - }); - return selection; - }, [initialSelectedDocuments]); - - const [rowSelection, setRowSelection] = useState>( - () => initialRowSelection - ); - - // Maintain a separate state for actually selected documents (across all pages) - const [selectedDocumentsMap, setSelectedDocumentsMap] = useState>(() => { - const map = new Map(); - initialSelectedDocuments.forEach((doc) => map.set(doc.id, doc)); - return map; - }); - - // Track the last notified selection to avoid redundant parent calls - const lastNotifiedSelection = useRef(""); - - // Update row selection only when initialSelectedDocuments changes (not rowSelection itself) - useEffect(() => { - const initialKeys = Object.keys(initialRowSelection); - if (initialKeys.length === 0) return; - - const currentKeys = Object.keys(rowSelection); - // Quick length check before expensive comparison - if (currentKeys.length === initialKeys.length) { - // Check if all keys match (order doesn't matter for Sets) - const hasAllKeys = initialKeys.every((key) => rowSelection[key]); - if (hasAllKeys) return; - } - - setRowSelection(initialRowSelection); - }, [initialRowSelection]); // Remove rowSelection from dependencies to prevent loop - - // Update the selected documents map when row selection changes - useEffect(() => { - if (!actualDocuments || actualDocuments.length === 0) return; - - setSelectedDocumentsMap((prev) => { - const newMap = new Map(prev); - let hasChanges = false; - - // Process only current page documents - for (const doc of actualDocuments) { - const docId = doc.id; - const isSelected = rowSelection[docId.toString()]; - const wasInMap = newMap.has(docId); - - if (isSelected && !wasInMap) { - newMap.set(docId, doc); - hasChanges = true; - } else if (!isSelected && wasInMap) { - newMap.delete(docId); - hasChanges = true; - } - } - - // Return same reference if no changes to avoid unnecessary re-renders - return hasChanges ? newMap : prev; - }); - }, [rowSelection, documents]); - - // Memoize selected documents array - const selectedDocumentsArray = useMemo(() => { - return Array.from(selectedDocumentsMap.values()); - }, [selectedDocumentsMap]); - - // Notify parent of selection changes only when content actually changes - useEffect(() => { - // Create a stable string representation for comparison - const selectionKey = selectedDocumentsArray - .map((d) => d.id) - .sort() - .join(","); - - // Skip if selection hasn't actually changed - if (selectionKey === lastNotifiedSelection.current) return; - - lastNotifiedSelection.current = selectionKey; - onSelectionChange(selectedDocumentsArray); - }, [selectedDocumentsArray, onSelectionChange]); - - const table = useReactTable({ - data: actualDocuments || [], - columns, - getRowId: (row) => row.id.toString(), - onSortingChange: setSorting, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - manualPagination: true, - pageCount: Math.ceil(actualTotal / pageSize), - state: { sorting, rowSelection, pagination: { pageIndex, pageSize } }, - }); - - const handleClearAll = useCallback(() => { - setRowSelection({}); - setSelectedDocumentsMap(new Map()); - }, []); - - const handleSelectPage = useCallback(() => { - const currentPageRows = table.getRowModel().rows; - const newSelection = { ...rowSelection }; - currentPageRows.forEach((row) => { - newSelection[row.id] = true; - }); - setRowSelection(newSelection); - }, [table, rowSelection]); - - const handleToggleType = useCallback((type: DocumentTypeEnum, checked: boolean) => { - setDocumentTypeFilter((prev) => { - if (checked) { - return [...prev, type]; - } - return prev.filter((t) => t !== type); - }); - setPageIndex(0); // Reset to first page when filter changes - }, []); - - const selectedCount = selectedDocumentsMap.size; - - // Get available document types from type counts (memoized) - const availableTypes = useMemo(() => { - const types = typeCounts ? (Object.keys(typeCounts) as DocumentTypeEnum[]) : []; - return types.length > 0 ? types.sort() : []; - }, [typeCounts]); - - return ( -
- {/* Header Controls */} -
- {/* Search and Filter Row */} -
-
- - { - setSearch(event.target.value); - setPageIndex(0); // Reset to first page on search - }} - className="pl-10 text-sm" - /> -
- - - - - -
-
Filter by Type
-
- {availableTypes.map((type) => ( -
- handleToggleType(type, !!checked)} - /> - -
- ))} -
- {documentTypeFilter.length > 0 && ( - - )} -
-
-
-
- - {/* Action Controls Row */} -
-
- - {selectedCount} selected {actualLoading && "· Loading..."} - -
-
- - - -
-
- -
-
- - {/* Table Container */} -
-
- {actualLoading ? ( -
-
-
-

Loading documents...

-
-
- ) : ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} - - ))} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - -
-
- -
-
-

No documents found

-

- Get started by adding your first data source to build your knowledge - base. -

-
- -
-
-
- )} -
-
- )} -
-
- - {/* Footer Pagination */} -
-
- Showing {pageIndex * pageSize + 1} to {Math.min((pageIndex + 1) * pageSize, actualTotal)}{" "} - of {actualTotal} documents -
-
- -
- Page - {pageIndex + 1} - of - {Math.ceil(actualTotal / pageSize)} -
- -
-
-
- ); -} diff --git a/surfsense_web/components/chat/PodcastUtils.ts b/surfsense_web/components/chat/PodcastUtils.ts deleted file mode 100644 index 5662f96b5..000000000 --- a/surfsense_web/components/chat/PodcastUtils.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Determines if a podcast is stale compared to the current chat state. - * A podcast is considered stale if: - * - The chat's current state_version is greater than the podcast's chat_state_version - * - * @param chatVersion - The current state_version of the chat - * @param podcastVersion - The chat_state_version stored when the podcast was generated (nullable) - * @returns true if the podcast is stale, false otherwise - */ -export function isPodcastStale( - chatVersion: number, - podcastVersion: number | null | undefined -): boolean { - // If podcast has no version, it's stale (generated before this feature) - if (!podcastVersion) { - return true; - } - // If chat version is greater than podcast version, it's stale : We can change this condition to consider staleness after a huge number of updates - return chatVersion > podcastVersion; -} - -/** - * Gets a human-readable message about podcast staleness - * - * @param chatVersion - The current state_version of the chat - * @param podcastVersion - The chat_state_version stored when the podcast was generated - * @returns A descriptive message about the podcast's staleness status - */ -export function getPodcastStalenessMessage( - chatVersion: number, - podcastVersion: number | null | undefined -): string { - if (!podcastVersion) { - return "This podcast was generated before chat updates were tracked. Consider regenerating it."; - } - - if (chatVersion > podcastVersion) { - const versionDiff = chatVersion - podcastVersion; - return `This podcast is outdated. The chat has been updated ${versionDiff} time${versionDiff > 1 ? "s" : ""} since this podcast was generated.`; - } - - return "This podcast is up to date with the current chat."; -} diff --git a/surfsense_web/components/chat/ScrollUtils.tsx b/surfsense_web/components/chat/ScrollUtils.tsx deleted file mode 100644 index 3984246ae..000000000 --- a/surfsense_web/components/chat/ScrollUtils.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { type RefObject, useEffect } from "react"; - -/** - * Function to scroll to the bottom of a container - */ -export const scrollToBottom = (ref: RefObject) => { - ref.current?.scrollIntoView({ behavior: "smooth" }); -}; - -/** - * Hook to scroll to bottom when messages change - */ -export const useScrollToBottom = (ref: RefObject, dependencies: any[]) => { - useEffect(() => { - scrollToBottom(ref); - }, dependencies); -}; - -/** - * Function to check scroll position and update indicators - */ -export const updateScrollIndicators = ( - tabsListRef: RefObject, - setCanScrollLeft: (value: boolean) => void, - setCanScrollRight: (value: boolean) => void -) => { - if (tabsListRef.current) { - const { scrollLeft, scrollWidth, clientWidth } = tabsListRef.current; - setCanScrollLeft(scrollLeft > 0); - setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 10); // 10px buffer - } -}; - -/** - * Hook to initialize scroll indicators and add resize listener - */ -export const useScrollIndicators = ( - tabsListRef: RefObject, - setCanScrollLeft: (value: boolean) => void, - setCanScrollRight: (value: boolean) => void -) => { - const updateIndicators = () => - updateScrollIndicators(tabsListRef, setCanScrollLeft, setCanScrollRight); - - useEffect(() => { - updateIndicators(); - // Add resize listener to update indicators when window size changes - window.addEventListener("resize", updateIndicators); - return () => window.removeEventListener("resize", updateIndicators); - }, [updateIndicators]); - - return updateIndicators; -}; - -/** - * Function to scroll tabs list left - */ -export const scrollTabsLeft = ( - tabsListRef: RefObject, - updateIndicators: () => void -) => { - if (tabsListRef.current) { - tabsListRef.current.scrollBy({ left: -200, behavior: "smooth" }); - // Update indicators after scrolling - setTimeout(updateIndicators, 300); - } -}; - -/** - * Function to scroll tabs list right - */ -export const scrollTabsRight = ( - tabsListRef: RefObject, - updateIndicators: () => void -) => { - if (tabsListRef.current) { - tabsListRef.current.scrollBy({ left: 200, behavior: "smooth" }); - // Update indicators after scrolling - setTimeout(updateIndicators, 300); - } -}; diff --git a/surfsense_web/components/chat/SegmentedControl.tsx b/surfsense_web/components/chat/SegmentedControl.tsx deleted file mode 100644 index 91314ae18..000000000 --- a/surfsense_web/components/chat/SegmentedControl.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import type React from "react"; -import { Button } from "@/components/ui/button"; - -type SegmentedControlProps = { - value: T; - onChange: (value: T) => void; - options: Array<{ - value: T; - label: string; - icon: React.ReactNode; - }>; -}; - -/** - * A segmented control component for selecting between different options - */ -function SegmentedControl({ - value, - onChange, - options, -}: SegmentedControlProps) { - return ( -
- {options.map((option) => ( - - ))} -
- ); -} - -export default SegmentedControl; diff --git a/surfsense_web/components/chat/SourceDetailSheet.tsx b/surfsense_web/components/chat/SourceDetailSheet.tsx deleted file mode 100644 index 7f5ebbeab..000000000 --- a/surfsense_web/components/chat/SourceDetailSheet.tsx +++ /dev/null @@ -1,254 +0,0 @@ -"use client"; - -import { useQuery } from "@tanstack/react-query"; -import { ChevronDown, ChevronUp, ExternalLink, Loader2 } from "lucide-react"; -import type React from "react"; -import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react"; -import { MarkdownViewer } from "@/components/markdown-viewer"; -import { Button } from "@/components/ui/button"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { - Sheet, - SheetContent, - SheetDescription, - SheetHeader, - SheetTitle, -} from "@/components/ui/sheet"; -import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; -import { documentsApiService } from "@/lib/apis/documents-api.service"; -import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { cn } from "@/lib/utils"; - -interface SourceDetailSheetProps { - open: boolean; - onOpenChange: (open: boolean) => void; - chunkId: number; - sourceType: string; - title: string; - description?: string; - url?: string; - children?: ReactNode; -} - -const formatDocumentType = (type: string) => { - return type - .split("_") - .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) - .join(" "); -}; - -export function SourceDetailSheet({ - open, - onOpenChange, - chunkId, - sourceType, - title, - description, - url, - children, -}: SourceDetailSheetProps) { - const chunksContainerRef = useRef(null); - const highlightedChunkRef = useRef(null); - const [summaryOpen, setSummaryOpen] = useState(false); - - const { - data: document, - isLoading: isDocumentByChunkFetching, - error: documentByChunkFetchingError, - } = useQuery({ - queryKey: cacheKeys.documents.byChunk(chunkId.toString()), - queryFn: () => documentsApiService.getDocumentByChunk({ chunk_id: chunkId }), - enabled: !!chunkId && open, - staleTime: 5 * 60 * 1000, // 5 minutes - }); - - // Check if this is a source type that should render directly from node - const isDirectRenderSource = - sourceType === "TAVILY_API" || - sourceType === "LINKUP_API" || - sourceType === "SEARXNG_API" || - sourceType === "BAIDU_SEARCH_API"; - - useEffect(() => { - // Scroll to highlighted chunk when document loads - if (document) { - setTimeout(() => { - highlightedChunkRef.current?.scrollIntoView({ - behavior: "smooth", - block: "start", - }); - }, 100); - } - }, [document, open]); - - const handleUrlClick = (e: React.MouseEvent, clickUrl: string) => { - e.preventDefault(); - e.stopPropagation(); - window.open(clickUrl, "_blank", "noopener,noreferrer"); - }; - - return ( - - {children} - - - - {getConnectorIcon(sourceType)} - {document?.title || title} - - - {document - ? formatDocumentType(document.document_type) - : sourceType && formatDocumentType(sourceType)} - - - - {!isDirectRenderSource && isDocumentByChunkFetching && ( -
- -
- )} - - {!isDirectRenderSource && documentByChunkFetchingError && ( -
-

- {documentByChunkFetchingError.message || "Failed to load document"} -

-
- )} - - {/* Direct render for web search providers */} - {isDirectRenderSource && ( - -
- {/* External Link */} - {url && ( -
- -
- )} - - {/* Source Information */} -
-

Source Information

-
- {title || "Untitled"} -
-
- {description || "No content available"} -
-
-
-
- )} - - {/* API-fetched document content */} - {!isDirectRenderSource && document && ( - -
- {/* Document Metadata */} - {document.document_metadata && Object.keys(document.document_metadata).length > 0 && ( -
-

Document Information

-
- {Object.entries(document.document_metadata).map(([key, value]) => ( -
-
- {key.replace(/_/g, " ")}: -
-
{String(value)}
-
- ))} -
-
- )} - - {/* External Link */} - {url && ( -
- -
- )} - - {/* Chunks */} -
-
- {/* Header row: header and button side by side */} -
-

Document Content

- {document.content && ( - - - Summary - {summaryOpen ? ( - - ) : ( - - )} - - - )} -
- {/* Expanded summary content: always full width, below the row */} - {document.content && ( - - -
- -
-
-
- )} -
- - {document.chunks.map((chunk, idx) => ( -
-
- - Chunk {idx + 1} of {document.chunks.length} - - {chunk.id === chunkId && ( - - Referenced Chunk - - )} -
-
- -
-
- ))} -
-
-
- )} -
-
- ); -} diff --git a/surfsense_web/components/chat/SourceUtils.tsx b/surfsense_web/components/chat/SourceUtils.tsx deleted file mode 100644 index a384a7b56..000000000 --- a/surfsense_web/components/chat/SourceUtils.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { Connector, Source } from "./types"; - -/** - * Function to get sources for the main view - */ -export const getMainViewSources = (connector: Connector, initialSourcesDisplay: number) => { - return connector.sources?.slice(0, initialSourcesDisplay); -}; - -/** - * Function to get filtered sources for the dialog - */ -export const getFilteredSources = (connector: Connector, sourceFilter: string) => { - if (!sourceFilter.trim()) { - return connector.sources; - } - - const filter = sourceFilter.toLowerCase().trim(); - return connector.sources?.filter( - (source) => - source.title.toLowerCase().includes(filter) || - source.description.toLowerCase().includes(filter) - ); -}; - -/** - * Function to get paginated and filtered sources for the dialog - */ -export const getPaginatedDialogSources = ( - connector: Connector, - sourceFilter: string, - expandedSources: boolean, - sourcesPage: number, - sourcesPerPage: number -) => { - const filteredSources = getFilteredSources(connector, sourceFilter); - - if (expandedSources) { - return filteredSources; - } - return filteredSources?.slice(0, sourcesPage * sourcesPerPage); -}; - -/** - * Function to get the count of sources for a connector type - */ -export const getSourcesCount = (connectorSources: Connector[], connectorType: string) => { - const connector = connectorSources.find((c) => c.type === connectorType); - return connector?.sources?.length || 0; -}; - -/** - * Function to get a citation source by ID - */ -export const getCitationSource = ( - citationId: number, - connectorSources: Connector[] -): Source | null => { - for (const connector of connectorSources) { - const source = connector.sources?.find((s) => s.id === citationId); - if (source) { - return { - ...source, - connectorType: connector.type, - }; - } - } - return null; -}; diff --git a/surfsense_web/components/chat/index.ts b/surfsense_web/components/chat/index.ts deleted file mode 100644 index 74bd5eea0..000000000 --- a/surfsense_web/components/chat/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Export all components and utilities from the chat folder - -export * from "./Citation"; -export * from "./CodeBlock"; -export * from "./ConnectorComponents"; -export * from "./ScrollUtils"; -export { default as SegmentedControl } from "./SegmentedControl"; -export * from "./SourceUtils"; -export * from "./types"; diff --git a/surfsense_web/components/chat/types.ts b/surfsense_web/components/chat/types.ts deleted file mode 100644 index 8e4e1e295..000000000 --- a/surfsense_web/components/chat/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Types for chat components - */ - -export type Source = { - id: number; - title: string; - description: string; - url: string; - connectorType?: string; -}; - -export type Connector = { - id: number; - type: string; - name: string; - sources?: Source[]; -}; - -export type StatusMessage = { - id: number; - message: string; - type: "info" | "success" | "error" | "warning"; - timestamp: string; -}; - -export type ChatMessage = { - id: string; - role: "user" | "assistant"; - content: string; - timestamp?: string; -}; - -// Define message types to match useChat() structure -export type MessageRole = "user" | "assistant" | "system" | "data"; - -export interface ToolInvocation { - state: "call" | "result"; - toolCallId: string; - toolName: string; - args: any; - result?: any; -} - -export interface ToolInvocationUIPart { - type: "tool-invocation"; - toolInvocation: ToolInvocation; -} - -export type ResearchMode = "QNA"; diff --git a/surfsense_web/components/dashboard-breadcrumb.tsx b/surfsense_web/components/dashboard-breadcrumb.tsx index e2115a002..df1021762 100644 --- a/surfsense_web/components/dashboard-breadcrumb.tsx +++ b/surfsense_web/components/dashboard-breadcrumb.tsx @@ -1,11 +1,9 @@ "use client"; import { useQuery } from "@tanstack/react-query"; -import { useAtomValue } from "jotai"; import { usePathname } from "next/navigation"; import { useTranslations } from "next-intl"; import React, { useEffect, useState } from "react"; -import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; import { Breadcrumb, BreadcrumbItem, @@ -26,7 +24,6 @@ interface BreadcrumbItemInterface { export function DashboardBreadcrumb() { const t = useTranslations("breadcrumb"); const pathname = usePathname(); - const { data: activeChatState } = useAtomValue(activeChatAtom); // Extract search space ID and chat ID from pathname const segments = pathname.split("/").filter(Boolean); const searchSpaceId = segments[0] === "dashboard" && segments[1] ? segments[1] : null; @@ -98,13 +95,11 @@ export function DashboardBreadcrumb() { // Map section names to more readable labels const sectionLabels: Record = { - researcher: t("researcher"), + "new-chat": t("chat") || "Chat", documents: t("documents"), connectors: t("connectors"), sources: "Sources", - podcasts: t("podcasts"), logs: t("logs"), - chats: t("chats"), settings: t("settings"), editor: t("editor"), }; @@ -169,15 +164,15 @@ export function DashboardBreadcrumb() { return breadcrumbs; } - // Handle researcher sub-sections (chat IDs) - if (section === "researcher") { - // Use the actual chat title if available, otherwise fall back to the ID - const chatLabel = activeChatState?.chatDetails?.title || subSection; + // Handle new-chat sub-sections (thread IDs) + if (section === "new-chat") { breadcrumbs.push({ - label: t("researcher"), - href: `/dashboard/${segments[1]}/researcher`, + label: t("chat") || "Chat", + href: `/dashboard/${segments[1]}/new-chat`, }); - breadcrumbs.push({ label: chatLabel }); + if (subSection) { + breadcrumbs.push({ label: `Thread ${subSection}` }); + } return breadcrumbs; } diff --git a/surfsense_web/components/tool-ui/generate-podcast.tsx b/surfsense_web/components/tool-ui/generate-podcast.tsx index 9f7248082..669b11a57 100644 --- a/surfsense_web/components/tool-ui/generate-podcast.tsx +++ b/surfsense_web/components/tool-ui/generate-podcast.tsx @@ -4,9 +4,8 @@ import { makeAssistantToolUI } from "@assistant-ui/react"; import { AlertCircleIcon, Loader2Icon, MicIcon } from "lucide-react"; import { useCallback, useEffect, useRef, useState } from "react"; import { Audio } from "@/components/tool-ui/audio"; -import type { PodcastTranscriptEntry } from "@/contracts/types/podcast.types"; import { baseApiService } from "@/lib/apis/base-api.service"; -import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; +import { authenticatedFetch } from "@/lib/auth-utils"; import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state"; /** @@ -113,6 +112,14 @@ function AudioLoadingState({ title }: { title: string }) { /** * Podcast Player Component - Fetches audio and transcript with authentication */ +/** + * Transcript entry type for podcast transcripts + */ +interface PodcastTranscriptEntry { + speaker_id: number; + dialog: string; +} + function PodcastPlayer({ podcastId, title, @@ -156,14 +163,22 @@ function PodcastPlayer({ try { // Fetch audio blob and podcast details in parallel - const [audioBlob, podcastDetails] = await Promise.all([ - podcastsApiService.loadPodcast({ - request: { id: podcastId }, - controller, - }), - podcastsApiService.getPodcastById(podcastId), + const [audioResponse, podcastDetails] = await Promise.all([ + authenticatedFetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/${podcastId}/audio`, + { method: "GET", signal: controller.signal } + ), + baseApiService.get<{ podcast_transcript?: PodcastTranscriptEntry[] }>( + `/api/v1/podcasts/${podcastId}` + ), ]); + if (!audioResponse.ok) { + throw new Error(`Failed to load audio: ${audioResponse.status}`); + } + + const audioBlob = await audioResponse.blob(); + // Create object URL from blob const objectUrl = URL.createObjectURL(audioBlob); objectUrlRef.current = objectUrl; diff --git a/surfsense_web/contracts/types/chat.types.ts b/surfsense_web/contracts/types/chat.types.ts deleted file mode 100644 index a8d9a7cd2..000000000 --- a/surfsense_web/contracts/types/chat.types.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Message } from "@ai-sdk/react"; -import { z } from "zod"; -import { paginationQueryParams } from "."; - -export const chatTypeEnum = z.enum(["QNA"]); - -export const chatSummary = z.object({ - created_at: z.string(), - id: z.number(), - type: chatTypeEnum, - title: z.string(), - search_space_id: z.number(), - state_version: z.number(), -}); - -export const chatDetails = chatSummary.extend({ - initial_connectors: z.array(z.string()).nullable().optional(), - messages: z.array(z.any()), -}); - -export const getChatDetailsRequest = chatSummary.pick({ id: true }); - -export const getChatsRequest = z.object({ - queryParams: paginationQueryParams - .extend({ - search_space_id: z.number().or(z.string()).optional(), - }) - .nullish(), -}); - -export const searchChatsRequest = z.object({ - queryParams: paginationQueryParams.extend({ - title: z.string(), - search_space_id: z.number().or(z.string()).optional(), - }), -}); - -export const deleteChatResponse = z.object({ - message: z.literal("Chat deleted successfully"), -}); - -export const deleteChatRequest = chatSummary.pick({ id: true }); - -export const createChatRequest = chatDetails.omit({ - created_at: true, - id: true, - state_version: true, -}); - -export const updateChatRequest = chatDetails.omit({ - created_at: true, - state_version: true, -}); - -export type ChatSummary = z.infer; -export type ChatDetails = z.infer & { messages: Message[] }; -export type GetChatDetailsRequest = z.infer; -export type GetChatsRequest = z.infer; -export type SearchChatsRequest = z.infer; -export type DeleteChatResponse = z.infer; -export type DeleteChatRequest = z.infer; -export type CreateChatRequest = z.infer; -export type UpdateChatRequest = z.infer; diff --git a/surfsense_web/contracts/types/podcast.types.ts b/surfsense_web/contracts/types/podcast.types.ts deleted file mode 100644 index dc7c35ba4..000000000 --- a/surfsense_web/contracts/types/podcast.types.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { z } from "zod"; -import { paginationQueryParams } from "."; - -export const podcastTranscriptEntry = z.object({ - speaker_id: z.number(), - dialog: z.string(), -}); - -export const podcast = z.object({ - id: z.number(), - title: z.string(), - created_at: z.string(), - file_location: z.string(), - podcast_transcript: z.array(podcastTranscriptEntry), - search_space_id: z.number(), - chat_state_version: z.number().nullable(), -}); - -export const generatePodcastRequest = z.object({ - type: z.enum(["CHAT", "DOCUMENT"]), - ids: z.array(z.number()), - search_space_id: z.number(), - podcast_title: z.string().optional(), - user_prompt: z.string().optional(), -}); - -export const getPodcastByChatIdRequest = z.object({ - chat_id: z.number(), -}); - -export const getPodcastByChaIdResponse = podcast.nullish(); - -export const deletePodcastRequest = z.object({ - id: z.number(), -}); - -export const deletePodcastResponse = z.object({ - message: z.literal("Podcast deleted successfully"), -}); - -export const loadPodcastRequest = z.object({ - id: z.number(), -}); - -export const getPodcastsRequest = z.object({ - queryParams: paginationQueryParams.nullish(), -}); - -export type PodcastTranscriptEntry = z.infer; -export type GeneratePodcastRequest = z.infer; -export type GetPodcastByChatIdRequest = z.infer; -export type GetPodcastByChatIdResponse = z.infer; -export type DeletePodcastRequest = z.infer; -export type DeletePodcastResponse = z.infer; -export type LoadPodcastRequest = z.infer; -export type Podcast = z.infer; -export type GetPodcastsRequest = z.infer; diff --git a/surfsense_web/lib/apis/chats-api.service.ts b/surfsense_web/lib/apis/chats-api.service.ts deleted file mode 100644 index 2ca253ea7..000000000 --- a/surfsense_web/lib/apis/chats-api.service.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { z } from "zod"; -import { - type CreateChatRequest, - chatDetails, - chatSummary, - createChatRequest, - type DeleteChatRequest, - deleteChatRequest, - deleteChatResponse, - type GetChatDetailsRequest, - type GetChatsRequest, - getChatDetailsRequest, - getChatsRequest, - type SearchChatsRequest, - searchChatsRequest, - type UpdateChatRequest, - updateChatRequest, -} from "@/contracts/types/chat.types"; -import { ValidationError } from "../error"; -import { baseApiService } from "./base-api.service"; - -class ChatApiService { - getChatDetails = async (request: GetChatDetailsRequest) => { - // Validate the request - const parsedRequest = getChatDetailsRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.get(`/api/v1/chats/${request.id}`, chatDetails); - }; - - getChats = async (request: GetChatsRequest) => { - // Validate the request - const parsedRequest = getChatsRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - // Transform queries params to be string values - const transformedQueryParams = parsedRequest.data.queryParams - ? Object.fromEntries( - Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)]) - ) - : undefined; - - const queryParams = transformedQueryParams - ? new URLSearchParams(transformedQueryParams).toString() - : undefined; - - return baseApiService.get(`/api/v1/chats?${queryParams}`, z.array(chatSummary)); - }; - - searchChats = async (request: SearchChatsRequest) => { - // Validate the request - const parsedRequest = searchChatsRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - // Transform queries params to be string values - const transformedQueryParams = Object.fromEntries( - Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)]) - ); - - const queryParams = new URLSearchParams(transformedQueryParams).toString(); - - return baseApiService.get(`/api/v1/chats/search?${queryParams}`, z.array(chatSummary)); - }; - - deleteChat = async (request: DeleteChatRequest) => { - // Validate the request - const parsedRequest = deleteChatRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.delete(`/api/v1/chats/${request.id}`, deleteChatResponse); - }; - - createChat = async (request: CreateChatRequest) => { - // Validate the request - const parsedRequest = createChatRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.post( - `/api/v1/chats`, - - chatSummary, - { - body: parsedRequest.data, - } - ); - }; - - updateChat = async (request: UpdateChatRequest) => { - // Validate the request - const parsedRequest = updateChatRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - const { type, title, initial_connectors, messages, search_space_id, id } = parsedRequest.data; - - return baseApiService.put( - `/api/v1/chats/${id}`, - - chatSummary, - { - body: { - type, - title, - initial_connectors, - messages, - search_space_id, - }, - } - ); - }; -} - -export const chatsApiService = new ChatApiService(); diff --git a/surfsense_web/lib/apis/podcasts-api.service.ts b/surfsense_web/lib/apis/podcasts-api.service.ts deleted file mode 100644 index 0defff7d4..000000000 --- a/surfsense_web/lib/apis/podcasts-api.service.ts +++ /dev/null @@ -1,128 +0,0 @@ -import z from "zod"; -import { - type DeletePodcastRequest, - deletePodcastRequest, - deletePodcastResponse, - type GeneratePodcastRequest, - type GetPodcastByChatIdRequest, - type GetPodcastsRequest, - generatePodcastRequest, - getPodcastByChaIdResponse, - getPodcastByChatIdRequest, - getPodcastsRequest, - type LoadPodcastRequest, - loadPodcastRequest, - podcast, -} from "@/contracts/types/podcast.types"; -import { ValidationError } from "../error"; -import { baseApiService } from "./base-api.service"; - -class PodcastsApiService { - getPodcasts = async (request: GetPodcastsRequest) => { - // Validate the request - const parsedRequest = getPodcastsRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - // Transform queries params to be string values - const transformedQueryParams = parsedRequest.data.queryParams - ? Object.fromEntries( - Object.entries(parsedRequest.data.queryParams).map(([k, v]) => [k, String(v)]) - ) - : undefined; - - const queryParams = transformedQueryParams - ? new URLSearchParams(transformedQueryParams).toString() - : undefined; - - return baseApiService.get(`/api/v1/podcasts?${queryParams}`, z.array(podcast)); - }; - - getPodcastByChatId = async (request: GetPodcastByChatIdRequest) => { - // Validate the request - const parsedRequest = getPodcastByChatIdRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.get( - `/api/v1/podcasts/by-chat/${request.chat_id}`, - getPodcastByChaIdResponse - ); - }; - - /** - * Get a podcast by its ID (includes full transcript) - */ - getPodcastById = async (podcastId: number) => { - return baseApiService.get(`/api/v1/podcasts/${podcastId}`, podcast); - }; - - generatePodcast = async (request: GeneratePodcastRequest) => { - // Validate the request - const parsedRequest = generatePodcastRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.post(`/api/v1/podcasts/generate`, undefined, { - body: parsedRequest.data, - }); - }; - - loadPodcast = async ({ - request, - controller, - }: { - request: LoadPodcastRequest; - controller?: AbortController; - }) => { - // Validate the request - const parsedRequest = loadPodcastRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return await baseApiService.getBlob(`/api/v1/podcasts/${request.id}/stream`, { - signal: controller?.signal, - }); - }; - - deletePodcast = async (request: DeletePodcastRequest) => { - // Validate the request - const parsedRequest = deletePodcastRequest.safeParse(request); - - if (!parsedRequest.success) { - console.error("Invalid request:", parsedRequest.error); - - // Format a user frendly error message - const errorMessage = parsedRequest.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.delete(`/api/v1/podcasts/${request.id}`, deletePodcastResponse); - }; -} - -export const podcastsApiService = new PodcastsApiService(); diff --git a/surfsense_web/lib/chat/attachment-adapter.ts b/surfsense_web/lib/chat/attachment-adapter.ts new file mode 100644 index 000000000..c53ca00e8 --- /dev/null +++ b/surfsense_web/lib/chat/attachment-adapter.ts @@ -0,0 +1,304 @@ +/** + * Attachment adapter for assistant-ui + * + * This adapter handles file uploads by: + * 1. Uploading the file to the backend /attachments/process endpoint + * 2. The backend extracts markdown content using the configured ETL service + * 3. The extracted content is stored in the attachment and sent with messages + */ + +import type { AttachmentAdapter, CompleteAttachment, PendingAttachment } from "@assistant-ui/react"; +import { getBearerToken } from "@/lib/auth-utils"; + +/** + * Supported file types for the attachment adapter + * + * - Text/Markdown: .md, .markdown, .txt + * - Audio (if STT configured): .mp3, .mp4, .mpeg, .mpga, .m4a, .wav, .webm + * - Documents (depends on ETL service): .pdf, .docx, .doc, .pptx, .xlsx, .html + * - Images: .jpg, .jpeg, .png, .gif, .webp + */ +const ACCEPTED_FILE_TYPES = [ + // Text/Markdown (always supported) + ".md", + ".markdown", + ".txt", + // Audio files + ".mp3", + ".mp4", + ".mpeg", + ".mpga", + ".m4a", + ".wav", + ".webm", + // Document files (depends on ETL service) + ".pdf", + ".docx", + ".doc", + ".pptx", + ".xlsx", + ".html", + // Image files + ".jpg", + ".jpeg", + ".png", + ".gif", + ".webp", +].join(","); + +/** + * Response from the attachment processing endpoint + */ +interface ProcessAttachmentResponse { + id: string; + name: string; + type: "document" | "image" | "file"; + content: string; + contentLength: number; +} + +/** + * Extended CompleteAttachment with our custom extractedContent field + * We store the extracted text in a custom field so we can access it in onNew + */ +export interface ChatAttachment extends CompleteAttachment { + extractedContent: string; +} + +/** + * Process a file through the backend ETL service + */ +async function processAttachment(file: File): Promise { + const token = getBearerToken(); + if (!token) { + throw new Error("Not authenticated"); + } + + const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; + + const formData = new FormData(); + formData.append("file", file); + + const response = await fetch(`${backendUrl}/api/v1/attachments/process`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }); + + if (!response.ok) { + const errorText = await response.text(); + console.error("[processAttachment] Error response:", errorText); + let errorDetail = "Unknown error"; + try { + const errorJson = JSON.parse(errorText); + // FastAPI validation errors return detail as array + if (Array.isArray(errorJson.detail)) { + errorDetail = errorJson.detail + .map((err: { msg?: string; loc?: string[] }) => { + const field = err.loc?.join(".") || "unknown"; + return `${field}: ${err.msg || "validation error"}`; + }) + .join("; "); + } else if (typeof errorJson.detail === "string") { + errorDetail = errorJson.detail; + } else { + errorDetail = JSON.stringify(errorJson); + } + } catch { + errorDetail = errorText || `HTTP ${response.status}`; + } + throw new Error(errorDetail); + } + + return response.json(); +} + +// Store processed results for the send() method +const processedAttachments = new Map(); + +/** + * Create the attachment adapter for assistant-ui + * + * This adapter: + * 1. Accepts file upload + * 2. Processes the file through the backend ETL service + * 3. Returns the attachment with extracted markdown content + * + * The content is stored in the attachment and will be sent with the message. + */ +export function createAttachmentAdapter(): AttachmentAdapter { + return { + accept: ACCEPTED_FILE_TYPES, + + /** + * Async generator that yields pending states while processing + * and returns a pending attachment when done. + * + * IMPORTANT: The generator should return status: { type: "running", progress: 100 } + * NOT status: { type: "complete" }. The "complete" status is set by send(). + * Returning "complete" from the generator will prevent send() from being called! + * + * This pattern allows the UI to show a loading indicator + * while the file is being processed by the backend. + * The send() method is called to finalize the attachment. + */ + async *add(input: File | { file: File }): AsyncGenerator { + // Handle both direct File and { file: File } patterns + const file = input instanceof File ? input : input.file; + + if (!file) { + console.error("[AttachmentAdapter] No file found in input:", input); + throw new Error("No file provided"); + } + + console.log("[AttachmentAdapter] Processing file:", file.name); + + // Generate a unique ID for this attachment + const id = crypto.randomUUID(); + + // Determine attachment type from file + const attachmentType = file.type.startsWith("image/") ? "image" : "document"; + + // Yield initial pending state with "running" status (0% progress) + // This triggers the loading indicator in the UI + yield { + id, + type: attachmentType, + name: file.name, + file, + status: { type: "running", reason: "uploading", progress: 0 }, + } as PendingAttachment; + + try { + // Process the file through the backend ETL service + const result = await processAttachment(file); + + console.log( + "[AttachmentAdapter] File processed:", + result.name, + "content length:", + result.contentLength + ); + + // Verify we have the required fields + if (!result.content) { + console.error("[AttachmentAdapter] WARNING: No content received from backend!"); + } + + // Store the processed result for send() + processedAttachments.set(id, result); + + // Create the final pending attachment + // IMPORTANT: Use "running" status with progress: 100 to indicate processing is done + // but attachment is still pending. The "complete" status will be set by send(). + // Yield the final state to ensure it gets processed by the UI + yield { + id, + type: result.type, + name: result.name, + file, + status: { type: "running", reason: "uploading", progress: 100 }, + } as PendingAttachment; + } catch (error) { + console.error("[AttachmentAdapter] Failed to process attachment:", error); + throw error; + } + }, + + /** + * Called when user sends the message. + * Converts the pending attachment to a complete attachment. + */ + async send(pendingAttachment: PendingAttachment): Promise { + const result = processedAttachments.get(pendingAttachment.id); + + if (result) { + // Clean up stored result + processedAttachments.delete(pendingAttachment.id); + + return { + id: result.id, + type: result.type, + name: result.name, + contentType: "text/markdown", + status: { type: "complete" }, + content: [ + { + type: "text", + text: result.content, + }, + ], + extractedContent: result.content, + }; + } + + // Fallback if no processed result found + console.warn( + "[AttachmentAdapter] send() - No processed result found for attachment:", + pendingAttachment.id + ); + return { + id: pendingAttachment.id, + type: pendingAttachment.type, + name: pendingAttachment.name, + contentType: "text/plain", + status: { type: "complete" }, + content: [], + extractedContent: "", + }; + }, + + async remove() { + // No server-side cleanup needed since we don't persist attachments + }, + }; +} + +/** + * Extract attachment content for chat request + * + * This function extracts the content from attachments to be sent with the chat request. + * Only attachments that have been fully processed (have content) will be included. + */ +export function extractAttachmentContent( + attachments: Array +): Array<{ id: string; name: string; type: string; content: string }> { + return attachments + .filter((att): att is ChatAttachment => { + if (!att || typeof att !== "object") return false; + const a = att as Record; + // Check for our custom extractedContent field first + if (typeof a.extractedContent === "string" && a.extractedContent.length > 0) { + return true; + } + // Fallback: check if content array has text content + if (Array.isArray(a.content)) { + const textContent = (a.content as Array<{ type: string; text?: string }>).find( + (c) => c.type === "text" && typeof c.text === "string" && c.text.length > 0 + ); + return Boolean(textContent); + } + return false; + }) + .map((att) => { + // Get content from extractedContent or from content array + let content = ""; + if (typeof att.extractedContent === "string") { + content = att.extractedContent; + } else if (Array.isArray(att.content)) { + const textContent = (att.content as Array<{ type: string; text?: string }>).find( + (c) => c.type === "text" + ); + content = textContent?.text || ""; + } + + return { + id: att.id, + name: att.name, + type: att.type, + content, + }; + }); +} diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index 7eae21e05..e13f3cf79 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -1,27 +1,14 @@ -import type { GetChatsRequest } from "@/contracts/types/chat.types"; import type { GetDocumentsRequest } from "@/contracts/types/document.types"; import type { GetLLMConfigsRequest } from "@/contracts/types/llm-config.types"; -import type { GetMembersRequest } from "@/contracts/types/members.types"; -import type { GetPodcastsRequest } from "@/contracts/types/podcast.types"; -import type { GetRolesRequest } from "@/contracts/types/roles.types"; import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types"; export const cacheKeys = { - chats: { - activeChat: (chatId: string) => ["active-chat", chatId] as const, - globalQueryParams: (queries: GetChatsRequest["queryParams"]) => - [ - "chats", - queries?.search_space_id, - queries?.limit, - queries?.skip, - queries?.page, - queries?.page_size, - ] as const, - }, - podcasts: { - globalQueryParams: (queries: GetPodcastsRequest["queryParams"]) => - ["podcasts", ...(queries ? Object.values(queries) : [])] as const, + // New chat threads (assistant-ui) + threads: { + list: (searchSpaceId: number) => ["threads", searchSpaceId] as const, + detail: (threadId: number) => ["threads", "detail", threadId] as const, + search: (searchSpaceId: number, query: string) => + ["threads", "search", searchSpaceId, query] as const, }, documents: { globalQueryParams: (queries: GetDocumentsRequest["queryParams"]) => diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index 63ade6802..eac362b9c 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -83,7 +83,7 @@ "documents": "Documents", "connectors": "Connectors", "settings": "Settings", - "researcher": "Researcher", + "chat": "Chat", "api_keys": "API Keys", "profile": "Profile", "loading_dashboard": "Loading Dashboard", @@ -117,7 +117,7 @@ "try_again": "Try Again", "go_home": "Go Home", "delete_search_space": "Delete Search Space", - "delete_space_confirm": "Are you sure you want to delete \"{name}\"? This action cannot be undone. All documents, chats, and podcasts in this search space will be permanently deleted.", + "delete_space_confirm": "Are you sure you want to delete \"{name}\"? This action cannot be undone. All documents and chats in this search space will be permanently deleted.", "no_spaces_found": "No search spaces found", "create_first_space": "Create your first search space to get started", "created": "Created" @@ -136,7 +136,7 @@ "nav_menu": { "settings": "Settings", "platform": "Platform", - "researcher": "Researcher", + "chat": "Chat", "manage_llms": "Manage LLMs", "sources": "Sources", "add_sources": "Add Sources", @@ -149,10 +149,8 @@ "connectors": "Connectors", "add_connector": "Add Connector", "manage_connectors": "Manage Connectors", - "podcasts": "Podcasts", "logs": "Logs", "all_search_spaces": "All Search Spaces", - "chat": "Chat", "team": "Team" }, "pricing": { @@ -209,40 +207,6 @@ "try_again_later": "Please try again later.", "something_wrong": "Something went wrong" }, - "researcher": { - "loading": "Loading...", - "select_documents": "Select Documents", - "select_documents_desc": "Choose documents to include in your research context", - "loading_documents": "Loading documents...", - "select_connectors": "Select Connectors", - "select_connectors_desc": "Choose which data sources to include in your research", - "clear_all": "Clear All", - "select_all": "Select All", - "scope": "Scope", - "documents": "Documents", - "docs": "Docs", - "chunks": "Chunks", - "mode": "Mode", - "research_mode": "Research Mode", - "mode_qna": "Q&A", - "mode_general": "General Report", - "mode_general_short": "General", - "mode_deep": "Deep Report", - "mode_deep_short": "Deep", - "mode_deeper": "Deeper Report", - "mode_deeper_short": "Deeper", - "fast_llm": "Fast LLM", - "select_llm": "Select LLM", - "fast_llm_selection": "Fast LLM Selection", - "no_llm_configs": "No LLM configurations", - "configure_llm_to_start": "Configure AI models to get started", - "open_settings": "Open Settings", - "start_surfing": "Let's Start Surfing", - "through_knowledge_base": "through your knowledge base.", - "all_connectors": "All Connectors", - "connectors_selected": "{count} Connectors", - "placeholder": "Ask me anything..." - }, "connectors": { "title": "Connectors", "subtitle": "Manage your connected services and data sources.", @@ -468,27 +432,6 @@ "progress": "Progress", "roles_assigned_count": "{assigned} of {total} roles assigned" }, - "podcasts": { - "title": "Podcasts", - "subtitle": "Listen to generated podcasts.", - "search_placeholder": "Search podcasts...", - "sort_order": "Sort order", - "newest_first": "Newest First", - "oldest_first": "Oldest First", - "loading": "Loading podcasts...", - "error_loading": "Error loading podcasts", - "no_podcasts": "No podcasts found", - "adjust_filters": "Try adjusting your search filters", - "generate_hint": "Generate podcasts from your chats to get started", - "loading_podcast": "Loading podcast...", - "now_playing": "Now Playing", - "delete_podcast": "Delete Podcast", - "delete_confirm_1": "Are you sure you want to delete", - "delete_confirm_2": "This action cannot be undone.", - "cancel": "Cancel", - "delete": "Delete", - "deleting": "Deleting..." - }, "logs": { "title": "Task Logs", "subtitle": "Monitor and analyze all task execution logs", @@ -622,13 +565,11 @@ "breadcrumb": { "dashboard": "Dashboard", "search_space": "Search Space", - "researcher": "Researcher", + "chat": "Chat", "documents": "Documents", "connectors": "Connectors", - "podcasts": "Podcasts", "editor": "Editor", "logs": "Logs", - "chats": "Chats", "settings": "Settings", "upload_documents": "Upload Documents", "add_youtube": "Add YouTube Videos", @@ -647,7 +588,7 @@ "all_chats": "All Chats", "all_chats_description": "Browse and manage all your chats", "no_chats": "No chats yet", - "start_new_chat_hint": "Start a new chat from the researcher", + "start_new_chat_hint": "Start a new chat", "error_loading_chats": "Error loading chats", "chat_deleted": "Chat deleted successfully", "error_deleting_chat": "Failed to delete chat", diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index d33354e06..b943a3c2c 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -83,7 +83,7 @@ "documents": "文档", "connectors": "连接器", "settings": "设置", - "researcher": "AI 研究", + "chat": "聊天", "api_keys": "API 密钥", "profile": "个人资料", "loading_dashboard": "正在加载仪表盘", @@ -136,7 +136,7 @@ "nav_menu": { "settings": "设置", "platform": "平台", - "researcher": "AI 研究", + "chat": "聊天", "manage_llms": "管理 LLM", "sources": "数据源", "add_sources": "添加数据源", @@ -149,10 +149,8 @@ "connectors": "连接器", "add_connector": "添加连接器", "manage_connectors": "管理连接器", - "podcasts": "播客", "logs": "日志", "all_search_spaces": "所有搜索空间", - "chat": "聊天", "team": "团队" }, "pricing": { @@ -209,40 +207,6 @@ "try_again_later": "请稍后重试。", "something_wrong": "出错了" }, - "researcher": { - "loading": "加载中...", - "select_documents": "选择文档", - "select_documents_desc": "选择要包含在研究上下文中的文档", - "loading_documents": "正在加载文档...", - "select_connectors": "选择连接器", - "select_connectors_desc": "选择要包含在研究中的数据源", - "clear_all": "全部清除", - "select_all": "全部选择", - "scope": "范围", - "documents": "文档", - "docs": "文档", - "chunks": "块", - "mode": "模式", - "research_mode": "研究模式", - "mode_qna": "问答", - "mode_general": "通用报告", - "mode_general_short": "通用", - "mode_deep": "深度报告", - "mode_deep_short": "深度", - "mode_deeper": "更深度报告", - "mode_deeper_short": "更深", - "fast_llm": "快速 LLM", - "select_llm": "选择 LLM", - "fast_llm_selection": "快速 LLM 选择", - "no_llm_configs": "未配置 LLM", - "configure_llm_to_start": "配置 AI 模型以开始使用", - "open_settings": "打开设置", - "start_surfing": "开始探索", - "through_knowledge_base": "您的知识库。", - "all_connectors": "所有连接器", - "connectors_selected": "{count} 个连接器", - "placeholder": "问我任何问题..." - }, "connectors": { "title": "连接器", "subtitle": "管理您的已连接服务和数据源。", @@ -468,27 +432,6 @@ "progress": "进度", "roles_assigned_count": "{assigned} / {total} 个角色已分配" }, - "podcasts": { - "title": "播客", - "subtitle": "收听生成的播客。", - "search_placeholder": "搜索播客...", - "sort_order": "排序方式", - "newest_first": "最新优先", - "oldest_first": "最旧优先", - "loading": "正在加载播客...", - "error_loading": "加载播客时出错", - "no_podcasts": "未找到播客", - "adjust_filters": "尝试调整搜索条件", - "generate_hint": "从您的聊天中生成播客以开始使用", - "loading_podcast": "正在加载播客...", - "now_playing": "正在播放", - "delete_podcast": "删除播客", - "delete_confirm_1": "您确定要删除", - "delete_confirm_2": "此操作无法撤销。", - "cancel": "取消", - "delete": "删除", - "deleting": "删除中..." - }, "logs": { "title": "任务日志", "subtitle": "监控和分析所有任务执行日志", @@ -622,13 +565,11 @@ "breadcrumb": { "dashboard": "仪表盘", "search_space": "搜索空间", - "researcher": "AI 研究", + "chat": "聊天", "documents": "文档", "connectors": "连接器", - "podcasts": "播客", "editor": "编辑器", "logs": "日志", - "chats": "聊天", "settings": "设置", "upload_documents": "上传文档", "add_youtube": "添加 YouTube 视频", @@ -647,7 +588,7 @@ "all_chats": "所有对话", "all_chats_description": "浏览和管理您的所有对话", "no_chats": "暂无对话", - "start_new_chat_hint": "从研究员开始新对话", + "start_new_chat_hint": "开始新对话", "error_loading_chats": "加载对话时出错", "chat_deleted": "对话删除成功", "error_deleting_chat": "删除对话失败", diff --git a/surfsense_web/package.json b/surfsense_web/package.json index 719811c54..df0628981 100644 --- a/surfsense_web/package.json +++ b/surfsense_web/package.json @@ -30,7 +30,6 @@ "@blocknote/react": "^0.45.0", "@blocknote/server-util": "^0.45.0", "@hookform/resolvers": "^5.2.2", - "@llamaindex/chat-ui": "^0.5.17", "@next/third-parties": "^16.1.0", "@number-flow/react": "^0.5.10", "@radix-ui/react-accordion": "^1.2.11", diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml index a6b34ef59..8a48fc666 100644 --- a/surfsense_web/pnpm-lock.yaml +++ b/surfsense_web/pnpm-lock.yaml @@ -35,9 +35,6 @@ importers: '@hookform/resolvers': specifier: ^5.2.2 version: 5.2.2(react-hook-form@7.69.0(react@19.2.3)) - '@llamaindex/chat-ui': - specifier: ^0.5.17 - version: 0.5.17(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(codemirror@6.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28) '@next/third-parties': specifier: ^16.1.0 version: 16.1.0(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) @@ -522,114 +519,6 @@ packages: '@chevrotain/utils@11.0.3': resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - '@codemirror/autocomplete@6.20.0': - resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==} - - '@codemirror/commands@6.10.1': - resolution: {integrity: sha512-uWDWFypNdQmz2y1LaNJzK7fL7TYKLeUAU0npEC685OKTF3KcQ2Vu3klIM78D7I6wGhktme0lh3CuQLv0ZCrD9Q==} - - '@codemirror/lang-angular@0.1.4': - resolution: {integrity: sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g==} - - '@codemirror/lang-cpp@6.0.3': - resolution: {integrity: sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==} - - '@codemirror/lang-css@6.3.1': - resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} - - '@codemirror/lang-go@6.0.1': - resolution: {integrity: sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==} - - '@codemirror/lang-html@6.4.11': - resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} - - '@codemirror/lang-java@6.0.2': - resolution: {integrity: sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==} - - '@codemirror/lang-javascript@6.2.4': - resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} - - '@codemirror/lang-jinja@6.0.0': - resolution: {integrity: sha512-47MFmRcR8UAxd8DReVgj7WJN1WSAMT7OJnewwugZM4XiHWkOjgJQqvEM1NpMj9ALMPyxmlziEI1opH9IaEvmaw==} - - '@codemirror/lang-json@6.0.2': - resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} - - '@codemirror/lang-less@6.0.2': - resolution: {integrity: sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==} - - '@codemirror/lang-liquid@6.3.1': - resolution: {integrity: sha512-S/jE/D7iij2Pu70AC65ME6AYWxOOcX20cSJvaPgY5w7m2sfxsArAcUAuUgm/CZCVmqoi9KiOlS7gj/gyLipABw==} - - '@codemirror/lang-markdown@6.5.0': - resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==} - - '@codemirror/lang-php@6.0.2': - resolution: {integrity: sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==} - - '@codemirror/lang-python@6.2.1': - resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==} - - '@codemirror/lang-rust@6.0.2': - resolution: {integrity: sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==} - - '@codemirror/lang-sass@6.0.2': - resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==} - - '@codemirror/lang-sql@6.10.0': - resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==} - - '@codemirror/lang-vue@0.1.3': - resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==} - - '@codemirror/lang-wast@6.0.2': - resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==} - - '@codemirror/lang-xml@6.1.0': - resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==} - - '@codemirror/lang-yaml@6.1.2': - resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==} - - '@codemirror/language-data@6.5.2': - resolution: {integrity: sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg==} - - '@codemirror/language@6.11.3': - resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} - - '@codemirror/legacy-modes@6.5.2': - resolution: {integrity: sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==} - - '@codemirror/lint@6.9.2': - resolution: {integrity: sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==} - - '@codemirror/merge@6.11.2': - resolution: {integrity: sha512-NO5EJd2rLRbwVWLgMdhIntDIhfDtMOKYEZgqV5WnkNUS2oXOCVWLPjG/kgl/Jth2fGiOuG947bteqxP9nBXmMg==} - - '@codemirror/search@6.5.11': - resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} - - '@codemirror/state@6.5.2': - resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} - - '@codemirror/theme-one-dark@6.1.3': - resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - - '@codemirror/view@6.39.4': - resolution: {integrity: sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==} - - '@codesandbox/nodebox@0.1.8': - resolution: {integrity: sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==} - - '@codesandbox/sandpack-client@2.19.8': - resolution: {integrity: sha512-CMV4nr1zgKzVpx4I3FYvGRM5YT0VaQhALMW9vy4wZRhEyWAtJITQIqZzrTGWqB1JvV7V72dVEUCUPLfYz5hgJQ==} - - '@codesandbox/sandpack-react@2.20.0': - resolution: {integrity: sha512-takd1YpW/PMQ6KPQfvseWLHWklJovGY8QYj8MtWnskGKbjOGJ6uZfyZbcJ6aCFLQMpNyjTqz9AKNbvhCOZ1TUQ==} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 - react-dom: ^16.8.0 || ^17 || ^18 || ^19 - '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -1411,146 +1300,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@lexical/clipboard@0.35.0': - resolution: {integrity: sha512-ko7xSIIiayvDiqjNDX6fgH9RlcM6r9vrrvJYTcfGVBor5httx16lhIi0QJZ4+RNPvGtTjyFv4bwRmsixRRwImg==} - - '@lexical/code@0.35.0': - resolution: {integrity: sha512-ox4DZwETQ9IA7+DS6PN8RJNwSAF7RMjL7YTVODIqFZ5tUFIf+5xoCHbz7Fll0Bvixlp12hVH90xnLwTLRGpkKw==} - - '@lexical/devtools-core@0.35.0': - resolution: {integrity: sha512-C2wwtsMCR6ZTfO0TqpSM17RLJWyfHmifAfCTjFtOJu15p3M6NO/nHYK5Mt7YMQteuS89mOjB4ng8iwoLEZ6QpQ==} - peerDependencies: - react: '>=17.x' - react-dom: '>=17.x' - - '@lexical/dragon@0.35.0': - resolution: {integrity: sha512-SL6mT5pcqrt6hEbJ16vWxip5+r3uvMd0bQV5UUxuk+cxIeuP86iTgRh0HFR7SM2dRTYovL6/tM/O+8QLAUGTIg==} - - '@lexical/hashtag@0.35.0': - resolution: {integrity: sha512-LYJWzXuO2ZjKsvQwrLkNZiS2TsjwYkKjlDgtugzejquTBQ/o/nfSn/MmVx6EkYLOYizaJemmZbz3IBh+u732FA==} - - '@lexical/history@0.35.0': - resolution: {integrity: sha512-onjDRLLxGbCfHexSxxrQaDaieIHyV28zCDrbxR5dxTfW8F8PxjuNyuaG0z6o468AXYECmclxkP+P4aT6poHEpQ==} - - '@lexical/html@0.35.0': - resolution: {integrity: sha512-rXGFE5S5rKsg3tVnr1s4iEgOfCApNXGpIFI3T2jGEShaCZ5HLaBY9NVBXnE9Nb49e9bkDkpZ8FZd1qokCbQXbw==} - - '@lexical/link@0.35.0': - resolution: {integrity: sha512-+0Wx6cBwO8TfdMzpkYFacsmgFh8X1rkiYbq3xoLvk3qV8upYxaMzK1s8Q1cpKmWyI0aZrU6z7fiK4vUqB7+69w==} - - '@lexical/list@0.35.0': - resolution: {integrity: sha512-owsmc8iwgExBX8sFe8fKTiwJVhYULt9hD1RZ/HwfaiEtRZZkINijqReOBnW2mJfRxBzhFSWc4NG3ISB+fHYzqw==} - - '@lexical/mark@0.35.0': - resolution: {integrity: sha512-W0hwMTAVeexvpk9/+J6n1G/sNkpI/Meq1yeDazahFLLAwXLHtvhIAq2P/klgFknDy1hr8X7rcsQuN/bqKcKHYg==} - - '@lexical/markdown@0.35.0': - resolution: {integrity: sha512-BlNyXZAt4gWidMw0SRWrhBETY1BpPglFBZI7yzfqukFqgXRh7HUQA28OYeI/nsx9pgNob8TiUduUwShqqvOdEA==} - - '@lexical/offset@0.35.0': - resolution: {integrity: sha512-DRE4Df6qYf2XiV6foh6KpGNmGAv2ANqt3oVXpyS6W8hTx3+cUuAA1APhCZmLNuU107um4zmHym7taCu6uXW5Yg==} - - '@lexical/overflow@0.35.0': - resolution: {integrity: sha512-B25YvnJQTGlZcrNv7b0PJBLWq3tl8sql497OHfYYLem7EOMPKKDGJScJAKM/91D4H/mMAsx5gnA/XgKobriuTg==} - - '@lexical/plain-text@0.35.0': - resolution: {integrity: sha512-lwBCUNMJf7Gujp2syVWMpKRahfbTv5Wq+H3HK1Q1gKH1P2IytPRxssCHvexw9iGwprSyghkKBlbF3fGpEdIJvQ==} - - '@lexical/react@0.35.0': - resolution: {integrity: sha512-uYAZSqumH8tRymMef+A0f2hQvMwplKK9DXamcefnk3vSNDHHqRWQXpiUo6kD+rKWuQmMbVa5RW4xRQebXEW+1A==} - peerDependencies: - react: '>=17.x' - react-dom: '>=17.x' - - '@lexical/rich-text@0.35.0': - resolution: {integrity: sha512-qEHu8g7vOEzz9GUz1VIUxZBndZRJPh9iJUFI+qTDHj+tQqnd5LCs+G9yz6jgNfiuWWpezTp0i1Vz/udNEuDPKQ==} - - '@lexical/selection@0.35.0': - resolution: {integrity: sha512-mMtDE7Q0nycXdFTTH/+ta6EBrBwxBB4Tg8QwsGntzQ1Cq//d838dpXpFjJOqHEeVHUqXpiuj+cBG8+bvz/rPRw==} - - '@lexical/table@0.35.0': - resolution: {integrity: sha512-9jlTlkVideBKwsEnEkqkdg7A3mije1SvmfiqoYnkl1kKJCLA5iH90ywx327PU0p+bdnURAytWUeZPXaEuEl2OA==} - - '@lexical/text@0.35.0': - resolution: {integrity: sha512-uaMh46BkysV8hK8wQwp5g/ByZW+2hPDt8ahAErxtf8NuzQem1FHG/f5RTchmFqqUDVHO3qLNTv4AehEGmXv8MA==} - - '@lexical/utils@0.35.0': - resolution: {integrity: sha512-2H393EYDnFznYCDFOW3MHiRzwEO5M/UBhtUjvTT+9kc+qhX4U3zc8ixQalo5UmZ5B2nh7L/inXdTFzvSRXtsRA==} - - '@lexical/yjs@0.35.0': - resolution: {integrity: sha512-3DSP7QpmTGYU9bN/yljP0PIao4tNIQtsR4ycauWNSawxs/GQCZtSmAPcLRnCm6qpqsDDjUtKjO/1Ej8FRp0m0w==} - peerDependencies: - yjs: '>=13.5.22' - - '@lezer/common@1.4.0': - resolution: {integrity: sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==} - - '@lezer/cpp@1.1.4': - resolution: {integrity: sha512-aYSdZyUueeTgnfXQntiGUqKNW5WujlAsIbbHzkfJDneSZoyjPg8ObmWG3bzDPVYMC/Kf4l43WJLCunPnYFfQ0g==} - - '@lezer/css@1.3.0': - resolution: {integrity: sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==} - - '@lezer/go@1.0.1': - resolution: {integrity: sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==} - - '@lezer/highlight@1.2.3': - resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} - - '@lezer/html@1.3.12': - resolution: {integrity: sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==} - - '@lezer/java@1.1.3': - resolution: {integrity: sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==} - - '@lezer/javascript@1.5.4': - resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - - '@lezer/json@1.0.3': - resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} - - '@lezer/lr@1.4.5': - resolution: {integrity: sha512-/YTRKP5yPPSo1xImYQk7AZZMAgap0kegzqCSYHjAL9x1AZ0ZQW+IpcEzMKagCsbTsLnVeWkxYrCNeXG8xEPrjg==} - - '@lezer/markdown@1.6.1': - resolution: {integrity: sha512-72ah+Sml7lD8Wn7lnz9vwYmZBo9aQT+I2gjK/0epI+gjdwUbWw3MJ/ZBGEqG1UfrIauRqH37/c5mVHXeCTGXtA==} - - '@lezer/php@1.0.5': - resolution: {integrity: sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==} - - '@lezer/python@1.1.18': - resolution: {integrity: sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==} - - '@lezer/rust@1.0.2': - resolution: {integrity: sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==} - - '@lezer/sass@1.1.0': - resolution: {integrity: sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==} - - '@lezer/xml@1.0.6': - resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==} - - '@lezer/yaml@1.0.3': - resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==} - - '@llamaindex/chat-ui@0.5.17': - resolution: {integrity: sha512-/rw+gfYlCVzUJScyKEs+41pTq/YrojrsgRB1xExjpTm0fMY9YWfY94HsDpPl6VU0w2HImQ3SBktEW9gmEmucbw==} - peerDependencies: - react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc - - '@llamaindex/llama-deploy@0.0.2': - resolution: {integrity: sha512-Vh41PKVtgaNtiyXKwQEDteS4S7Bt0mS8a44OmqRE8bIGeBwQ3GRZExMXTeERuJH4va6UO/2KzHU6u6m6kSeAvg==} - - '@llamaindex/pdf-viewer@1.3.0': - resolution: {integrity: sha512-HJtjzmxn+erb3Vq89W5atPq0q6uyZMMCgzOnmstxudzaHW/Yj1dp1ojCuBh/wlP1tUnIRoe9RmvC0ahmqSwRUA==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@mantine/core@8.3.10': resolution: {integrity: sha512-aKQFETN14v6GtM07b/G5yJneMM1yrgf9mNrTah6GVy5DvQM0AeutITT7toHqh5gxxwzdg/DoY+HQsv5zhqnc5g==} peerDependencies: @@ -1568,26 +1317,9 @@ packages: peerDependencies: react: '>=16.8.0' - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} - '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - '@mdxeditor/editor@3.52.1': - resolution: {integrity: sha512-+YoRPaNwXyVhjqQMfyo/yVTOik8UCDovUTAJ67ED49z+c2dhXn89QSaBgFFeknJ7wd67Y6HjCPX37NjZ7UuILQ==} - engines: {node: '>=16'} - peerDependencies: - react: '>= 18 || >= 19' - react-dom: '>= 18 || >= 19' - - '@mdxeditor/gurx@1.2.4': - resolution: {integrity: sha512-9ZykIFYhKaXaaSPCs1cuI+FvYDegJjbKwmA4ASE/zY+hJY6EYqvoye4esiO85CjhOw9aoD/izD/CU78/egVqmg==} - engines: {node: '>=16'} - peerDependencies: - react: '>= 18 || >= 19' - react-dom: '>= 18 || >= 19' - '@mermaid-js/parser@0.6.3': resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} @@ -1676,9 +1408,6 @@ packages: react: ^18 || ^19 react-dom: ^18 || ^19 - '@open-draft/deferred-promise@2.2.0': - resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} - '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -1769,9 +1498,6 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} - '@radix-ui/colors@3.0.0': - resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} - '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -2071,24 +1797,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.15': - resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-icons@1.3.2': - resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} - peerDependencies: - react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc - '@radix-ui/react-id@1.0.0': resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} peerDependencies: @@ -2286,19 +1994,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.8': - resolution: {integrity: sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-radio-group@1.3.8': resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} peerDependencies: @@ -2351,19 +2046,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.7': - resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-separator@1.1.8': resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} peerDependencies: @@ -2474,19 +2156,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toolbar@1.1.11': - resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-tooltip@1.2.8': resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: @@ -2653,16 +2322,6 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@react-hook/intersection-observer@3.1.2': - resolution: {integrity: sha512-mWU3BMkmmzyYMSuhO9wu3eJVP21N8TcgYm9bZnTrMwuM818bEk+0NRM3hP+c/TqA9Ln5C7qE53p1H0QMtzYdvQ==} - peerDependencies: - react: '>=16.8' - - '@react-hook/passive-layout-effect@1.2.1': - resolution: {integrity: sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==} - peerDependencies: - react: '>=16.8' - '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -2708,9 +2367,6 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@stitches/core@1.2.8': - resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} - '@swc/core-darwin-arm64@1.15.7': resolution: {integrity: sha512-+hNVUfezUid7LeSHqnhoC6Gh3BROABxjlDNInuZ/fie1RUxaEX4qzDwdTgozJELgHhvYxyPIg1ro8ibnKtgO4g==} engines: {node: '>=10'} @@ -3151,9 +2807,6 @@ packages: '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - '@types/mdast@3.0.15': - resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} - '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -3172,9 +2825,6 @@ packages: '@types/pg@8.16.0': resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3260,38 +2910,6 @@ packages: resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/codemirror-extensions-basic-setup@4.25.4': - resolution: {integrity: sha512-YzNwkm0AbPv1EXhCHYR5v0nqfemG2jEB0Z3Att4rBYqKrlG7AA9Rhjc3IyBaOzsBu18wtrp9/+uhTyu7TXSRng==} - peerDependencies: - '@codemirror/autocomplete': '>=6.0.0' - '@codemirror/commands': '>=6.0.0' - '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' - '@codemirror/search': '>=6.0.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - - '@uiw/codemirror-theme-github@4.25.4': - resolution: {integrity: sha512-M5zRT2vIpNsuKN0Lz+DwLnmhHW8Eddp1M9zC0hm3V+bvffmaSn/pUDey1eqGIv5xNNmjhqvDAz0a90xLYCzvSw==} - - '@uiw/codemirror-themes@4.25.4': - resolution: {integrity: sha512-2SLktItgcZC4p0+PfFusEbAHwbuAWe3bOOntCevVgHtrWGtGZX3IPv2k8IKZMgOXtAHyGKpJvT9/nspPn/uCQg==} - peerDependencies: - '@codemirror/language': '>=6.0.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - - '@uiw/react-codemirror@4.25.4': - resolution: {integrity: sha512-ipO067oyfUw+DVaXhQCxkB0ZD9b7RnY+ByrprSYSKCHaULvJ3sqWYC/Zen6zVQ8/XC4o5EPBfatGiX20kC7XGA==} - peerDependencies: - '@babel/runtime': '>=7.11.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' - react: '>=17.0.0' - react-dom: '>=17.0.0' - '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -3394,11 +3012,6 @@ packages: resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} engines: {node: '>= 20'} - '@wojtekmaj/react-hooks@1.17.2': - resolution: {integrity: sha512-E2I1D39Sw6AmXSArfvHjCoB2KE8QxmpuoKn0x+xq7IXKCQi3lGAQn1MrFqDKiwJt08Mmg+I9sp5Zt0nSfStfuQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -3432,9 +3045,6 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - anser@2.3.5: - resolution: {integrity: sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -3530,16 +3140,10 @@ packages: base16@1.0.0: resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.11: resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -3553,12 +3157,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -3581,10 +3179,6 @@ packages: canvas-confetti@1.9.4: resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==} - canvas@3.2.0: - resolution: {integrity: sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==} - engines: {node: ^18.12.0 || >= 20.9.0} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3629,18 +3223,12 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - clean-set@1.1.2: - resolution: {integrity: sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==} - client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -3648,14 +3236,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - cm6-theme-basic-light@0.2.0: - resolution: {integrity: sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==} - peerDependencies: - '@codemirror/language': ^6.0.0 - '@codemirror/state': ^6.0.0 - '@codemirror/view': ^6.0.0 - '@lezer/highlight': ^1.0.0 - cmdk@0.2.1: resolution: {integrity: sha512-U6//9lQ6JvT47+6OF6Gi8BvkxYQ8SCRRSKIJkthIMsFsLZRG0cKvTtuTaefyIKMQb8rvvXy0wGdpTNq/jPtm+g==} peerDependencies: @@ -3668,9 +3248,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - codemirror@6.0.2: - resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -3699,9 +3276,6 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - compute-scroll-into-view@2.0.4: - resolution: {integrity: sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g==} - compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} @@ -3897,10 +3471,6 @@ packages: resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} engines: {node: '>=12'} - d@1.0.2: - resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} - engines: {node: '>=0.12'} - dagre-d3-es@7.0.13: resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==} @@ -3955,14 +3525,6 @@ packages: decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -4003,10 +3565,6 @@ packages: diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -4014,19 +3572,10 @@ packages: dompurify@3.3.1: resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} - downshift@7.6.2: - resolution: {integrity: sha512-iOv+E1Hyt3JDdL9yYcOgW7nZ7GQ2Uz6YbggwXvKUSleetYhU2nXD482Rz6CzvM4lvI1At34BYruKAL4swRGxaA==} - peerDependencies: - react: '>=16.12.0' - drizzle-kit@0.31.8: resolution: {integrity: sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==} hasBin: true @@ -4139,9 +3688,6 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.18.4: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} @@ -4186,17 +3732,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es5-ext@0.10.64: - resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} - engines: {node: '>=0.10'} - - es6-iterator@2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - - es6-symbol@3.1.4: - resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} - engines: {node: '>=0.12'} - esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -4223,9 +3758,6 @@ packages: engines: {node: '>=18'} hasBin: true - escape-carriage@1.3.1: - resolution: {integrity: sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -4333,10 +3865,6 @@ packages: esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - esniff@2.0.1: - resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} - engines: {node: '>=0.10'} - espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4381,20 +3909,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - event-emitter@0.3.5: - resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - - ext@1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} - extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -4421,9 +3939,6 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - fault@2.0.1: - resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - fbemitter@3.0.0: resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} @@ -4496,9 +4011,6 @@ packages: react-dom: optional: true - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4587,10 +4099,6 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - fuse.js@6.6.2: - resolution: {integrity: sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==} - engines: {node: '>=10'} - geist@1.5.1: resolution: {integrity: sha512-mAHZxIsL2o3ZITFaBVFBnwyDOw+zNLYum6A6nIjpzCGIO8QtC3V76XF2RnZTyLx1wlDTmMDy8jg3Ib52MIjGvQ==} peerDependencies: @@ -4623,9 +4131,6 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -4748,9 +4253,6 @@ packages: hast-util-to-text@4.0.2: resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} - hast-util-whitespace@2.0.1: - resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} - hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -4799,9 +4301,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -4823,15 +4322,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - inline-style-parser@0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -4846,10 +4336,6 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - intersection-observer@0.10.0: - resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} - deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. - intl-messageformat@10.7.18: resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} @@ -4881,10 +4367,6 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} - is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - is-bun-module@2.0.0: resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} @@ -5090,10 +4572,6 @@ packages: khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - langium@3.3.1: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} @@ -5115,9 +4593,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lexical@0.35.0: - resolution: {integrity: sha512-3VuV8xXhh5xJA6tzvfDvE0YBCMkIZUmxtRilJQDDdCgJCc+eut6qAv2qbN+pbqvarqcQqPN1UF+8YvsjmyOZpw==} - lib0@0.2.115: resolution: {integrity: sha512-noaW4yNp6hCjOgDnWWxW0vGXE3kZQI5Kqiwz+jIWXavI9J9WyfJ9zjsbQlQlgjIbHBrvlA/x3TSIXBUJj+0L6g==} engines: {node: '>=16'} @@ -5212,18 +4687,12 @@ packages: lodash.curry@4.1.1: resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} - lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.flow@3.5.0: resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -5237,11 +4706,6 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lucide-react@0.453.0: - resolution: {integrity: sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc - lucide-react@0.477.0: resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==} peerDependencies: @@ -5252,19 +4716,9 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-cancellable-promise@1.3.2: - resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==} - - make-event-props@1.6.2: - resolution: {integrity: sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==} - markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -5285,69 +4739,30 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-definitions@5.1.2: - resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} - - mdast-util-directive@3.1.0: - resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} - - mdast-util-find-and-replace@2.2.2: - resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} - mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - mdast-util-from-markdown@1.3.1: - resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} - mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} - mdast-util-frontmatter@2.0.1: - resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} - - mdast-util-gfm-autolink-literal@1.0.3: - resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} - mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - mdast-util-gfm-footnote@1.0.2: - resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} - mdast-util-gfm-footnote@2.1.0: resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} - mdast-util-gfm-strikethrough@1.0.3: - resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} - mdast-util-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - mdast-util-gfm-table@1.0.7: - resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} - mdast-util-gfm-table@2.0.0: resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - mdast-util-gfm-task-list-item@1.0.2: - resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} - mdast-util-gfm-task-list-item@2.0.0: resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - mdast-util-gfm@2.0.2: - resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} - mdast-util-gfm@3.1.0: resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} - mdast-util-highlight-mark@1.2.2: - resolution: {integrity: sha512-OYumVoytj+B9YgwzBhBcYUCLYHIPvJtAvwnMyKhUXbfUFuER5S+FDZyu9fadUxm2TCT5fRYK3jQXh2ioWAxrMw==} - - mdast-util-math@2.0.2: - resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} - mdast-util-math@3.0.0: resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} @@ -5363,44 +4778,21 @@ packages: mdast-util-mdxjs-esm@2.0.1: resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - mdast-util-phrasing@3.0.1: - resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} - mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - mdast-util-to-hast@12.3.0: - resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} - mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - mdast-util-to-markdown@1.5.0: - resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} - mdast-util-to-markdown@2.1.2: resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - mdast-util-to-string@3.2.0: - resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} - mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - memoize-one@5.2.1: - resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - - merge-refs@1.3.0: - resolution: {integrity: sha512-nqXPXbso+1dcKDpPCXvwZyJILz+vSLqGGOnDrYHQYE+B8n9JTCekVLC65AfCpR4ggVyA/45Y0iR9LDyS2iI+zA==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -5408,9 +4800,6 @@ packages: mermaid@11.12.2: resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} - micromark-core-commonmark@1.1.0: - resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -5443,60 +4832,27 @@ packages: micromark-util-types: optional: true - micromark-extension-directive@3.0.2: - resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} - - micromark-extension-frontmatter@2.0.0: - resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} - - micromark-extension-gfm-autolink-literal@1.0.5: - resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} - micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - micromark-extension-gfm-footnote@1.1.2: - resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} - micromark-extension-gfm-footnote@2.1.0: resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - micromark-extension-gfm-strikethrough@1.0.7: - resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} - micromark-extension-gfm-strikethrough@2.1.0: resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@1.0.7: - resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} - micromark-extension-gfm-table@2.1.1: resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} - micromark-extension-gfm-tagfilter@1.0.2: - resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} - micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - micromark-extension-gfm-task-list-item@1.0.5: - resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} - micromark-extension-gfm-task-list-item@2.1.0: resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - micromark-extension-gfm@2.0.3: - resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} - micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - micromark-extension-highlight-mark@1.2.0: - resolution: {integrity: sha512-huGtbd/9kQsMk8u7nrVMaS5qH/47yDG6ZADggo5Owz5JoY8wdfQjfuy118/QiYNCvdFuFDbzT0A7K7Hp2cBsXA==} - - micromark-extension-math@2.1.2: - resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} - micromark-extension-math@3.1.0: resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} @@ -5515,129 +4871,69 @@ packages: micromark-extension-mdxjs@3.0.0: resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} - micromark-factory-destination@1.1.0: - resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} - micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - micromark-factory-label@1.1.0: - resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} - micromark-factory-label@2.0.1: resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} micromark-factory-mdx-expression@2.0.3: resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} - micromark-factory-space@1.1.0: - resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} - micromark-factory-space@2.0.1: resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - micromark-factory-title@1.1.0: - resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} - micromark-factory-title@2.0.1: resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - micromark-factory-whitespace@1.1.0: - resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} - micromark-factory-whitespace@2.0.1: resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - micromark-util-character@1.2.0: - resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} - micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - micromark-util-chunked@1.1.0: - resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} - micromark-util-chunked@2.0.1: resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - micromark-util-classify-character@1.1.0: - resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} - micromark-util-classify-character@2.0.1: resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - micromark-util-combine-extensions@1.1.0: - resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} - micromark-util-combine-extensions@2.0.1: resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - micromark-util-decode-numeric-character-reference@1.1.0: - resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} - micromark-util-decode-numeric-character-reference@2.0.2: resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - micromark-util-decode-string@1.1.0: - resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} - micromark-util-decode-string@2.0.1: resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - micromark-util-encode@1.1.0: - resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} - micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} micromark-util-events-to-acorn@2.0.3: resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} - micromark-util-html-tag-name@1.2.0: - resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} - micromark-util-html-tag-name@2.0.1: resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - micromark-util-normalize-identifier@1.1.0: - resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} - micromark-util-normalize-identifier@2.0.1: resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - micromark-util-resolve-all@1.1.0: - resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} - micromark-util-resolve-all@2.0.1: resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - micromark-util-sanitize-uri@1.2.0: - resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} - micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@1.1.0: - resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} - micromark-util-subtokenize@2.1.0: resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - micromark-util-symbol@1.1.0: - resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} - micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - micromark-util-types@1.1.0: - resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} - micromark-util-types@2.0.2: resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - micromark@3.2.0: - resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} - micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} @@ -5649,18 +4945,10 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -5671,9 +4959,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -5697,10 +4982,6 @@ packages: react-dom: optional: true - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5714,9 +4995,6 @@ packages: engines: {node: ^18 || >=20} hasBin: true - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -5748,9 +5026,6 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next-tick@1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - next@16.1.0: resolution: {integrity: sha512-Y+KbmDbefYtHDDQKLNrmzE/YYzG2msqo2VXhzh5yrJ54tx/6TmGdkR5+kP9ma7i7LwZpZMfoY3m/AoPPPKxtVw==} engines: {node: '>=20.9.0'} @@ -5772,10 +5047,6 @@ packages: sass: optional: true - node-abi@3.85.0: - resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} - engines: {node: '>=10'} - node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} @@ -5830,9 +5101,6 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -5846,9 +5114,6 @@ packages: orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} - outvariant@1.4.0: - resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} - own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -5894,17 +5159,9 @@ packages: path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - path2d@0.2.2: - resolution: {integrity: sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ==} - engines: {node: '>=6'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdfjs-dist@4.8.69: - resolution: {integrity: sha512-IHZsA4T7YElCKNNXtiLgqScw4zPd3pG9do8UrznC757gMd7UPeHSL2qwNNMJo4r79fl8oj1Xx+1nh2YkzdMpLQ==} - engines: {node: '>=18'} - pg-cloudflare@1.2.7: resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} @@ -6002,11 +5259,6 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - hasBin: true - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -6028,9 +5280,6 @@ packages: property-information@5.6.0: resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -6127,9 +5376,6 @@ packages: prosemirror-view@1.41.4: resolution: {integrity: sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} @@ -6144,10 +5390,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - react-base16-styling@0.6.0: resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} @@ -6157,9 +5399,6 @@ packages: peerDependencies: react: '>=16.8.0' - react-devtools-inline@4.4.0: - resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} - react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: @@ -6178,12 +5417,6 @@ packages: react: '>=16.4.0' react-dom: '>=16.4.0' - react-error-boundary@3.1.4: - resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} - engines: {node: '>=10', npm: '>=6'} - peerDependencies: - react: '>=16.13.1' - react-hook-form@7.69.0: resolution: {integrity: sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==} engines: {node: '>=18.0.0'} @@ -6195,20 +5428,9 @@ packages: peerDependencies: react: '*' - react-intersection-observer@9.5.1: - resolution: {integrity: sha512-YwcNF/4WsMAG1rLVDQHSbpdEW9vDaIl4QW88d+vqeXNUewFV4AJDQB14oHpAJ3rRCnKRmwu3nqfwwYe6wioNIg==} - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-json-view-lite@2.5.0: resolution: {integrity: sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g==} engines: {node: '>=18'} @@ -6230,12 +5452,6 @@ packages: '@types/react': '>=18' react: '>=18' - react-markdown@8.0.7: - resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==} - peerDependencies: - '@types/react': '>=16' - react: '>=16' - react-medium-image-zoom@5.4.0: resolution: {integrity: sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg==} peerDependencies: @@ -6248,16 +5464,6 @@ packages: react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-pdf@9.2.1: - resolution: {integrity: sha512-AJt0lAIkItWEZRA5d/mO+Om4nPCuTiQ0saA+qItO967DTjmGjnhmF+Bi2tL286mOTfBlF5CyLzJ35KTMaDoH+A==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -6325,13 +5531,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-window@1.8.9: - resolution: {integrity: sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q==} - engines: {node: '>8.0.0'} - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-wrap-balancer@1.1.1: resolution: {integrity: sha512-AB+l7FPRWl6uZ28VcJ8skkwLn2+UC62bjiw8tQUrZPlEWDVnR9MG0lghyn7EyxuJSsFEpht4G+yh2WikEqQ/5Q==} peerDependencies: @@ -6341,10 +5540,6 @@ packages: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readdirp@5.0.0: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} @@ -6433,34 +5628,18 @@ packages: '@types/mdast': optional: true - remark-code-import@1.2.0: - resolution: {integrity: sha512-fgwLruqlZbVOIhCJFjY+JDwPZhA4/eK3InJzN8Ox8UDdtudpG212JwtRj6la+lAzJU7JmSEyewZSukVZdknt3Q==} - engines: {node: '>= 12'} - - remark-gfm@3.0.1: - resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==} - remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - remark-math@5.1.1: - resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} - remark-math@6.0.0: resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} remark-mdx@3.1.1: resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} - remark-parse@10.0.2: - resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} - remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - remark-rehype@10.1.0: - resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} - remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} @@ -6517,17 +5696,10 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -6613,12 +5785,6 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: @@ -6653,9 +5819,6 @@ packages: stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} - static-browser-server@1.0.3: - resolution: {integrity: sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==} - stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -6665,9 +5828,6 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 - strict-event-emitter@0.4.6: - resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} - string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -6691,9 +5851,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -6701,27 +5858,13 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-indent@4.1.1: - resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} - engines: {node: '>=12'} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-mod@4.1.3: - resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - style-to-object@0.4.4: - resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} - style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} @@ -6760,9 +5903,6 @@ packages: tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} - tailwind-merge@2.6.0: - resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} - tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} @@ -6778,13 +5918,6 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - third-party-capital@1.0.20: resolution: {integrity: sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==} @@ -6792,9 +5925,6 @@ packages: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -6810,16 +5940,10 @@ packages: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - to-gatsby-remark-plugin@0.1.0: - resolution: {integrity: sha512-blmhJ/gIrytWnWLgPSRCkhCPeki6UBK2daa3k9mGahN7GjwHu8KrS7F70MvwlsG7IE794JLgwAdCbi4hU4faFQ==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - to-vfile@6.1.0: - resolution: {integrity: sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==} - tough-cookie@5.1.2: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} @@ -6861,9 +5985,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -6872,9 +5993,6 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type@2.7.3: - resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -6913,57 +6031,30 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - unidiff@1.0.4: - resolution: {integrity: sha512-ynU0vsAXw0ir8roa+xPCUHmnJ5goc5BTM2Kuc3IJd8UwgaeRs7VSD5+eeaQL+xp1JtB92hu/Zy/Lgy7RZcr1pQ==} - - unified@10.1.2: - resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} - unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} - unist-util-generated@2.0.1: - resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} - - unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} - unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-position-from-estree@2.0.0: resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} - unist-util-position@4.0.4: - resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} - unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} unist-util-remove-position@5.0.0: resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - unist-util-stringify-position@2.0.3: - resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} - - unist-util-stringify-position@3.0.3: - resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} - unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - unist-util-visit-parents@5.1.3: - resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} - unist-util-visit-parents@6.0.2: resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - unist-util-visit@4.1.2: - resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} - unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -7041,35 +6132,12 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - uvu@0.5.6: - resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} - engines: {node: '>=8'} - hasBin: true - - vaul@0.9.9: - resolution: {integrity: sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} - vfile-message@2.0.4: - resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} - - vfile-message@3.1.4: - resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - vfile@4.2.1: - resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} - - vfile@5.3.7: - resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} - vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} @@ -7100,9 +6168,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - warning@4.0.3: - resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -7153,9 +6218,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -7554,307 +6616,6 @@ snapshots: '@chevrotain/utils@11.0.3': {} - '@codemirror/autocomplete@6.20.0': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - - '@codemirror/commands@6.10.1': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - - '@codemirror/lang-angular@0.1.4': - dependencies: - '@codemirror/lang-html': 6.4.11 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/language': 6.11.3 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-cpp@6.0.3': - dependencies: - '@codemirror/language': 6.11.3 - '@lezer/cpp': 1.1.4 - - '@codemirror/lang-css@6.3.1': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/css': 1.3.0 - - '@codemirror/lang-go@6.0.1': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/go': 1.0.1 - - '@codemirror/lang-html@6.4.11': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/css': 1.3.0 - '@lezer/html': 1.3.12 - - '@codemirror/lang-java@6.0.2': - dependencies: - '@codemirror/language': 6.11.3 - '@lezer/java': 1.1.3 - - '@codemirror/lang-javascript@6.2.4': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.2 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/javascript': 1.5.4 - - '@codemirror/lang-jinja@6.0.0': - dependencies: - '@codemirror/lang-html': 6.4.11 - '@codemirror/language': 6.11.3 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-json@6.0.2': - dependencies: - '@codemirror/language': 6.11.3 - '@lezer/json': 1.0.3 - - '@codemirror/lang-less@6.0.2': - dependencies: - '@codemirror/lang-css': 6.3.1 - '@codemirror/language': 6.11.3 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-liquid@6.3.1': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/lang-html': 6.4.11 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-markdown@6.5.0': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/lang-html': 6.4.11 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/markdown': 1.6.1 - - '@codemirror/lang-php@6.0.2': - dependencies: - '@codemirror/lang-html': 6.4.11 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/php': 1.0.5 - - '@codemirror/lang-python@6.2.1': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/python': 1.1.18 - - '@codemirror/lang-rust@6.0.2': - dependencies: - '@codemirror/language': 6.11.3 - '@lezer/rust': 1.0.2 - - '@codemirror/lang-sass@6.0.2': - dependencies: - '@codemirror/lang-css': 6.3.1 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/sass': 1.1.0 - - '@codemirror/lang-sql@6.10.0': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-vue@0.1.3': - dependencies: - '@codemirror/lang-html': 6.4.11 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/language': 6.11.3 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-wast@6.0.2': - dependencies: - '@codemirror/language': 6.11.3 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@codemirror/lang-xml@6.1.0': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/xml': 1.0.6 - - '@codemirror/lang-yaml@6.1.2': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - '@lezer/yaml': 1.0.3 - - '@codemirror/language-data@6.5.2': - dependencies: - '@codemirror/lang-angular': 0.1.4 - '@codemirror/lang-cpp': 6.0.3 - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-go': 6.0.1 - '@codemirror/lang-html': 6.4.11 - '@codemirror/lang-java': 6.0.2 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/lang-jinja': 6.0.0 - '@codemirror/lang-json': 6.0.2 - '@codemirror/lang-less': 6.0.2 - '@codemirror/lang-liquid': 6.3.1 - '@codemirror/lang-markdown': 6.5.0 - '@codemirror/lang-php': 6.0.2 - '@codemirror/lang-python': 6.2.1 - '@codemirror/lang-rust': 6.0.2 - '@codemirror/lang-sass': 6.0.2 - '@codemirror/lang-sql': 6.10.0 - '@codemirror/lang-vue': 0.1.3 - '@codemirror/lang-wast': 6.0.2 - '@codemirror/lang-xml': 6.1.0 - '@codemirror/lang-yaml': 6.1.2 - '@codemirror/language': 6.11.3 - '@codemirror/legacy-modes': 6.5.2 - - '@codemirror/language@6.11.3': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - style-mod: 4.1.3 - - '@codemirror/legacy-modes@6.5.2': - dependencies: - '@codemirror/language': 6.11.3 - - '@codemirror/lint@6.9.2': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - crelt: 1.0.6 - - '@codemirror/merge@6.11.2': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/highlight': 1.2.3 - style-mod: 4.1.3 - - '@codemirror/search@6.5.11': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - crelt: 1.0.6 - - '@codemirror/state@6.5.2': - dependencies: - '@marijn/find-cluster-break': 1.0.2 - - '@codemirror/theme-one-dark@6.1.3': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/highlight': 1.2.3 - - '@codemirror/view@6.39.4': - dependencies: - '@codemirror/state': 6.5.2 - crelt: 1.0.6 - style-mod: 4.1.3 - w3c-keyname: 2.2.8 - - '@codesandbox/nodebox@0.1.8': - dependencies: - outvariant: 1.4.0 - strict-event-emitter: 0.4.6 - - '@codesandbox/sandpack-client@2.19.8': - dependencies: - '@codesandbox/nodebox': 0.1.8 - buffer: 6.0.3 - dequal: 2.0.3 - mime-db: 1.54.0 - outvariant: 1.4.0 - static-browser-server: 1.0.3 - - '@codesandbox/sandpack-react@2.20.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/commands': 6.10.1 - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-html': 6.4.11 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@codesandbox/sandpack-client': 2.19.8 - '@lezer/highlight': 1.2.3 - '@react-hook/intersection-observer': 3.1.2(react@19.2.3) - '@stitches/core': 1.2.8 - anser: 2.3.5 - clean-set: 1.1.2 - dequal: 2.0.3 - escape-carriage: 1.3.1 - lz-string: 1.5.0 - react: 19.2.3 - react-devtools-inline: 4.4.0 - react-dom: 19.2.3(react@19.2.3) - react-is: 17.0.2 - '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -8406,315 +7167,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lexical/clipboard@0.35.0': - dependencies: - '@lexical/html': 0.35.0 - '@lexical/list': 0.35.0 - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/code@0.35.0': - dependencies: - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - prismjs: 1.30.0 - - '@lexical/devtools-core@0.35.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@lexical/html': 0.35.0 - '@lexical/link': 0.35.0 - '@lexical/mark': 0.35.0 - '@lexical/table': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@lexical/dragon@0.35.0': - dependencies: - lexical: 0.35.0 - - '@lexical/hashtag@0.35.0': - dependencies: - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/history@0.35.0': - dependencies: - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/html@0.35.0': - dependencies: - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/link@0.35.0': - dependencies: - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/list@0.35.0': - dependencies: - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/mark@0.35.0': - dependencies: - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/markdown@0.35.0': - dependencies: - '@lexical/code': 0.35.0 - '@lexical/link': 0.35.0 - '@lexical/list': 0.35.0 - '@lexical/rich-text': 0.35.0 - '@lexical/text': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/offset@0.35.0': - dependencies: - lexical: 0.35.0 - - '@lexical/overflow@0.35.0': - dependencies: - lexical: 0.35.0 - - '@lexical/plain-text@0.35.0': - dependencies: - '@lexical/clipboard': 0.35.0 - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/react@0.35.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28)': - dependencies: - '@floating-ui/react': 0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@lexical/devtools-core': 0.35.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@lexical/dragon': 0.35.0 - '@lexical/hashtag': 0.35.0 - '@lexical/history': 0.35.0 - '@lexical/link': 0.35.0 - '@lexical/list': 0.35.0 - '@lexical/mark': 0.35.0 - '@lexical/markdown': 0.35.0 - '@lexical/overflow': 0.35.0 - '@lexical/plain-text': 0.35.0 - '@lexical/rich-text': 0.35.0 - '@lexical/table': 0.35.0 - '@lexical/text': 0.35.0 - '@lexical/utils': 0.35.0 - '@lexical/yjs': 0.35.0(yjs@13.6.28) - lexical: 0.35.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-error-boundary: 3.1.4(react@19.2.3) - transitivePeerDependencies: - - yjs - - '@lexical/rich-text@0.35.0': - dependencies: - '@lexical/clipboard': 0.35.0 - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/selection@0.35.0': - dependencies: - lexical: 0.35.0 - - '@lexical/table@0.35.0': - dependencies: - '@lexical/clipboard': 0.35.0 - '@lexical/utils': 0.35.0 - lexical: 0.35.0 - - '@lexical/text@0.35.0': - dependencies: - lexical: 0.35.0 - - '@lexical/utils@0.35.0': - dependencies: - '@lexical/list': 0.35.0 - '@lexical/selection': 0.35.0 - '@lexical/table': 0.35.0 - lexical: 0.35.0 - - '@lexical/yjs@0.35.0(yjs@13.6.28)': - dependencies: - '@lexical/offset': 0.35.0 - '@lexical/selection': 0.35.0 - lexical: 0.35.0 - yjs: 13.6.28 - - '@lezer/common@1.4.0': {} - - '@lezer/cpp@1.1.4': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/css@1.3.0': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/go@1.0.1': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/highlight@1.2.3': - dependencies: - '@lezer/common': 1.4.0 - - '@lezer/html@1.3.12': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/java@1.1.3': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/javascript@1.5.4': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/json@1.0.3': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/lr@1.4.5': - dependencies: - '@lezer/common': 1.4.0 - - '@lezer/markdown@1.6.1': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - - '@lezer/php@1.0.5': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/python@1.1.18': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/rust@1.0.2': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/sass@1.1.0': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/xml@1.0.6': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@lezer/yaml@1.0.3': - dependencies: - '@lezer/common': 1.4.0 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.5 - - '@llamaindex/chat-ui@0.5.17(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(codemirror@6.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28)': - dependencies: - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-html': 6.4.11 - '@codemirror/lang-javascript': 6.2.4 - '@codemirror/lang-python': 6.2.1 - '@llamaindex/llama-deploy': 0.0.2 - '@llamaindex/pdf-viewer': 1.3.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@mdxeditor/editor': 3.52.1(@codemirror/language@6.11.3)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-icons': 1.3.2(react@19.2.3) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-progress': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@uiw/codemirror-theme-github': 4.25.4(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4) - '@uiw/react-codemirror': 4.25.4(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(codemirror@6.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - class-variance-authority: 0.7.1 - clsx: 2.1.1 - highlight.js: 11.11.1 - katex: 0.16.27 - lucide-react: 0.453.0(react@19.2.3) - react: 19.2.3 - react-markdown: 8.0.7(@types/react@19.2.7)(react@19.2.3) - rehype-katex: 7.0.1 - remark: 15.0.1 - remark-code-import: 1.2.0 - remark-gfm: 3.0.1 - remark-math: 5.1.1 - remark-parse: 11.0.0 - tailwind-merge: 2.6.0 - unist-util-visit: 5.0.0 - vaul: 0.9.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - transitivePeerDependencies: - - '@babel/runtime' - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - - '@codemirror/state' - - '@codemirror/theme-one-dark' - - '@codemirror/view' - - '@lezer/highlight' - - '@types/react' - - '@types/react-dom' - - codemirror - - react-dom - - supports-color - - yjs - - '@llamaindex/llama-deploy@0.0.2': {} - - '@llamaindex/pdf-viewer@1.3.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@wojtekmaj/react-hooks': 1.17.2(react@19.2.3) - clsx: 2.1.1 - fuse.js: 6.6.2 - lodash: 4.17.21 - lodash.debounce: 4.0.8 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-intersection-observer: 9.5.1(react@19.2.3) - react-pdf: 9.2.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-window: 1.8.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - '@mantine/core@8.3.10(@mantine/hooks@8.3.10(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@floating-ui/react': 0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -8737,8 +7189,6 @@ snapshots: dependencies: react: 19.2.3 - '@marijn/find-cluster-break@1.0.2': {} - '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.8 @@ -8769,79 +7219,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@mdxeditor/editor@3.52.1(@codemirror/language@6.11.3)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28)': - dependencies: - '@codemirror/commands': 6.10.1 - '@codemirror/lang-markdown': 6.5.0 - '@codemirror/language-data': 6.5.2 - '@codemirror/merge': 6.11.2 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@codesandbox/sandpack-react': 2.20.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@lexical/clipboard': 0.35.0 - '@lexical/link': 0.35.0 - '@lexical/list': 0.35.0 - '@lexical/markdown': 0.35.0 - '@lexical/plain-text': 0.35.0 - '@lexical/react': 0.35.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.28) - '@lexical/rich-text': 0.35.0 - '@lexical/selection': 0.35.0 - '@lexical/utils': 0.35.0 - '@mdxeditor/gurx': 1.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/colors': 3.0.0 - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-icons': 1.3.2(react@19.2.3) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - cm6-theme-basic-light: 0.2.0(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4)(@lezer/highlight@1.2.3) - codemirror: 6.0.2 - downshift: 7.6.2(react@19.2.3) - js-yaml: 4.1.1 - lexical: 0.35.0 - mdast-util-directive: 3.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-frontmatter: 2.0.1 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-highlight-mark: 1.2.2 - mdast-util-mdx: 3.0.0 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-to-markdown: 2.1.2 - micromark-extension-directive: 3.0.2 - micromark-extension-frontmatter: 2.0.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.1 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-extension-highlight-mark: 1.2.0 - micromark-extension-mdx-jsx: 3.0.2 - micromark-extension-mdx-md: 2.0.0 - micromark-extension-mdxjs: 3.0.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-hook-form: 7.69.0(react@19.2.3) - unidiff: 1.0.4 - transitivePeerDependencies: - - '@codemirror/language' - - '@lezer/highlight' - - '@types/react' - - '@types/react-dom' - - supports-color - - yjs - - '@mdxeditor/gurx@1.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - '@mermaid-js/parser@0.6.3': dependencies: langium: 3.3.1 @@ -8910,8 +7287,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@open-draft/deferred-promise@2.2.0': {} - '@opentelemetry/api@1.9.0': {} '@orama/orama@3.1.18': {} @@ -8976,8 +7351,6 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.1 '@parcel/watcher-win32-x64': 2.5.1 - '@radix-ui/colors@3.0.0': {} - '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.0.0': @@ -9305,27 +7678,6 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - '@types/react-dom': 19.2.3(@types/react@19.2.7) - - '@radix-ui/react-icons@1.3.2(react@19.2.3)': - dependencies: - react: 19.2.3 - '@radix-ui/react-id@1.0.0(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 @@ -9536,16 +7888,6 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-progress@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -9627,15 +7969,6 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -9749,21 +8082,6 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - '@types/react-dom': 19.2.3(@types/react@19.2.7) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -9908,16 +8226,6 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@react-hook/intersection-observer@3.1.2(react@19.2.3)': - dependencies: - '@react-hook/passive-layout-effect': 1.2.1(react@19.2.3) - intersection-observer: 0.10.0 - react: 19.2.3 - - '@react-hook/passive-layout-effect@1.2.1(react@19.2.3)': - dependencies: - react: 19.2.3 - '@remirror/core-constants@3.0.0': {} '@rtsao/scc@1.1.0': {} @@ -9977,8 +8285,6 @@ snapshots: '@standard-schema/utils@0.3.0': {} - '@stitches/core@1.2.8': {} - '@swc/core-darwin-arm64@1.15.7': optional: true @@ -10411,10 +8717,6 @@ snapshots: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 - '@types/mdast@3.0.15': - dependencies: - '@types/unist': 2.0.11 - '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -10435,8 +8737,6 @@ snapshots: pg-protocol: 1.10.3 pg-types: 2.2.0 - '@types/prop-types@15.7.15': {} - '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 @@ -10551,47 +8851,6 @@ snapshots: '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 - '@uiw/codemirror-extensions-basic-setup@4.25.4(@codemirror/autocomplete@6.20.0)(@codemirror/commands@6.10.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4)': - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/commands': 6.10.1 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.2 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - - '@uiw/codemirror-theme-github@4.25.4(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4)': - dependencies: - '@uiw/codemirror-themes': 4.25.4(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4) - transitivePeerDependencies: - - '@codemirror/language' - - '@codemirror/state' - - '@codemirror/view' - - '@uiw/codemirror-themes@4.25.4(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4)': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - - '@uiw/react-codemirror@4.25.4(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(codemirror@6.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@codemirror/commands': 6.10.1 - '@codemirror/state': 6.5.2 - '@codemirror/theme-one-dark': 6.1.3 - '@codemirror/view': 6.39.4 - '@uiw/codemirror-extensions-basic-setup': 4.25.4(@codemirror/autocomplete@6.20.0)(@codemirror/commands@6.10.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4) - codemirror: 6.0.2 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - transitivePeerDependencies: - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -10655,11 +8914,6 @@ snapshots: '@vercel/oidc@3.0.5': {} - '@wojtekmaj/react-hooks@1.17.2(react@19.2.3)': - dependencies: - '@types/react': 19.2.7 - react: 19.2.3 - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -10695,8 +8949,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - anser@2.3.5: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -10812,17 +9064,8 @@ snapshots: base16@1.0.0: {} - base64-js@1.5.1: {} - baseline-browser-mapping@2.9.11: {} - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - optional: true - brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -10838,17 +9081,6 @@ snapshots: buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -10872,12 +9104,6 @@ snapshots: canvas-confetti@1.9.4: {} - canvas@3.2.0: - dependencies: - node-addon-api: 7.1.1 - prebuild-install: 7.1.3 - optional: true - ccount@2.0.1: {} chalk@4.1.2: @@ -10919,28 +9145,16 @@ snapshots: dependencies: readdirp: 5.0.0 - chownr@1.1.4: - optional: true - class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 classnames@2.5.1: {} - clean-set@1.1.2: {} - client-only@0.0.1: {} clsx@2.1.1: {} - cm6-theme-basic-light@0.2.0(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.39.4)(@lezer/highlight@1.2.3): - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - '@lezer/highlight': 1.2.3 - cmdk@0.2.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@radix-ui/react-dialog': 1.0.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -10961,16 +9175,6 @@ snapshots: - '@types/react' - '@types/react-dom' - codemirror@6.0.2: - dependencies: - '@codemirror/autocomplete': 6.20.0 - '@codemirror/commands': 6.10.1 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.2 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.39.4 - collapse-white-space@2.1.0: {} color-convert@2.0.1: @@ -10991,8 +9195,6 @@ snapshots: commander@8.3.0: {} - compute-scroll-into-view@2.0.4: {} - compute-scroll-into-view@3.1.1: {} concat-map@0.0.1: {} @@ -11213,11 +9415,6 @@ snapshots: d3-transition: 3.0.1(d3-selection@3.0.0) d3-zoom: 3.0.0 - d@1.0.2: - dependencies: - es5-ext: 0.10.64 - type: 2.7.3 - dagre-d3-es@7.0.13: dependencies: d3: 7.9.0 @@ -11268,14 +9465,6 @@ snapshots: dependencies: character-entities: 2.0.2 - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - optional: true - - deep-extend@0.6.0: - optional: true - deep-is@0.1.4: {} define-data-property@1.1.4: @@ -11310,8 +9499,6 @@ snapshots: diff-match-patch@1.0.5: {} - diff@5.2.0: {} - doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -11320,19 +9507,8 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 - dotenv@16.6.1: {} - dotenv@17.2.3: {} - downshift@7.6.2(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - compute-scroll-into-view: 2.0.4 - prop-types: 15.8.1 - react: 19.2.3 - react-is: 17.0.2 - tslib: 2.8.1 - drizzle-kit@0.31.8: dependencies: '@drizzle-team/brocli': 0.10.2 @@ -11375,11 +9551,6 @@ snapshots: emoji-regex@9.2.2: {} - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - optional: true - enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 @@ -11490,24 +9661,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es5-ext@0.10.64: - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - esniff: 2.0.1 - next-tick: 1.1.0 - - es6-iterator@2.0.3: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-symbol: 3.1.4 - - es6-symbol@3.1.4: - dependencies: - d: 1.0.2 - ext: 1.7.0 - esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 @@ -11612,8 +9765,6 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 - escape-carriage@1.3.1: {} - escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} @@ -11798,13 +9949,6 @@ snapshots: esm-env@1.2.2: {} - esniff@2.0.1: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - type: 2.7.3 - espree@10.4.0: dependencies: acorn: 8.15.0 @@ -11860,20 +10004,8 @@ snapshots: esutils@2.0.3: {} - event-emitter@0.3.5: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - eventsource-parser@3.0.6: {} - expand-template@2.0.3: - optional: true - - ext@1.7.0: - dependencies: - type: 2.7.3 - extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -11900,10 +10032,6 @@ snapshots: dependencies: format: 0.2.2 - fault@2.0.1: - dependencies: - format: 0.2.2 - fbemitter@3.0.0: dependencies: fbjs: 3.0.5 @@ -11983,9 +10111,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - fs-constants@1.0.0: - optional: true - fsevents@2.3.3: optional: true @@ -12094,8 +10219,6 @@ snapshots: functions-have-names@1.2.3: {} - fuse.js@6.6.2: {} - geist@1.5.1(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)): dependencies: next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -12134,9 +10257,6 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - github-from-package@0.0.0: - optional: true - github-slugger@2.0.0: {} glob-parent@5.1.2: @@ -12379,8 +10499,6 @@ snapshots: hast-util-is-element: 3.0.0 unist-util-find-after: 5.0.0 - hast-util-whitespace@2.0.1: {} - hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 @@ -12405,7 +10523,8 @@ snapshots: highlight.js@10.7.3: {} - highlight.js@11.11.1: {} + highlight.js@11.11.1: + optional: true highlightjs-vue@1.0.0: {} @@ -12437,8 +10556,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - ieee754@1.2.1: {} - ignore@5.3.2: {} ignore@7.0.5: {} @@ -12452,14 +10569,6 @@ snapshots: imurmurhash@0.1.4: {} - inherits@2.0.4: - optional: true - - ini@1.3.8: - optional: true - - inline-style-parser@0.1.1: {} - inline-style-parser@0.2.7: {} internal-slot@1.1.0: @@ -12472,8 +10581,6 @@ snapshots: internmap@2.0.3: {} - intersection-observer@0.10.0: {} - intl-messageformat@10.7.18: dependencies: '@formatjs/ecma402-abstract': 2.3.6 @@ -12518,8 +10625,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-buffer@2.0.5: {} - is-bun-module@2.0.0: dependencies: semver: 7.7.3 @@ -12720,8 +10825,6 @@ snapshots: khroma@2.1.0: {} - kleur@4.1.5: {} - langium@3.3.1: dependencies: chevrotain: 11.0.3 @@ -12745,8 +10848,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lexical@0.35.0: {} - lib0@0.2.115: dependencies: isomorphic.js: 0.2.5 @@ -12816,14 +10917,10 @@ snapshots: lodash.curry@4.1.1: {} - lodash.debounce@4.0.8: {} - lodash.flow@3.5.0: {} lodash.merge@4.6.2: {} - lodash@4.17.21: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -12837,10 +10934,6 @@ snapshots: lru-cache@10.4.3: {} - lucide-react@0.453.0(react@19.2.3): - dependencies: - react: 19.2.3 - lucide-react@0.477.0(react@19.2.3): dependencies: react: 19.2.3 @@ -12849,16 +10942,10 @@ snapshots: dependencies: react: 19.2.3 - lz-string@1.5.0: {} - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-cancellable-promise@1.3.2: {} - - make-event-props@1.6.2: {} - markdown-extensions@2.0.0: {} markdown-it@14.1.0: @@ -12876,33 +10963,6 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-definitions@5.1.2: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - unist-util-visit: 4.1.2 - - mdast-util-directive@3.1.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-visit-parents: 6.0.2 - transitivePeerDependencies: - - supports-color - - mdast-util-find-and-replace@2.2.2: - dependencies: - '@types/mdast': 3.0.15 - escape-string-regexp: 5.0.0 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -12910,23 +10970,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@1.3.1: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - decode-named-character-reference: 1.2.0 - mdast-util-to-string: 3.2.0 - micromark: 3.2.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-decode-string: 1.1.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - unist-util-stringify-position: 3.0.3 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - mdast-util-from-markdown@2.0.2: dependencies: '@types/mdast': 4.0.4 @@ -12944,24 +10987,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-frontmatter@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - micromark-extension-frontmatter: 2.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@1.0.3: - dependencies: - '@types/mdast': 3.0.15 - ccount: 2.0.1 - mdast-util-find-and-replace: 2.2.2 - micromark-util-character: 1.2.0 - mdast-util-gfm-autolink-literal@2.0.1: dependencies: '@types/mdast': 4.0.4 @@ -12970,12 +10995,6 @@ snapshots: mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@1.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - micromark-util-normalize-identifier: 1.1.0 - mdast-util-gfm-footnote@2.1.0: dependencies: '@types/mdast': 4.0.4 @@ -12986,11 +11005,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm-strikethrough@1.0.3: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - mdast-util-gfm-strikethrough@2.0.0: dependencies: '@types/mdast': 4.0.4 @@ -12999,15 +11013,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm-table@1.0.7: - dependencies: - '@types/mdast': 3.0.15 - markdown-table: 3.0.4 - mdast-util-from-markdown: 1.3.1 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - mdast-util-gfm-table@2.0.0: dependencies: '@types/mdast': 4.0.4 @@ -13018,11 +11023,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm-task-list-item@1.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - mdast-util-gfm-task-list-item@2.0.0: dependencies: '@types/mdast': 4.0.4 @@ -13032,18 +11032,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-gfm@2.0.2: - dependencies: - mdast-util-from-markdown: 1.3.1 - mdast-util-gfm-autolink-literal: 1.0.3 - mdast-util-gfm-footnote: 1.0.2 - mdast-util-gfm-strikethrough: 1.0.3 - mdast-util-gfm-table: 1.0.7 - mdast-util-gfm-task-list-item: 1.0.2 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - mdast-util-gfm@3.1.0: dependencies: mdast-util-from-markdown: 2.0.2 @@ -13056,16 +11044,6 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-highlight-mark@1.2.2: - dependencies: - micromark-extension-highlight-mark: 1.2.0 - - mdast-util-math@2.0.2: - dependencies: - '@types/mdast': 3.0.15 - longest-streak: 3.1.0 - mdast-util-to-markdown: 1.5.0 - mdast-util-math@3.0.0: dependencies: '@types/hast': 3.0.4 @@ -13127,27 +11105,11 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-phrasing@3.0.1: - dependencies: - '@types/mdast': 3.0.15 - unist-util-is: 5.2.1 - mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 unist-util-is: 6.0.1 - mdast-util-to-hast@12.3.0: - dependencies: - '@types/hast': 2.3.10 - '@types/mdast': 3.0.15 - mdast-util-definitions: 5.1.2 - micromark-util-sanitize-uri: 1.2.0 - trim-lines: 3.0.1 - unist-util-generated: 2.0.1 - unist-util-position: 4.0.4 - unist-util-visit: 4.1.2 - mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.4 @@ -13160,17 +11122,6 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 - mdast-util-to-markdown@1.5.0: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - longest-streak: 3.1.0 - mdast-util-phrasing: 3.0.1 - mdast-util-to-string: 3.2.0 - micromark-util-decode-string: 1.1.0 - unist-util-visit: 4.1.2 - zwitch: 2.0.4 - mdast-util-to-markdown@2.1.2: dependencies: '@types/mdast': 4.0.4 @@ -13183,22 +11134,12 @@ snapshots: unist-util-visit: 5.0.0 zwitch: 2.0.4 - mdast-util-to-string@3.2.0: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-string@4.0.0: dependencies: '@types/mdast': 4.0.4 mdurl@2.0.0: {} - memoize-one@5.2.1: {} - - merge-refs@1.3.0(@types/react@19.2.7): - optionalDependencies: - '@types/react': 19.2.7 - merge2@1.4.1: {} mermaid@11.12.2: @@ -13224,25 +11165,6 @@ snapshots: ts-dedent: 2.2.0 uuid: 11.1.0 - micromark-core-commonmark@1.1.0: - dependencies: - decode-named-character-reference: 1.2.0 - micromark-factory-destination: 1.1.0 - micromark-factory-label: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-factory-title: 1.1.0 - micromark-factory-whitespace: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-chunked: 1.1.0 - micromark-util-classify-character: 1.1.0 - micromark-util-html-tag-name: 1.2.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-subtokenize: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -13294,30 +11216,6 @@ snapshots: optionalDependencies: micromark-util-types: 2.0.2 - micromark-extension-directive@3.0.2: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - parse-entities: 4.0.2 - - micromark-extension-frontmatter@2.0.0: - dependencies: - fault: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-autolink-literal@1.0.5: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.1 @@ -13325,17 +11223,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-footnote@1.1.2: - dependencies: - micromark-core-commonmark: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 @@ -13347,15 +11234,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-strikethrough@1.0.7: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-classify-character: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 @@ -13365,14 +11243,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-table@1.0.7: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-extension-gfm-table@2.1.1: dependencies: devlop: 1.1.0 @@ -13381,22 +11251,10 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm-tagfilter@1.0.2: - dependencies: - micromark-util-types: 1.1.0 - micromark-extension-gfm-tagfilter@2.0.0: dependencies: micromark-util-types: 2.0.2 - micromark-extension-gfm-task-list-item@1.0.5: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 @@ -13405,17 +11263,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-gfm@2.0.3: - dependencies: - micromark-extension-gfm-autolink-literal: 1.0.5 - micromark-extension-gfm-footnote: 1.1.2 - micromark-extension-gfm-strikethrough: 1.0.7 - micromark-extension-gfm-table: 1.0.7 - micromark-extension-gfm-tagfilter: 1.0.2 - micromark-extension-gfm-task-list-item: 1.0.5 - micromark-util-combine-extensions: 1.1.0 - micromark-util-types: 1.1.0 - micromark-extension-gfm@3.0.0: dependencies: micromark-extension-gfm-autolink-literal: 2.1.0 @@ -13427,25 +11274,6 @@ snapshots: micromark-util-combine-extensions: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-highlight-mark@1.2.0: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - uvu: 0.5.6 - - micromark-extension-math@2.1.2: - dependencies: - '@types/katex': 0.16.7 - katex: 0.16.27 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-extension-math@3.1.0: dependencies: '@types/katex': 0.16.7 @@ -13507,25 +11335,12 @@ snapshots: micromark-util-combine-extensions: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-destination@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-label@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-factory-label@2.0.1: dependencies: devlop: 1.1.0 @@ -13545,23 +11360,11 @@ snapshots: unist-util-position-from-estree: 2.0.0 vfile-message: 4.0.3 - micromark-factory-space@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-types: 1.1.0 - micromark-factory-space@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-types: 2.0.2 - micromark-factory-title@1.1.0: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-factory-title@2.0.1: dependencies: micromark-factory-space: 2.0.1 @@ -13569,13 +11372,6 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-factory-whitespace@1.1.0: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-factory-whitespace@2.0.1: dependencies: micromark-factory-space: 2.0.1 @@ -13583,61 +11379,30 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-character@1.2.0: - dependencies: - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-util-character@2.1.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-chunked@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - micromark-util-chunked@2.0.1: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-classify-character@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - micromark-util-classify-character@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-combine-extensions@1.1.0: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-types: 1.1.0 - micromark-util-combine-extensions@2.0.1: dependencies: micromark-util-chunked: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-decode-numeric-character-reference@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - micromark-util-decode-numeric-character-reference@2.0.2: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-decode-string@1.1.0: - dependencies: - decode-named-character-reference: 1.2.0 - micromark-util-character: 1.2.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-decode-string@2.0.1: dependencies: decode-named-character-reference: 1.2.0 @@ -13645,8 +11410,6 @@ snapshots: micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-symbol: 2.0.1 - micromark-util-encode@1.1.0: {} - micromark-util-encode@2.0.1: {} micromark-util-events-to-acorn@2.0.3: @@ -13659,45 +11422,22 @@ snapshots: micromark-util-types: 2.0.2 vfile-message: 4.0.3 - micromark-util-html-tag-name@1.2.0: {} - micromark-util-html-tag-name@2.0.1: {} - micromark-util-normalize-identifier@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - micromark-util-normalize-identifier@2.0.1: dependencies: micromark-util-symbol: 2.0.1 - micromark-util-resolve-all@1.1.0: - dependencies: - micromark-util-types: 1.1.0 - micromark-util-resolve-all@2.0.1: dependencies: micromark-util-types: 2.0.2 - micromark-util-sanitize-uri@1.2.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-encode: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-sanitize-uri@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@1.1.0: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - micromark-util-subtokenize@2.1.0: dependencies: devlop: 1.1.0 @@ -13705,36 +11445,10 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-util-symbol@1.1.0: {} - micromark-util-symbol@2.0.1: {} - micromark-util-types@1.1.0: {} - micromark-util-types@2.0.2: {} - micromark@3.2.0: - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.3 - decode-named-character-reference: 1.2.0 - micromark-core-commonmark: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-chunked: 1.1.0 - micromark-util-combine-extensions: 1.1.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-encode: 1.1.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-subtokenize: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - micromark@4.0.2: dependencies: '@types/debug': 4.1.12 @@ -13764,15 +11478,10 @@ snapshots: mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mimic-response@3.1.0: - optional: true - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -13783,9 +11492,6 @@ snapshots: minimist@1.2.8: {} - mkdirp-classic@0.5.3: - optional: true - mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -13807,17 +11513,12 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - mri@1.2.0: {} - ms@2.1.3: {} nanoid@3.3.11: {} nanoid@5.1.6: {} - napi-build-utils@2.0.0: - optional: true - napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -13847,8 +11548,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - next-tick@1.1.0: {} - next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@next/env': 16.1.0 @@ -13874,11 +11573,6 @@ snapshots: - '@babel/core' - babel-plugin-macros - node-abi@3.85.0: - dependencies: - semver: 7.7.3 - optional: true - node-addon-api@7.1.1: {} node-fetch@2.7.0: @@ -13935,11 +11629,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - once@1.4.0: - dependencies: - wrappy: 1.0.2 - optional: true - oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.4: @@ -13959,8 +11648,6 @@ snapshots: orderedmap@2.1.1: {} - outvariant@1.4.0: {} - own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -14014,16 +11701,8 @@ snapshots: path-to-regexp@8.3.0: {} - path2d@0.2.2: - optional: true - pathe@2.0.3: {} - pdfjs-dist@4.8.69: - optionalDependencies: - canvas: 3.2.0 - path2d: 0.2.2 - pg-cloudflare@1.2.7: optional: true @@ -14116,22 +11795,6 @@ snapshots: postgres@3.4.7: {} - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.85.0 - pump: 3.0.3 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.4 - tunnel-agent: 0.6.0 - optional: true - prelude-ls@1.2.1: {} prismjs@1.27.0: {} @@ -14152,8 +11815,6 @@ snapshots: dependencies: xtend: 4.0.2 - property-information@6.5.0: {} - property-information@7.1.0: {} prosemirror-changeset@2.3.1: @@ -14269,12 +11930,6 @@ snapshots: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - optional: true - punycode.js@2.3.1: {} punycode@2.3.1: {} @@ -14283,14 +11938,6 @@ snapshots: queue-microtask@1.2.3: {} - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - optional: true - react-base16-styling@0.6.0: dependencies: base16: 1.0.0 @@ -14305,10 +11952,6 @@ snapshots: date-fns-jalali: 4.1.0-0 react: 19.2.3 - react-devtools-inline@4.4.0: - dependencies: - es6-symbol: 3.1.4 - react-dom@19.2.3(react@19.2.3): dependencies: react: 19.2.3 @@ -14327,11 +11970,6 @@ snapshots: react-dom: 19.2.3(react@19.2.3) tslib: 2.8.1 - react-error-boundary@3.1.4(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - react: 19.2.3 - react-hook-form@7.69.0(react@19.2.3): dependencies: react: 19.2.3 @@ -14340,16 +11978,8 @@ snapshots: dependencies: react: 19.2.3 - react-intersection-observer@9.5.1(react@19.2.3): - dependencies: - react: 19.2.3 - react-is@16.13.1: {} - react-is@17.0.2: {} - - react-is@18.3.1: {} - react-json-view-lite@2.5.0(react@19.2.3): dependencies: react: 19.2.3 @@ -14386,28 +12016,6 @@ snapshots: transitivePeerDependencies: - supports-color - react-markdown@8.0.7(@types/react@19.2.7)(react@19.2.3): - dependencies: - '@types/hast': 2.3.10 - '@types/prop-types': 15.7.15 - '@types/react': 19.2.7 - '@types/unist': 2.0.11 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 2.0.1 - prop-types: 15.8.1 - property-information: 6.5.0 - react: 19.2.3 - react-is: 18.3.1 - remark-parse: 10.0.2 - remark-rehype: 10.1.0 - space-separated-tokens: 2.0.2 - style-to-object: 0.4.4 - unified: 10.1.2 - unist-util-visit: 4.1.2 - vfile: 5.3.7 - transitivePeerDependencies: - - supports-color - react-medium-image-zoom@5.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -14418,21 +12026,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - react-pdf@9.2.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - clsx: 2.1.1 - dequal: 2.0.3 - make-cancellable-promise: 1.3.2 - make-event-props: 1.6.2 - merge-refs: 1.3.0(@types/react@19.2.7) - pdfjs-dist: 4.8.69 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - tiny-invariant: 1.3.3 - warning: 4.0.3 - optionalDependencies: - '@types/react': 19.2.7 - react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3): dependencies: react: 19.2.3 @@ -14507,26 +12100,12 @@ snapshots: transitivePeerDependencies: - '@types/react' - react-window@1.8.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.28.4 - memoize-one: 5.2.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-wrap-balancer@1.1.1(react@19.2.3): dependencies: react: 19.2.3 react@19.2.3: {} - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - optional: true - readdirp@5.0.0: {} recma-build-jsx@1.0.0: @@ -14677,21 +12256,6 @@ snapshots: - micromark - micromark-util-types - remark-code-import@1.2.0: - dependencies: - strip-indent: 4.1.1 - to-gatsby-remark-plugin: 0.1.0 - unist-util-visit: 4.1.2 - - remark-gfm@3.0.1: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-gfm: 2.0.2 - micromark-extension-gfm: 2.0.3 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -14703,13 +12267,6 @@ snapshots: transitivePeerDependencies: - supports-color - remark-math@5.1.1: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-math: 2.0.2 - micromark-extension-math: 2.1.2 - unified: 10.1.2 - remark-math@6.0.0: dependencies: '@types/mdast': 4.0.4 @@ -14726,14 +12283,6 @@ snapshots: transitivePeerDependencies: - supports-color - remark-parse@10.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-from-markdown: 1.3.1 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -14743,13 +12292,6 @@ snapshots: transitivePeerDependencies: - supports-color - remark-rehype@10.1.0: - dependencies: - '@types/hast': 2.3.10 - '@types/mdast': 3.0.15 - mdast-util-to-hast: 12.3.0 - unified: 10.1.2 - remark-rehype@11.1.2: dependencies: '@types/hast': 3.0.4 @@ -14816,10 +12358,6 @@ snapshots: rw@1.3.3: {} - sade@1.8.1: - dependencies: - mri: 1.2.0 - safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -14828,9 +12366,6 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 - safe-buffer@5.2.1: - optional: true - safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -14965,16 +12500,6 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - simple-concat@1.0.1: - optional: true - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - optional: true - sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -14999,13 +12524,6 @@ snapshots: stable-hash@0.0.5: {} - static-browser-server@1.0.3: - dependencies: - '@open-draft/deferred-promise': 2.2.0 - dotenv: 16.6.1 - mime-db: 1.54.0 - outvariant: 1.4.0 - stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -15042,8 +12560,6 @@ snapshots: - micromark-util-types - supports-color - strict-event-emitter@0.4.6: {} - string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -15094,11 +12610,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -15106,23 +12617,12 @@ snapshots: strip-bom@3.0.0: {} - strip-indent@4.1.1: {} - - strip-json-comments@2.0.1: - optional: true - strip-json-comments@3.1.1: {} - style-mod@4.1.3: {} - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 - style-to-object@0.4.4: - dependencies: - inline-style-parser: 0.1.1 - style-to-object@1.0.14: dependencies: inline-style-parser: 0.2.7 @@ -15150,8 +12650,6 @@ snapshots: tabbable@6.3.0: {} - tailwind-merge@2.6.0: {} - tailwind-merge@3.4.0: {} tailwindcss-animate@1.0.7(tailwindcss@4.1.18): @@ -15162,29 +12660,10 @@ snapshots: tapable@2.3.0: {} - tar-fs@2.1.4: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.3 - tar-stream: 2.2.0 - optional: true - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - optional: true - third-party-capital@1.0.20: {} throttleit@2.1.0: {} - tiny-invariant@1.3.3: {} - tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -15198,19 +12677,10 @@ snapshots: dependencies: tldts-core: 6.1.86 - to-gatsby-remark-plugin@0.1.0: - dependencies: - to-vfile: 6.1.0 - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - to-vfile@6.1.0: - dependencies: - is-buffer: 2.0.5 - vfile: 4.2.1 - tough-cookie@5.1.2: dependencies: tldts: 6.1.86 @@ -15249,19 +12719,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 type-fest@4.41.0: {} - type@2.7.3: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -15312,20 +12775,6 @@ snapshots: undici-types@6.21.0: {} - unidiff@1.0.4: - dependencies: - diff: 5.2.0 - - unified@10.1.2: - dependencies: - '@types/unist': 2.0.11 - bail: 2.0.2 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 5.3.7 - unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -15341,12 +12790,6 @@ snapshots: '@types/unist': 3.0.3 unist-util-is: 6.0.1 - unist-util-generated@2.0.1: {} - - unist-util-is@5.2.1: - dependencies: - '@types/unist': 2.0.11 - unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -15355,10 +12798,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - unist-util-position@4.0.4: - dependencies: - '@types/unist': 2.0.11 - unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -15368,34 +12807,15 @@ snapshots: '@types/unist': 3.0.3 unist-util-visit: 5.0.0 - unist-util-stringify-position@2.0.3: - dependencies: - '@types/unist': 2.0.11 - - unist-util-stringify-position@3.0.3: - dependencies: - '@types/unist': 2.0.11 - unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-visit-parents@5.1.3: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - unist-util-visit-parents@6.0.2: dependencies: '@types/unist': 3.0.3 unist-util-is: 6.0.1 - unist-util-visit@4.1.2: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -15481,56 +12901,16 @@ snapshots: uuid@8.3.2: {} - uvu@0.5.6: - dependencies: - dequal: 2.0.3 - diff: 5.2.0 - kleur: 4.1.5 - sade: 1.8.1 - - vaul@0.9.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 vfile: 6.0.3 - vfile-message@2.0.4: - dependencies: - '@types/unist': 2.0.11 - unist-util-stringify-position: 2.0.3 - - vfile-message@3.1.4: - dependencies: - '@types/unist': 2.0.11 - unist-util-stringify-position: 3.0.3 - vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 - vfile@4.2.1: - dependencies: - '@types/unist': 2.0.11 - is-buffer: 2.0.5 - unist-util-stringify-position: 2.0.3 - vfile-message: 2.0.4 - - vfile@5.3.7: - dependencies: - '@types/unist': 2.0.11 - is-buffer: 2.0.5 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - vfile@6.0.3: dependencies: '@types/unist': 3.0.3 @@ -15559,10 +12939,6 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - warning@4.0.3: - dependencies: - loose-envify: 1.4.0 - web-namespaces@2.0.1: {} webidl-conversions@3.0.1: {} @@ -15632,9 +13008,6 @@ snapshots: word-wrap@1.2.5: {} - wrappy@1.0.2: - optional: true - ws@8.18.3: {} xml-name-validator@5.0.0: {}