'use client' import { useEffect, useMemo, useState } from 'react' import { useCreateBlockNote } from '@blocknote/react' import { BlockNoteView } from '@blocknote/mantine' import '@blocknote/core/fonts/inter.css' import '@blocknote/mantine/style.css' import type { PartialBlock } from '@blocknote/core' interface BlockEditorProps { initialContent?: string | null onChange?: (content: string) => void onUploadFile?: (file: File) => Promise editable?: boolean className?: string } export function BlockEditor({ initialContent, onChange, onUploadFile, editable = true, className, }: BlockEditorProps) { const [mounted, setMounted] = useState(false) // Parse initial content const parsedContent = useMemo(() => { if (!initialContent) return undefined try { return JSON.parse(initialContent) as PartialBlock[] } catch { return undefined } }, [initialContent]) // Default upload handler that uses the provided callback or creates blob URLs const uploadFile = async (file: File): Promise => { if (onUploadFile) { return onUploadFile(file) } // Fallback: create blob URL (not persistent) return URL.createObjectURL(file) } const editor = useCreateBlockNote({ initialContent: parsedContent, uploadFile, }) // Handle content changes useEffect(() => { if (!onChange || !editor) return const handleChange = () => { const content = JSON.stringify(editor.document) onChange(content) } // Subscribe to changes editor.onEditorContentChange(handleChange) }, [editor, onChange]) // Client-side only rendering useEffect(() => { setMounted(true) }, []) if (!mounted) { return (
) } return (
) } // Read-only viewer component interface BlockViewerProps { content?: string | null className?: string } export function BlockViewer({ content, className }: BlockViewerProps) { const [mounted, setMounted] = useState(false) const parsedContent = useMemo(() => { if (!content) return undefined try { return JSON.parse(content) as PartialBlock[] } catch { return undefined } }, [content]) const editor = useCreateBlockNote({ initialContent: parsedContent, }) useEffect(() => { setMounted(true) }, []) if (!mounted) { return (
) } if (!content) { return (
No content available
) } return (
) }