diff --git a/surfsense_backend/app/routes/editor_routes.py b/surfsense_backend/app/routes/editor_routes.py index 0f986e416..7abbdd31d 100644 --- a/surfsense_backend/app/routes/editor_routes.py +++ b/surfsense_backend/app/routes/editor_routes.py @@ -38,6 +38,8 @@ logger = logging.getLogger(__name__) router = APIRouter() +EDITOR_PLATE_MAX_BYTES = 2 * 1024 * 1024 + @router.get("/search-spaces/{search_space_id}/documents/{document_id}/editor-content") async def get_editor_content( @@ -84,6 +86,7 @@ async def get_editor_content( def _build_response(md: str) -> dict: size_bytes = len(md.encode("utf-8")) + viewer_mode = "monaco" if size_bytes > EDITOR_PLATE_MAX_BYTES else "plate" truncated = False output_md = md if max_length is not None and size_bytes > max_length: @@ -97,6 +100,7 @@ async def get_editor_content( "content_size_bytes": size_bytes, "chunk_count": chunk_count, "truncated": truncated, + "viewer_mode": viewer_mode, "updated_at": document.updated_at.isoformat() if document.updated_at else None, diff --git a/surfsense_web/components/editor-panel/editor-panel.tsx b/surfsense_web/components/editor-panel/editor-panel.tsx index 945fbc529..2c0d316d9 100644 --- a/surfsense_web/components/editor-panel/editor-panel.tsx +++ b/surfsense_web/components/editor-panel/editor-panel.tsx @@ -51,10 +51,12 @@ interface EditorContent { content_size_bytes?: number; chunk_count?: number; truncated?: boolean; + viewer_mode?: ViewerMode; } const EDITABLE_DOCUMENT_TYPES = new Set(["FILE", "NOTE"]); type EditorRenderMode = "rich_markdown" | "source_code"; +type ViewerMode = "plate" | "monaco"; type AgentFilesystemMount = { mount: string; @@ -168,6 +170,9 @@ export function EditorPanelContent({ ); const isLargeDocument = (editorDoc?.content_size_bytes ?? 0) > LARGE_DOCUMENT_THRESHOLD; + const viewerMode: ViewerMode = isMemoryMode + ? "plate" + : (editorDoc?.viewer_mode ?? (isLargeDocument ? "monaco" : "plate")); useEffect(() => { const controller = new AbortController(); @@ -432,12 +437,10 @@ export function EditorPanelContent({ ? (isMemoryMode || editorRenderMode === "source_code" || EDITABLE_DOCUMENT_TYPES.has(editorDoc.document_type ?? "")) && - !isLargeDocument + viewerMode === "plate" : false; - // Render through PlateEditor for editable doc types (FILE/NOTE). - // Everything else (large docs, non-editable types) falls back to the - // lightweight `MarkdownViewer` — Plate is heavy on multi-MB docs and - // non-editable types don't benefit from its editing UX. + // Render through PlateEditor only when the backend says the rich editor is safe. + // Monaco mode is a raw markdown safety path for large documents. const renderInPlateEditor = isEditableType; const hasUnsavedChanges = editedMarkdown !== null; const showDesktopHeader = !!onClose; @@ -492,14 +495,14 @@ export function EditorPanelContent({ } }, [documentId, editorDoc?.title, searchSpaceId]); - const largeDocAlert = isLargeDocument && !isLocalFileMode && editorDoc && ( - + const largeDocAlert = viewerMode === "monaco" && !isLocalFileMode && editorDoc && ( + This document is too large for the editor ( {Math.round((editorDoc.content_size_bytes ?? 0) / 1024 / 1024)}MB,{" "} - {editorDoc.chunk_count ?? 0} chunks). Showing a preview below. + {editorDoc.chunk_count ?? 0} chunks). Showing raw markdown below. )} -
-
- {isLargeDocument ? ( - <> - - - - - This document is too large for the editor ( - {Math.round((doc.content_size_bytes ?? 0) / 1024 / 1024)}MB,{" "} - {doc.chunk_count ?? 0} chunks). Showing a preview below. +
+ {viewerMode === "monaco" ? ( +
+ + + + + This document is too large for the editor ( + {Math.round((doc.content_size_bytes ?? 0) / 1024 / 1024)}MB,{" "} + {doc.chunk_count ?? 0} chunks). Showing raw markdown below. + + - - + {downloading && } + + + +
+ {}} + /> +
+
+ ) : ( +
+
- - ) : ( - - )} -
+
+
+ )}
); diff --git a/surfsense_web/tests/helpers/api/documents.ts b/surfsense_web/tests/helpers/api/documents.ts index 92055602b..0d8cc8839 100644 --- a/surfsense_web/tests/helpers/api/documents.ts +++ b/surfsense_web/tests/helpers/api/documents.ts @@ -44,6 +44,7 @@ export type EditorContent = { content_size_bytes: number; chunk_count: number; truncated: boolean; + viewer_mode?: "plate" | "monaco"; }; // Same endpoint the UI hits when a user opens a document in the dashboard.