diff --git a/surfsense_backend/alembic/versions/43_add_blocknote_fields_to_documents.py b/surfsense_backend/alembic/versions/43_add_blocknote_fields_to_documents.py new file mode 100644 index 000000000..32e7780eb --- /dev/null +++ b/surfsense_backend/alembic/versions/43_add_blocknote_fields_to_documents.py @@ -0,0 +1,75 @@ +"""43_add_blocknote_fields_to_documents + +Revision ID: 43 +Revises: 42 +Create Date: 2025-11-30 + +Adds fields for live document editing: +- blocknote_document: JSONB editor state +- content_needs_reindexing: Flag for regenerating chunks/summary +- last_edited_at: Last edit timestamp +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "43" +down_revision: str | None = "42" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + """Upgrade schema - Add BlockNote fields and trigger population task.""" + + # Add the columns + op.add_column( + "documents", + sa.Column( + "blocknote_document", postgresql.JSONB(astext_type=sa.Text()), nullable=True + ), + ) + op.add_column( + "documents", + sa.Column( + "content_needs_reindexing", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ), + ) + op.add_column( + "documents", + sa.Column("last_edited_at", sa.TIMESTAMP(timezone=True), nullable=True), + ) + + # Trigger the Celery task to populate blocknote_document for existing documents + try: + from app.tasks.celery_tasks.blocknote_migration_tasks import ( + populate_blocknote_for_documents_task, + ) + + # Queue the task to run asynchronously + populate_blocknote_for_documents_task.apply_async() + print( + "✓ Queued Celery task to populate blocknote_document for existing documents" + ) + except Exception as e: + # If Celery is not available or task queueing fails, log but don't fail the migration + print(f"⚠ Warning: Could not queue blocknote population task: {e}") + print(" You can manually trigger it later with:") + print( + " celery -A app.celery_app call app.tasks.celery_tasks.blocknote_migration_tasks.populate_blocknote_for_documents_task" + ) + + +def downgrade() -> None: + """Downgrade schema - Remove BlockNote fields.""" + op.drop_column("documents", "last_edited_at") + op.drop_column("documents", "content_needs_reindexing") + op.drop_column("documents", "blocknote_document") diff --git a/surfsense_backend/app/celery_app.py b/surfsense_backend/app/celery_app.py index 898ab9735..f7bea8cc3 100644 --- a/surfsense_backend/app/celery_app.py +++ b/surfsense_backend/app/celery_app.py @@ -63,6 +63,8 @@ def parse_schedule_interval(interval: str) -> dict: "app.tasks.celery_tasks.podcast_tasks", "app.tasks.celery_tasks.connector_tasks", "app.tasks.celery_tasks.schedule_checker_task", + "app.tasks.celery_tasks.blocknote_migration_tasks", + "app.tasks.celery_tasks.document_reindex_tasks", ], ) diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index f3147a42b..20a4adc23 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -20,7 +20,7 @@ UniqueConstraint, text, ) -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.dialects.postgresql import JSONB, UUID from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, relationship @@ -343,6 +343,17 @@ class Document(BaseModel, TimestampMixin): unique_identifier_hash = Column(String, nullable=True, index=True, unique=True) embedding = Column(Vector(config.embedding_model_instance.dimension)) + # BlockNote live editing state (NULL when never edited) + blocknote_document = Column(JSONB, nullable=True) + + # blocknote background reindex flag + content_needs_reindexing = Column( + Boolean, nullable=False, default=False, server_default=text("false") + ) + + # Track when blocknote document was last edited + last_edited_at = Column(TIMESTAMP(timezone=True), nullable=True) + search_space_id = Column( Integer, ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False ) diff --git a/surfsense_backend/app/routes/__init__.py b/surfsense_backend/app/routes/__init__.py index 127a8d927..4b829fe84 100644 --- a/surfsense_backend/app/routes/__init__.py +++ b/surfsense_backend/app/routes/__init__.py @@ -5,6 +5,7 @@ ) from .chats_routes import router as chats_router from .documents_routes import router as documents_router +from .editor_routes import router as editor_router from .google_calendar_add_connector_route import ( router as google_calendar_add_connector_router, ) @@ -23,6 +24,7 @@ router.include_router(search_spaces_router) router.include_router(rbac_router) # RBAC routes for roles, members, invites +router.include_router(editor_router) router.include_router(documents_router) router.include_router(podcasts_router) router.include_router(chats_router) diff --git a/surfsense_backend/app/routes/editor_routes.py b/surfsense_backend/app/routes/editor_routes.py new file mode 100644 index 000000000..0bc7ed50b --- /dev/null +++ b/surfsense_backend/app/routes/editor_routes.py @@ -0,0 +1,140 @@ +""" +Editor routes for BlockNote document editing. +""" + +from datetime import UTC, datetime +from typing import Any + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.db import Document, SearchSpace, User, get_async_session +from app.users import current_active_user + +router = APIRouter() + + +@router.get("/documents/{document_id}/editor-content") +async def get_editor_content( + document_id: int, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Get document content for editing. + + Returns BlockNote JSON document. If blocknote_document is NULL, + attempts to generate it from chunks (lazy migration). + """ + from sqlalchemy.orm import selectinload + + result = await session.execute( + select(Document) + .options(selectinload(Document.chunks)) + .join(SearchSpace) + .filter(Document.id == document_id, SearchSpace.user_id == user.id) + ) + document = result.scalars().first() + + if not document: + raise HTTPException(status_code=404, detail="Document not found") + + # If blocknote_document exists, return it + if document.blocknote_document: + return { + "document_id": document.id, + "title": document.title, + "blocknote_document": document.blocknote_document, + "last_edited_at": document.last_edited_at.isoformat() + if document.last_edited_at + else None, + } + + # Lazy migration: Try to generate blocknote_document from chunks + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + chunks = sorted(document.chunks, key=lambda c: c.id) + + if not chunks: + raise HTTPException( + status_code=400, + detail="This document has no chunks and cannot be edited. Please re-upload to enable editing.", + ) + + # Reconstruct markdown from chunks + markdown_content = "\n\n".join(chunk.content for chunk in chunks) + + if not markdown_content.strip(): + raise HTTPException( + status_code=400, + detail="This document has empty content and cannot be edited.", + ) + + # Convert to BlockNote + blocknote_json = await convert_markdown_to_blocknote(markdown_content) + + if not blocknote_json: + raise HTTPException( + status_code=500, + detail="Failed to convert document to editable format. Please try again later.", + ) + + # Save the generated blocknote_document (lazy migration) + document.blocknote_document = blocknote_json + document.content_needs_reindexing = False + document.last_edited_at = None + await session.commit() + + return { + "document_id": document.id, + "title": document.title, + "blocknote_document": blocknote_json, + "last_edited_at": None, + } + + +@router.post("/documents/{document_id}/save") +async def save_document( + document_id: int, + data: dict[str, Any], + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + """ + Save BlockNote document and trigger reindexing. + Called when user clicks 'Save & Exit'. + """ + from app.tasks.celery_tasks.document_reindex_tasks import reindex_document_task + + # Verify ownership + result = await session.execute( + select(Document) + .join(SearchSpace) + .filter(Document.id == document_id, SearchSpace.user_id == user.id) + ) + document = result.scalars().first() + + if not document: + raise HTTPException(status_code=404, detail="Document not found") + + blocknote_document = data.get("blocknote_document") + if not blocknote_document: + raise HTTPException(status_code=400, detail="blocknote_document is required") + + # Save BlockNote document + document.blocknote_document = blocknote_document + document.last_edited_at = datetime.now(UTC) + document.content_needs_reindexing = True + + await session.commit() + + # Queue reindex task + reindex_document_task.delay(document_id, str(user.id)) + + return { + "status": "saved", + "document_id": document_id, + "message": "Document saved and will be reindexed in the background", + "last_edited_at": document.last_edited_at.isoformat(), + } diff --git a/surfsense_backend/app/tasks/celery_tasks/blocknote_migration_tasks.py b/surfsense_backend/app/tasks/celery_tasks/blocknote_migration_tasks.py new file mode 100644 index 000000000..c945bcb04 --- /dev/null +++ b/surfsense_backend/app/tasks/celery_tasks/blocknote_migration_tasks.py @@ -0,0 +1,168 @@ +"""Celery tasks for populating blocknote_document for existing documents.""" + +import logging + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine +from sqlalchemy.orm import selectinload +from sqlalchemy.pool import NullPool + +from app.celery_app import celery_app +from app.config import config +from app.db import Document +from app.utils.blocknote_converter import convert_markdown_to_blocknote + +logger = logging.getLogger(__name__) + + +def get_celery_session_maker(): + """ + Create a new async session maker for Celery tasks. + This is necessary because Celery tasks run in a new event loop, + and the default session maker is bound to the main app's event loop. + """ + engine = create_async_engine( + config.DATABASE_URL, + poolclass=NullPool, + echo=False, + ) + return async_sessionmaker(engine, expire_on_commit=False) + + +@celery_app.task(name="populate_blocknote_for_documents", bind=True) +def populate_blocknote_for_documents_task( + self, document_ids: list[int] | None = None, batch_size: int = 50 +): + """ + Celery task to populate blocknote_document for existing documents. + + Args: + document_ids: Optional list of specific document IDs to process. + If None, processes all documents with blocknote_document IS NULL. + batch_size: Number of documents to process in each batch (default: 50) + """ + import asyncio + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + try: + loop.run_until_complete( + _populate_blocknote_for_documents(document_ids, batch_size) + ) + finally: + loop.close() + + +async def _populate_blocknote_for_documents( + document_ids: list[int] | None = None, batch_size: int = 50 +): + """ + Async function to populate blocknote_document for documents. + + Args: + document_ids: Optional list of specific document IDs to process + batch_size: Number of documents to process per batch + """ + async with get_celery_session_maker()() as session: + try: + # Build query for documents that need blocknote_document populated + query = select(Document).where(Document.blocknote_document.is_(None)) + + # If specific document IDs provided, filter by them + if document_ids: + query = query.where(Document.id.in_(document_ids)) + + # Load chunks relationship to avoid N+1 queries + query = query.options(selectinload(Document.chunks)) + + # Execute query + result = await session.execute(query) + documents = result.scalars().all() + + total_documents = len(documents) + logger.info(f"Found {total_documents} documents to process") + + if total_documents == 0: + logger.info("No documents to process") + return + + # Process documents in batches + processed = 0 + failed = 0 + + for i in range(0, total_documents, batch_size): + batch = documents[i : i + batch_size] + logger.info( + f"Processing batch {i // batch_size + 1}: documents {i + 1}-{min(i + batch_size, total_documents)}" + ) + + for document in batch: + try: + # Use preloaded chunks from selectinload - no need to query again + chunks = sorted(document.chunks, key=lambda c: c.id) + + if not chunks: + logger.warning( + f"Document {document.id} ({document.title}) has no chunks, skipping" + ) + failed += 1 + continue + + # Reconstruct markdown by concatenating chunk contents + markdown_content = "\n\n".join( + chunk.content for chunk in chunks + ) + + if not markdown_content or not markdown_content.strip(): + logger.warning( + f"Document {document.id} ({document.title}) has empty markdown content, skipping" + ) + failed += 1 + continue + + # Convert markdown to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote( + markdown_content + ) + + if not blocknote_json: + logger.warning( + f"Failed to convert markdown to BlockNote for document {document.id} ({document.title})" + ) + failed += 1 + continue + + # Update document with blocknote_document (other fields already have correct defaults) + document.blocknote_document = blocknote_json + + processed += 1 + + # Commit every batch_size documents to avoid long transactions + if processed % batch_size == 0: + await session.commit() + logger.info( + f"Committed batch: {processed} documents processed so far" + ) + + except Exception as e: + logger.error( + f"Error processing document {document.id} ({document.title}): {e}", + exc_info=True, + ) + failed += 1 + # Continue with next document instead of failing entire batch + continue + + # Commit remaining changes in the batch + await session.commit() + logger.info(f"Completed batch {i // batch_size + 1}") + + logger.info( + f"Migration complete: {processed} documents processed, {failed} failed" + ) + + except Exception as e: + await session.rollback() + logger.error(f"Error in blocknote migration task: {e}", exc_info=True) + raise diff --git a/surfsense_backend/app/tasks/celery_tasks/document_reindex_tasks.py b/surfsense_backend/app/tasks/celery_tasks/document_reindex_tasks.py new file mode 100644 index 000000000..8ab5309f2 --- /dev/null +++ b/surfsense_backend/app/tasks/celery_tasks/document_reindex_tasks.py @@ -0,0 +1,126 @@ +"""Celery tasks for reindexing edited documents.""" + +import logging + +from sqlalchemy import delete, select +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine +from sqlalchemy.orm import selectinload +from sqlalchemy.pool import NullPool + +from app.celery_app import celery_app +from app.config import config +from app.db import Document +from app.services.llm_service import get_user_long_context_llm +from app.utils.blocknote_converter import convert_blocknote_to_markdown +from app.utils.document_converters import ( + create_document_chunks, + generate_document_summary, +) + +logger = logging.getLogger(__name__) + + +def get_celery_session_maker(): + """Create async session maker for Celery tasks.""" + engine = create_async_engine( + config.DATABASE_URL, + poolclass=NullPool, + echo=False, + ) + return async_sessionmaker(engine, expire_on_commit=False) + + +@celery_app.task(name="reindex_document", bind=True) +def reindex_document_task(self, document_id: int, user_id: str): + """ + Celery task to reindex a document after editing. + + Args: + document_id: ID of document to reindex + user_id: ID of user who edited the document + """ + import asyncio + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + try: + loop.run_until_complete(_reindex_document(document_id, user_id)) + finally: + loop.close() + + +async def _reindex_document(document_id: int, user_id: str): + """Async function to reindex a document.""" + async with get_celery_session_maker()() as session: + try: + # Get document + result = await session.execute( + select(Document) + .options(selectinload(Document.chunks)) # Eagerly load chunks + .where(Document.id == document_id) + ) + document = result.scalars().first() + + if not document: + logger.error(f"Document {document_id} not found") + return + + if not document.blocknote_document: + logger.warning(f"Document {document_id} has no BlockNote content") + return + + logger.info(f"Reindexing document {document_id} ({document.title})") + + # 1. Convert BlockNote → Markdown + markdown_content = await convert_blocknote_to_markdown( + document.blocknote_document + ) + + if not markdown_content: + logger.error(f"Failed to convert document {document_id} to markdown") + return + + # 2. Delete old chunks explicitly + from app.db import Chunk + + await session.execute(delete(Chunk).where(Chunk.document_id == document_id)) + await session.flush() # Ensure old chunks are deleted + + # 3. Create new chunks + new_chunks = await create_document_chunks(markdown_content) + + # 4. Add new chunks to session + for chunk in new_chunks: + chunk.document_id = document_id + session.add(chunk) + + logger.info(f"Created {len(new_chunks)} chunks for document {document_id}") + + # 5. Regenerate summary + user_llm = await get_user_long_context_llm( + session, user_id, document.search_space_id + ) + + document_metadata = { + "title": document.title, + "document_type": document.document_type.value, + } + + summary_content, summary_embedding = await generate_document_summary( + markdown_content, user_llm, document_metadata + ) + + # 6. Update document + document.content = summary_content + document.embedding = summary_embedding + document.content_needs_reindexing = False + + await session.commit() + + logger.info(f"Successfully reindexed document {document_id}") + + except Exception as e: + await session.rollback() + logger.error(f"Error reindexing document {document_id}: {e}", exc_info=True) + raise diff --git a/surfsense_backend/app/tasks/document_processors/extension_processor.py b/surfsense_backend/app/tasks/document_processors/extension_processor.py index 663093375..48e3efe27 100644 --- a/surfsense_backend/app/tasks/document_processors/extension_processor.py +++ b/surfsense_backend/app/tasks/document_processors/extension_processor.py @@ -145,6 +145,16 @@ async def add_extension_received_document( # Process chunks chunks = await create_document_chunks(content.pageContent) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert markdown to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(combined_document_string) + if not blocknote_json: + logging.warning( + f"Failed to convert extension document '{content.metadata.VisitedWebPageTitle}' " + f"to BlockNote JSON, document will not be editable" + ) + # Update or create document if existing_document: # Update existing document @@ -154,6 +164,7 @@ async def add_extension_received_document( existing_document.embedding = summary_embedding existing_document.document_metadata = content.metadata.model_dump() existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json await session.commit() await session.refresh(existing_document) @@ -170,6 +181,7 @@ async def add_extension_received_document( chunks=chunks, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, ) session.add(document) diff --git a/surfsense_backend/app/tasks/document_processors/file_processors.py b/surfsense_backend/app/tasks/document_processors/file_processors.py index 859f6a25d..4ae04e050 100644 --- a/surfsense_backend/app/tasks/document_processors/file_processors.py +++ b/surfsense_backend/app/tasks/document_processors/file_processors.py @@ -100,6 +100,15 @@ async def add_received_file_document_using_unstructured( # Process chunks chunks = await create_document_chunks(file_in_markdown) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert markdown to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(file_in_markdown) + if not blocknote_json: + logging.warning( + f"Failed to convert {file_name} to BlockNote JSON, document will not be editable" + ) + # Update or create document if existing_document: # Update existing document @@ -112,6 +121,9 @@ async def add_received_file_document_using_unstructured( "ETL_SERVICE": "UNSTRUCTURED", } existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json + existing_document.content_needs_reindexing = False + existing_document.last_edited_at = None await session.commit() await session.refresh(existing_document) @@ -131,6 +143,9 @@ async def add_received_file_document_using_unstructured( chunks=chunks, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, + content_needs_reindexing=False, + last_edited_at=None, ) session.add(document) @@ -214,6 +229,15 @@ async def add_received_file_document_using_llamacloud( # Process chunks chunks = await create_document_chunks(file_in_markdown) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert markdown to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(file_in_markdown) + if not blocknote_json: + logging.warning( + f"Failed to convert {file_name} to BlockNote JSON, document will not be editable" + ) + # Update or create document if existing_document: # Update existing document @@ -226,6 +250,9 @@ async def add_received_file_document_using_llamacloud( "ETL_SERVICE": "LLAMACLOUD", } existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json + existing_document.content_needs_reindexing = False + existing_document.last_edited_at = None await session.commit() await session.refresh(existing_document) @@ -245,6 +272,9 @@ async def add_received_file_document_using_llamacloud( chunks=chunks, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, + content_needs_reindexing=False, + last_edited_at=None, ) session.add(document) @@ -353,6 +383,15 @@ async def add_received_file_document_using_docling( # Process chunks chunks = await create_document_chunks(file_in_markdown) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert markdown to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(file_in_markdown) + if not blocknote_json: + logging.warning( + f"Failed to convert {file_name} to BlockNote JSON, document will not be editable" + ) + # Update or create document if existing_document: # Update existing document @@ -365,6 +404,9 @@ async def add_received_file_document_using_docling( "ETL_SERVICE": "DOCLING", } existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json + existing_document.content_needs_reindexing = False + existing_document.last_edited_at = None await session.commit() await session.refresh(existing_document) @@ -384,6 +426,9 @@ async def add_received_file_document_using_docling( chunks=chunks, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, + content_needs_reindexing=False, + last_edited_at=None, ) session.add(document) diff --git a/surfsense_backend/app/tasks/document_processors/markdown_processor.py b/surfsense_backend/app/tasks/document_processors/markdown_processor.py index 76215ed51..3036071c9 100644 --- a/surfsense_backend/app/tasks/document_processors/markdown_processor.py +++ b/surfsense_backend/app/tasks/document_processors/markdown_processor.py @@ -110,6 +110,15 @@ async def add_received_markdown_file_document( # Process chunks chunks = await create_document_chunks(file_in_markdown) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(file_in_markdown) + if not blocknote_json: + logging.warning( + f"Failed to convert {file_name} to BlockNote JSON, document will not be editable" + ) + # Update or create document if existing_document: # Update existing document @@ -121,6 +130,7 @@ async def add_received_markdown_file_document( "FILE_NAME": file_name, } existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json await session.commit() await session.refresh(existing_document) @@ -139,6 +149,7 @@ async def add_received_markdown_file_document( chunks=chunks, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, ) session.add(document) diff --git a/surfsense_backend/app/tasks/document_processors/youtube_processor.py b/surfsense_backend/app/tasks/document_processors/youtube_processor.py index c7d396974..332e775e1 100644 --- a/surfsense_backend/app/tasks/document_processors/youtube_processor.py +++ b/surfsense_backend/app/tasks/document_processors/youtube_processor.py @@ -291,6 +291,16 @@ async def add_youtube_video_document( {"stage": "chunk_processing"}, ) + from app.utils.blocknote_converter import convert_markdown_to_blocknote + + # Convert transcript to BlockNote JSON + blocknote_json = await convert_markdown_to_blocknote(combined_document_string) + if not blocknote_json: + logging.warning( + f"Failed to convert YouTube video '{video_id}' to BlockNote JSON, " + "document will not be editable" + ) + chunks = await create_document_chunks(combined_document_string) # Update or create document @@ -314,6 +324,7 @@ async def add_youtube_video_document( "thumbnail": video_data.get("thumbnail_url", ""), } existing_document.chunks = chunks + existing_document.blocknote_document = blocknote_json await session.commit() await session.refresh(existing_document) @@ -342,6 +353,7 @@ async def add_youtube_video_document( search_space_id=search_space_id, content_hash=content_hash, unique_identifier_hash=unique_identifier_hash, + blocknote_document=blocknote_json, ) session.add(document) diff --git a/surfsense_backend/app/utils/blocknote_converter.py b/surfsense_backend/app/utils/blocknote_converter.py new file mode 100644 index 000000000..b57a82996 --- /dev/null +++ b/surfsense_backend/app/utils/blocknote_converter.py @@ -0,0 +1,123 @@ +import logging +from typing import Any + +import httpx + +from app.config import config + +logger = logging.getLogger(__name__) + + +async def convert_markdown_to_blocknote(markdown: str) -> dict[str, Any] | None: + """ + Convert markdown to BlockNote JSON via Next.js API. + + Args: + markdown: Markdown string to convert + + Returns: + BlockNote document as dict, or None if conversion fails + """ + if not markdown or not markdown.strip(): + logger.warning("Empty markdown provided for conversion") + return None + + if not markdown or len(markdown) < 10: + logger.warning("Markdown became too short after sanitization") + # Return a minimal BlockNote document + return [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Document content could not be converted for editing.", + "styles": {}, + } + ], + "children": [], + } + ] + + async with httpx.AsyncClient() as client: + try: + response = await client.post( + f"{config.NEXT_FRONTEND_URL}/api/convert-to-blocknote", + json={"markdown": markdown}, + timeout=30.0, + ) + response.raise_for_status() + data = response.json() + blocknote_document = data.get("blocknote_document") + + if blocknote_document: + logger.info( + f"Successfully converted markdown to BlockNote (original: {len(markdown)} chars, sanitized: {len(markdown)} chars)" + ) + return blocknote_document + else: + logger.warning("Next.js API returned empty blocknote_document") + return None + + except httpx.TimeoutException: + logger.error("Timeout converting markdown to BlockNote after 30s") + return None + except httpx.HTTPStatusError as e: + logger.error( + f"HTTP error converting markdown to BlockNote: {e.response.status_code} - {e.response.text}" + ) + # Log first 1000 chars of problematic markdown for debugging + logger.debug(f"Problematic markdown sample: {markdown[:1000]}") + return None + except Exception as e: + logger.error(f"Failed to convert markdown to BlockNote: {e}", exc_info=True) + return None + + +async def convert_blocknote_to_markdown( + blocknote_document: dict[str, Any] | list[dict[str, Any]], +) -> str | None: + """ + Convert BlockNote JSON to markdown via Next.js API. + + Args: + blocknote_document: BlockNote document as dict or list of blocks + + Returns: + Markdown string, or None if conversion fails + """ + if not blocknote_document: + logger.warning("Empty BlockNote document provided for conversion") + return None + + async with httpx.AsyncClient() as client: + try: + response = await client.post( + f"{config.NEXT_FRONTEND_URL}/api/convert-to-markdown", + json={"blocknote_document": blocknote_document}, + timeout=30.0, + ) + response.raise_for_status() + data = response.json() + markdown = data.get("markdown") + + if markdown: + logger.info( + f"Successfully converted BlockNote to markdown ({len(markdown)} chars)" + ) + return markdown + else: + logger.warning("Next.js API returned empty markdown") + return None + + except httpx.TimeoutException: + logger.error("Timeout converting BlockNote to markdown after 30s") + return None + except httpx.HTTPStatusError as e: + logger.error( + f"HTTP error converting BlockNote to markdown: {e.response.status_code} - {e.response.text}" + ) + return None + except Exception as e: + logger.error(f"Failed to convert BlockNote to markdown: {e}", exc_info=True) + return None diff --git a/surfsense_web/app/api/convert-to-blocknote/route.ts b/surfsense_web/app/api/convert-to-blocknote/route.ts new file mode 100644 index 000000000..e11c9cb47 --- /dev/null +++ b/surfsense_web/app/api/convert-to-blocknote/route.ts @@ -0,0 +1,40 @@ +import { ServerBlockNoteEditor } from "@blocknote/server-util"; +import { type NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + try { + const { markdown } = await request.json(); + + if (!markdown || typeof markdown !== "string") { + return NextResponse.json({ error: "Markdown string is required" }, { status: 400 }); + } + + // Log raw markdown input before conversion + // console.log(`\n${"=".repeat(80)}`); + // console.log("RAW MARKDOWN INPUT (BEFORE CONVERSION):"); + // console.log("=".repeat(80)); + // console.log(markdown); + // console.log(`${"=".repeat(80)}\n`); + + // Create server-side editor instance + const editor = ServerBlockNoteEditor.create(); + + // Convert markdown directly to BlockNote blocks + const blocks = await editor.tryParseMarkdownToBlocks(markdown); + + if (!blocks || blocks.length === 0) { + throw new Error("Markdown parsing returned no blocks"); + } + + return NextResponse.json({ blocknote_document: blocks }); + } catch (error: any) { + console.error("Failed to convert markdown to BlockNote:", error); + return NextResponse.json( + { + error: "Failed to convert markdown to BlockNote blocks", + details: error.message, + }, + { status: 500 } + ); + } +} diff --git a/surfsense_web/app/api/convert-to-markdown/route.ts b/surfsense_web/app/api/convert-to-markdown/route.ts new file mode 100644 index 000000000..7005a800f --- /dev/null +++ b/surfsense_web/app/api/convert-to-markdown/route.ts @@ -0,0 +1,28 @@ +import { ServerBlockNoteEditor } from "@blocknote/server-util"; +import { type NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + try { + const { blocknote_document } = await request.json(); + + if (!blocknote_document || !Array.isArray(blocknote_document)) { + return NextResponse.json({ error: "BlockNote document array is required" }, { status: 400 }); + } + + // Create server-side editor instance + const editor = ServerBlockNoteEditor.create(); + + // Convert BlockNote blocks to markdown + const markdown = await editor.blocksToMarkdownLossy(blocknote_document); + + return NextResponse.json({ + markdown, + }); + } catch (error) { + console.error("Failed to convert BlockNote to markdown:", error); + return NextResponse.json( + { error: "Failed to convert BlockNote blocks to markdown" }, + { status: 500 } + ); + } +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx index 20f2be15f..0483940e0 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx @@ -309,6 +309,7 @@ export function DocumentsTableShell({ refreshDocuments={async () => { await onRefresh(); }} + searchSpaceId={searchSpaceId as string} /> @@ -340,6 +341,7 @@ export function DocumentsTableShell({ refreshDocuments={async () => { await onRefresh(); }} + searchSpaceId={searchSpaceId as string} />
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx index bd1e182d9..ea4c66228 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx @@ -1,6 +1,7 @@ "use client"; -import { MoreHorizontal } from "lucide-react"; +import { FileText, MoreHorizontal, Pencil, Trash2 } from "lucide-react"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; import { JsonMetadataViewer } from "@/components/json-metadata-viewer"; @@ -28,13 +29,16 @@ export function RowActions({ document, deleteDocument, refreshDocuments, + searchSpaceId, }: { document: Document; deleteDocument: (id: number) => Promise; refreshDocuments: () => Promise; + searchSpaceId: string; }) { const [isOpen, setIsOpen] = useState(false); const [isDeleting, setIsDeleting] = useState(false); + const router = useRouter(); const handleDelete = async () => { setIsDeleting(true); @@ -52,6 +56,10 @@ export function RowActions({ } }; + const handleEdit = () => { + router.push(`/dashboard/${searchSpaceId}/editor/${document.id}`); + }; + return (
@@ -62,11 +70,17 @@ export function RowActions({ + + + Edit Document + + e.preventDefault()}> + View Metadata } @@ -81,6 +95,7 @@ export function RowActions({ setIsOpen(true); }} > + Delete diff --git a/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx new file mode 100644 index 000000000..544834372 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx @@ -0,0 +1,263 @@ +"use client"; + +import { AlertCircle, FileText, Loader2, Save, X } from "lucide-react"; +import { motion } from "motion/react"; +import { useParams, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; +import { BlockNoteEditor } from "@/components/DynamicBlockNoteEditor"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; + +interface EditorContent { + document_id: number; + title: string; + blocknote_document: any; + last_edited_at: string | null; +} + +export default function EditorPage() { + const params = useParams(); + const router = useRouter(); + const documentId = params.documentId as string; + + const [document, setDocument] = useState(null); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [editorContent, setEditorContent] = useState(null); + const [error, setError] = useState(null); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + // Get auth token + const token = + typeof window !== "undefined" ? localStorage.getItem("surfsense_bearer_token") : null; + + // Fetch document content - DIRECT CALL TO FASTAPI + useEffect(() => { + async function fetchDocument() { + if (!token) { + console.error("No auth token found"); + setError("Please login to access the editor"); + setLoading(false); + return; + } + + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/${documentId}/editor-content`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + if (!response.ok) { + const errorData = await response + .json() + .catch(() => ({ detail: "Failed to fetch document" })); + throw new Error(errorData.detail || "Failed to fetch document"); + } + + const data = await response.json(); + + // Check if blocknote_document exists + if (!data.blocknote_document) { + setError( + "This document does not have BlockNote content. Please re-upload the document to enable editing." + ); + setLoading(false); + return; + } + + setDocument(data); + setEditorContent(data.blocknote_document); + setError(null); + } catch (error) { + console.error("Error fetching document:", error); + setError( + error instanceof Error ? error.message : "Failed to fetch document. Please try again." + ); + } finally { + setLoading(false); + } + } + + if (documentId && token) { + fetchDocument(); + } + }, [documentId, token]); + + // Track changes to mark as unsaved + useEffect(() => { + if (editorContent && document) { + setHasUnsavedChanges(true); + } + }, [editorContent, document]); + + // TODO: Maybe add Auto-save every 30 seconds - DIRECT CALL TO FASTAPI + + // Save and exit - DIRECT CALL TO FASTAPI + const handleSave = async () => { + if (!token) { + toast.error("Please login to save"); + return; + } + + if (!editorContent) { + toast.error("No content to save"); + return; + } + + setSaving(true); + try { + // Save blocknote_document and trigger reindexing in background + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/${documentId}/save`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ blocknote_document: editorContent }), + } + ); + + if (!response.ok) { + const errorData = await response + .json() + .catch(() => ({ detail: "Failed to save document" })); + throw new Error(errorData.detail || "Failed to save document"); + } + + setHasUnsavedChanges(false); + toast.success("Document saved! Reindexing in background..."); + + // Small delay before redirect to show success message + setTimeout(() => { + router.push(`/dashboard/${params.search_space_id}/documents`); + }, 500); + } catch (error) { + console.error("Error saving document:", error); + toast.error( + error instanceof Error ? error.message : "Failed to save document. Please try again." + ); + } finally { + setSaving(false); + } + }; + + const handleCancel = () => { + if (hasUnsavedChanges) { + if (confirm("You have unsaved changes. Are you sure you want to leave?")) { + router.back(); + } + } else { + router.back(); + } + }; + + if (loading) { + return ( +
+ + + +

Loading editor...

+
+
+
+ ); + } + + if (error) { + return ( +
+ + + +
+ + Error +
+ {error} +
+ + + +
+
+
+ ); + } + + if (!document) { + return ( +
+ + + +

Document not found

+
+
+
+ ); + } + + return ( + + {/* Toolbar */} +
+
+ +
+

{document.title}

+ {hasUnsavedChanges &&

Unsaved changes

} +
+
+ +
+ + +
+
+ + {/* Editor Container */} +
+
+
+ +
+
+
+
+ ); +} diff --git a/surfsense_web/components/BlockNoteEditor.tsx b/surfsense_web/components/BlockNoteEditor.tsx new file mode 100644 index 000000000..8064a0dc4 --- /dev/null +++ b/surfsense_web/components/BlockNoteEditor.tsx @@ -0,0 +1,68 @@ +"use client"; + +import { useTheme } from "next-themes"; +import { useEffect, useMemo, useRef } from "react"; +import "@blocknote/core/fonts/inter.css"; +import "@blocknote/mantine/style.css"; +import { BlockNoteView } from "@blocknote/mantine"; +import { useCreateBlockNote } from "@blocknote/react"; + +interface BlockNoteEditorProps { + initialContent?: any; + onChange?: (content: any) => void; +} + +export default function BlockNoteEditor({ initialContent, onChange }: BlockNoteEditorProps) { + const { resolvedTheme } = useTheme(); + + // Track the initial content to prevent re-initialization + const initialContentRef = useRef(null); + const isInitializedRef = useRef(false); + + // Creates a new editor instance - only use initialContent on first render + const editor = useCreateBlockNote({ + initialContent: initialContentRef.current === null ? initialContent || undefined : undefined, + }); + + // Store initial content on first render only + useEffect(() => { + if (initialContent && initialContentRef.current === null) { + initialContentRef.current = initialContent; + isInitializedRef.current = true; + } + }, [initialContent]); + + // Call onChange when document changes (but don't update from props) + useEffect(() => { + if (!onChange || !editor || !isInitializedRef.current) return; + + const handleChange = () => { + onChange(editor.document); + }; + + // Subscribe to document changes + const unsubscribe = editor.onChange(handleChange); + + return () => { + unsubscribe(); + }; + }, [editor, onChange]); + + // Determine theme for BlockNote with custom dark mode background + const blockNoteTheme = useMemo(() => { + if (resolvedTheme === "dark") { + // Custom dark theme - only override editor background, let BlockNote handle the rest + return { + colors: { + editor: { + background: "#0A0A0A", // Custom dark background + }, + }, + }; + } + return "light" as const; + }, [resolvedTheme]); + + // Renders the editor instance + return ; +} diff --git a/surfsense_web/components/DynamicBlockNoteEditor.tsx b/surfsense_web/components/DynamicBlockNoteEditor.tsx new file mode 100644 index 000000000..60fc6b11c --- /dev/null +++ b/surfsense_web/components/DynamicBlockNoteEditor.tsx @@ -0,0 +1,6 @@ +"use client"; + +import dynamic from "next/dynamic"; + +// Dynamically import BlockNote editor with SSR disabled +export const BlockNoteEditor = dynamic(() => import("./BlockNoteEditor"), { ssr: false }); diff --git a/surfsense_web/components/dashboard-breadcrumb.tsx b/surfsense_web/components/dashboard-breadcrumb.tsx index 3e2e5199e..a05368eba 100644 --- a/surfsense_web/components/dashboard-breadcrumb.tsx +++ b/surfsense_web/components/dashboard-breadcrumb.tsx @@ -3,7 +3,7 @@ import { useAtomValue } from "jotai"; import { usePathname } from "next/navigation"; import { useTranslations } from "next-intl"; -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { activeChatAtom } from "@/atoms/chats/chat-query.atoms"; import { Breadcrumb, @@ -34,6 +34,41 @@ export function DashboardBreadcrumb() { autoFetch: !!searchSpaceId, }); + // State to store document title for editor breadcrumb + const [documentTitle, setDocumentTitle] = useState(null); + + // Fetch document title when on editor page + useEffect(() => { + if (segments[2] === "editor" && segments[3] && searchSpaceId) { + const documentId = segments[3]; + const token = + typeof window !== "undefined" ? localStorage.getItem("surfsense_bearer_token") : null; + + if (token) { + fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/${documentId}/editor-content`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) + .then((res) => res.json()) + .then((data) => { + if (data.title) { + setDocumentTitle(data.title); + } + }) + .catch(() => { + // If fetch fails, just use the document ID + setDocumentTitle(null); + }); + } + } else { + setDocumentTitle(null); + } + }, [segments, searchSpaceId]); + // Parse the pathname to create breadcrumb items const generateBreadcrumbs = (path: string): BreadcrumbItemInterface[] => { const segments = path.split("/").filter(Boolean); @@ -66,6 +101,7 @@ export function DashboardBreadcrumb() { logs: t("logs"), chats: t("chats"), settings: t("settings"), + editor: t("editor"), }; sectionLabel = sectionLabels[section] || sectionLabel; @@ -73,7 +109,21 @@ export function DashboardBreadcrumb() { // Handle sub-sections if (segments[3]) { const subSection = segments[3]; - let subSectionLabel = subSection.charAt(0).toUpperCase() + subSection.slice(1); + + // Handle editor sub-sections (document ID) + if (section === "editor") { + const documentLabel = documentTitle || subSection; + breadcrumbs.push({ + label: t("documents"), + href: `/dashboard/${segments[1]}/documents`, + }); + breadcrumbs.push({ + label: sectionLabel, + href: `/dashboard/${segments[1]}/documents`, + }); + breadcrumbs.push({ label: documentLabel }); + return breadcrumbs; + } // Handle sources sub-sections if (section === "sources") { @@ -81,7 +131,7 @@ export function DashboardBreadcrumb() { add: "Add Sources", }; - const sourceLabel = sourceLabels[subSection] || subSectionLabel; + const sourceLabel = sourceLabels[subSection] || subSection; breadcrumbs.push({ label: "Sources", href: `/dashboard/${segments[1]}/sources`, @@ -98,7 +148,7 @@ export function DashboardBreadcrumb() { webpage: t("add_webpages"), }; - const documentLabel = documentLabels[subSection] || subSectionLabel; + const documentLabel = documentLabels[subSection] || subSection; breadcrumbs.push({ label: t("documents"), href: `/dashboard/${segments[1]}/documents`, @@ -159,7 +209,7 @@ export function DashboardBreadcrumb() { manage: t("manage_connectors"), }; - const connectorLabel = connectorLabels[subSection] || subSectionLabel; + const connectorLabel = connectorLabels[subSection] || subSection; breadcrumbs.push({ label: t("connectors"), href: `/dashboard/${segments[1]}/connectors`, @@ -169,6 +219,7 @@ export function DashboardBreadcrumb() { } // Handle other sub-sections + let subSectionLabel = subSection.charAt(0).toUpperCase() + subSection.slice(1); const subSectionLabels: Record = { upload: t("upload_documents"), youtube: t("add_youtube"), diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index 45663859d..e4b46ad58 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -621,6 +621,7 @@ "documents": "Documents", "connectors": "Connectors", "podcasts": "Podcasts", + "editor": "Editor", "logs": "Logs", "chats": "Chats", "settings": "Settings", diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index 809b654a5..f1ec9a6fb 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -621,6 +621,7 @@ "documents": "文档", "connectors": "连接器", "podcasts": "播客", + "editor": "编辑器", "logs": "日志", "chats": "聊天", "settings": "设置", diff --git a/surfsense_web/next.config.ts b/surfsense_web/next.config.ts index aca3e2d37..da58fa10c 100644 --- a/surfsense_web/next.config.ts +++ b/surfsense_web/next.config.ts @@ -7,6 +7,8 @@ const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); const nextConfig: NextConfig = { output: "standalone", + // Disable StrictMode for BlockNote compatibility with React 19/Next 15 + reactStrictMode: false, typescript: { ignoreBuildErrors: true, }, @@ -21,6 +23,17 @@ const nextConfig: NextConfig = { }, ], }, + // Mark BlockNote server packages as external + serverExternalPackages: ["@blocknote/server-util"], + + // Configure webpack to handle blocknote packages + webpack: (config, { isServer }) => { + if (isServer) { + // Don't bundle these packages on the server + config.externals = [...(config.externals || []), "@blocknote/server-util"]; + } + return config; + }, }; // Wrap the config with MDX and next-intl plugins diff --git a/surfsense_web/package.json b/surfsense_web/package.json index a9de1d069..7f97111b9 100644 --- a/surfsense_web/package.json +++ b/surfsense_web/package.json @@ -22,6 +22,10 @@ }, "dependencies": { "@ai-sdk/react": "^1.2.12", + "@blocknote/core": "^0.42.3", + "@blocknote/mantine": "^0.42.3", + "@blocknote/react": "^0.42.3", + "@blocknote/server-util": "^0.42.3", "@hookform/resolvers": "^4.1.3", "@llamaindex/chat-ui": "^0.5.17", "@next/third-parties": "^15.5.6", diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml index bee0c6c56..285099816 100644 --- a/surfsense_web/pnpm-lock.yaml +++ b/surfsense_web/pnpm-lock.yaml @@ -11,6 +11,18 @@ importers: '@ai-sdk/react': specifier: ^1.2.12 version: 1.2.12(react@19.1.0)(zod@3.25.76) + '@blocknote/core': + specifier: ^0.42.3 + version: 0.42.3(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(highlight.js@11.11.1) + '@blocknote/mantine': + specifier: ^0.42.3 + version: 0.42.3(@floating-ui/dom@1.7.4)(@mantine/core@8.3.9(@mantine/hooks@8.3.9(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.9(react@19.1.0))(@mantine/utils@6.0.22(react@19.1.0))(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@blocknote/react': + specifier: ^0.42.3 + version: 0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@blocknote/server-util': + specifier: ^0.42.3 + version: 0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@hookform/resolvers': specifier: ^4.1.3 version: 4.1.3(react-hook-form@7.61.1(react@19.1.0)) @@ -136,7 +148,7 @@ importers: version: 15.6.6(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) fumadocs-mdx: specifier: ^11.7.1 - version: 11.7.1(acorn@8.14.0)(fumadocs-core@15.6.6(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 11.7.1(acorn@8.15.0)(fumadocs-core@15.6.6(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) fumadocs-ui: specifier: ^15.6.6 version: 15.6.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) @@ -310,6 +322,9 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@babel/runtime@7.26.9': resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} engines: {node: '>=6.9.0'} @@ -367,6 +382,35 @@ packages: cpu: [x64] os: [win32] + '@blocknote/core@0.42.3': + resolution: {integrity: sha512-wtZki6Gok5Ac9Ek6QTQztcDymstEQgVCisJwiUZTWXh8CD4UKfnIxM7C9+6eEnZMmQ8GNTvRf1HXFl+E4N78VA==} + peerDependencies: + '@hocuspocus/provider': ^2.15.2 + peerDependenciesMeta: + '@hocuspocus/provider': + optional: true + + '@blocknote/mantine@0.42.3': + resolution: {integrity: sha512-xzLweZG1KfFoOp/aSHTXE10IrfEHnhDlP0C2Qt2eNO2IHHa7l8XZJpIGhCoVMsn0yylm91OSynNfTO7JkZZi8w==} + peerDependencies: + '@mantine/core': ^8.3.4 + '@mantine/hooks': ^8.3.4 + '@mantine/utils': ^6.0.22 + react: ^18.0 || ^19.0 || >= 19.0.0-rc + react-dom: ^18.0 || ^19.0 || >= 19.0.0-rc + + '@blocknote/react@0.42.3': + resolution: {integrity: sha512-YnrQ1uyezDbaxYcFstWOJ2r8BMxqwwEc7QAhrEjCMEyBAiOxSCPnrM4/GE2mOgCS0Xa9wIp2LDoPQP2Syv+2EA==} + peerDependencies: + react: ^18.0 || ^19.0 || >= 19.0.0-rc + react-dom: ^18.0 || ^19.0 || >= 19.0.0-rc + + '@blocknote/server-util@0.42.3': + resolution: {integrity: sha512-M+jtKeC2aHOYBp6GQ0YR19iv0/0f1HElrrnKwlaSPbwR6bw6tg+yb3yQkaJJioLTpd2X2Z/RwcEvxSJGnlZ81w==} + peerDependencies: + react: ^18.0 || ^19.0 || >= 19.0.0-rc + react-dom: ^18.0 || ^19.0 || >= 19.0.0-rc + '@codemirror/autocomplete@6.18.6': resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} @@ -472,6 +516,34 @@ packages: 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'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@date-fns/tz@1.2.0': resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} @@ -481,6 +553,9 @@ packages: '@emnapi/runtime@1.7.0': resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} + '@emoji-mart/data@1.2.1': + resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} + '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} deprecated: 'Merged into tsx: https://tsx.is' @@ -821,12 +896,18 @@ packages: '@floating-ui/core@1.7.2': resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + '@floating-ui/dom@1.6.13': resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} '@floating-ui/dom@1.7.2': resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} peerDependencies: @@ -839,12 +920,24 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@floating-ui/react@0.27.13': resolution: {integrity: sha512-Qmj6t9TjgWAvbygNEu1hj4dbHI9CY0ziCMIJrmYoDIn9TUAH5lRmiIeZmRd4c6QEZkzdoH7jNnoNyoY1AIESiA==} peerDependencies: react: '>=17.0.0' react-dom: '>=17.0.0' + '@floating-ui/react@0.27.16': + resolution: {integrity: sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} @@ -1196,6 +1289,23 @@ packages: '@types/react': optional: true + '@mantine/core@8.3.9': + resolution: {integrity: sha512-ivj0Crn5N521cI2eWZBsBGckg0ZYRqfOJz5vbbvYmfj65bp0EdsyqZuOxXzIcn2aUScQhskfvzyhV5XIUv81PQ==} + peerDependencies: + '@mantine/hooks': 8.3.9 + react: ^18.x || ^19.x + react-dom: ^18.x || ^19.x + + '@mantine/hooks@8.3.9': + resolution: {integrity: sha512-Dfz7W0+K1cq4Gb1WFQCZn8tsMXkLH6MV409wZR/ToqsxdNDUMJ/xxbfnwEXWEZjXNJd1wDETHgc+cZG2lTe3Xw==} + peerDependencies: + react: ^18.x || ^19.x + + '@mantine/utils@6.0.22': + resolution: {integrity: sha512-RSKlNZvxhMCkOFZ6slbYvZYbWjHUM+PxDQnupIOxIdsTZQQjx/BFfrfJ7kQFOP+g7MtpOds8weAetEs5obwMOQ==} + peerDependencies: + react: '>=16.8.0' + '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} @@ -2236,6 +2346,9 @@ packages: peerDependencies: react: '>=16.8' + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -2263,6 +2376,9 @@ packages: '@shikijs/transformers@3.8.1': resolution: {integrity: sha512-nmTyFfBrhJk6HJi118jes0wuWdfKXeVUq1Nq+hm8h6wbk1KUfvtg+LY/uDfxZD2VDItHO3QoINIs3NtoKBmgxw==} + '@shikijs/types@3.13.0': + resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==} + '@shikijs/types@3.8.1': resolution: {integrity: sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==} @@ -2410,6 +2526,100 @@ packages: resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} + '@tiptap/core@3.11.0': + resolution: {integrity: sha512-kmS7ZVpHm1EMnW1Wmft9H5ZLM7E0G0NGBx+aGEHGDcNxZBXD2ZUa76CuWjIhOGpwsPbELp684ZdpF2JWoNi4Dg==} + peerDependencies: + '@tiptap/pm': ^3.11.0 + + '@tiptap/extension-bold@3.11.0': + resolution: {integrity: sha512-V/c3XYO09Le9GlBGq1MK4c97Fffi0GADQTbZ+LFoi65nUrAwutn5wYnXBcEyWQI6RmFWVDJTieamqtc4j9teyw==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-bubble-menu@3.11.0': + resolution: {integrity: sha512-P3j9lQ+EZ5Zg/isJzLpCPX7bp7WUBmz8GPs/HPlyMyN2su8LqXntITBZr8IP1JNBlB/wR83k/W0XqdC57mG7cA==} + peerDependencies: + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + + '@tiptap/extension-code@3.11.0': + resolution: {integrity: sha512-5OpR5O4bveHe1KG9CJsto86NgkuerYq3OLY78vzh9uFCLdv7xgXA2aZYJfRMhbZ7hKsR7hHg1etBJUCk+TKsMg==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-floating-menu@3.11.0': + resolution: {integrity: sha512-nEHdWZHEJYX1II1oJQ4aeZ8O/Kss4BRbYFXQFGIvPelCfCYEATpUJh3aq3767ARSq40bOWyu+Dcd4SCW0We6Sw==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + + '@tiptap/extension-gapcursor@3.11.0': + resolution: {integrity: sha512-lXGEZiYX7k/pEFr8BgDE91vqjLTwuf+qhHLTgIpfhbt562nShLPIDj9Vzu3xrR4fwUAMiUNiLyaeInb8j3I4kg==} + peerDependencies: + '@tiptap/extensions': ^3.11.0 + + '@tiptap/extension-history@3.11.0': + resolution: {integrity: sha512-Q/kuNDCoeH2dZ2P+OqEKnRW047SkrngNq+vSrwQlAKO8osO/eAS7aLzn1NELzE5jLvzOKqUda43bSTKsBeTh+w==} + peerDependencies: + '@tiptap/extensions': ^3.11.0 + + '@tiptap/extension-horizontal-rule@3.11.0': + resolution: {integrity: sha512-FugFHZG+oiMBV6k42hn9NOA4wRNc2b9UeEIMR+XwEMpWJInV4VwSwDvu8JClgkDo8z7FEnker9e51DZ00CLWqg==} + peerDependencies: + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + + '@tiptap/extension-italic@3.11.0': + resolution: {integrity: sha512-WP6wL2b//8bLVdeUCWOpYA7nUStvrAMMD0nRn0F9CEW+l7vH6El2PZFhHmJ9uqXo5MnyugBpARiwgxfoAlef5w==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-link@3.11.0': + resolution: {integrity: sha512-RoUkGqowVMKLE76KktNOGhzNMyKtwrSDRqeYCe1ODPuOMZvDGexOE8cIuA4A1ODkgN6ji9qE/9Sf8uhpZdH39Q==} + peerDependencies: + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + + '@tiptap/extension-paragraph@3.11.0': + resolution: {integrity: sha512-hxgjZOXOqstRTWv+QjWJjK23rD5qzIV9ePlhX3imLeq/MgX0aU9VBDaG5SGKbSjaBNQnpLw6+sABJi3CDP6Z5A==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-strike@3.11.0': + resolution: {integrity: sha512-XVP/WMYLrqLBfUsGPu2H9MrOUZLhGUaxtZ3hSRffDi/lsw53x/coZ9eO0FxOB9R7z2ksHWmticIs+0YnKt9LNQ==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-text@3.11.0': + resolution: {integrity: sha512-ELAYm2BuChzZOqDG9B0k3W6zqM4pwNvXkam28KgHGiT2y7Ni68Rb+NXp16uVR+5zR6hkqnQ/BmJSKzAW59MXpA==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extension-underline@3.11.0': + resolution: {integrity: sha512-D3PsS/84RlQKFjd5eerMIUioC0mNh4yy1RRV/WbXx6ugu+6T+0hT42gNk9Ap8pDsVQZCk0SHfDyBEUFC2KOwKw==} + peerDependencies: + '@tiptap/core': ^3.11.0 + + '@tiptap/extensions@3.11.0': + resolution: {integrity: sha512-g43beA73ZMLezez1st9LEwYrRHZ0FLzlsSlOZKk7sdmtHLmuqWHf4oyb0XAHol1HZIdGv104rYaGNgmQXr1ecQ==} + peerDependencies: + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + + '@tiptap/pm@3.11.0': + resolution: {integrity: sha512-plCQDLCZIOc92cizB8NNhBRN0szvYR3cx9i5IXo6v9Xsgcun8KHNcJkesc2AyeqdIs0BtOJZaqQ9adHThz8UDw==} + + '@tiptap/react@3.11.0': + resolution: {integrity: sha512-SDGei/2DjwmhzsxIQNr6dkB6NxLgXZjQ6hF36NfDm4937r5NLrWrNk5tCsoDQiKZ0DHEzuJ6yZM5C7I7LZLB6w==} + peerDependencies: + '@tiptap/core': ^3.11.0 + '@tiptap/pm': ^3.11.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/canvas-confetti@1.9.0': resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==} @@ -2440,12 +2650,21 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@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==} + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} @@ -2478,6 +2697,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@typescript-eslint/eslint-plugin@8.25.0': resolution: {integrity: sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2580,6 +2802,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ai@4.3.19: resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} engines: {node: '>=18'} @@ -2661,6 +2887,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + attr-accept@2.2.5: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} @@ -2829,6 +3058,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} @@ -2868,6 +3101,10 @@ packages: engines: {node: '>=4'} hasBin: true + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2878,6 +3115,10 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2938,6 +3179,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -3090,6 +3335,9 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + emoji-mart@5.6.0: + resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==} + emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -3430,6 +3678,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -3614,6 +3866,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + hast-util-from-dom@5.0.1: resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} @@ -3626,15 +3884,27 @@ packages: hast-util-from-parse5@8.0.3: resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + hast-util-has-property@3.0.0: + resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} + + hast-util-is-body-ok-link@3.0.1: + resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} + hast-util-is-element@3.0.0: resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + hast-util-minify-whitespace@1.0.1: + resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} + hast-util-parse-selector@2.2.5: resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + hast-util-raw@9.1.0: resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} @@ -3650,6 +3920,9 @@ packages: hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + hast-util-to-mdast@10.1.2: + resolution: {integrity: sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==} + hast-util-to-parse5@8.0.0: resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} @@ -3681,12 +3954,31 @@ packages: highlightjs-vue@1.0.0: resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3824,6 +4116,9 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3915,6 +4210,15 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -4034,6 +4338,12 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -4069,6 +4379,9 @@ packages: lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.1.0: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} @@ -4100,6 +4413,10 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -4203,6 +4520,9 @@ packages: 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==} @@ -4420,10 +4740,18 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + 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'} @@ -4559,6 +4887,9 @@ packages: number-flow@0.5.8: resolution: {integrity: sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA==} + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4604,6 +4935,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + outvariant@1.4.0: resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} @@ -4775,9 +5109,106 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + prosemirror-changeset@2.3.1: + resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + + prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.4.0: + resolution: {integrity: sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==} + + prosemirror-highlight@0.13.0: + resolution: {integrity: sha512-GIC2VCTUnukNdsEGLQWWOVpYPl/7/KrVp4xs7XMB48/4rhUrHK8hp8TEog4Irmv+2kmjx24RLnaisGOCP6U8jw==} + peerDependencies: + '@shikijs/types': ^1.29.2 || ^2.0.0 || ^3.0.0 + '@types/hast': ^3.0.0 + highlight.js: ^11.9.0 + lowlight: ^3.1.0 + prosemirror-model: ^1.19.3 + prosemirror-state: ^1.4.3 + prosemirror-transform: ^1.8.0 + prosemirror-view: ^1.32.4 + refractor: ^5.0.0 + sugar-high: ^0.6.1 || ^0.7.0 || ^0.8.0 || ^0.9.0 + peerDependenciesMeta: + '@shikijs/types': + optional: true + '@types/hast': + optional: true + highlight.js: + optional: true + lowlight: + optional: true + prosemirror-model: + optional: true + prosemirror-state: + optional: true + prosemirror-transform: + optional: true + prosemirror-view: + optional: true + refractor: + optional: true + sugar-high: + optional: true + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-markdown@1.13.2: + resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} + + prosemirror-menu@1.2.5: + resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} + + prosemirror-model@1.25.4: + resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} + + prosemirror-schema-basic@1.2.4: + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.1: + resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==} + + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + + prosemirror-transform@1.10.5: + resolution: {integrity: sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==} + + prosemirror-view@1.41.3: + resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==} + 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'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4834,6 +5265,11 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-icons@5.5.0: + resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + peerDependencies: + react: '*' + react-intersection-observer@9.5.1: resolution: {integrity: sha512-YwcNF/4WsMAG1rLVDQHSbpdEW9vDaIl4QW88d+vqeXNUewFV4AJDQB14oHpAJ3rRCnKRmwu3nqfwwYe6wioNIg==} peerDependencies: @@ -4881,6 +5317,12 @@ packages: 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 + react-number-format@5.4.4: + resolution: {integrity: sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==} + peerDependencies: + 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: @@ -4958,6 +5400,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + 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'} @@ -5017,18 +5465,33 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + rehype-katex@7.0.1: resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} + rehype-minify-whitespace@6.0.2: + resolution: {integrity: sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} rehype-recma@1.0.0: resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + rehype-remark@10.0.1: + resolution: {integrity: sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==} + rehype-sanitize@6.0.0: resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + remark-code-import@1.2.0: resolution: {integrity: sha512-fgwLruqlZbVOIhCJFjY+JDwPZhA4/eK3InJzN8Ox8UDdtudpG212JwtRj6la+lAzJU7JmSEyewZSukVZdknt3Q==} engines: {node: '>= 12'} @@ -5083,9 +5546,18 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + rough-notation@0.5.1: resolution: {integrity: sha512-ITHofTzm13cWFVfoGsh/4c/k2Mg8geKgBCwex71UZLnNuw403tCRjYPQ68jSAd37DMbZIePXPjDgY0XdZi9HPw==} + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5108,6 +5580,13 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -5306,6 +5785,9 @@ packages: peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} @@ -5362,6 +5844,13 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + to-gatsby-remark-plugin@0.1.0: resolution: {integrity: sha512-blmhJ/gIrytWnWLgPSRCkhCPeki6UBK2daa3k9mGahN7GjwHu8KrS7F70MvwlsG7IE794JLgwAdCbi4hU4faFQ==} @@ -5372,12 +5861,23 @@ packages: 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'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trim-trailing-lines@2.1.0: + resolution: {integrity: sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==} + trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} @@ -5408,6 +5908,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -5436,6 +5940,9 @@ packages: resolution: {integrity: sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -5565,6 +6072,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -5600,6 +6111,10 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -5609,6 +6124,22 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -5640,10 +6171,45 @@ packages: 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'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y-prosemirror@1.3.7: + resolution: {integrity: sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + prosemirror-model: ^1.7.1 + prosemirror-state: ^1.2.3 + prosemirror-view: ^1.9.10 + y-protocols: ^1.0.1 + yjs: ^13.5.38 + + y-protocols@1.0.6: + resolution: {integrity: sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -5707,6 +6273,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@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) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + '@babel/runtime@7.26.9': dependencies: regenerator-runtime: 0.14.1 @@ -5746,6 +6320,136 @@ snapshots: '@biomejs/cli-win32-x64@2.1.2': optional: true + '@blocknote/core@0.42.3(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(highlight.js@11.11.1)': + dependencies: + '@emoji-mart/data': 1.2.1 + '@shikijs/types': 3.13.0 + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/extension-bold': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-code': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-gapcursor': 3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) + '@tiptap/extension-history': 3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) + '@tiptap/extension-horizontal-rule': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-italic': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-link': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-paragraph': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-strike': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-text': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/extension-underline': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + '@tiptap/pm': 3.11.0 + emoji-mart: 5.6.0 + fast-deep-equal: 3.1.3 + hast-util-from-dom: 5.0.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-highlight: 0.13.0(@shikijs/types@3.13.0)(@types/hast@3.0.4)(highlight.js@11.11.1)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-transform@1.10.5)(prosemirror-view@1.41.3) + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.1 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + rehype-format: 5.0.1 + rehype-parse: 9.0.1 + rehype-remark: 10.0.1 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-stringify: 11.0.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + uuid: 8.3.2 + y-prosemirror: 1.3.7(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + y-protocols: 1.0.6(yjs@13.6.27) + yjs: 13.6.27 + transitivePeerDependencies: + - '@tiptap/extensions' + - '@types/hast' + - highlight.js + - lowlight + - refractor + - sugar-high + - supports-color + + '@blocknote/mantine@0.42.3(@floating-ui/dom@1.7.4)(@mantine/core@8.3.9(@mantine/hooks@8.3.9(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.3.9(react@19.1.0))(@mantine/utils@6.0.22(react@19.1.0))(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@blocknote/core': 0.42.3(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(highlight.js@11.11.1) + '@blocknote/react': 0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/core': 8.3.9(@mantine/hooks@8.3.9(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 8.3.9(react@19.1.0) + '@mantine/utils': 6.0.22(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-icons: 5.5.0(react@19.1.0) + transitivePeerDependencies: + - '@floating-ui/dom' + - '@hocuspocus/provider' + - '@tiptap/extensions' + - '@types/hast' + - '@types/react' + - '@types/react-dom' + - highlight.js + - lowlight + - refractor + - sugar-high + - supports-color + + '@blocknote/react@0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@blocknote/core': 0.42.3(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(highlight.js@11.11.1) + '@emoji-mart/data': 1.2.1 + '@floating-ui/react': 0.27.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + '@tiptap/react': 3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + emoji-mart: 5.6.0 + lodash.merge: 4.6.2 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-icons: 5.5.0(react@19.1.0) + transitivePeerDependencies: + - '@floating-ui/dom' + - '@hocuspocus/provider' + - '@tiptap/extensions' + - '@types/hast' + - '@types/react' + - '@types/react-dom' + - highlight.js + - lowlight + - refractor + - sugar-high + - supports-color + + '@blocknote/server-util@0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@blocknote/core': 0.42.3(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(highlight.js@11.11.1) + '@blocknote/react': 0.42.3(@floating-ui/dom@1.7.4)(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))(@types/hast@3.0.4)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(highlight.js@11.11.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + jsdom: 25.0.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + y-prosemirror: 1.3.7(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + y-protocols: 1.0.6(yjs@13.6.27) + yjs: 13.6.27 + transitivePeerDependencies: + - '@floating-ui/dom' + - '@hocuspocus/provider' + - '@tiptap/extensions' + - '@types/hast' + - '@types/react' + - '@types/react-dom' + - bufferutil + - canvas + - highlight.js + - lowlight + - prosemirror-model + - prosemirror-state + - prosemirror-view + - refractor + - sugar-high + - supports-color + - utf-8-validate + '@codemirror/autocomplete@6.18.6': dependencies: '@codemirror/language': 6.11.2 @@ -6038,6 +6742,26 @@ snapshots: react-dom: 19.1.0(react@19.1.0) 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)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@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) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + '@date-fns/tz@1.2.0': {} '@drizzle-team/brocli@0.10.2': {} @@ -6047,6 +6771,8 @@ snapshots: tslib: 2.8.1 optional: true + '@emoji-mart/data@1.2.1': {} + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 @@ -6253,6 +6979,10 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.10 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + '@floating-ui/dom@1.6.13': dependencies: '@floating-ui/core': 1.6.9 @@ -6263,6 +6993,11 @@ snapshots: '@floating-ui/core': 1.7.2 '@floating-ui/utils': 0.2.10 + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + '@floating-ui/react-dom@2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/dom': 1.6.13 @@ -6275,6 +7010,12 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + '@floating-ui/react-dom@2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@floating-ui/react@0.27.13(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -6283,6 +7024,14 @@ snapshots: react-dom: 19.1.0(react@19.1.0) tabbable: 6.2.0 + '@floating-ui/react@0.27.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@floating-ui/utils': 0.2.10 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.10': {} '@floating-ui/utils@0.2.9': {} @@ -6766,9 +7515,31 @@ snapshots: optionalDependencies: '@types/react': 19.1.8 + '@mantine/core@8.3.9(@mantine/hooks@8.3.9(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react': 0.27.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 8.3.9(react@19.1.0) + clsx: 2.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-number-format: 5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) + react-textarea-autosize: 8.5.9(@types/react@19.1.8)(react@19.1.0) + type-fest: 4.41.0 + transitivePeerDependencies: + - '@types/react' + + '@mantine/hooks@8.3.9(react@19.1.0)': + dependencies: + react: 19.1.0 + + '@mantine/utils@6.0.22(react@19.1.0)': + dependencies: + react: 19.1.0 + '@marijn/find-cluster-break@1.0.2': {} - '@mdx-js/mdx@3.1.0(acorn@8.14.0)': + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': dependencies: '@types/estree': 1.0.8 '@types/estree-jsx': 1.0.5 @@ -6782,7 +7553,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 markdown-extensions: 2.0.0 recma-build-jsx: 1.0.0 - recma-jsx: 1.0.0(acorn@8.14.0) + recma-jsx: 1.0.0(acorn@8.15.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 remark-mdx: 3.1.0 @@ -7915,6 +8686,8 @@ snapshots: dependencies: react: 19.1.0 + '@remirror/core-constants@3.0.0': {} + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.10.5': {} @@ -7959,6 +8732,11 @@ snapshots: '@shikijs/core': 3.8.1 '@shikijs/types': 3.8.1 + '@shikijs/types@3.13.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/types@3.8.1': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -8086,6 +8864,114 @@ snapshots: '@tanstack/table-core@8.21.3': {} + '@tiptap/core@3.11.0(@tiptap/pm@3.11.0)': + dependencies: + '@tiptap/pm': 3.11.0 + + '@tiptap/extension-bold@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-bubble-menu@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + optional: true + + '@tiptap/extension-code@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-floating-menu@3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + optional: true + + '@tiptap/extension-gapcursor@3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + + '@tiptap/extension-history@3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + + '@tiptap/extension-horizontal-rule@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + + '@tiptap/extension-italic@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-link@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + linkifyjs: 4.3.2 + + '@tiptap/extension-paragraph@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-strike@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-text@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extension-underline@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + + '@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + + '@tiptap/pm@3.11.0': + dependencies: + prosemirror-changeset: 2.3.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.4.0 + prosemirror-history: 1.5.0 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.4 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3) + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + + '@tiptap/react@3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/pm': 3.11.0 + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/use-sync-external-store': 0.0.6 + fast-deep-equal: 3.1.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + use-sync-external-store: 1.5.0(react@19.1.0) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-floating-menu': 3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + transitivePeerDependencies: + - '@floating-ui/dom' + '@types/canvas-confetti@1.9.0': {} '@types/debug@4.1.12': @@ -8114,6 +9000,13 @@ snapshots: '@types/katex@0.16.7': {} + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + '@types/mdast@3.0.15': dependencies: '@types/unist': 2.0.11 @@ -8122,6 +9015,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/mdurl@2.0.0': {} + '@types/mdx@2.0.13': {} '@types/ms@2.1.0': {} @@ -8154,6 +9049,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/eslint-plugin@8.25.0(@typescript-eslint/parser@8.25.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.32.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -8291,6 +9188,8 @@ snapshots: acorn@8.15.0: {} + agent-base@7.1.4: {} + ai@4.3.19(react@19.1.0)(zod@3.25.76): dependencies: '@ai-sdk/provider': 1.1.3 @@ -8398,6 +9297,8 @@ snapshots: async-function@1.0.0: {} + asynckit@0.4.0: {} + attr-accept@2.2.5: {} available-typed-arrays@1.0.7: @@ -8567,6 +9468,10 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comma-separated-tokens@1.0.8: {} comma-separated-tokens@2.0.3: {} @@ -8599,6 +9504,11 @@ snapshots: cssesc@3.0.0: {} + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + csstype@3.1.3: {} d@1.0.2: @@ -8608,6 +9518,11 @@ snapshots: damerau-levenshtein@1.0.8: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.3 @@ -8666,6 +9581,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + dequal@2.0.3: {} detect-libc@2.0.3: {} @@ -8740,6 +9657,8 @@ snapshots: - '@types/react' - '@types/react-dom' + emoji-mart@5.6.0: {} + emoji-regex@9.2.2: {} end-of-stream@1.4.5: @@ -9314,6 +10233,14 @@ snapshots: dependencies: is-callable: 1.2.7 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + format@0.2.2: {} framer-motion@12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -9358,9 +10285,9 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@11.7.1(acorn@8.14.0)(fumadocs-core@15.6.6(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): + fumadocs-mdx@11.7.1(acorn@8.15.0)(fumadocs-core@15.6.6(@types/react@19.1.8)(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): dependencies: - '@mdx-js/mdx': 3.1.0(acorn@8.14.0) + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) '@standard-schema/spec': 1.0.0 chokidar: 4.0.3 esbuild: 0.25.8 @@ -9510,6 +10437,21 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-embedded@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.1 + hast-util-from-dom@5.0.1: dependencies: '@types/hast': 3.0.4 @@ -9543,16 +10485,40 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 + hast-util-has-property@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element@3.0.0: dependencies: '@types/hast': 3.0.4 + hast-util-minify-whitespace@1.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.0 + hast-util-parse-selector@2.2.5: {} hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 + hast-util-phrasing@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + hast-util-raw@9.1.0: dependencies: '@types/hast': 3.0.4 @@ -9630,6 +10596,23 @@ snapshots: transitivePeerDependencies: - supports-color + hast-util-to-mdast@10.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + hast-util-phrasing: 3.0.1 + hast-util-to-html: 9.0.5 + hast-util-to-text: 4.0.2 + hast-util-whitespace: 3.0.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-hast: 13.2.0 + mdast-util-to-string: 4.0.0 + rehype-minify-whitespace: 6.0.2 + trim-trailing-lines: 2.1.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + hast-util-to-parse5@8.0.0: dependencies: '@types/hast': 3.0.4 @@ -9679,10 +10662,34 @@ snapshots: highlightjs-vue@1.0.0: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} + html-whitespace-sensitive-tag-names@3.0.1: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -9817,6 +10824,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.3 @@ -9892,6 +10901,34 @@ snapshots: dependencies: argparse: 2.0.1 + jsdom@25.0.1: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + form-data: 4.0.5 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.22 + parse5: 7.2.1 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -9989,6 +11026,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + linkifyjs@4.3.2: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -10018,6 +11061,8 @@ snapshots: fault: 1.0.4 highlight.js: 10.7.3 + lru-cache@10.4.3: {} + lru-cache@11.1.0: {} lucide-react@0.453.0(react@19.1.0): @@ -10040,6 +11085,15 @@ snapshots: markdown-extensions@2.0.0: {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + markdown-table@3.0.4: {} math-intrinsics@1.1.0: {} @@ -10343,6 +11397,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + mdurl@2.0.0: {} + memoize-one@5.2.1: {} merge-refs@1.3.0(@types/react@19.1.8): @@ -10838,8 +11894,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + 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 @@ -10950,6 +12012,8 @@ snapshots: dependencies: esm-env: 1.2.2 + nwsapi@2.2.22: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -11013,6 +12077,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + orderedmap@2.1.1: {} + outvariant@1.4.0: {} own-keys@1.0.1: @@ -11191,12 +12257,127 @@ snapshots: property-information@7.1.0: {} + prosemirror-changeset@2.3.1: + dependencies: + prosemirror-transform: 1.10.5 + + prosemirror-collab@1.3.1: + dependencies: + prosemirror-state: 1.4.4 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + + prosemirror-gapcursor@1.4.0: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + + prosemirror-highlight@0.13.0(@shikijs/types@3.13.0)(@types/hast@3.0.4)(highlight.js@11.11.1)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-transform@1.10.5)(prosemirror-view@1.41.3): + optionalDependencies: + '@shikijs/types': 3.13.0 + '@types/hast': 3.0.4 + highlight.js: 11.11.1 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.2: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + prosemirror-model: 1.25.4 + + prosemirror-menu@1.2.5: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.5.0 + prosemirror-state: 1.4.4 + + prosemirror-model@1.25.4: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.4: + dependencies: + prosemirror-model: 1.25.4 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.4 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + + prosemirror-tables@1.8.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.10.5 + prosemirror-view: 1.41.3 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + + prosemirror-transform@1.10.5: + dependencies: + prosemirror-model: 1.25.4 + + prosemirror-view@1.41.3: + dependencies: + prosemirror-model: 1.25.4 + 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: {} pure-color@1.3.0: {} @@ -11257,6 +12438,10 @@ snapshots: dependencies: react: 19.1.0 + react-icons@5.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + react-intersection-observer@9.5.1(react@19.1.0): dependencies: react: 19.1.0 @@ -11330,6 +12515,11 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + react-number-format@5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-pdf@9.2.1(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: clsx: 2.1.1 @@ -11419,6 +12609,15 @@ snapshots: transitivePeerDependencies: - '@types/react' + react-textarea-autosize@8.5.9(@types/react@19.1.8)(react@19.1.0): + dependencies: + '@babel/runtime': 7.26.9 + react: 19.1.0 + use-composed-ref: 1.4.0(@types/react@19.1.8)(react@19.1.0) + use-latest: 1.3.0(@types/react@19.1.8)(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + react-window@1.8.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.26.9 @@ -11447,9 +12646,9 @@ snapshots: estree-util-build-jsx: 3.0.1 vfile: 6.0.3 - recma-jsx@1.0.0(acorn@8.14.0): + recma-jsx@1.0.0(acorn@8.15.0): dependencies: - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn-jsx: 5.3.2(acorn@8.15.0) estree-util-to-js: 2.0.0 recma-parse: 1.0.0 recma-stringify: 1.0.0 @@ -11509,6 +12708,11 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + rehype-katex@7.0.1: dependencies: '@types/hast': 3.0.4 @@ -11519,6 +12723,17 @@ snapshots: unist-util-visit-parents: 6.0.1 vfile: 6.0.3 + rehype-minify-whitespace@6.0.2: + dependencies: + '@types/hast': 3.0.4 + hast-util-minify-whitespace: 1.0.1 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -11533,11 +12748,25 @@ snapshots: transitivePeerDependencies: - supports-color + rehype-remark@10.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + hast-util-to-mdast: 10.1.2 + unified: 11.0.5 + vfile: 6.0.3 + rehype-sanitize@6.0.0: dependencies: '@types/hast': 3.0.4 hast-util-sanitize: 5.0.2 + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + remark-code-import@1.2.0: dependencies: strip-indent: 4.0.0 @@ -11643,8 +12872,14 @@ snapshots: reusify@1.1.0: {} + rope-sequence@1.3.4: {} + rough-notation@0.5.1: {} + rrweb-cssom@0.7.1: {} + + rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -11675,6 +12910,12 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.26.0: {} scroll-into-view-if-needed@3.1.0: @@ -11936,6 +13177,8 @@ snapshots: react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) + symbol-tree@3.2.4: {} + tabbable@6.2.0: {} tailwind-merge@2.6.0: {} @@ -11996,6 +13239,12 @@ snapshots: fdir: 6.4.6(picomatch@4.0.3) picomatch: 4.0.3 + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + to-gatsby-remark-plugin@0.1.0: dependencies: to-vfile: 6.1.0 @@ -12009,10 +13258,20 @@ snapshots: is-buffer: 2.0.5 vfile: 4.2.1 + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + tr46@0.0.3: {} + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + trim-lines@3.0.1: {} + trim-trailing-lines@2.1.0: {} + trough@2.2.0: {} ts-api-utils@2.0.1(typescript@5.8.3): @@ -12046,6 +13305,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@4.41.0: {} + type@2.7.3: {} typed-array-buffer@1.0.3: @@ -12085,6 +13346,8 @@ snapshots: ua-parser-js@1.0.40: {} + uc.micro@2.1.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.3 @@ -12238,6 +13501,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@8.3.2: {} + uvu@0.5.6: dependencies: dequal: 2.0.3 @@ -12295,6 +13560,10 @@ snapshots: w3c-keyname@2.2.8: {} + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + warning@4.0.3: dependencies: loose-envify: 1.4.0 @@ -12303,6 +13572,19 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -12357,8 +13639,28 @@ snapshots: wrappy@1.0.2: optional: true + ws@8.18.3: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + xtend@4.0.2: {} + y-prosemirror@1.3.7(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27): + dependencies: + lib0: 0.2.114 + prosemirror-model: 1.25.4 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.3 + y-protocols: 1.0.6(yjs@13.6.27) + yjs: 13.6.27 + + y-protocols@1.0.6(yjs@13.6.27): + dependencies: + lib0: 0.2.114 + yjs: 13.6.27 + yallist@5.0.0: {} yjs@13.6.27: