1 line
9.3 KiB
Text
1 line
9.3 KiB
Text
|
|
{"version":3,"sources":["../../src/nodes/NoteNode/NoteNode.tsx","../../src/utils/debug.ts"],"sourcesContent":["/**\n * NoteNode Component\n *\n * A rich text note editor node using BlockNote.\n * Storage-agnostic - accepts a storage adapter for any backend.\n */\n\n'use client';\n\nimport React, { useEffect, useRef } from 'react';\nimport { useCreateBlockNote, useEditorChange } from '@blocknote/react';\nimport { BlockNoteView } from '@blocknote/shadcn';\nimport { en } from '@blocknote/core/locales';\nimport { createDebug } from '../../utils/debug';\nconst debug = createDebug('note-node');\n\n// CSS imports - consumers must ensure these are available\nimport '@blocknote/core/fonts/inter.css';\nimport '@blocknote/shadcn/style.css';\n\n/**\n * NoteNode - A rich text editor node for canvas.\n *\n * Uses BlockNote for the editor and accepts a storage adapter\n * for persistence. This allows it to work with any backend:\n * Jazz, Supabase, local state, etc.\n *\n * @example\n * ```tsx\n * // With Jazz storage\n * <NoteNode\n * nodeData={nodeData}\n * storage={useJazzNoteStorage(nodeData.id)}\n * />\n *\n * // With local state\n * const [content, setContent] = useState('');\n * <NoteNode\n * nodeData={nodeData}\n * storage={{ content, onChange: setContent }}\n * />\n * ```\n */\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport function NoteNode({\n nodeData,\n storage,\n theme = 'light',\n placeholder = 'Start typing...',\n isResizing\n}) {\n // Refs to prevent sync loops between editor and storage\n const isSyncingFromStorage = useRef(false);\n const isSyncingToStorage = useRef(false);\n const lastStorageContent = useRef('');\n\n // Create BlockNote editor\n const editor = useCreateBlockNote({\n initialContent: [{\n id: crypto.randomUUID(),\n type: 'paragraph',\n content: ''\n }],\n dictionary: {\n ...en,\n placeholders: {\n ...en.placeholders,\n emptyDocument: placeholder,\n default: placeholder,\n heading: 'Enter a heading'\n }\n }\n }, []);\n\n // Sync editor changes → storage\n useEditorChange(() => {\n if (isSyncingFromStorage.current) {\n return;\n }\n try {\n isSyncingToStorage.current = true;\n const html = editor.blocksToFullHTML(editor.document);\n if (html !== lastStorageContent.current) {\n lastStorageContent.current = html;\n storage.onChange(html);\n }\n } catch (err) {\n debug.error('Failed to sync to storage: %O', err);\n } finally {\n isSyncingToStorage.current = false;\n }\n }, editor);\n\n // Sync storage changes → editor (for real-time sync)\n useEffect(() => {\n if (!editor) return;\n const handleStorageChange = newContent => {\n if (isSyncingToStorage.current) {\n return;\n }\n\n // Throttle to next microtask to avoid invalid intermediate states\n queueMicrotask(() => {\n if (newContent === lastStorageContent.current) return;\n lastStorageContent.current = newContent;\n if (newContent && newContent.trim()) {\n try {\n isSyncingFromStorage.current = true;\n const blocks = editor.tryParseHTMLToBlocks(newContent);\n editor.replaceBlocks(editor.document, blocks);\n } catch (err_0) {\n debug.error('Failed to parse HTML from storage: %O', err_0);\n } finally {\n isSyncingFromStorage.current = false;\n }\n }\n });\n };\n\n // Subscribe to external changes if available\n const unsubscribe = storage.subscribe?.(handleStorageChange);\n\n // Initial sync from storage\n if (storage.content && storage.content !== lastStorageContent.current) {\n handleStorageChange(storage.content);\n }\n return () => {\n unsubscribe?.();\n };\n }, [editor, storage]);\n\n // Show loading state if storage is loading\n if (storage.isLoading) {\n return /*#__PURE__*/_jsx(\"div\", {\n className: \"note-node note-node--loading\",\n style: styles.loading,\n ch
|