dopecanvas 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/dopecanvas.cjs +10 -3
- package/dist/dopecanvas.cjs.map +1 -1
- package/dist/dopecanvas.js +1301 -474
- package/dist/dopecanvas.js.map +1 -1
- package/dist/index.d.ts +82 -12
- package/package.json +2 -2
package/dist/dopecanvas.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dopecanvas.cjs","sources":["../src/components/Page.tsx","../src/core/types.ts","../src/components/PagedView.tsx","../src/core/PageLayoutEngine.ts","../src/core/EditableManager.ts","../src/components/DopeCanvas.tsx","../src/core/DocumentEngine.ts","../src/api/DocumentAPI.ts","../src/hooks/useSelectionContext.ts","../src/components/Toolbar/TextToolbar.tsx","../src/components/Toolbar/PageSetupToolbar.tsx","../src/components/Toolbar/Toolbar.tsx","../src/hooks/useDocumentEngine.ts"],"sourcesContent":["// ============================================================\n// Page — Single page frame component\n// ============================================================\n// Renders a fixed-size white page with margins and page number.\n// ============================================================\n\nimport React from 'react';\nimport type { PageDimensions, PageMargins } from '../core/types';\n\ninterface PageProps {\n /** Page dimensions in pixels */\n dimensions: PageDimensions;\n /** Page margins in pixels */\n margins: PageMargins;\n /** Page number (1-indexed) */\n pageNumber: number;\n /** Total number of pages */\n totalPages: number;\n /** The content blocks to render inside the page */\n children: React.ReactNode;\n}\n\nexport const Page: React.FC<PageProps> = ({\n dimensions,\n margins,\n pageNumber,\n totalPages,\n children,\n}) => {\n return (\n <div\n className=\"dopecanvas-page\"\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n backgroundColor: '#ffffff',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1)',\n position: 'relative',\n overflow: 'hidden',\n flexShrink: 0,\n }}\n >\n {/* Content area with margins */}\n <div\n className=\"dopecanvas-page-content\"\n style={{\n paddingTop: `${margins.top}px`,\n paddingRight: `${margins.right}px`,\n paddingBottom: `${margins.bottom}px`,\n paddingLeft: `${margins.left}px`,\n height: '100%',\n boxSizing: 'border-box',\n overflow: 'hidden',\n }}\n >\n {children}\n </div>\n\n {/* Page number footer */}\n <div\n className=\"dopecanvas-page-number\"\n style={{\n position: 'absolute',\n bottom: `${Math.max(margins.bottom / 3, 16)}px`,\n left: 0,\n right: 0,\n textAlign: 'center',\n fontSize: '11px',\n color: '#999',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n pointerEvents: 'none',\n userSelect: 'none',\n }}\n >\n {pageNumber} / {totalPages}\n </div>\n </div>\n );\n};\n","// ============================================================\n// DopeCanvas Core Types\n// ============================================================\n\n/** Named page size presets */\nexport type PageSizeName = 'letter' | 'a4' | 'legal';\n\n/** Custom page dimensions in pixels */\nexport interface PageDimensions {\n width: number;\n height: number;\n}\n\n/** Page margins in pixels */\nexport interface PageMargins {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** Full page configuration */\nexport interface PageConfig {\n size: PageSizeName | PageDimensions;\n margins: PageMargins;\n}\n\n/** A single page containing block element indices */\nexport interface PageContent {\n /** Indices into the flat block array */\n blockIndices: number[];\n}\n\n/** Result of the pagination algorithm */\nexport interface PaginationResult {\n pages: PageContent[];\n pageCount: number;\n}\n\n/** Block measurement info */\nexport interface BlockMeasurement {\n index: number;\n height: number;\n element: HTMLElement;\n breakBefore: boolean;\n breakAfter: boolean;\n}\n\n/** Toolbar context — what kind of element is selected */\nexport type ToolbarContext = 'text' | 'table' | 'image' | 'chart' | 'none';\n\n/** Undo/redo snapshot */\nexport interface UndoSnapshot {\n html: string;\n timestamp: number;\n}\n\n/** Unsubscribe function returned by event listeners */\nexport type Unsubscribe = () => void;\n\n// ============================================================\n// Page size presets (at 96 DPI)\n// ============================================================\n\n/** Page sizes in pixels at 96 DPI */\nexport const PAGE_SIZE_PRESETS: Record<PageSizeName, PageDimensions> = {\n letter: { width: 816, height: 1056 }, // 8.5 x 11 inches\n a4: { width: 794, height: 1123 }, // 210 x 297 mm\n legal: { width: 816, height: 1344 }, // 8.5 x 14 inches\n};\n\n/** Default margins: 1 inch (96px) on all sides */\nexport const DEFAULT_MARGINS: PageMargins = {\n top: 96,\n right: 96,\n bottom: 96,\n left: 96,\n};\n\n/** Default page config */\nexport const DEFAULT_PAGE_CONFIG: PageConfig = {\n size: 'letter',\n margins: { ...DEFAULT_MARGINS },\n};\n","// ============================================================\n// PagedView — Renders paginated blocks across page frames\n// ============================================================\n// Takes HTML content, paginates it into visual pages, and makes\n// each block editable via contentEditable.\n//\n// After initial pagination, the live DOM is the source of truth.\n// User edits trigger live re-pagination when the block distribution\n// across pages changes (e.g. content grows past a page boundary).\n// Cursor position is saved/restored across re-pagination re-renders.\n// ============================================================\n\nimport React, { useRef, useEffect, useCallback, useState } from 'react';\nimport { Page } from './Page';\nimport type { PageLayoutEngine } from '../core/PageLayoutEngine';\nimport type { EditableManager } from '../core/EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n PageSizeName,\n} from '../core/types';\nimport { PAGE_SIZE_PRESETS } from '../core/types';\n\ninterface PagedViewProps {\n /** The raw HTML content to render */\n html: string;\n /** Optional CSS to inject */\n css?: string;\n /** Page configuration */\n pageConfig: PageConfig;\n /** The layout engine instance */\n layoutEngine: PageLayoutEngine;\n /** The editable manager instance */\n editableManager: EditableManager;\n /** Callback when content changes (after user edit) — updates a ref, NOT state */\n onContentChange?: (html: string) => void;\n /** Callback when pagination changes */\n onPaginationChange?: (result: PaginationResult) => void;\n}\n\n/**\n * Represents a block of content assigned to a specific page.\n * We store the HTML string so React can render it once, then\n * the live DOM takes over for editing.\n */\ninterface PageData {\n blocks: string[]; // outerHTML of each block in this page\n}\n\n/**\n * Execute <script> tags found in a container.\n * Scripts set via innerHTML / dangerouslySetInnerHTML do NOT auto-execute.\n * We clone each one into a fresh <script> element so the browser runs it.\n */\nfunction activateScripts(container: HTMLElement): HTMLScriptElement[] {\n const activated: HTMLScriptElement[] = [];\n\n container.querySelectorAll('script').forEach((orig) => {\n const fresh = document.createElement('script');\n // Preserve attributes (type, src, data-*, etc.)\n Array.from(orig.attributes).forEach((attr) =>\n fresh.setAttribute(attr.name, attr.value)\n );\n fresh.textContent = orig.textContent || '';\n // Replacing the node triggers synchronous execution for inline scripts\n orig.parentNode?.replaceChild(fresh, orig);\n activated.push(fresh);\n });\n\n return activated;\n}\n\n// ----------------------------------------------------------\n// Cursor save / restore — used across re-pagination re-renders\n// ----------------------------------------------------------\n\ninterface CursorState {\n /** Global index of the block wrapper the cursor is inside */\n blockIndex: number;\n /** Character offset within that block's text content */\n textOffset: number;\n}\n\n/** Save the current cursor position relative to block wrappers */\nfunction saveCursorPosition(container: HTMLElement): CursorState | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n\n const range = sel.getRangeAt(0);\n const anchorNode = range.startContainer;\n\n const wrappers = Array.from(\n container.querySelectorAll('.dopecanvas-block-wrapper')\n );\n const blockIndex = wrappers.findIndex((w) => w.contains(anchorNode));\n if (blockIndex === -1) return null;\n\n // Compute character offset within the block\n try {\n const preRange = document.createRange();\n preRange.selectNodeContents(wrappers[blockIndex]);\n preRange.setEnd(range.startContainer, range.startOffset);\n const textOffset = preRange.toString().length;\n return { blockIndex, textOffset };\n } catch {\n return null;\n }\n}\n\n/** Restore cursor position after a re-pagination re-render */\nfunction restoreCursorPosition(\n container: HTMLElement,\n state: CursorState\n): void {\n const wrappers = container.querySelectorAll('.dopecanvas-block-wrapper');\n if (state.blockIndex >= wrappers.length) return;\n\n const wrapper = wrappers[state.blockIndex];\n const walker = document.createTreeWalker(wrapper, NodeFilter.SHOW_TEXT);\n let remaining = state.textOffset;\n\n while (walker.nextNode()) {\n const textNode = walker.currentNode as Text;\n if (remaining <= textNode.length) {\n try {\n const range = document.createRange();\n range.setStart(textNode, remaining);\n range.collapse(true);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n } catch {\n // Best effort — position may have shifted\n }\n return;\n }\n remaining -= textNode.length;\n }\n\n // Fallback: place cursor at the end of the block\n try {\n const range = document.createRange();\n range.selectNodeContents(wrapper);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n } catch {\n // Ignore\n }\n}\n\nexport const PagedView: React.FC<PagedViewProps> = ({\n html,\n css,\n pageConfig,\n layoutEngine,\n editableManager: _editableManager,\n onContentChange,\n onPaginationChange,\n}) => {\n const measureRef = useRef<HTMLDivElement>(null);\n const pagesContainerRef = useRef<HTMLDivElement>(null);\n const mutationObserverRef = useRef<MutationObserver | null>(null);\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n // Pages state — set during pagination AND live re-pagination\n const [pages, setPages] = useState<PageData[]>([]);\n\n // Refs for live re-pagination\n const pendingCursorRef = useRef<CursorState | null>(null);\n const isRePaginatingRef = useRef(false);\n const pagesRef = useRef<PageData[]>([]);\n\n // Resolve page dimensions\n const dimensions =\n typeof pageConfig.size === 'string'\n ? PAGE_SIZE_PRESETS[pageConfig.size as PageSizeName]\n : pageConfig.size;\n\n // ----------------------------------------------------------\n // Collect current HTML from the live DOM (no re-render)\n // ----------------------------------------------------------\n\n const collectHTMLFromDOM = useCallback(() => {\n if (!pagesContainerRef.current) return;\n\n const wrappers = pagesContainerRef.current.querySelectorAll(\n '.dopecanvas-block-wrapper'\n );\n\n const htmlParts: string[] = [];\n wrappers.forEach((wrapper) => {\n // The wrapper contains the actual content element\n const content = wrapper.firstElementChild as HTMLElement;\n if (content) {\n htmlParts.push(content.outerHTML);\n }\n });\n\n const updatedHTML = htmlParts.join('\\n');\n onContentChangeRef.current?.(updatedHTML);\n }, []);\n\n // ----------------------------------------------------------\n // Live re-pagination — runs after user edits change block sizes\n // ----------------------------------------------------------\n\n const rePaginateFromDOM = useCallback(() => {\n if (!pagesContainerRef.current || !measureRef.current) return;\n\n const container = pagesContainerRef.current;\n\n // Save cursor position before we potentially re-render\n const cursor = saveCursorPosition(container);\n\n // Collect block HTML from the live DOM\n const wrappers = container.querySelectorAll('.dopecanvas-block-wrapper');\n const blockHTMLs: string[] = [];\n wrappers.forEach((w) => {\n const content = w.firstElementChild as HTMLElement;\n if (content) blockHTMLs.push(content.outerHTML);\n });\n if (blockHTMLs.length === 0) return;\n\n // Set up hidden measure container\n const mc = measureRef.current;\n const contentWidth = layoutEngine.getContentAreaWidth();\n mc.style.width = `${contentWidth}px`;\n mc.style.position = 'absolute';\n mc.style.left = '-9999px';\n mc.style.top = '0';\n mc.style.visibility = 'hidden';\n mc.innerHTML = '';\n\n if (css) {\n const styleEl = document.createElement('style');\n styleEl.textContent = css;\n mc.appendChild(styleEl);\n }\n\n const measureWrapper = document.createElement('div');\n measureWrapper.innerHTML = blockHTMLs.join('\\n');\n mc.appendChild(measureWrapper);\n\n // Measure and paginate\n const measurements = layoutEngine.measureBlocks(measureWrapper);\n const measuredHTMLs = measurements.map(\n (m) => (m.element.cloneNode(true) as HTMLElement).outerHTML\n );\n const result = layoutEngine.paginate(measurements);\n mc.innerHTML = '';\n\n // Build new page data\n const newPages: PageData[] = result.pages.map((p) => ({\n blocks: p.blockIndices.map((idx) => measuredHTMLs[idx]),\n }));\n\n // Only re-render if the block distribution across pages actually changed\n const oldDist = pagesRef.current.map((p) => p.blocks.length);\n const newDist = newPages.map((p) => p.blocks.length);\n const changed =\n oldDist.length !== newDist.length ||\n oldDist.some((count, i) => count !== newDist[i]);\n\n if (changed) {\n isRePaginatingRef.current = true;\n pendingCursorRef.current = cursor;\n pagesRef.current = newPages;\n setPages(newPages);\n onPaginationChange?.(result);\n }\n }, [css, layoutEngine, onPaginationChange]);\n\n // Keep a stable ref so the MutationObserver closure always calls the latest version\n const rePaginateFromDOMRef = useRef(rePaginateFromDOM);\n rePaginateFromDOMRef.current = rePaginateFromDOM;\n\n // ----------------------------------------------------------\n // Pagination — runs on initial load and config changes\n // ----------------------------------------------------------\n\n const runPagination = useCallback(() => {\n if (!measureRef.current) return;\n\n const measureContainer = measureRef.current;\n const contentWidth = layoutEngine.getContentAreaWidth();\n\n // Set up hidden measurement container\n measureContainer.style.width = `${contentWidth}px`;\n measureContainer.style.position = 'absolute';\n measureContainer.style.left = '-9999px';\n measureContainer.style.top = '0';\n measureContainer.style.visibility = 'hidden';\n measureContainer.innerHTML = '';\n\n // Inject CSS if provided via prop\n if (css) {\n const styleEl = document.createElement('style');\n styleEl.textContent = css;\n measureContainer.appendChild(styleEl);\n }\n\n // Parse the LLM-authored HTML.\n // An LLM may produce a full document (<!DOCTYPE html><html><head>\n // <style>…</style></head><body>…</body></html>) or a plain fragment.\n // DOMParser handles both: for fragments it wraps in html/body;\n // for full documents it parses normally. We then move any <head>\n // styles/links into the body so they participate in pagination\n // as zero-height blocks and their styles apply to the content.\n const parsed = new DOMParser().parseFromString(html, 'text/html');\n\n // Rescue <style> and <link rel=\"stylesheet\"> from <head>\n parsed.head\n .querySelectorAll('style, link[rel=\"stylesheet\"]')\n .forEach((el) => {\n parsed.body.insertBefore(el, parsed.body.firstChild);\n });\n\n const contentHTML = parsed.body.innerHTML;\n\n // Inject content into a wrapper for measurement\n const wrapper = document.createElement('div');\n wrapper.innerHTML = contentHTML;\n measureContainer.appendChild(wrapper);\n\n // Measure all blocks\n const measurements = layoutEngine.measureBlocks(wrapper);\n\n // Clone block elements and get their HTML\n const blockHTMLs = measurements.map(\n (m) => (m.element.cloneNode(true) as HTMLElement).outerHTML\n );\n\n // Run pagination algorithm\n const result = layoutEngine.paginate(measurements);\n\n // Build page data (HTML strings, not live elements)\n const pageData: PageData[] = result.pages.map((page) => ({\n blocks: page.blockIndices.map((idx) => blockHTMLs[idx]),\n }));\n\n // Clean up\n measureContainer.innerHTML = '';\n\n // Update state — this triggers a React render\n pagesRef.current = pageData;\n setPages(pageData);\n onPaginationChange?.(result);\n }, [html, css, layoutEngine, onPaginationChange]);\n\n // Run pagination when html or pageConfig changes\n useEffect(() => {\n runPagination();\n }, [runPagination]);\n\n // ----------------------------------------------------------\n // After pages render: make editable + observe changes\n // ----------------------------------------------------------\n\n useEffect(() => {\n const container = pagesContainerRef.current;\n if (!container) return;\n\n // Disconnect previous observer\n if (mutationObserverRef.current) {\n mutationObserverRef.current.disconnect();\n }\n\n // Make all blocks contentEditable\n const blockWrappers = container.querySelectorAll('.dopecanvas-block-wrapper');\n blockWrappers.forEach((wrapper) => {\n const child = wrapper.firstElementChild as HTMLElement;\n if (!child) return;\n\n if (child.tagName === 'TABLE') {\n // For tables, make individual cells editable\n const cells = child.querySelectorAll('td, th');\n cells.forEach((cell) => {\n (cell as HTMLElement).contentEditable = 'true';\n });\n } else if (child.tagName === 'SCRIPT' || child.tagName === 'STYLE') {\n // Don't make script/style blocks editable\n } else {\n child.contentEditable = 'true';\n }\n });\n\n // Execute <script> tags embedded in the LLM-authored HTML.\n // This must run AFTER contentEditable setup so scripts can override\n // editability on specific cells (e.g. formula cells).\n const activatedScripts = activateScripts(container);\n\n // Set up MutationObserver — collects HTML and re-paginates when needed\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new MutationObserver(() => {\n if (isRePaginatingRef.current) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n collectHTMLFromDOM();\n rePaginateFromDOMRef.current();\n }, 300);\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: false,\n });\n\n mutationObserverRef.current = observer;\n\n // Restore cursor after a re-pagination re-render\n if (pendingCursorRef.current) {\n requestAnimationFrame(() => {\n if (pendingCursorRef.current && pagesContainerRef.current) {\n restoreCursorPosition(\n pagesContainerRef.current,\n pendingCursorRef.current\n );\n pendingCursorRef.current = null;\n }\n isRePaginatingRef.current = false;\n });\n } else {\n isRePaginatingRef.current = false;\n }\n\n return () => {\n observer.disconnect();\n activatedScripts.forEach((s) => s.remove());\n if (debounceTimer) clearTimeout(debounceTimer);\n };\n }, [pages, collectHTMLFromDOM]);\n\n // ----------------------------------------------------------\n // Render\n // ----------------------------------------------------------\n\n return (\n <div className=\"dopecanvas-paged-view\" style={scrollContainerStyle}>\n {/* Hidden measurement container */}\n <div ref={measureRef} aria-hidden=\"true\" />\n\n {/* Visible pages */}\n <div ref={pagesContainerRef} style={pagesWrapperStyle}>\n {css && <style dangerouslySetInnerHTML={{ __html: css }} />}\n {pages.map((pageData, pageIndex) => (\n <Page\n key={pageIndex}\n dimensions={dimensions}\n margins={pageConfig.margins}\n pageNumber={pageIndex + 1}\n totalPages={pages.length}\n >\n {pageData.blocks.map((blockHTML, blockIndex) => (\n <div\n key={`${pageIndex}-${blockIndex}`}\n className=\"dopecanvas-block-wrapper\"\n dangerouslySetInnerHTML={{ __html: blockHTML }}\n />\n ))}\n </Page>\n ))}\n\n {/* Show at least one empty page if no content */}\n {pages.length === 0 && (\n <Page\n dimensions={dimensions}\n margins={pageConfig.margins}\n pageNumber={1}\n totalPages={1}\n >\n <div\n contentEditable=\"true\"\n style={{ minHeight: '1em', outline: 'none' }}\n data-placeholder=\"Start typing...\"\n />\n </Page>\n )}\n </div>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst scrollContainerStyle: React.CSSProperties = {\n flex: 1,\n overflow: 'auto',\n backgroundColor: '#e8e8e8',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n};\n\nconst pagesWrapperStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: '24px',\n padding: '24px 0',\n};\n","// ============================================================\n// PageLayoutEngine — Pagination algorithm\n// ============================================================\n// Pure TypeScript class with no React dependency.\n// Takes a container of block elements, measures them, and\n// distributes them across fixed-size pages.\n// ============================================================\n\nimport type {\n PageConfig,\n PageDimensions,\n PageSizeName,\n PaginationResult,\n BlockMeasurement,\n} from './types';\nimport {\n PAGE_SIZE_PRESETS,\n DEFAULT_PAGE_CONFIG,\n} from './types';\n\nexport class PageLayoutEngine {\n private config: PageConfig;\n\n constructor(config: PageConfig = DEFAULT_PAGE_CONFIG) {\n this.config = { ...config };\n }\n\n // ----------------------------------------------------------\n // Config accessors\n // ----------------------------------------------------------\n\n getConfig(): PageConfig {\n return { ...this.config };\n }\n\n setConfig(config: Partial<PageConfig>): void {\n if (config.size !== undefined) {\n this.config.size = config.size;\n }\n if (config.margins !== undefined) {\n this.config.margins = { ...config.margins };\n }\n }\n\n /** Resolve page size name to pixel dimensions */\n getPageDimensions(): PageDimensions {\n if (typeof this.config.size === 'string') {\n return PAGE_SIZE_PRESETS[this.config.size as PageSizeName];\n }\n return this.config.size;\n }\n\n /** Usable content area height (page height minus top+bottom margins) */\n getContentAreaHeight(): number {\n const dims = this.getPageDimensions();\n return dims.height - this.config.margins.top - this.config.margins.bottom;\n }\n\n /** Usable content area width (page width minus left+right margins) */\n getContentAreaWidth(): number {\n const dims = this.getPageDimensions();\n return dims.width - this.config.margins.left - this.config.margins.right;\n }\n\n // ----------------------------------------------------------\n // Measurement\n // ----------------------------------------------------------\n\n /**\n * Measure all direct child block elements of the container.\n * The container should be styled to match the content area width\n * so that measurements reflect actual rendered heights.\n */\n measureBlocks(container: HTMLElement): BlockMeasurement[] {\n const children = Array.from(container.children) as HTMLElement[];\n const measurements: BlockMeasurement[] = [];\n\n for (let i = 0; i < children.length; i++) {\n const el = children[i];\n const style = window.getComputedStyle(el);\n\n // Check for CSS break-before / break-after\n const breakBefore =\n style.getPropertyValue('break-before') === 'page' ||\n style.getPropertyValue('page-break-before') === 'always';\n const breakAfter =\n style.getPropertyValue('break-after') === 'page' ||\n style.getPropertyValue('page-break-after') === 'always';\n\n // Use getBoundingClientRect for precise measurement including margins\n const rect = el.getBoundingClientRect();\n const marginTop = parseFloat(style.marginTop) || 0;\n const marginBottom = parseFloat(style.marginBottom) || 0;\n const totalHeight = rect.height + marginTop + marginBottom;\n\n measurements.push({\n index: i,\n height: totalHeight,\n element: el,\n breakBefore,\n breakAfter,\n });\n }\n\n return measurements;\n }\n\n // ----------------------------------------------------------\n // Pagination\n // ----------------------------------------------------------\n\n /**\n * Paginate: distribute measured blocks across pages.\n * \n * Algorithm:\n * 1. Walk blocks sequentially\n * 2. Accumulate height on current page\n * 3. When a block would overflow, start a new page\n * 4. Respect break-before / break-after CSS\n * 5. If a single block is taller than a page, give it its own page\n */\n paginate(measurements: BlockMeasurement[]): PaginationResult {\n if (measurements.length === 0) {\n return { pages: [{ blockIndices: [] }], pageCount: 1 };\n }\n\n const contentHeight = this.getContentAreaHeight();\n const pages: { blockIndices: number[] }[] = [];\n let currentPage: number[] = [];\n let currentHeight = 0;\n\n for (let i = 0; i < measurements.length; i++) {\n const block = measurements[i];\n\n // Force a new page if break-before is set (and current page has content)\n if (block.breakBefore && currentPage.length > 0) {\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n\n // Check if adding this block would overflow the current page\n if (currentHeight + block.height > contentHeight && currentPage.length > 0) {\n // Current page is full — start a new one\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n\n // Add block to current page\n currentPage.push(block.index);\n currentHeight += block.height;\n\n // Force a new page after this block if break-after is set\n if (block.breakAfter) {\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n }\n\n // Push the last page if it has content\n if (currentPage.length > 0) {\n pages.push({ blockIndices: currentPage });\n }\n\n // Ensure at least one page\n if (pages.length === 0) {\n pages.push({ blockIndices: [] });\n }\n\n return {\n pages,\n pageCount: pages.length,\n };\n }\n}\n","// ============================================================\n// EditableManager — contentEditable integration\n// ============================================================\n// Manages making block elements editable, tracking changes\n// via MutationObserver, and providing undo/redo.\n// ============================================================\n\nimport type { UndoSnapshot, Unsubscribe, ToolbarContext } from './types';\n\nexport type ChangeCallback = () => void;\nexport type ContextChangeCallback = (context: ToolbarContext) => void;\n\nexport class EditableManager {\n private observer: MutationObserver | null = null;\n private changeCallbacks: Set<ChangeCallback> = new Set();\n private contextCallbacks: Set<ContextChangeCallback> = new Set();\n private undoStack: UndoSnapshot[] = [];\n private redoStack: UndoSnapshot[] = [];\n private container: HTMLElement | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private selectionHandler: (() => void) | null = null;\n private currentContext: ToolbarContext = 'none';\n\n private static readonly MAX_UNDO_STACK = 100;\n private static readonly DEBOUNCE_MS = 150;\n\n // ----------------------------------------------------------\n // Lifecycle\n // ----------------------------------------------------------\n\n /**\n * Attach to a container element. Sets up contentEditable on\n * all direct child blocks and starts observing changes.\n */\n attach(container: HTMLElement): void {\n this.detach(); // Clean up any previous attachment\n this.container = container;\n\n // Make all direct children editable\n this.makeChildrenEditable(container);\n\n // Take initial snapshot for undo\n this.pushUndoSnapshot();\n\n // Start observing mutations\n this.observer = new MutationObserver(this.handleMutations);\n this.observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: ['style', 'class'],\n });\n\n // Listen for selection changes to detect context\n this.selectionHandler = this.handleSelectionChange.bind(this);\n document.addEventListener('selectionchange', this.selectionHandler);\n }\n\n /**\n * Detach from the container. Stop observing and clean up.\n */\n detach(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n if (this.selectionHandler) {\n document.removeEventListener('selectionchange', this.selectionHandler);\n this.selectionHandler = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.container = null;\n }\n\n // ----------------------------------------------------------\n // Make elements editable\n // ----------------------------------------------------------\n\n /**\n * Set contentEditable on all direct children of the container.\n * Table cells get individual editability; other blocks are editable as wholes.\n */\n makeChildrenEditable(container: HTMLElement): void {\n const children = Array.from(container.children) as HTMLElement[];\n for (const child of children) {\n if (child.tagName === 'TABLE') {\n // For tables, make individual cells editable (not the table itself)\n this.makeTableCellsEditable(child);\n } else {\n child.contentEditable = 'true';\n }\n }\n }\n\n private makeTableCellsEditable(table: HTMLElement): void {\n const cells = table.querySelectorAll('td, th');\n cells.forEach((cell) => {\n (cell as HTMLElement).contentEditable = 'true';\n });\n }\n\n // ----------------------------------------------------------\n // Mutation handling\n // ----------------------------------------------------------\n\n private handleMutations = (_mutations: MutationRecord[]): void => {\n // Debounce to avoid excessive re-pagination\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n this.pushUndoSnapshot();\n this.notifyChange();\n }, EditableManager.DEBOUNCE_MS);\n };\n\n // ----------------------------------------------------------\n // Selection / context detection\n // ----------------------------------------------------------\n\n private handleSelectionChange(): void {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0 || !this.container) {\n this.setContext('none');\n return;\n }\n\n const range = selection.getRangeAt(0);\n const node = range.startContainer;\n\n // Walk up from the selection to find context\n let current: Node | null = node;\n while (current && current !== this.container) {\n if (current instanceof HTMLElement) {\n const tag = current.tagName;\n if (tag === 'TD' || tag === 'TH' || tag === 'TABLE') {\n this.setContext('table');\n return;\n }\n if (tag === 'IMG') {\n this.setContext('image');\n return;\n }\n if (current.dataset?.dopecanvasChart) {\n this.setContext('chart');\n return;\n }\n }\n current = current.parentNode;\n }\n\n // Default to text context if inside our container\n if (this.container.contains(node)) {\n this.setContext('text');\n } else {\n this.setContext('none');\n }\n }\n\n private setContext(ctx: ToolbarContext): void {\n if (ctx !== this.currentContext) {\n this.currentContext = ctx;\n this.contextCallbacks.forEach((cb) => cb(ctx));\n }\n }\n\n getContext(): ToolbarContext {\n return this.currentContext;\n }\n\n // ----------------------------------------------------------\n // Undo / Redo\n // ----------------------------------------------------------\n\n private pushUndoSnapshot(): void {\n if (!this.container) return;\n const html = this.container.innerHTML;\n const last = this.undoStack[this.undoStack.length - 1];\n // Don't push duplicates\n if (last && last.html === html) return;\n\n this.undoStack.push({ html, timestamp: Date.now() });\n // Clear redo stack on new change\n this.redoStack = [];\n // Limit stack size\n if (this.undoStack.length > EditableManager.MAX_UNDO_STACK) {\n this.undoStack.shift();\n }\n }\n\n undo(): boolean {\n if (!this.container || this.undoStack.length <= 1) return false;\n\n // Pop current state to redo stack\n const current = this.undoStack.pop()!;\n this.redoStack.push(current);\n\n // Restore previous state\n const previous = this.undoStack[this.undoStack.length - 1];\n this.pauseObserver(() => {\n this.container!.innerHTML = previous.html;\n this.makeChildrenEditable(this.container!);\n });\n this.notifyChange();\n return true;\n }\n\n redo(): boolean {\n if (!this.container || this.redoStack.length === 0) return false;\n\n const next = this.redoStack.pop()!;\n this.undoStack.push(next);\n\n this.pauseObserver(() => {\n this.container!.innerHTML = next.html;\n this.makeChildrenEditable(this.container!);\n });\n this.notifyChange();\n return true;\n }\n\n /** Temporarily disconnect observer to avoid feedback loops */\n private pauseObserver(fn: () => void): void {\n if (this.observer) {\n this.observer.disconnect();\n }\n fn();\n if (this.observer && this.container) {\n this.observer.observe(this.container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: ['style', 'class'],\n });\n }\n }\n\n // ----------------------------------------------------------\n // Event callbacks\n // ----------------------------------------------------------\n\n onChange(callback: ChangeCallback): Unsubscribe {\n this.changeCallbacks.add(callback);\n return () => {\n this.changeCallbacks.delete(callback);\n };\n }\n\n onContextChange(callback: ContextChangeCallback): Unsubscribe {\n this.contextCallbacks.add(callback);\n return () => {\n this.contextCallbacks.delete(callback);\n };\n }\n\n private notifyChange(): void {\n this.changeCallbacks.forEach((cb) => cb());\n }\n\n // ----------------------------------------------------------\n // Formatting commands\n // ----------------------------------------------------------\n\n /**\n * Execute a formatting command on the current selection.\n * Uses document.execCommand for broad browser support.\n */\n execCommand(command: string, value?: string): boolean {\n return document.execCommand(command, false, value);\n }\n\n /** Check if a command is active for the current selection */\n queryCommandState(command: string): boolean {\n return document.queryCommandState(command);\n }\n\n /** Get the value of a command for the current selection */\n queryCommandValue(command: string): string {\n return document.queryCommandValue(command);\n }\n\n // ----------------------------------------------------------\n // Content access\n // ----------------------------------------------------------\n\n getHTML(): string {\n if (!this.container) return '';\n return this.container.innerHTML;\n }\n\n getPlainText(): string {\n if (!this.container) return '';\n return this.container.innerText || this.container.textContent || '';\n }\n}\n","// ============================================================\n// DopeCanvas — Main canvas component\n// ============================================================\n// The top-level React component that composes the paged view\n// and document engine together.\n//\n// The toolbar is NOT rendered by default. Instead, all toolbar\n// actions are exposed via a ref-based API (DopeCanvasHandle).\n// Consumers can build their own toolbar UI or use the provided\n// Toolbar components separately.\n//\n// CRITICAL DESIGN: Content edits from the user update a ref,\n// NOT state. This prevents React from re-rendering (and thus\n// destroying) the live editable DOM. Only page config changes\n// or external HTML loads trigger re-pagination.\n// ============================================================\n\nimport React, { useMemo, useCallback, useState, useRef, useEffect, useImperativeHandle, forwardRef } from 'react';\nimport { PagedView } from './PagedView';\nimport { PageLayoutEngine } from '../core/PageLayoutEngine';\nimport { EditableManager } from '../core/EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n} from '../core/types';\nimport { DEFAULT_PAGE_CONFIG } from '../core/types';\n\n// ----------------------------------------------------------\n// Public API handle exposed via ref\n// ----------------------------------------------------------\n\nexport interface DopeCanvasHandle {\n // Text formatting\n /** Execute a formatting command (e.g. 'bold', 'italic', 'fontSize') */\n execCommand: (command: string, value?: string) => boolean;\n /** Check if a command is active for the current selection */\n queryCommandState: (command: string) => boolean;\n /** Get the value of a command for the current selection */\n queryCommandValue: (command: string) => string;\n\n // Page configuration\n /** Get the current page configuration */\n getPageConfig: () => PageConfig;\n /** Update page configuration (size, margins). Triggers re-pagination. */\n setPageConfig: (config: Partial<PageConfig>) => void;\n /** Get the current number of pages */\n getPageCount: () => number;\n\n // Content access\n /** Get the current document HTML (reflects user edits) */\n getHTML: () => string;\n /** Get the document content as plain text */\n getPlainText: () => string;\n\n // Undo / Redo\n /** Undo the last edit. Returns false if nothing to undo. */\n undo: () => boolean;\n /** Redo the last undone edit. Returns false if nothing to redo. */\n redo: () => boolean;\n}\n\n// ----------------------------------------------------------\n// Component props\n// ----------------------------------------------------------\n\nexport interface DopeCanvasProps {\n /** Initial HTML content to load */\n html?: string;\n /** Optional CSS to inject alongside the HTML */\n css?: string;\n /** Page configuration */\n pageConfig?: PageConfig;\n /** Callback when content changes */\n onContentChange?: (html: string) => void;\n /** Callback when page config changes */\n onPageConfigChange?: (config: PageConfig) => void;\n /** Style overrides for the root container */\n style?: React.CSSProperties;\n}\n\nexport const DopeCanvas = forwardRef<DopeCanvasHandle, DopeCanvasProps>(({\n html = '',\n css,\n pageConfig: externalPageConfig,\n onContentChange,\n onPageConfigChange,\n style,\n}, ref) => {\n const [internalPageConfig, setInternalPageConfig] = useState<PageConfig>(\n externalPageConfig || DEFAULT_PAGE_CONFIG\n );\n const [paginationResult, setPaginationResult] = useState<PaginationResult>({\n pages: [],\n pageCount: 0,\n });\n // Store the latest HTML in a ref — edits update this without triggering re-render\n const currentHTMLRef = useRef(html);\n\n // Stable callback ref for onContentChange so PagedView doesn't re-render\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n // Use external config if provided, otherwise internal\n const pageConfig = externalPageConfig || internalPageConfig;\n\n // Create engine instances (stable across renders)\n const layoutEngine = useMemo(() => new PageLayoutEngine(pageConfig), []);\n const editableManager = useMemo(() => new EditableManager(), []);\n\n // Update layout engine when config changes\n useEffect(() => {\n layoutEngine.setConfig(pageConfig);\n }, [pageConfig, layoutEngine]);\n\n // Handle content changes from editing — update ref only, no state\n const handleContentChange = useCallback(\n (newHTML: string) => {\n currentHTMLRef.current = newHTML;\n onContentChangeRef.current?.(newHTML);\n },\n []\n );\n\n // Handle page config changes\n const handlePageConfigChange = useCallback(\n (newConfig: Partial<PageConfig>) => {\n const updated = {\n ...pageConfig,\n ...newConfig,\n margins: {\n ...pageConfig.margins,\n ...(newConfig.margins || {}),\n },\n };\n setInternalPageConfig(updated);\n layoutEngine.setConfig(updated);\n onPageConfigChange?.(updated);\n },\n [pageConfig, layoutEngine, onPageConfigChange]\n );\n\n // Handle pagination updates\n const handlePaginationChange = useCallback((result: PaginationResult) => {\n setPaginationResult(result);\n }, []);\n\n // ----------------------------------------------------------\n // Expose API via ref\n // ----------------------------------------------------------\n\n useImperativeHandle(ref, () => ({\n execCommand: (command: string, value?: string) => {\n return editableManager.execCommand(command, value);\n },\n queryCommandState: (command: string) => {\n return editableManager.queryCommandState(command);\n },\n queryCommandValue: (command: string) => {\n return editableManager.queryCommandValue(command);\n },\n getPageConfig: () => ({ ...pageConfig }),\n setPageConfig: (config: Partial<PageConfig>) => {\n handlePageConfigChange(config);\n },\n getPageCount: () => paginationResult.pageCount,\n getHTML: () => currentHTMLRef.current,\n getPlainText: () => {\n const tmp = document.createElement('div');\n tmp.innerHTML = currentHTMLRef.current;\n return tmp.innerText || tmp.textContent || '';\n },\n undo: () => editableManager.undo(),\n redo: () => editableManager.redo(),\n }), [editableManager, pageConfig, paginationResult.pageCount, handlePageConfigChange]);\n\n return (\n <div\n className=\"dopecanvas-root\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n width: '100%',\n fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',\n ...style,\n }}\n >\n {/* Paged document view */}\n <PagedView\n html={html}\n css={css}\n pageConfig={pageConfig}\n layoutEngine={layoutEngine}\n editableManager={editableManager}\n onContentChange={handleContentChange}\n onPaginationChange={handlePaginationChange}\n />\n </div>\n );\n});\n","// ============================================================\n// DocumentEngine — Core orchestrator\n// ============================================================\n// Ties PageLayoutEngine and EditableManager together.\n// Manages the lifecycle: load → parse → paginate → edit → re-paginate\n// ============================================================\n\nimport { PageLayoutEngine } from './PageLayoutEngine';\nimport { EditableManager } from './EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n BlockMeasurement,\n Unsubscribe,\n} from './types';\nimport { DEFAULT_PAGE_CONFIG } from './types';\n\nexport class DocumentEngine {\n private layoutEngine: PageLayoutEngine;\n private editableManager: EditableManager;\n private sourceHTML: string = '';\n private sourceCSS: string = '';\n private measureContainer: HTMLElement | null = null;\n private contentContainer: HTMLElement | null = null;\n private paginationResult: PaginationResult = { pages: [], pageCount: 0 };\n private paginationCallbacks: Set<(result: PaginationResult) => void> = new Set();\n private changeCallbacks: Set<(html: string) => void> = new Set();\n\n constructor(config: PageConfig = DEFAULT_PAGE_CONFIG) {\n this.layoutEngine = new PageLayoutEngine(config);\n this.editableManager = new EditableManager();\n }\n\n // ----------------------------------------------------------\n // Accessors\n // ----------------------------------------------------------\n\n getLayoutEngine(): PageLayoutEngine {\n return this.layoutEngine;\n }\n\n getEditableManager(): EditableManager {\n return this.editableManager;\n }\n\n getPaginationResult(): PaginationResult {\n return this.paginationResult;\n }\n\n getSourceHTML(): string {\n return this.sourceHTML;\n }\n\n getPageConfig(): PageConfig {\n return this.layoutEngine.getConfig();\n }\n\n // ----------------------------------------------------------\n // Load HTML content\n // ----------------------------------------------------------\n\n /**\n * Load LLM-generated HTML (and optional CSS) into the engine.\n * This parses the HTML and prepares it for pagination.\n */\n loadHTML(html: string, css?: string): void {\n this.sourceHTML = html;\n this.sourceCSS = css || '';\n }\n\n // ----------------------------------------------------------\n // Attach to DOM containers\n // ----------------------------------------------------------\n\n /**\n * Set the measurement container — a hidden container styled\n * to match the content area width. Used for measuring block heights.\n */\n setMeasureContainer(el: HTMLElement): void {\n this.measureContainer = el;\n }\n\n /**\n * Set the content container — the visible container where\n * editable blocks live. The EditableManager attaches here.\n */\n setContentContainer(el: HTMLElement): void {\n if (this.contentContainer) {\n this.editableManager.detach();\n }\n this.contentContainer = el;\n }\n\n // ----------------------------------------------------------\n // Pagination cycle\n // ----------------------------------------------------------\n\n /**\n * Run the full pagination cycle:\n * 1. Inject HTML into measure container\n * 2. Inject CSS (via <style> tag)\n * 3. Measure all blocks\n * 4. Run pagination algorithm\n * 5. Return result for rendering\n */\n runPagination(): { result: PaginationResult; measurements: BlockMeasurement[] } {\n if (!this.measureContainer) {\n return {\n result: { pages: [{ blockIndices: [] }], pageCount: 1 },\n measurements: [],\n };\n }\n\n // Set up the measure container width to match content area\n const contentWidth = this.layoutEngine.getContentAreaWidth();\n this.measureContainer.style.width = `${contentWidth}px`;\n this.measureContainer.style.position = 'absolute';\n this.measureContainer.style.left = '-9999px';\n this.measureContainer.style.top = '0';\n this.measureContainer.style.visibility = 'hidden';\n\n // Inject CSS if provided\n let styleEl: HTMLStyleElement | null = null;\n if (this.sourceCSS) {\n styleEl = document.createElement('style');\n styleEl.textContent = this.sourceCSS;\n this.measureContainer.appendChild(styleEl);\n }\n\n // Inject HTML content\n const contentWrapper = document.createElement('div');\n contentWrapper.innerHTML = this.sourceHTML;\n this.measureContainer.appendChild(contentWrapper);\n\n // Measure blocks\n const measurements = this.layoutEngine.measureBlocks(contentWrapper);\n\n // Paginate\n this.paginationResult = this.layoutEngine.paginate(measurements);\n\n // Clean up measure container\n this.measureContainer.innerHTML = '';\n\n // Notify listeners\n this.paginationCallbacks.forEach((cb) => cb(this.paginationResult));\n\n return { result: this.paginationResult, measurements };\n }\n\n /**\n * Re-paginate using the current content container's live DOM.\n * Called after user edits.\n */\n rePaginate(): PaginationResult {\n if (!this.contentContainer) {\n return this.paginationResult;\n }\n\n // Measure from the live content\n const measurements = this.layoutEngine.measureBlocks(this.contentContainer);\n this.paginationResult = this.layoutEngine.paginate(measurements);\n\n // Notify\n this.paginationCallbacks.forEach((cb) => cb(this.paginationResult));\n return this.paginationResult;\n }\n\n // ----------------------------------------------------------\n // Attach editing\n // ----------------------------------------------------------\n\n /**\n * Attach the editable manager to the content container\n * and set up change listener for re-pagination.\n */\n attachEditing(container: HTMLElement): void {\n this.contentContainer = container;\n this.editableManager.attach(container);\n\n // Re-paginate on content change\n this.editableManager.onChange(() => {\n // Update source HTML from live DOM\n this.sourceHTML = container.innerHTML;\n this.changeCallbacks.forEach((cb) => cb(this.sourceHTML));\n });\n }\n\n // ----------------------------------------------------------\n // Page config\n // ----------------------------------------------------------\n\n setPageConfig(config: Partial<PageConfig>): void {\n this.layoutEngine.setConfig(config);\n }\n\n // ----------------------------------------------------------\n // Event listeners\n // ----------------------------------------------------------\n\n onPagination(callback: (result: PaginationResult) => void): Unsubscribe {\n this.paginationCallbacks.add(callback);\n return () => {\n this.paginationCallbacks.delete(callback);\n };\n }\n\n onChange(callback: (html: string) => void): Unsubscribe {\n this.changeCallbacks.add(callback);\n return () => {\n this.changeCallbacks.delete(callback);\n };\n }\n\n // ----------------------------------------------------------\n // Content access\n // ----------------------------------------------------------\n\n getHTML(): string {\n if (this.contentContainer) {\n return this.contentContainer.innerHTML;\n }\n return this.sourceHTML;\n }\n\n getPlainText(): string {\n if (this.contentContainer) {\n return this.contentContainer.innerText || this.contentContainer.textContent || '';\n }\n return '';\n }\n\n // ----------------------------------------------------------\n // Cleanup\n // ----------------------------------------------------------\n\n destroy(): void {\n this.editableManager.detach();\n this.paginationCallbacks.clear();\n this.changeCallbacks.clear();\n this.measureContainer = null;\n this.contentContainer = null;\n }\n}\n","// ============================================================\n// DocumentAPI — External programmatic interface\n// ============================================================\n// This class provides a clean API for external systems\n// (LLMs, database sync, parent applications) to interact\n// with a DopeCanvas document.\n// ============================================================\n\nimport type { PageConfig, PaginationResult, Unsubscribe } from '../core/types';\n\n/**\n * DocumentAPI provides a programmatic interface to the DopeCanvas\n * document. It wraps the internal state management and exposes\n * methods for loading, reading, and modifying document content.\n *\n * Usage:\n * const api = new DocumentAPI();\n * // Connect to a DopeCanvas instance (done internally by DopeCanvas component)\n * api.loadHTML('<h1>Hello</h1><p>World</p>');\n * api.onChange((html) => console.log('Content changed:', html));\n */\nexport class DocumentAPI {\n private _html: string = '';\n private _css: string = '';\n private _pageConfig: PageConfig | null = null;\n private _paginationResult: PaginationResult = { pages: [], pageCount: 0 };\n\n // Callbacks\n private _changeCallbacks: Set<(html: string) => void> = new Set();\n private _loadCallbacks: Set<(html: string, css?: string) => void> = new Set();\n private _pageConfigCallbacks: Set<(config: PageConfig) => void> = new Set();\n\n // Connector functions set by DopeCanvas component\n private _getHTMLFn: (() => string) | null = null;\n private _getPlainTextFn: (() => string) | null = null;\n\n // ----------------------------------------------------------\n // Content loading\n // ----------------------------------------------------------\n\n /**\n * Load HTML content into the canvas.\n * Optionally provide CSS to inject alongside.\n */\n loadHTML(html: string, css?: string): void {\n this._html = html;\n this._css = css || '';\n this._loadCallbacks.forEach((cb) => cb(html, css));\n }\n\n /**\n * Get the current document HTML content.\n * Reflects any user edits.\n */\n getHTML(): string {\n if (this._getHTMLFn) {\n return this._getHTMLFn();\n }\n return this._html;\n }\n\n /**\n * Get the document content as plain text.\n */\n getPlainText(): string {\n if (this._getPlainTextFn) {\n return this._getPlainTextFn();\n }\n // Fallback: strip HTML tags\n const tmp = document.createElement('div');\n tmp.innerHTML = this._html;\n return tmp.innerText || tmp.textContent || '';\n }\n\n // ----------------------------------------------------------\n // Event listeners\n // ----------------------------------------------------------\n\n /**\n * Listen for content changes (triggered by user edits).\n * Returns an unsubscribe function.\n */\n onChange(callback: (html: string) => void): Unsubscribe {\n this._changeCallbacks.add(callback);\n return () => {\n this._changeCallbacks.delete(callback);\n };\n }\n\n /**\n * Listen for load events (when loadHTML is called).\n */\n onLoad(callback: (html: string, css?: string) => void): Unsubscribe {\n this._loadCallbacks.add(callback);\n return () => {\n this._loadCallbacks.delete(callback);\n };\n }\n\n /**\n * Listen for page config changes.\n */\n onPageConfigChange(callback: (config: PageConfig) => void): Unsubscribe {\n this._pageConfigCallbacks.add(callback);\n return () => {\n this._pageConfigCallbacks.delete(callback);\n };\n }\n\n // ----------------------------------------------------------\n // Page operations\n // ----------------------------------------------------------\n\n /**\n * Get the current number of pages.\n */\n getPageCount(): number {\n return this._paginationResult.pageCount;\n }\n\n /**\n * Get the current page configuration.\n */\n getPageConfig(): PageConfig | null {\n return this._pageConfig;\n }\n\n /**\n * Set page configuration (size, margins).\n * Triggers re-pagination.\n */\n setPageConfig(config: Partial<PageConfig>): void {\n if (this._pageConfig) {\n this._pageConfig = {\n ...this._pageConfig,\n ...config,\n margins: {\n ...this._pageConfig.margins,\n ...(config.margins || {}),\n },\n };\n this._pageConfigCallbacks.forEach((cb) => cb(this._pageConfig!));\n }\n }\n\n // ----------------------------------------------------------\n // Element access (for future DB sync)\n // ----------------------------------------------------------\n\n /**\n * Query elements within the document by CSS selector.\n * Useful for targeting specific sections for database sync.\n */\n querySelectorAll(selector: string): Element[] {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n return Array.from(tmp.querySelectorAll(selector));\n }\n\n /**\n * Get the innerHTML of a specific element by its ID.\n */\n getElementContent(id: string): string | null {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n const el = tmp.querySelector(`#${id}`);\n return el ? el.innerHTML : null;\n }\n\n /**\n * Set the innerHTML of a specific element by its ID.\n * Re-loads the full document with the modification.\n */\n setElementContent(id: string, html: string): void {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n const el = tmp.querySelector(`#${id}`);\n if (el) {\n el.innerHTML = html;\n this.loadHTML(tmp.innerHTML, this._css);\n }\n }\n\n // ----------------------------------------------------------\n // Internal connectors (used by DopeCanvas component)\n // ----------------------------------------------------------\n\n /** @internal Called by DopeCanvas to wire up live content access */\n _connectGetHTML(fn: () => string): void {\n this._getHTMLFn = fn;\n }\n\n /** @internal Called by DopeCanvas to wire up plain text access */\n _connectGetPlainText(fn: () => string): void {\n this._getPlainTextFn = fn;\n }\n\n /** @internal Called by DopeCanvas when content changes */\n _notifyChange(html: string): void {\n this._html = html;\n this._changeCallbacks.forEach((cb) => cb(html));\n }\n\n /** @internal Called by DopeCanvas when pagination runs */\n _updatePagination(result: PaginationResult): void {\n this._paginationResult = result;\n }\n\n /** @internal Called by DopeCanvas when page config changes */\n _updatePageConfig(config: PageConfig): void {\n this._pageConfig = config;\n }\n}\n","// ============================================================\n// useSelectionContext — Track what element type is selected\n// ============================================================\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { ToolbarContext } from '../core/types';\nimport type { EditableManager } from '../core/EditableManager';\n\nexport function useSelectionContext(\n editableManager: EditableManager | null\n): ToolbarContext {\n const [context, setContext] = useState<ToolbarContext>('none');\n\n useEffect(() => {\n if (!editableManager) return;\n\n const unsub = editableManager.onContextChange((ctx) => {\n setContext(ctx);\n });\n\n return unsub;\n }, [editableManager]);\n\n return context;\n}\n\n/**\n * Hook to query the current formatting state (bold, italic, etc.)\n * from the browser's selection.\n */\nexport function useFormattingState() {\n const [state, setState] = useState({\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n justifyLeft: false,\n justifyCenter: false,\n justifyRight: false,\n justifyFull: false,\n });\n\n const updateState = useCallback(() => {\n setState({\n bold: document.queryCommandState('bold'),\n italic: document.queryCommandState('italic'),\n underline: document.queryCommandState('underline'),\n strikethrough: document.queryCommandState('strikethrough'),\n justifyLeft: document.queryCommandState('justifyLeft'),\n justifyCenter: document.queryCommandState('justifyCenter'),\n justifyRight: document.queryCommandState('justifyRight'),\n justifyFull: document.queryCommandState('justifyFull'),\n });\n }, []);\n\n useEffect(() => {\n document.addEventListener('selectionchange', updateState);\n return () => {\n document.removeEventListener('selectionchange', updateState);\n };\n }, [updateState]);\n\n return state;\n}\n","// ============================================================\n// TextToolbar — Text formatting tools\n// ============================================================\n\nimport React, { useCallback, useRef } from 'react';\nimport { useFormattingState } from '../../hooks/useSelectionContext';\n\ninterface TextToolbarProps {\n onExecCommand: (command: string, value?: string) => void;\n}\n\nexport const TextToolbar: React.FC<TextToolbarProps> = ({ onExecCommand }) => {\n const formatState = useFormattingState();\n\n // ----------------------------------------------------------\n // Selection save / restore\n // ----------------------------------------------------------\n // Toolbar controls (selects, color pickers) steal focus from\n // the contentEditable area, clearing the browser selection.\n // We save the range on mouseDown (before focus moves) and\n // restore it before executing the formatting command.\n // ----------------------------------------------------------\n\n const savedRangeRef = useRef<Range | null>(null);\n\n const saveSelection = useCallback(() => {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n savedRangeRef.current = sel.getRangeAt(0).cloneRange();\n }\n }, []);\n\n const restoreSelection = useCallback(() => {\n const range = savedRangeRef.current;\n if (!range) return;\n const sel = window.getSelection();\n if (sel) {\n sel.removeAllRanges();\n sel.addRange(range);\n }\n }, []);\n\n const handleCommand = useCallback(\n (command: string, value?: string) => (e: React.MouseEvent) => {\n e.preventDefault(); // Don't steal focus from content\n onExecCommand(command, value);\n },\n [onExecCommand]\n );\n\n const handleFontSize = useCallback(\n (e: React.ChangeEvent<HTMLSelectElement>) => {\n restoreSelection();\n onExecCommand('fontSize', e.target.value);\n },\n [onExecCommand, restoreSelection]\n );\n\n const handleHeading = useCallback(\n (e: React.ChangeEvent<HTMLSelectElement>) => {\n restoreSelection();\n const value = e.target.value;\n if (value === 'p') {\n onExecCommand('formatBlock', 'p');\n } else {\n onExecCommand('formatBlock', value);\n }\n },\n [onExecCommand, restoreSelection]\n );\n\n const handleForeColor = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n restoreSelection();\n onExecCommand('foreColor', e.target.value);\n },\n [onExecCommand, restoreSelection]\n );\n\n const handleBackColor = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n restoreSelection();\n onExecCommand('hiliteColor', e.target.value);\n },\n [onExecCommand, restoreSelection]\n );\n\n return (\n <div style={toolbarSectionStyle}>\n {/* Block format */}\n <select\n onChange={handleHeading}\n defaultValue=\"p\"\n style={selectStyle}\n title=\"Block Format\"\n onMouseDown={saveSelection}\n >\n <option value=\"p\">Paragraph</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"h5\">Heading 5</option>\n <option value=\"h6\">Heading 6</option>\n </select>\n\n <div style={dividerStyle} />\n\n {/* Font size */}\n <select\n onChange={handleFontSize}\n defaultValue=\"3\"\n style={selectStyle}\n title=\"Font Size\"\n onMouseDown={saveSelection}\n >\n <option value=\"1\">8pt</option>\n <option value=\"2\">10pt</option>\n <option value=\"3\">12pt</option>\n <option value=\"4\">14pt</option>\n <option value=\"5\">18pt</option>\n <option value=\"6\">24pt</option>\n <option value=\"7\">36pt</option>\n </select>\n\n <div style={dividerStyle} />\n\n {/* Inline formatting */}\n <ToolbarButton\n icon=\"B\"\n title=\"Bold (Ctrl+B)\"\n active={formatState.bold}\n onMouseDown={handleCommand('bold')}\n extraStyle={{ fontWeight: 'bold' }}\n />\n <ToolbarButton\n icon=\"I\"\n title=\"Italic (Ctrl+I)\"\n active={formatState.italic}\n onMouseDown={handleCommand('italic')}\n extraStyle={{ fontStyle: 'italic' }}\n />\n\n <div style={dividerStyle} />\n\n {/* Colors */}\n <label style={colorLabelStyle} title=\"Text Color\" onMouseDown={saveSelection}>\n A\n <input\n type=\"color\"\n defaultValue=\"#000000\"\n onChange={handleForeColor}\n style={colorInputStyle}\n />\n </label>\n <label style={colorLabelStyle} title=\"Highlight Color\" onMouseDown={saveSelection}>\n <span style={{ backgroundColor: '#ffff00', padding: '0 2px' }}>A</span>\n <input\n type=\"color\"\n defaultValue=\"#ffff00\"\n onChange={handleBackColor}\n style={colorInputStyle}\n />\n </label>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// ToolbarButton sub-component\n// ----------------------------------------------------------\n\ninterface ToolbarButtonProps {\n icon: string;\n title: string;\n active?: boolean;\n onMouseDown: (e: React.MouseEvent) => void;\n extraStyle?: React.CSSProperties;\n}\n\nconst ToolbarButton: React.FC<ToolbarButtonProps> = ({\n icon,\n title,\n active,\n onMouseDown,\n extraStyle,\n}) => {\n return (\n <button\n type=\"button\"\n title={title}\n onMouseDown={onMouseDown}\n style={{\n width: '28px',\n height: '28px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: active ? '#b0b5bd' : 'transparent',\n borderRadius: '3px',\n backgroundColor: active ? '#d0d5dd' : 'transparent',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '13px',\n color: '#333',\n padding: 0,\n fontFamily: 'inherit',\n ...extraStyle,\n }}\n dangerouslySetInnerHTML={{ __html: icon }}\n />\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst toolbarSectionStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n flexWrap: 'wrap',\n};\n\nconst selectStyle: React.CSSProperties = {\n height: '28px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '12px',\n padding: '0 4px',\n cursor: 'pointer',\n backgroundColor: '#fff',\n};\n\nconst dividerStyle: React.CSSProperties = {\n width: '1px',\n height: '20px',\n backgroundColor: '#ddd',\n margin: '0 4px',\n};\n\nconst colorLabelStyle: React.CSSProperties = {\n position: 'relative',\n width: '28px',\n height: '28px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 'bold',\n};\n\nconst colorInputStyle: React.CSSProperties = {\n position: 'absolute',\n bottom: 0,\n left: 0,\n width: '100%',\n height: '4px',\n padding: 0,\n borderWidth: 0,\n borderStyle: 'none',\n cursor: 'pointer',\n};\n","// ============================================================\n// PageSetupToolbar — Page size and margin controls\n// ============================================================\n\nimport React, { useCallback } from 'react';\nimport type { PageConfig, PageSizeName } from '../../core/types';\n\ninterface PageSetupToolbarProps {\n pageConfig: PageConfig;\n pageCount: number;\n onPageConfigChange: (config: Partial<PageConfig>) => void;\n}\n\nexport const PageSetupToolbar: React.FC<PageSetupToolbarProps> = ({\n pageConfig,\n pageCount,\n onPageConfigChange,\n}) => {\n const handleSizeChange = useCallback(\n (e: React.ChangeEvent<HTMLSelectElement>) => {\n const value = e.target.value as PageSizeName;\n onPageConfigChange({ size: value });\n },\n [onPageConfigChange]\n );\n\n const handleMarginChange = useCallback(\n (side: keyof typeof pageConfig.margins) =>\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = Math.max(0, parseInt(e.target.value) || 0);\n onPageConfigChange({\n margins: {\n ...pageConfig.margins,\n [side]: value,\n },\n });\n },\n [pageConfig.margins, onPageConfigChange]\n );\n\n const currentSize =\n typeof pageConfig.size === 'string' ? pageConfig.size : 'custom';\n\n return (\n <div style={sectionStyle}>\n {/* Page size */}\n <label style={labelStyle}>\n Page:\n <select\n value={currentSize}\n onChange={handleSizeChange}\n style={selectStyle}\n >\n <option value=\"letter\">Letter (8.5 x 11)</option>\n <option value=\"a4\">A4 (210 x 297mm)</option>\n <option value=\"legal\">Legal (8.5 x 14)</option>\n </select>\n </label>\n\n <div style={dividerStyle} />\n\n {/* Margins */}\n <span style={{ fontSize: '12px', color: '#666' }}>Margins (px):</span>\n <MarginInput\n label=\"T\"\n value={pageConfig.margins.top}\n onChange={handleMarginChange('top')}\n />\n <MarginInput\n label=\"R\"\n value={pageConfig.margins.right}\n onChange={handleMarginChange('right')}\n />\n <MarginInput\n label=\"B\"\n value={pageConfig.margins.bottom}\n onChange={handleMarginChange('bottom')}\n />\n <MarginInput\n label=\"L\"\n value={pageConfig.margins.left}\n onChange={handleMarginChange('left')}\n />\n\n <div style={dividerStyle} />\n\n {/* Page count */}\n <span style={{ fontSize: '12px', color: '#666' }}>\n {pageCount} {pageCount === 1 ? 'page' : 'pages'}\n </span>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// MarginInput sub-component\n// ----------------------------------------------------------\n\ninterface MarginInputProps {\n label: string;\n value: number;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n}\n\nconst MarginInput: React.FC<MarginInputProps> = ({ label, value, onChange }) => (\n <label style={marginLabelStyle} title={`${label} margin`}>\n {label}:\n <input\n type=\"number\"\n value={value}\n onChange={onChange}\n style={marginInputStyle}\n min={0}\n max={300}\n step={12}\n />\n </label>\n);\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst sectionStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n flexWrap: 'wrap',\n};\n\nconst labelStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n fontSize: '12px',\n color: '#666',\n};\n\nconst selectStyle: React.CSSProperties = {\n height: '26px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '12px',\n padding: '0 4px',\n cursor: 'pointer',\n backgroundColor: '#fff',\n};\n\nconst dividerStyle: React.CSSProperties = {\n width: '1px',\n height: '20px',\n backgroundColor: '#ddd',\n margin: '0 4px',\n};\n\nconst marginLabelStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n fontSize: '11px',\n color: '#666',\n};\n\nconst marginInputStyle: React.CSSProperties = {\n width: '44px',\n height: '24px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '11px',\n textAlign: 'center',\n padding: '0 2px',\n};\n","// ============================================================\n// Toolbar — Main toolbar container with context switching\n// ============================================================\n\nimport React from 'react';\nimport { TextToolbar } from './TextToolbar';\nimport { PageSetupToolbar } from './PageSetupToolbar';\nimport type { PageConfig } from '../../core/types';\n\ninterface ToolbarProps {\n pageConfig: PageConfig;\n pageCount: number;\n onExecCommand: (command: string, value?: string) => void;\n onPageConfigChange: (config: Partial<PageConfig>) => void;\n}\n\nexport const Toolbar: React.FC<ToolbarProps> = ({\n pageConfig,\n pageCount,\n onExecCommand,\n onPageConfigChange,\n}) => {\n return (\n <div style={toolbarContainerStyle}>\n {/* Top row: Text formatting */}\n <div style={toolbarRowStyle}>\n <TextToolbar onExecCommand={onExecCommand} />\n </div>\n\n {/* Bottom row: Page setup */}\n <div style={toolbarRowStyle}>\n <PageSetupToolbar\n pageConfig={pageConfig}\n pageCount={pageCount}\n onPageConfigChange={onPageConfigChange}\n />\n </div>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst toolbarContainerStyle: React.CSSProperties = {\n borderBottomWidth: '1px',\n borderBottomStyle: 'solid',\n borderBottomColor: '#d0d0d0',\n backgroundColor: '#f8f8f8',\n padding: '4px 8px',\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n flexShrink: 0,\n zIndex: 10,\n};\n\nconst toolbarRowStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n minHeight: '32px',\n};\n","// ============================================================\n// useDocumentEngine — React hook wrapping DocumentEngine\n// ============================================================\n\nimport { useRef, useState, useCallback, useEffect } from 'react';\nimport { DocumentEngine } from '../core/DocumentEngine';\nimport type { PageConfig, PaginationResult } from '../core/types';\nimport { DEFAULT_PAGE_CONFIG } from '../core/types';\n\ninterface UseDocumentEngineOptions {\n initialHTML?: string;\n initialCSS?: string;\n initialConfig?: PageConfig;\n}\n\ninterface UseDocumentEngineReturn {\n engine: DocumentEngine;\n paginationResult: PaginationResult;\n pageConfig: PageConfig;\n loadHTML: (html: string, css?: string) => void;\n setPageConfig: (config: Partial<PageConfig>) => void;\n triggerPagination: () => void;\n getHTML: () => string;\n getPlainText: () => string;\n}\n\nexport function useDocumentEngine(\n options: UseDocumentEngineOptions = {}\n): UseDocumentEngineReturn {\n const {\n initialHTML = '',\n initialCSS = '',\n initialConfig = DEFAULT_PAGE_CONFIG,\n } = options;\n\n const engineRef = useRef<DocumentEngine>(new DocumentEngine(initialConfig));\n const [paginationResult, setPaginationResult] = useState<PaginationResult>({\n pages: [{ blockIndices: [] }],\n pageCount: 1,\n });\n const [pageConfig, setPageConfigState] = useState<PageConfig>(initialConfig);\n\n // Load HTML into engine\n const loadHTML = useCallback((html: string, css?: string) => {\n engineRef.current.loadHTML(html, css);\n }, []);\n\n // Set page configuration\n const setPageConfig = useCallback((config: Partial<PageConfig>) => {\n engineRef.current.setPageConfig(config);\n setPageConfigState(engineRef.current.getPageConfig());\n }, []);\n\n // Trigger re-pagination\n const triggerPagination = useCallback(() => {\n const { result } = engineRef.current.runPagination();\n setPaginationResult(result);\n }, []);\n\n // Get current HTML content\n const getHTML = useCallback(() => {\n return engineRef.current.getHTML();\n }, []);\n\n // Get plain text\n const getPlainText = useCallback(() => {\n return engineRef.current.getPlainText();\n }, []);\n\n // Load initial HTML\n useEffect(() => {\n if (initialHTML) {\n engineRef.current.loadHTML(initialHTML, initialCSS);\n }\n }, [initialHTML, initialCSS]);\n\n // Subscribe to pagination events\n useEffect(() => {\n const unsub = engineRef.current.onPagination((result) => {\n setPaginationResult(result);\n });\n return unsub;\n }, []);\n\n // Cleanup\n useEffect(() => {\n return () => {\n engineRef.current.destroy();\n };\n }, []);\n\n return {\n engine: engineRef.current,\n paginationResult,\n pageConfig,\n loadHTML,\n setPageConfig,\n triggerPagination,\n getHTML,\n getPlainText,\n };\n}\n"],"names":["Page","dimensions","margins","pageNumber","totalPages","children","jsxs","jsx","PAGE_SIZE_PRESETS","DEFAULT_MARGINS","DEFAULT_PAGE_CONFIG","activateScripts","container","activated","orig","fresh","attr","saveCursorPosition","sel","range","anchorNode","wrappers","blockIndex","w","preRange","textOffset","restoreCursorPosition","state","wrapper","walker","remaining","textNode","PagedView","html","css","pageConfig","layoutEngine","_editableManager","onContentChange","onPaginationChange","measureRef","useRef","pagesContainerRef","mutationObserverRef","onContentChangeRef","pages","setPages","useState","pendingCursorRef","isRePaginatingRef","pagesRef","collectHTMLFromDOM","useCallback","htmlParts","content","updatedHTML","rePaginateFromDOM","cursor","blockHTMLs","mc","contentWidth","styleEl","measureWrapper","measurements","measuredHTMLs","m","result","newPages","p","idx","oldDist","newDist","count","i","rePaginateFromDOMRef","runPagination","measureContainer","parsed","el","contentHTML","pageData","page","useEffect","child","cell","activatedScripts","debounceTimer","observer","s","scrollContainerStyle","pagesWrapperStyle","pageIndex","blockHTML","PageLayoutEngine","config","style","breakBefore","breakAfter","rect","marginTop","marginBottom","totalHeight","contentHeight","currentPage","currentHeight","block","EditableManager","table","_mutations","selection","node","current","tag","ctx","cb","last","previous","next","fn","callback","command","value","DopeCanvas","forwardRef","externalPageConfig","onPageConfigChange","ref","internalPageConfig","setInternalPageConfig","paginationResult","setPaginationResult","currentHTMLRef","useMemo","editableManager","handleContentChange","newHTML","handlePageConfigChange","newConfig","updated","handlePaginationChange","useImperativeHandle","tmp","DocumentEngine","contentWrapper","DocumentAPI","selector","id","useSelectionContext","context","setContext","useFormattingState","setState","updateState","TextToolbar","onExecCommand","formatState","savedRangeRef","saveSelection","restoreSelection","handleCommand","e","handleFontSize","handleHeading","handleForeColor","handleBackColor","toolbarSectionStyle","selectStyle","dividerStyle","ToolbarButton","colorLabelStyle","colorInputStyle","icon","title","active","onMouseDown","extraStyle","PageSetupToolbar","pageCount","handleSizeChange","handleMarginChange","side","currentSize","sectionStyle","labelStyle","MarginInput","label","onChange","marginLabelStyle","marginInputStyle","Toolbar","toolbarContainerStyle","toolbarRowStyle","useDocumentEngine","options","initialHTML","initialCSS","initialConfig","engineRef","setPageConfigState","loadHTML","setPageConfig","triggerPagination","getHTML","getPlainText"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dopecanvas.cjs","sources":["../src/components/Page.tsx","../src/components/BlockToolbar.tsx","../src/components/HTMLEditorModal.tsx","../src/core/types.ts","../src/core/BlockSplitter.ts","../src/components/PagedView.tsx","../src/core/PageLayoutEngine.ts","../src/core/EditableManager.ts","../src/components/DopeCanvas.tsx","../src/core/DocumentEngine.ts","../src/api/DocumentAPI.ts","../src/hooks/useSelectionContext.ts","../src/hooks/useSelectionSaver.ts","../src/components/Toolbar/TextToolbar.tsx","../src/components/Toolbar/PageSetupToolbar.tsx","../src/components/Toolbar/Toolbar.tsx","../src/hooks/useDocumentEngine.ts"],"sourcesContent":["// ============================================================\n// Page — Single page frame component\n// ============================================================\n// Renders a fixed-size white page with margins and page number.\n// ============================================================\n\nimport React from 'react';\nimport type { PageDimensions, PageMargins } from '../core/types';\n\ninterface PageProps {\n /** Page dimensions in pixels */\n dimensions: PageDimensions;\n /** Page margins in pixels */\n margins: PageMargins;\n /** Page number (1-indexed) */\n pageNumber: number;\n /** Total number of pages */\n totalPages: number;\n /** The content blocks to render inside the page */\n children: React.ReactNode;\n}\n\nexport const Page: React.FC<PageProps> = ({\n dimensions,\n margins,\n pageNumber,\n totalPages,\n children,\n}) => {\n return (\n <div\n className=\"dopecanvas-page\"\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n backgroundColor: '#ffffff',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1)',\n position: 'relative',\n overflow: 'hidden',\n flexShrink: 0,\n }}\n >\n {/* Content area with margins */}\n <div\n className=\"dopecanvas-page-content\"\n style={{\n paddingTop: `${margins.top}px`,\n paddingRight: `${margins.right}px`,\n paddingBottom: `${margins.bottom}px`,\n paddingLeft: `${margins.left}px`,\n height: '100%',\n boxSizing: 'border-box',\n overflow: 'hidden',\n }}\n >\n {children}\n </div>\n\n {/* Page number footer */}\n <div\n className=\"dopecanvas-page-number\"\n style={{\n position: 'absolute',\n bottom: `${Math.max(margins.bottom / 3, 16)}px`,\n left: 0,\n right: 0,\n textAlign: 'center',\n fontSize: '11px',\n color: '#999',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n pointerEvents: 'none',\n userSelect: 'none',\n }}\n >\n {pageNumber} / {totalPages}\n </div>\n </div>\n );\n};\n","// ============================================================\n// BlockToolbar — Hover controls for individual content blocks\n// ============================================================\n// Shows add / edit-HTML / delete actions when a block is hovered.\n// Positioned absolutely in the left margin of the block.\n// ============================================================\n\nimport React, { useState } from 'react';\n\ninterface BlockToolbarProps {\n /** Whether the toolbar is currently visible */\n visible: boolean;\n /** Add a new empty block below this one */\n onAddBelow: () => void;\n /** Open the HTML source editor for this block */\n onEditHTML: () => void;\n /** Delete this block */\n onDelete: () => void;\n}\n\nexport const BlockToolbar: React.FC<BlockToolbarProps> = ({\n visible,\n onAddBelow,\n onEditHTML,\n onDelete,\n}) => (\n <div\n className=\"dopecanvas-block-toolbar\"\n style={{\n ...toolbarStyle,\n display: visible ? 'flex' : 'none',\n }}\n onMouseDown={(e) => e.preventDefault()}\n >\n <Btn onClick={onAddBelow} title=\"Add block below\">\n <PlusIcon />\n </Btn>\n <Btn onClick={onEditHTML} title=\"Edit HTML\">\n <CodeIcon />\n </Btn>\n <Btn onClick={onDelete} title=\"Delete block\" danger>\n <TrashIcon />\n </Btn>\n </div>\n);\n\n// ----------------------------------------------------------\n// Tiny SVG icons (no external dependencies)\n// ----------------------------------------------------------\n\nconst PlusIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\n <line x1=\"7\" y1=\"3\" x2=\"7\" y2=\"11\" />\n <line x1=\"3\" y1=\"7\" x2=\"11\" y2=\"7\" />\n </svg>\n);\n\nconst CodeIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"4.5,3 1.5,7 4.5,11\" />\n <polyline points=\"9.5,3 12.5,7 9.5,11\" />\n </svg>\n);\n\nconst TrashIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M2.5 4h9M5 4V2.5h4V4M3.5 4l.5 8h6l.5-8\" />\n </svg>\n);\n\n// ----------------------------------------------------------\n// Button sub-component with hover effect\n// ----------------------------------------------------------\n\nconst Btn: React.FC<{\n onClick: () => void;\n title: string;\n children: React.ReactNode;\n danger?: boolean;\n}> = ({ onClick, title, children, danger }) => {\n const [hovered, setHovered] = useState(false);\n return (\n <button\n onClick={onClick}\n title={title}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{\n ...btnStyle,\n background: hovered\n ? danger\n ? '#fff0f0'\n : '#f0f0f0'\n : 'transparent',\n color: hovered && danger ? '#d32f2f' : '#666',\n }}\n >\n {children}\n </button>\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst toolbarStyle: React.CSSProperties = {\n position: 'absolute',\n top: 0,\n left: -40,\n flexDirection: 'column',\n gap: '1px',\n zIndex: 100,\n background: '#fff',\n borderRadius: '6px',\n boxShadow: '0 1px 5px rgba(0,0,0,0.12)',\n padding: '3px',\n paddingRight: '6px',\n};\n\nconst btnStyle: React.CSSProperties = {\n width: '30px',\n height: '26px',\n border: 'none',\n cursor: 'pointer',\n borderRadius: '4px',\n fontSize: '13px',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 0,\n transition: 'background 0.1s, color 0.1s',\n};\n","// ============================================================\n// HTMLEditorModal — Raw HTML editor for a single block\n// ============================================================\n// Full-screen modal with a code-editor-style textarea.\n// Rendered as a React Portal to document.body.\n// ============================================================\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\ninterface HTMLEditorModalProps {\n /** The current HTML of the block being edited */\n html: string;\n /** Called with the updated HTML when the user saves */\n onSave: (html: string) => void;\n /** Called when the user cancels editing */\n onCancel: () => void;\n}\n\nexport const HTMLEditorModal: React.FC<HTMLEditorModalProps> = ({\n html,\n onSave,\n onCancel,\n}) => {\n const [value, setValue] = useState(html);\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n // Cmd/Ctrl+Enter → save\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onSave(value);\n }\n // Escape → cancel\n if (e.key === 'Escape') {\n e.preventDefault();\n onCancel();\n }\n // Tab → insert 2 spaces\n if (e.key === 'Tab') {\n e.preventDefault();\n const ta = e.target as HTMLTextAreaElement;\n const start = ta.selectionStart;\n const end = ta.selectionEnd;\n const updated = value.substring(0, start) + ' ' + value.substring(end);\n setValue(updated);\n requestAnimationFrame(() => {\n ta.selectionStart = ta.selectionEnd = start + 2;\n });\n }\n };\n\n return createPortal(\n <div style={overlayStyle} onClick={onCancel}>\n <div style={modalStyle} onClick={(e) => e.stopPropagation()}>\n {/* Header */}\n <div style={headerStyle}>\n <span style={{ fontWeight: 600, fontSize: '14px' }}>Edit Block HTML</span>\n <span style={{ fontSize: '11px', color: '#888' }}>\n ⌘Enter to save · Escape to cancel\n </span>\n </div>\n\n {/* Editor */}\n <textarea\n style={textareaStyle}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n spellCheck={false}\n autoFocus\n />\n\n {/* Footer */}\n <div style={footerStyle}>\n <button\n style={cancelBtnStyle}\n onClick={onCancel}\n onMouseEnter={(e) => {\n (e.target as HTMLElement).style.borderColor = '#888';\n }}\n onMouseLeave={(e) => {\n (e.target as HTMLElement).style.borderColor = '#555';\n }}\n >\n Cancel\n </button>\n <button\n style={saveBtnStyle}\n onClick={() => onSave(value)}\n onMouseEnter={(e) => {\n (e.target as HTMLElement).style.background = '#0055dd';\n }}\n onMouseLeave={(e) => {\n (e.target as HTMLElement).style.background = '#0066ff';\n }}\n >\n Save\n </button>\n </div>\n </div>\n </div>,\n document.body\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst overlayStyle: React.CSSProperties = {\n position: 'fixed',\n inset: 0,\n background: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 10000,\n};\n\nconst modalStyle: React.CSSProperties = {\n width: 'min(800px, 90vw)',\n height: 'min(600px, 80vh)',\n background: '#1e1e1e',\n borderRadius: '10px',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4)',\n};\n\nconst headerStyle: React.CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n padding: '12px 16px',\n background: '#2d2d2d',\n color: '#ccc',\n borderBottom: '1px solid #444',\n};\n\nconst textareaStyle: React.CSSProperties = {\n flex: 1,\n background: '#1e1e1e',\n color: '#d4d4d4',\n border: 'none',\n padding: '16px',\n fontFamily: \"'SF Mono', 'Fira Code', 'Consolas', 'Monaco', monospace\",\n fontSize: '13px',\n lineHeight: '1.6',\n resize: 'none',\n outline: 'none',\n tabSize: 2,\n};\n\nconst footerStyle: React.CSSProperties = {\n display: 'flex',\n justifyContent: 'flex-end',\n gap: '8px',\n padding: '12px 16px',\n background: '#2d2d2d',\n borderTop: '1px solid #444',\n};\n\nconst cancelBtnStyle: React.CSSProperties = {\n padding: '6px 16px',\n border: '1px solid #555',\n borderRadius: '6px',\n background: 'transparent',\n color: '#ccc',\n cursor: 'pointer',\n fontSize: '13px',\n transition: 'border-color 0.15s',\n};\n\nconst saveBtnStyle: React.CSSProperties = {\n padding: '6px 16px',\n border: 'none',\n borderRadius: '6px',\n background: '#0066ff',\n color: '#fff',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n transition: 'background 0.15s',\n};\n","// ============================================================\n// DopeCanvas Core Types\n// ============================================================\n\n/** Named page size presets */\nexport type PageSizeName = 'letter' | 'a4' | 'legal';\n\n/** Custom page dimensions in pixels */\nexport interface PageDimensions {\n width: number;\n height: number;\n}\n\n/** Page margins in pixels */\nexport interface PageMargins {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** Full page configuration */\nexport interface PageConfig {\n size: PageSizeName | PageDimensions;\n margins: PageMargins;\n}\n\n/** A single page containing block element indices */\nexport interface PageContent {\n /** Indices into the flat block array */\n blockIndices: number[];\n}\n\n/** Result of the pagination algorithm */\nexport interface PaginationResult {\n pages: PageContent[];\n pageCount: number;\n}\n\n/** Block measurement info */\nexport interface BlockMeasurement {\n index: number;\n height: number;\n element: HTMLElement;\n breakBefore: boolean;\n breakAfter: boolean;\n}\n\n/** Toolbar context — what kind of element is selected */\nexport type ToolbarContext = 'text' | 'table' | 'image' | 'chart' | 'none';\n\n/** Undo/redo snapshot */\nexport interface UndoSnapshot {\n html: string;\n timestamp: number;\n}\n\n/** Unsubscribe function returned by event listeners */\nexport type Unsubscribe = () => void;\n\n/**\n * Comprehensive formatting state for the current selection.\n * Consumers use this to reflect active states in toolbar UI.\n */\nexport interface FormattingState {\n // Toggle states (document.queryCommandState)\n bold: boolean;\n italic: boolean;\n underline: boolean;\n strikethrough: boolean;\n justifyLeft: boolean;\n justifyCenter: boolean;\n justifyRight: boolean;\n justifyFull: boolean;\n orderedList: boolean;\n unorderedList: boolean;\n superscript: boolean;\n subscript: boolean;\n // Value states (document.queryCommandValue)\n fontName: string;\n fontSize: string;\n foreColor: string;\n backColor: string;\n formatBlock: string;\n}\n\n// ============================================================\n// Page size presets (at 96 DPI)\n// ============================================================\n\n/** Page sizes in pixels at 96 DPI */\nexport const PAGE_SIZE_PRESETS: Record<PageSizeName, PageDimensions> = {\n letter: { width: 816, height: 1056 }, // 8.5 x 11 inches\n a4: { width: 794, height: 1123 }, // 210 x 297 mm\n legal: { width: 816, height: 1344 }, // 8.5 x 14 inches\n};\n\n/** Default margins: 1 inch (96px) on all sides */\nexport const DEFAULT_MARGINS: PageMargins = {\n top: 96,\n right: 96,\n bottom: 96,\n left: 96,\n};\n\n/** Default page config */\nexport const DEFAULT_PAGE_CONFIG: PageConfig = {\n size: 'letter',\n margins: { ...DEFAULT_MARGINS },\n};\n","// ============================================================\n// BlockSplitter — Split oversized blocks at page boundaries\n// ============================================================\n// When a block is taller than the remaining space on a page,\n// this utility splits it into two pieces so that content flows\n// naturally from one page to the next.\n//\n// Two splitting strategies:\n// 1. Child-element boundary — for blocks with multiple children\n// (e.g. <div> with <p>s, <ul> with <li>s, <table> with <tr>s)\n// 2. Text line boundary — for single text blocks (<p>, <h1>, etc.)\n// Uses the Range API to find the line that crosses the boundary.\n//\n// Split blocks are marked with data attributes so they can be\n// recombined before the next re-pagination cycle.\n// ============================================================\n\n/** Result of splitting a block */\nexport interface SplitResult {\n firstHTML: string;\n secondHTML: string;\n}\n\n/** Unique ID counter for split blocks */\nlet splitIdCounter = 0;\nfunction nextSplitId(): string {\n return `split-${++splitIdCounter}-${Date.now()}`;\n}\n\n/** Minimum remaining height (in px) to attempt a split */\nconst MIN_SPLIT_HEIGHT = 40;\n\n/** Tags that should never be split */\nconst UNSPLITTABLE_TAGS = new Set([\n 'script', 'style', 'img', 'video', 'canvas', 'svg', 'hr',\n 'iframe', 'object', 'embed', 'audio', 'picture', 'figure',\n]);\n\n/** Tags eligible for text-level splitting */\nconst TEXT_BLOCK_TAGS = new Set([\n 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote',\n 'div', 'li', 'span', 'td', 'th', 'pre', 'code',\n]);\n\n// ----------------------------------------------------------\n// Public API\n// ----------------------------------------------------------\n\n/**\n * Try to split a block element so that the first piece fits\n * within `availableHeight` pixels. Returns null if the block\n * cannot be split (too small, unsplittable tag, etc.).\n *\n * The element must be in the DOM (attached to a measurement\n * container) so that getBoundingClientRect / Range work.\n */\nexport function trySplitBlock(\n element: HTMLElement,\n availableHeight: number\n): SplitResult | null {\n const tag = element.tagName.toLowerCase();\n\n // Never split certain elements\n if (UNSPLITTABLE_TAGS.has(tag)) return null;\n\n // Skip if available height is too tiny\n if (availableHeight < MIN_SPLIT_HEIGHT) return null;\n\n // Skip page-break markers\n const style = window.getComputedStyle(element);\n if (\n style.getPropertyValue('break-before') === 'page' ||\n style.getPropertyValue('page-break-before') === 'always'\n ) {\n return null;\n }\n\n // Strategy 1: split at child element boundaries\n const children = Array.from(element.children) as HTMLElement[];\n if (children.length > 1) {\n const result = splitAtChildBoundary(element, children, availableHeight);\n if (result) return result;\n }\n\n // Strategy 2: split at text line boundary\n if (TEXT_BLOCK_TAGS.has(tag) || element.childNodes.length > 0) {\n const result = splitAtTextBoundary(element, availableHeight);\n if (result) return result;\n }\n\n return null;\n}\n\n/**\n * Recombine adjacent split blocks back into their original block.\n * Call this before re-measuring / re-paginating so that the\n * splitter can re-evaluate where to split.\n *\n * Takes an array of block HTML strings and returns a new array\n * with split blocks merged.\n */\nexport function recombineSplitBlocks(blockHTMLs: string[]): string[] {\n if (blockHTMLs.length === 0) return blockHTMLs;\n\n const result: string[] = [];\n let i = 0;\n\n while (i < blockHTMLs.length) {\n const html = blockHTMLs[i];\n\n // Check if this block is a split block\n const splitId = extractSplitId(html);\n if (!splitId) {\n result.push(html);\n i++;\n continue;\n }\n\n // Collect all parts with the same split-id\n const parts: string[] = [html];\n let j = i + 1;\n while (j < blockHTMLs.length) {\n const nextId = extractSplitId(blockHTMLs[j]);\n if (nextId === splitId) {\n parts.push(blockHTMLs[j]);\n j++;\n } else {\n break;\n }\n }\n\n // Recombine\n result.push(mergeBlockParts(parts));\n i = j;\n }\n\n return result;\n}\n\n// ----------------------------------------------------------\n// Strategy 1: Split at child element boundaries\n// ----------------------------------------------------------\n\nfunction splitAtChildBoundary(\n element: HTMLElement,\n children: HTMLElement[],\n availableHeight: number\n): SplitResult | null {\n const blockTop = element.getBoundingClientRect().top;\n let splitIndex = -1;\n\n // Find the first child whose bottom exceeds the available height\n for (let i = 0; i < children.length; i++) {\n const childRect = children[i].getBoundingClientRect();\n const childStyle = window.getComputedStyle(children[i]);\n const marginBottom = parseFloat(childStyle.marginBottom) || 0;\n const childBottom = childRect.bottom + marginBottom - blockTop;\n\n if (childBottom > availableHeight && i > 0) {\n splitIndex = i;\n break;\n }\n }\n\n if (splitIndex <= 0) return null; // Can't split (all children on first or nothing above)\n\n // Create two copies of the parent element (preserving tag + attributes)\n const firstEl = element.cloneNode(false) as HTMLElement;\n const secondEl = element.cloneNode(false) as HTMLElement;\n\n for (let i = 0; i < children.length; i++) {\n if (i < splitIndex) {\n firstEl.appendChild(children[i].cloneNode(true));\n } else {\n secondEl.appendChild(children[i].cloneNode(true));\n }\n }\n\n // Add split markers\n const id = nextSplitId();\n firstEl.setAttribute('data-dopecanvas-split-id', id);\n firstEl.setAttribute('data-dopecanvas-split-part', '0');\n secondEl.setAttribute('data-dopecanvas-split-id', id);\n secondEl.setAttribute('data-dopecanvas-split-part', '1');\n\n return {\n firstHTML: firstEl.outerHTML,\n secondHTML: secondEl.outerHTML,\n };\n}\n\n// ----------------------------------------------------------\n// Strategy 2: Split at text line boundary\n// ----------------------------------------------------------\n\nfunction splitAtTextBoundary(\n element: HTMLElement,\n availableHeight: number\n): SplitResult | null {\n const blockTop = element.getBoundingClientRect().top;\n\n // Collect all text nodes in document order\n const textNodes: Text[] = [];\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n textNodes.push(walker.currentNode as Text);\n }\n\n if (textNodes.length === 0) return null;\n\n // Find the text node + offset where content crosses the boundary\n let splitNode: Text | null = null;\n let splitOffset = 0;\n\n for (const textNode of textNodes) {\n const range = document.createRange();\n range.selectNodeContents(textNode);\n const nodeRect = range.getBoundingClientRect();\n\n // If this entire text node is above the boundary, skip\n if (nodeRect.bottom - blockTop <= availableHeight) continue;\n\n // If this entire text node starts below the boundary, split before it\n if (nodeRect.top - blockTop >= availableHeight) {\n splitNode = textNode;\n splitOffset = 0;\n break;\n }\n\n // This text node crosses the boundary — binary search within it\n let lo = 0;\n let hi = textNode.length;\n while (lo < hi) {\n const mid = Math.floor((lo + hi) / 2);\n const r = document.createRange();\n r.setStart(textNode, mid);\n r.collapse(true);\n const rr = r.getBoundingClientRect();\n if (rr.top - blockTop >= availableHeight) {\n hi = mid;\n } else {\n lo = mid + 1;\n }\n }\n\n splitNode = textNode;\n splitOffset = lo;\n break;\n }\n\n if (!splitNode) return null;\n\n // Back up to a word boundary (don't split in the middle of a word)\n const text = splitNode.textContent || '';\n let wordBound = splitOffset;\n while (wordBound > 0 && text[wordBound - 1] !== ' ' && text[wordBound - 1] !== '\\n') {\n wordBound--;\n }\n if (wordBound > 0) {\n splitOffset = wordBound;\n }\n\n // Don't split if the offset is at the very start or end\n if (splitOffset === 0 && splitNode === textNodes[0]) return null;\n if (splitOffset >= text.length && splitNode === textNodes[textNodes.length - 1]) return null;\n\n // Use Range.cloneContents() to create the two halves.\n // This correctly preserves inline formatting elements (strong, em, etc.)\n try {\n // Range for the first half: start of element → split point\n const firstRange = document.createRange();\n firstRange.setStart(element, 0);\n firstRange.setEnd(splitNode, splitOffset);\n\n // Range for the second half: split point → end of element\n const secondRange = document.createRange();\n secondRange.setStart(splitNode, splitOffset);\n secondRange.setEndAfter(element.lastChild!);\n\n const firstFragment = firstRange.cloneContents();\n const secondFragment = secondRange.cloneContents();\n\n // Check that both halves have actual content\n const firstText = fragmentToText(firstFragment);\n const secondText = fragmentToText(secondFragment);\n if (firstText.trim().length === 0 || secondText.trim().length === 0) {\n return null;\n }\n\n // Wrap each half in the same element tag with same attributes\n const firstEl = element.cloneNode(false) as HTMLElement;\n firstEl.appendChild(firstFragment);\n const secondEl = element.cloneNode(false) as HTMLElement;\n secondEl.appendChild(secondFragment);\n\n // Add split markers\n const id = nextSplitId();\n firstEl.setAttribute('data-dopecanvas-split-id', id);\n firstEl.setAttribute('data-dopecanvas-split-part', '0');\n secondEl.setAttribute('data-dopecanvas-split-id', id);\n secondEl.setAttribute('data-dopecanvas-split-part', '1');\n\n return {\n firstHTML: firstEl.outerHTML,\n secondHTML: secondEl.outerHTML,\n };\n } catch {\n return null;\n }\n}\n\n// ----------------------------------------------------------\n// Recombination helpers\n// ----------------------------------------------------------\n\n/** Extract the split-id from a block's HTML string (fast regex check) */\nfunction extractSplitId(html: string): string | null {\n const match = html.match(/data-dopecanvas-split-id=\"([^\"]+)\"/);\n return match ? match[1] : null;\n}\n\n/**\n * Merge multiple split-part HTML strings back into one block.\n * Combines the innerHTML of all parts into the first part's\n * outer element (preserving tag + original attributes minus split markers).\n */\nfunction mergeBlockParts(parts: string[]): string {\n const container = document.createElement('div');\n\n // Parse each part and collect inner content\n const innerParts: string[] = [];\n let outerTag = '';\n let outerAttrs = '';\n\n for (const partHTML of parts) {\n container.innerHTML = partHTML;\n const el = container.firstElementChild as HTMLElement;\n if (!el) continue;\n\n if (!outerTag) {\n // Use the first part as the template for the outer element\n outerTag = el.tagName.toLowerCase();\n const clone = el.cloneNode(false) as HTMLElement;\n clone.removeAttribute('data-dopecanvas-split-id');\n clone.removeAttribute('data-dopecanvas-split-part');\n // Get the opening tag\n const tmp = document.createElement('div');\n tmp.appendChild(clone);\n const fullTag = tmp.innerHTML;\n // Extract opening tag (everything before the closing)\n const closeIdx = fullTag.lastIndexOf('</');\n outerAttrs = closeIdx >= 0 ? fullTag.substring(0, closeIdx) : fullTag;\n }\n\n innerParts.push(el.innerHTML);\n }\n\n if (!outerTag) return parts[0]; // fallback\n\n return `${outerAttrs}${innerParts.join('')}</${outerTag}>`;\n}\n\n/** Get plain text from a DocumentFragment */\nfunction fragmentToText(fragment: DocumentFragment): string {\n const div = document.createElement('div');\n div.appendChild(fragment.cloneNode(true));\n return div.textContent || '';\n}\n","// ============================================================\n// PagedView — Renders paginated blocks across page frames\n// ============================================================\n// Takes HTML content, paginates it into visual pages, and makes\n// each block editable via contentEditable.\n//\n// After initial pagination, the live DOM is the source of truth.\n// User edits trigger live re-pagination when the block distribution\n// across pages changes (e.g. content grows past a page boundary).\n// Cursor position is saved/restored across re-pagination re-renders.\n// ============================================================\n\nimport React, { useRef, useEffect, useCallback, useState, useImperativeHandle, forwardRef } from 'react';\nimport { Page } from './Page';\nimport { BlockToolbar } from './BlockToolbar';\nimport { HTMLEditorModal } from './HTMLEditorModal';\nimport type { PageLayoutEngine } from '../core/PageLayoutEngine';\nimport type { EditableManager } from '../core/EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n PageSizeName,\n} from '../core/types';\nimport { PAGE_SIZE_PRESETS } from '../core/types';\nimport { trySplitBlock, recombineSplitBlocks } from '../core/BlockSplitter';\n\n/** Methods exposed by PagedView to its parent via ref */\nexport interface PagedViewHandle {\n /** Insert a page break after the block at the cursor, or at the end */\n insertPageBreak: () => void;\n}\n\ninterface PagedViewProps {\n /** The raw HTML content to render */\n html: string;\n /** Optional CSS to inject */\n css?: string;\n /** Page configuration */\n pageConfig: PageConfig;\n /** The layout engine instance */\n layoutEngine: PageLayoutEngine;\n /** The editable manager instance */\n editableManager: EditableManager;\n /** Callback when content changes (after user edit) — updates a ref, NOT state */\n onContentChange?: (html: string) => void;\n /** Callback when pagination changes */\n onPaginationChange?: (result: PaginationResult) => void;\n /** When true, render visual indicators for page break blocks */\n showPageBreaks?: boolean;\n}\n\n/**\n * Represents a block of content assigned to a specific page.\n * We store the HTML string so React can render it once, then\n * the live DOM takes over for editing.\n */\ninterface PageData {\n blocks: string[]; // outerHTML of each block in this page\n}\n\n/**\n * Execute <script> tags found in a container.\n * Scripts set via innerHTML / dangerouslySetInnerHTML do NOT auto-execute.\n * We clone each one into a fresh <script> element so the browser runs it.\n */\nfunction activateScripts(container: HTMLElement): HTMLScriptElement[] {\n const activated: HTMLScriptElement[] = [];\n\n container.querySelectorAll('script').forEach((orig) => {\n const fresh = document.createElement('script');\n // Preserve attributes (type, src, data-*, etc.)\n Array.from(orig.attributes).forEach((attr) =>\n fresh.setAttribute(attr.name, attr.value)\n );\n fresh.textContent = orig.textContent || '';\n // Replacing the node triggers synchronous execution for inline scripts\n orig.parentNode?.replaceChild(fresh, orig);\n activated.push(fresh);\n });\n\n return activated;\n}\n\n// ----------------------------------------------------------\n// Cursor save / restore — used across re-pagination re-renders\n// ----------------------------------------------------------\n\ninterface CursorState {\n /** Global index of the block wrapper the cursor is inside */\n blockIndex: number;\n /** Character offset within that block's text content */\n textOffset: number;\n}\n\n/** Save the current cursor position relative to block content divs */\nfunction saveCursorPosition(container: HTMLElement): CursorState | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n\n const range = sel.getRangeAt(0);\n const anchorNode = range.startContainer;\n\n const wrappers = Array.from(\n container.querySelectorAll('.dopecanvas-block-content')\n );\n const blockIndex = wrappers.findIndex((w) => w.contains(anchorNode));\n if (blockIndex === -1) return null;\n\n // Compute character offset within the block\n try {\n const preRange = document.createRange();\n preRange.selectNodeContents(wrappers[blockIndex]);\n preRange.setEnd(range.startContainer, range.startOffset);\n const textOffset = preRange.toString().length;\n return { blockIndex, textOffset };\n } catch {\n return null;\n }\n}\n\n/** Restore cursor position after a re-pagination re-render */\nfunction restoreCursorPosition(\n container: HTMLElement,\n state: CursorState\n): void {\n const wrappers = container.querySelectorAll('.dopecanvas-block-content');\n if (state.blockIndex >= wrappers.length) return;\n\n const wrapper = wrappers[state.blockIndex];\n const walker = document.createTreeWalker(wrapper, NodeFilter.SHOW_TEXT);\n let remaining = state.textOffset;\n\n while (walker.nextNode()) {\n const textNode = walker.currentNode as Text;\n if (remaining <= textNode.length) {\n try {\n const range = document.createRange();\n range.setStart(textNode, remaining);\n range.collapse(true);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n } catch {\n // Best effort — position may have shifted\n }\n return;\n }\n remaining -= textNode.length;\n }\n\n // Fallback: place cursor at the end of the block\n try {\n const range = document.createRange();\n range.selectNodeContents(wrapper);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n } catch {\n // Ignore\n }\n}\n\n/**\n * Memoized block content — prevents React from re-rendering (and\n * resetting innerHTML) when unrelated state like hoveredBlockIndex\n * changes. User edits via contentEditable are preserved.\n */\nconst MemoizedBlockContent = React.memo<{\n html: string;\n isEditable: boolean;\n isTable: boolean;\n}>(({ html, isEditable, isTable }) => (\n <div\n className=\"dopecanvas-block-content\"\n contentEditable={isEditable && !isTable ? true : undefined}\n suppressContentEditableWarning\n dangerouslySetInnerHTML={{ __html: html }}\n />\n));\n\n/** Detect whether a block HTML string is a page break element */\nfunction isPageBreakBlock(blockHTML: string): boolean {\n const s = blockHTML.replace(/\\s+/g, ' ').toLowerCase();\n return (\n (s.includes('break-before') && s.includes('page')) ||\n (s.includes('page-break-before') && s.includes('always'))\n );\n}\n\nexport const PagedView = forwardRef<PagedViewHandle, PagedViewProps>(({\n html,\n css,\n pageConfig,\n layoutEngine,\n editableManager: _editableManager,\n onContentChange,\n onPaginationChange,\n showPageBreaks = false,\n}, ref) => {\n const measureRef = useRef<HTMLDivElement>(null);\n const pagesContainerRef = useRef<HTMLDivElement>(null);\n const mutationObserverRef = useRef<MutationObserver | null>(null);\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n // Pages state — set during pagination AND live re-pagination\n const [pages, setPages] = useState<PageData[]>([]);\n\n // Refs for live re-pagination\n const pendingCursorRef = useRef<CursorState | null>(null);\n const isRePaginatingRef = useRef(false);\n const pagesRef = useRef<PageData[]>([]);\n\n // Block management state\n const [hoveredBlockIndex, setHoveredBlockIndex] = useState<number | null>(null);\n const [editingBlockIndex, setEditingBlockIndex] = useState<number | null>(null);\n const [editingHTML, setEditingHTML] = useState<string>('');\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Resolve page dimensions\n const dimensions =\n typeof pageConfig.size === 'string'\n ? PAGE_SIZE_PRESETS[pageConfig.size as PageSizeName]\n : pageConfig.size;\n\n // ----------------------------------------------------------\n // Collect current HTML from the live DOM (no re-render)\n // ----------------------------------------------------------\n\n const collectHTMLFromDOM = useCallback(() => {\n if (!pagesContainerRef.current) return;\n\n const contentDivs = pagesContainerRef.current.querySelectorAll(\n '.dopecanvas-block-content'\n );\n\n const htmlParts: string[] = [];\n contentDivs.forEach((contentDiv) => {\n const div = contentDiv as HTMLElement;\n const children = div.children;\n \n if (children.length === 0) {\n // Empty block - skip\n return;\n } else if (children.length === 1) {\n // Single child - use its outerHTML (original behavior)\n htmlParts.push((children[0] as HTMLElement).outerHTML);\n } else {\n // Multiple children (user pressed Enter and created new lines)\n // Wrap them in a div to keep as one block\n const wrapper = document.createElement('div');\n wrapper.innerHTML = div.innerHTML;\n htmlParts.push(wrapper.outerHTML);\n }\n });\n\n const updatedHTML = htmlParts.join('\\n');\n onContentChangeRef.current?.(updatedHTML);\n }, []);\n\n // ----------------------------------------------------------\n // Shared pagination: measure + paginate with block splitting\n // ----------------------------------------------------------\n\n const paginateHTML = useCallback((htmlContent: string) => {\n if (!measureRef.current) return;\n\n const mc = measureRef.current;\n const contentWidth = layoutEngine.getContentAreaWidth();\n const contentHeight = layoutEngine.getContentAreaHeight();\n mc.style.width = `${contentWidth}px`;\n mc.style.position = 'absolute';\n mc.style.left = '-9999px';\n mc.style.top = '0';\n mc.style.visibility = 'hidden';\n mc.innerHTML = '';\n\n if (css) {\n const styleEl = document.createElement('style');\n styleEl.textContent = css;\n mc.appendChild(styleEl);\n }\n\n const wrapper = document.createElement('div');\n wrapper.innerHTML = htmlContent;\n mc.appendChild(wrapper);\n\n // Measure all blocks\n const measurements = layoutEngine.measureBlocks(wrapper);\n\n // Build a queue of blocks to paginate (with splitting)\n interface QueueItem {\n html: string;\n height: number;\n element: HTMLElement;\n breakBefore: boolean;\n breakAfter: boolean;\n }\n\n const queue: QueueItem[] = measurements.map((m) => {\n const el = m.element;\n return {\n html: (el.cloneNode(true) as HTMLElement).outerHTML,\n height: m.height,\n element: el,\n breakBefore: m.breakBefore,\n breakAfter: m.breakAfter,\n };\n });\n\n // Paginate with splitting\n const pages: string[][] = [];\n let currentPage: string[] = [];\n let currentHeight = 0;\n\n let i = 0;\n while (i < queue.length) {\n const block = queue[i];\n\n // Handle break-before\n if (block.breakBefore && currentPage.length > 0) {\n pages.push(currentPage);\n currentPage = [];\n currentHeight = 0;\n }\n\n const remainingSpace = contentHeight - currentHeight;\n\n if (block.height <= remainingSpace) {\n // Block fits entirely on current page\n currentPage.push(block.html);\n currentHeight += block.height;\n } else if (currentPage.length > 0 && remainingSpace >= 60) {\n // Current page has content and enough remaining space to try a split\n const splitResult = trySplitBlock(block.element, remainingSpace);\n if (splitResult) {\n // First half goes on current page\n currentPage.push(splitResult.firstHTML);\n pages.push(currentPage);\n currentPage = [];\n currentHeight = 0;\n\n // Measure the second half and insert it into the queue\n const tempEl = document.createElement('div');\n tempEl.innerHTML = splitResult.secondHTML;\n const newElement = tempEl.firstElementChild as HTMLElement;\n wrapper.appendChild(newElement);\n\n const rect = newElement.getBoundingClientRect();\n const style = window.getComputedStyle(newElement);\n const marginTop = parseFloat(style.marginTop) || 0;\n const marginBottom = parseFloat(style.marginBottom) || 0;\n\n queue.splice(i + 1, 0, {\n html: newElement.outerHTML,\n height: rect.height + marginTop + marginBottom,\n element: newElement,\n breakBefore: false,\n breakAfter: block.breakAfter,\n });\n } else {\n // Can't split — move to next page\n pages.push(currentPage);\n currentPage = [block.html];\n currentHeight = block.height;\n }\n } else if (currentPage.length > 0) {\n // Not enough remaining space — move block to next page\n pages.push(currentPage);\n currentPage = [block.html];\n currentHeight = block.height;\n } else {\n // Block is first on the page and taller than page — try to split\n const splitResult = trySplitBlock(block.element, contentHeight);\n if (splitResult) {\n currentPage.push(splitResult.firstHTML);\n pages.push(currentPage);\n currentPage = [];\n currentHeight = 0;\n\n // Measure and queue the second half\n const tempEl = document.createElement('div');\n tempEl.innerHTML = splitResult.secondHTML;\n const newElement = tempEl.firstElementChild as HTMLElement;\n wrapper.appendChild(newElement);\n\n const rect = newElement.getBoundingClientRect();\n const style = window.getComputedStyle(newElement);\n const marginTop = parseFloat(style.marginTop) || 0;\n const marginBottom = parseFloat(style.marginBottom) || 0;\n\n queue.splice(i + 1, 0, {\n html: newElement.outerHTML,\n height: rect.height + marginTop + marginBottom,\n element: newElement,\n breakBefore: false,\n breakAfter: block.breakAfter,\n });\n } else {\n // Can't split — place on its own page (overflows)\n currentPage.push(block.html);\n pages.push(currentPage);\n currentPage = [];\n currentHeight = 0;\n }\n }\n\n // Handle break-after\n if (block.breakAfter && currentPage.length > 0) {\n pages.push(currentPage);\n currentPage = [];\n currentHeight = 0;\n }\n\n i++;\n }\n\n // Push the last page\n if (currentPage.length > 0) {\n pages.push(currentPage);\n }\n if (pages.length === 0) {\n pages.push([]);\n }\n\n mc.innerHTML = '';\n\n const pageData: PageData[] = pages.map((blocks) => ({ blocks }));\n const paginationResult: PaginationResult = {\n pages: pages.map((_, idx) => ({ blockIndices: Array.from({ length: pages[idx].length }, (__, j) => j) })),\n pageCount: pages.length,\n };\n\n pagesRef.current = pageData;\n setPages(pageData);\n onPaginationChange?.(paginationResult);\n onContentChangeRef.current?.(htmlContent);\n }, [css, layoutEngine, onPaginationChange]);\n\n // ----------------------------------------------------------\n // Live re-pagination — runs after user edits change block sizes\n // ----------------------------------------------------------\n // 1. Collect block HTML from the live DOM\n // 2. Recombine any previously-split blocks\n // 3. Re-paginate with splitting (uses paginateHTML)\n // ----------------------------------------------------------\n\n const rePaginateFromDOM = useCallback(() => {\n if (!pagesContainerRef.current || !measureRef.current) return;\n\n const container = pagesContainerRef.current;\n\n // Save cursor position before we potentially re-render\n const cursor = saveCursorPosition(container);\n\n // Collect block HTML from the live DOM\n const contentDivs = container.querySelectorAll('.dopecanvas-block-content');\n const rawBlockHTMLs: string[] = [];\n contentDivs.forEach((contentDiv) => {\n const div = contentDiv as HTMLElement;\n const children = div.children;\n \n if (children.length === 0) {\n // Empty block - skip\n return;\n } else if (children.length === 1) {\n // Single child - use its outerHTML (original behavior)\n rawBlockHTMLs.push((children[0] as HTMLElement).outerHTML);\n } else {\n // Multiple children (user pressed Enter and created new lines)\n // Wrap them in a div to keep as one block\n const wrapper = document.createElement('div');\n wrapper.innerHTML = div.innerHTML;\n rawBlockHTMLs.push(wrapper.outerHTML);\n }\n });\n if (rawBlockHTMLs.length === 0) return;\n\n // Recombine any previously-split blocks before re-measuring\n const recombined = recombineSplitBlocks(rawBlockHTMLs);\n const htmlContent = recombined.join('\\n');\n\n // Set up hidden measure container\n const mc = measureRef.current;\n const contentWidth = layoutEngine.getContentAreaWidth();\n const contentHeight = layoutEngine.getContentAreaHeight();\n mc.style.width = `${contentWidth}px`;\n mc.style.position = 'absolute';\n mc.style.left = '-9999px';\n mc.style.top = '0';\n mc.style.visibility = 'hidden';\n mc.innerHTML = '';\n\n if (css) {\n const styleEl = document.createElement('style');\n styleEl.textContent = css;\n mc.appendChild(styleEl);\n }\n\n const measureWrapper = document.createElement('div');\n measureWrapper.innerHTML = htmlContent;\n mc.appendChild(measureWrapper);\n\n // Measure all blocks\n const measurements = layoutEngine.measureBlocks(measureWrapper);\n\n // Build queue for splitting pagination\n interface QueueItem {\n html: string;\n height: number;\n element: HTMLElement;\n breakBefore: boolean;\n breakAfter: boolean;\n }\n\n const queue: QueueItem[] = measurements.map((m) => ({\n html: (m.element.cloneNode(true) as HTMLElement).outerHTML,\n height: m.height,\n element: m.element,\n breakBefore: m.breakBefore,\n breakAfter: m.breakAfter,\n }));\n\n // Paginate with splitting (same algorithm as paginateHTML)\n const pages: string[][] = [];\n let currentPage: string[] = [];\n let currentPageHeight = 0;\n\n let idx = 0;\n while (idx < queue.length) {\n const block = queue[idx];\n\n if (block.breakBefore && currentPage.length > 0) {\n pages.push(currentPage);\n currentPage = [];\n currentPageHeight = 0;\n }\n\n const remainingSpace = contentHeight - currentPageHeight;\n\n if (block.height <= remainingSpace) {\n currentPage.push(block.html);\n currentPageHeight += block.height;\n } else if (currentPage.length > 0 && remainingSpace >= 60) {\n const splitResult = trySplitBlock(block.element, remainingSpace);\n if (splitResult) {\n currentPage.push(splitResult.firstHTML);\n pages.push(currentPage);\n currentPage = [];\n currentPageHeight = 0;\n\n const tempEl = document.createElement('div');\n tempEl.innerHTML = splitResult.secondHTML;\n const newElement = tempEl.firstElementChild as HTMLElement;\n measureWrapper.appendChild(newElement);\n\n const rect = newElement.getBoundingClientRect();\n const st = window.getComputedStyle(newElement);\n const mt = parseFloat(st.marginTop) || 0;\n const mb = parseFloat(st.marginBottom) || 0;\n\n queue.splice(idx + 1, 0, {\n html: newElement.outerHTML,\n height: rect.height + mt + mb,\n element: newElement,\n breakBefore: false,\n breakAfter: block.breakAfter,\n });\n } else {\n pages.push(currentPage);\n currentPage = [block.html];\n currentPageHeight = block.height;\n }\n } else if (currentPage.length > 0) {\n pages.push(currentPage);\n currentPage = [block.html];\n currentPageHeight = block.height;\n } else {\n const splitResult = trySplitBlock(block.element, contentHeight);\n if (splitResult) {\n currentPage.push(splitResult.firstHTML);\n pages.push(currentPage);\n currentPage = [];\n currentPageHeight = 0;\n\n const tempEl = document.createElement('div');\n tempEl.innerHTML = splitResult.secondHTML;\n const newElement = tempEl.firstElementChild as HTMLElement;\n measureWrapper.appendChild(newElement);\n\n const rect = newElement.getBoundingClientRect();\n const st = window.getComputedStyle(newElement);\n const mt = parseFloat(st.marginTop) || 0;\n const mb = parseFloat(st.marginBottom) || 0;\n\n queue.splice(idx + 1, 0, {\n html: newElement.outerHTML,\n height: rect.height + mt + mb,\n element: newElement,\n breakBefore: false,\n breakAfter: block.breakAfter,\n });\n } else {\n currentPage.push(block.html);\n pages.push(currentPage);\n currentPage = [];\n currentPageHeight = 0;\n }\n }\n\n if (block.breakAfter && currentPage.length > 0) {\n pages.push(currentPage);\n currentPage = [];\n currentPageHeight = 0;\n }\n\n idx++;\n }\n\n if (currentPage.length > 0) pages.push(currentPage);\n if (pages.length === 0) pages.push([]);\n\n mc.innerHTML = '';\n\n // Build new page data\n const newPageData: PageData[] = pages.map((blocks) => ({ blocks }));\n\n // Only re-render if the PAGE STRUCTURE changed (blocks moved between pages)\n // NOT when content within a block changes - the live DOM already has correct content\n const oldDist = pagesRef.current.map((p) => p.blocks.length);\n const newDist = newPageData.map((p) => p.blocks.length);\n const pageStructureChanged =\n oldDist.length !== newDist.length ||\n oldDist.some((count, i) => count !== newDist[i]);\n\n if (pageStructureChanged) {\n isRePaginatingRef.current = true;\n pendingCursorRef.current = cursor;\n pagesRef.current = newPageData;\n setPages(newPageData);\n onPaginationChange?.({\n pages: pages.map(() => ({ blockIndices: [] })),\n pageCount: pages.length,\n });\n } else {\n // Update the ref without triggering re-render\n // This keeps the saved HTML in sync for getHTML() calls\n pagesRef.current = newPageData;\n }\n }, [css, layoutEngine, onPaginationChange]);\n\n // Keep a stable ref so the MutationObserver closure always calls the latest version\n const rePaginateFromDOMRef = useRef(rePaginateFromDOM);\n rePaginateFromDOMRef.current = rePaginateFromDOM;\n\n // ----------------------------------------------------------\n // Pagination — runs on initial load and config changes\n // ----------------------------------------------------------\n\n const runPagination = useCallback(() => {\n // Parse the LLM-authored HTML.\n // An LLM may produce a full document (<!DOCTYPE html><html><head>\n // <style>…</style></head><body>…</body></html>) or a plain fragment.\n // DOMParser handles both: for fragments it wraps in html/body;\n // for full documents it parses normally. We then move any <head>\n // styles/links into the body so they participate in pagination\n // as zero-height blocks and their styles apply to the content.\n const parsed = new DOMParser().parseFromString(html, 'text/html');\n\n // Rescue <style> and <link rel=\"stylesheet\"> from <head>\n parsed.head\n .querySelectorAll('style, link[rel=\"stylesheet\"]')\n .forEach((el) => {\n parsed.body.insertBefore(el, parsed.body.firstChild);\n });\n\n paginateHTML(parsed.body.innerHTML);\n }, [html, paginateHTML]);\n\n // Run pagination when html or pageConfig changes\n useEffect(() => {\n runPagination();\n }, [runPagination]);\n\n // ----------------------------------------------------------\n // Block management — add / delete / edit HTML\n // ----------------------------------------------------------\n\n /** Collect all block outerHTMLs from the live DOM */\n const collectBlocksFromDOM = useCallback((): string[] => {\n if (!pagesContainerRef.current) return [];\n const contentDivs = pagesContainerRef.current.querySelectorAll(\n '.dopecanvas-block-content'\n );\n const blocks: string[] = [];\n contentDivs.forEach((div) => {\n const child = div.firstElementChild as HTMLElement;\n if (child) blocks.push(child.outerHTML);\n });\n return blocks;\n }, []);\n\n /** Add a new empty block below the given global index */\n const handleAddBlock = useCallback(\n (globalIndex: number) => {\n const blocks = collectBlocksFromDOM();\n blocks.splice(\n globalIndex + 1,\n 0,\n '<p style=\"min-height: 1.5em; line-height: 1.6;\"> </p>'\n );\n setHoveredBlockIndex(null);\n paginateHTML(blocks.join('\\n'));\n },\n [collectBlocksFromDOM, paginateHTML]\n );\n\n /** Delete the block at the given global index */\n const handleDeleteBlock = useCallback(\n (globalIndex: number) => {\n const blocks = collectBlocksFromDOM();\n if (blocks.length <= 1) return; // keep at least one block\n blocks.splice(globalIndex, 1);\n setHoveredBlockIndex(null);\n paginateHTML(blocks.join('\\n'));\n },\n [collectBlocksFromDOM, paginateHTML]\n );\n\n /** Open the HTML source editor for the given block */\n const handleOpenEditor = useCallback(\n (globalIndex: number) => {\n const blocks = collectBlocksFromDOM();\n if (globalIndex < blocks.length) {\n setEditingBlockIndex(globalIndex);\n setEditingHTML(blocks[globalIndex]);\n }\n },\n [collectBlocksFromDOM]\n );\n\n /** Save edited HTML and re-paginate */\n const handleSaveHTML = useCallback(\n (newHTML: string) => {\n if (editingBlockIndex === null) return;\n const blocks = collectBlocksFromDOM();\n if (editingBlockIndex < blocks.length) {\n blocks[editingBlockIndex] = newHTML;\n }\n setEditingBlockIndex(null);\n setEditingHTML('');\n setHoveredBlockIndex(null);\n paginateHTML(blocks.join('\\n'));\n },\n [editingBlockIndex, collectBlocksFromDOM, paginateHTML]\n );\n\n /** Cancel the HTML editor */\n const handleCancelEditor = useCallback(() => {\n setEditingBlockIndex(null);\n setEditingHTML('');\n }, []);\n\n // ----------------------------------------------------------\n // Page break insertion\n // ----------------------------------------------------------\n\n /** Insert a page break after the block containing the cursor, or at the end */\n const insertPageBreak = useCallback(() => {\n const PAGE_BREAK_HTML = '<div style=\"break-before: page;\"></div>';\n const blocks = collectBlocksFromDOM();\n\n // Try to find which block the cursor is in\n let insertIdx = blocks.length; // default: append at end\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0 && pagesContainerRef.current) {\n const anchorNode = sel.getRangeAt(0).startContainer;\n const wrappers = Array.from(\n pagesContainerRef.current.querySelectorAll('.dopecanvas-block-content')\n );\n const cursorBlockIdx = wrappers.findIndex((w) => w.contains(anchorNode));\n if (cursorBlockIdx !== -1) {\n insertIdx = cursorBlockIdx + 1;\n }\n }\n\n blocks.splice(insertIdx, 0, PAGE_BREAK_HTML);\n setHoveredBlockIndex(null);\n paginateHTML(blocks.join('\\n'));\n }, [collectBlocksFromDOM, paginateHTML]);\n\n // Expose methods to parent via ref\n useImperativeHandle(ref, () => ({\n insertPageBreak,\n }), [insertPageBreak]);\n\n // ----------------------------------------------------------\n // After pages render: make editable + observe changes\n // ----------------------------------------------------------\n\n useEffect(() => {\n const container = pagesContainerRef.current;\n if (!container) return;\n\n // Disconnect previous observer\n if (mutationObserverRef.current) {\n mutationObserverRef.current.disconnect();\n }\n\n // Make table cells individually editable.\n // Non-table blocks get contentEditable from their React prop;\n // tables need cell-level editability so users can't break table structure.\n const blockContents = container.querySelectorAll('.dopecanvas-block-content');\n blockContents.forEach((contentDiv) => {\n const child = contentDiv.firstElementChild as HTMLElement;\n if (!child) return;\n\n if (child.tagName === 'TABLE') {\n const cells = child.querySelectorAll('td, th');\n cells.forEach((cell) => {\n (cell as HTMLElement).contentEditable = 'true';\n });\n }\n });\n\n // Execute <script> tags embedded in the LLM-authored HTML.\n // This must run AFTER contentEditable setup so scripts can override\n // editability on specific cells (e.g. formula cells).\n const activatedScripts = activateScripts(container);\n\n // Set up MutationObserver — collects HTML and re-paginates when needed\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new MutationObserver(() => {\n if (isRePaginatingRef.current) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n // Use longer debounce to avoid erratic behavior during normal typing\n // Re-pagination only needs to happen when content size changes significantly\n debounceTimer = setTimeout(() => {\n collectHTMLFromDOM();\n rePaginateFromDOMRef.current();\n }, 800);\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: false,\n });\n\n mutationObserverRef.current = observer;\n\n // Restore cursor after a re-pagination re-render\n if (pendingCursorRef.current) {\n requestAnimationFrame(() => {\n if (pendingCursorRef.current && pagesContainerRef.current) {\n restoreCursorPosition(\n pagesContainerRef.current,\n pendingCursorRef.current\n );\n pendingCursorRef.current = null;\n }\n isRePaginatingRef.current = false;\n });\n } else {\n isRePaginatingRef.current = false;\n }\n\n return () => {\n observer.disconnect();\n activatedScripts.forEach((s) => s.remove());\n if (debounceTimer) clearTimeout(debounceTimer);\n };\n }, [pages, collectHTMLFromDOM]);\n\n // ----------------------------------------------------------\n // Render\n // ----------------------------------------------------------\n\n return (\n <div className=\"dopecanvas-paged-view\" style={scrollContainerStyle}>\n {/* Hidden measurement container */}\n <div ref={measureRef} aria-hidden=\"true\" />\n\n {/* Visible pages */}\n <div ref={pagesContainerRef} style={pagesWrapperStyle}>\n {css && <style dangerouslySetInnerHTML={{ __html: css }} />}\n {pages.map((pageData, pageIndex) => {\n // Compute this page's starting global block index\n const pageStartIdx = pages\n .slice(0, pageIndex)\n .reduce((sum, p) => sum + p.blocks.length, 0);\n\n return (\n <Page\n key={pageIndex}\n dimensions={dimensions}\n margins={pageConfig.margins}\n pageNumber={pageIndex + 1}\n totalPages={pages.length}\n >\n {pageData.blocks.map((blockHTML, blockIndex) => {\n const globalIdx = pageStartIdx + blockIndex;\n const lower = blockHTML.trim().toLowerCase();\n const isEditable =\n !lower.startsWith('<script') && !lower.startsWith('<style');\n\n const isTable = lower.startsWith('<table');\n const isPageBreak = isPageBreakBlock(blockHTML);\n\n // Page break indicator (when showPageBreaks is on)\n if (isPageBreak && showPageBreaks) {\n return (\n <div\n key={`${pageIndex}-${blockIndex}`}\n className=\"dopecanvas-block-wrapper\"\n style={{ position: 'relative' }}\n >\n {/* Hidden original block so DOM collection still picks it up */}\n <div\n className=\"dopecanvas-block-content\"\n style={{ display: 'none' }}\n dangerouslySetInnerHTML={{ __html: blockHTML }}\n />\n {/* Visual indicator */}\n <div style={pageBreakIndicatorStyle}>\n <span style={pageBreakLineStyle} />\n <span style={pageBreakLabelStyle}>Page Break</span>\n <span style={pageBreakLineStyle} />\n <button\n type=\"button\"\n title=\"Remove page break\"\n onClick={() => handleDeleteBlock(globalIdx)}\n onMouseDown={(e) => e.preventDefault()}\n style={pageBreakRemoveBtnStyle}\n >\n ✕\n </button>\n </div>\n </div>\n );\n }\n\n // Page break block when indicator is hidden — still render\n // the hidden block content so DOM collection works\n if (isPageBreak) {\n return (\n <div\n key={`${pageIndex}-${blockIndex}`}\n className=\"dopecanvas-block-wrapper\"\n style={{ position: 'relative' }}\n >\n <div\n className=\"dopecanvas-block-content\"\n dangerouslySetInnerHTML={{ __html: blockHTML }}\n />\n </div>\n );\n }\n\n return (\n <div\n key={`${pageIndex}-${blockIndex}`}\n className=\"dopecanvas-block-wrapper\"\n style={{ position: 'relative' }}\n onMouseEnter={() => {\n if (!isEditable) return;\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n setHoveredBlockIndex(globalIdx);\n }}\n onMouseLeave={() => {\n hideTimeoutRef.current = setTimeout(() => {\n setHoveredBlockIndex((prev) =>\n prev === globalIdx ? null : prev\n );\n }, 250);\n }}\n >\n <MemoizedBlockContent\n html={blockHTML}\n isEditable={isEditable}\n isTable={isTable}\n />\n {isEditable && (\n <BlockToolbar\n visible={hoveredBlockIndex === globalIdx}\n onAddBelow={() => handleAddBlock(globalIdx)}\n onEditHTML={() => handleOpenEditor(globalIdx)}\n onDelete={() => handleDeleteBlock(globalIdx)}\n />\n )}\n </div>\n );\n })}\n </Page>\n );\n })}\n\n {/* Show at least one empty page if no content */}\n {pages.length === 0 && (\n <Page\n dimensions={dimensions}\n margins={pageConfig.margins}\n pageNumber={1}\n totalPages={1}\n >\n <div\n contentEditable=\"true\"\n style={{ minHeight: '1em', outline: 'none' }}\n data-placeholder=\"Start typing...\"\n />\n </Page>\n )}\n </div>\n\n {/* HTML Editor Modal */}\n {editingBlockIndex !== null && (\n <HTMLEditorModal\n html={editingHTML}\n onSave={handleSaveHTML}\n onCancel={handleCancelEditor}\n />\n )}\n </div>\n );\n});\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst scrollContainerStyle: React.CSSProperties = {\n flex: 1,\n overflow: 'auto',\n backgroundColor: '#e8e8e8',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n};\n\nconst pagesWrapperStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: '24px',\n padding: '24px 0',\n};\n\n// Page break indicator styles\nconst pageBreakIndicatorStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '6px 0',\n userSelect: 'none',\n};\n\nconst pageBreakLineStyle: React.CSSProperties = {\n flex: 1,\n height: 0,\n borderTop: '2px dashed #b0b0b0',\n};\n\nconst pageBreakLabelStyle: React.CSSProperties = {\n fontSize: 11,\n fontWeight: 600,\n color: '#888',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n whiteSpace: 'nowrap',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n};\n\nconst pageBreakRemoveBtnStyle: React.CSSProperties = {\n width: 20,\n height: 20,\n border: '1px solid #ccc',\n borderRadius: 4,\n backgroundColor: '#fff',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: 11,\n color: '#999',\n padding: 0,\n flexShrink: 0,\n lineHeight: 1,\n};\n","// ============================================================\n// PageLayoutEngine — Pagination algorithm\n// ============================================================\n// Pure TypeScript class with no React dependency.\n// Takes a container of block elements, measures them, and\n// distributes them across fixed-size pages.\n// ============================================================\n\nimport type {\n PageConfig,\n PageDimensions,\n PageSizeName,\n PaginationResult,\n BlockMeasurement,\n} from './types';\nimport {\n PAGE_SIZE_PRESETS,\n DEFAULT_PAGE_CONFIG,\n} from './types';\n\nexport class PageLayoutEngine {\n private config: PageConfig;\n\n constructor(config: PageConfig = DEFAULT_PAGE_CONFIG) {\n this.config = { ...config };\n }\n\n // ----------------------------------------------------------\n // Config accessors\n // ----------------------------------------------------------\n\n getConfig(): PageConfig {\n return { ...this.config };\n }\n\n setConfig(config: Partial<PageConfig>): void {\n if (config.size !== undefined) {\n this.config.size = config.size;\n }\n if (config.margins !== undefined) {\n this.config.margins = { ...config.margins };\n }\n }\n\n /** Resolve page size name to pixel dimensions */\n getPageDimensions(): PageDimensions {\n if (typeof this.config.size === 'string') {\n return PAGE_SIZE_PRESETS[this.config.size as PageSizeName];\n }\n return this.config.size;\n }\n\n /** Usable content area height (page height minus top+bottom margins) */\n getContentAreaHeight(): number {\n const dims = this.getPageDimensions();\n return dims.height - this.config.margins.top - this.config.margins.bottom;\n }\n\n /** Usable content area width (page width minus left+right margins) */\n getContentAreaWidth(): number {\n const dims = this.getPageDimensions();\n return dims.width - this.config.margins.left - this.config.margins.right;\n }\n\n // ----------------------------------------------------------\n // Measurement\n // ----------------------------------------------------------\n\n /**\n * Measure all direct child block elements of the container.\n * The container should be styled to match the content area width\n * so that measurements reflect actual rendered heights.\n */\n measureBlocks(container: HTMLElement): BlockMeasurement[] {\n const children = Array.from(container.children) as HTMLElement[];\n const measurements: BlockMeasurement[] = [];\n\n for (let i = 0; i < children.length; i++) {\n const el = children[i];\n const style = window.getComputedStyle(el);\n\n // Check for CSS break-before / break-after\n const breakBefore =\n style.getPropertyValue('break-before') === 'page' ||\n style.getPropertyValue('page-break-before') === 'always';\n const breakAfter =\n style.getPropertyValue('break-after') === 'page' ||\n style.getPropertyValue('page-break-after') === 'always';\n\n // Use getBoundingClientRect for precise measurement including margins\n const rect = el.getBoundingClientRect();\n const marginTop = parseFloat(style.marginTop) || 0;\n const marginBottom = parseFloat(style.marginBottom) || 0;\n const totalHeight = rect.height + marginTop + marginBottom;\n\n measurements.push({\n index: i,\n height: totalHeight,\n element: el,\n breakBefore,\n breakAfter,\n });\n }\n\n return measurements;\n }\n\n // ----------------------------------------------------------\n // Pagination\n // ----------------------------------------------------------\n\n /**\n * Paginate: distribute measured blocks across pages.\n * \n * Algorithm:\n * 1. Walk blocks sequentially\n * 2. Accumulate height on current page\n * 3. When a block would overflow, start a new page\n * 4. Respect break-before / break-after CSS\n * 5. If a single block is taller than a page, give it its own page\n */\n paginate(measurements: BlockMeasurement[]): PaginationResult {\n if (measurements.length === 0) {\n return { pages: [{ blockIndices: [] }], pageCount: 1 };\n }\n\n const contentHeight = this.getContentAreaHeight();\n const pages: { blockIndices: number[] }[] = [];\n let currentPage: number[] = [];\n let currentHeight = 0;\n\n for (let i = 0; i < measurements.length; i++) {\n const block = measurements[i];\n\n // Force a new page if break-before is set (and current page has content)\n if (block.breakBefore && currentPage.length > 0) {\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n\n // Check if adding this block would overflow the current page\n if (currentHeight + block.height > contentHeight && currentPage.length > 0) {\n // Current page is full — start a new one\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n\n // Add block to current page\n currentPage.push(block.index);\n currentHeight += block.height;\n\n // Force a new page after this block if break-after is set\n if (block.breakAfter) {\n pages.push({ blockIndices: currentPage });\n currentPage = [];\n currentHeight = 0;\n }\n }\n\n // Push the last page if it has content\n if (currentPage.length > 0) {\n pages.push({ blockIndices: currentPage });\n }\n\n // Ensure at least one page\n if (pages.length === 0) {\n pages.push({ blockIndices: [] });\n }\n\n return {\n pages,\n pageCount: pages.length,\n };\n }\n}\n","// ============================================================\n// EditableManager — contentEditable integration\n// ============================================================\n// Manages making block elements editable, tracking changes\n// via MutationObserver, and providing undo/redo.\n// ============================================================\n\nimport type { UndoSnapshot, Unsubscribe, ToolbarContext } from './types';\n\nexport type ChangeCallback = () => void;\nexport type ContextChangeCallback = (context: ToolbarContext) => void;\n\nexport class EditableManager {\n private observer: MutationObserver | null = null;\n private changeCallbacks: Set<ChangeCallback> = new Set();\n private contextCallbacks: Set<ContextChangeCallback> = new Set();\n private undoStack: UndoSnapshot[] = [];\n private redoStack: UndoSnapshot[] = [];\n private container: HTMLElement | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private selectionHandler: (() => void) | null = null;\n private currentContext: ToolbarContext = 'none';\n\n private static readonly MAX_UNDO_STACK = 100;\n private static readonly DEBOUNCE_MS = 150;\n\n // ----------------------------------------------------------\n // Lifecycle\n // ----------------------------------------------------------\n\n /**\n * Attach to a container element. Sets up contentEditable on\n * all direct child blocks and starts observing changes.\n */\n attach(container: HTMLElement): void {\n this.detach(); // Clean up any previous attachment\n this.container = container;\n\n // Make all direct children editable\n this.makeChildrenEditable(container);\n\n // Take initial snapshot for undo\n this.pushUndoSnapshot();\n\n // Start observing mutations\n this.observer = new MutationObserver(this.handleMutations);\n this.observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: ['style', 'class'],\n });\n\n // Listen for selection changes to detect context\n this.selectionHandler = this.handleSelectionChange.bind(this);\n document.addEventListener('selectionchange', this.selectionHandler);\n }\n\n /**\n * Detach from the container. Stop observing and clean up.\n */\n detach(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n if (this.selectionHandler) {\n document.removeEventListener('selectionchange', this.selectionHandler);\n this.selectionHandler = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.container = null;\n }\n\n // ----------------------------------------------------------\n // Make elements editable\n // ----------------------------------------------------------\n\n /**\n * Set contentEditable on all direct children of the container.\n * Table cells get individual editability; other blocks are editable as wholes.\n */\n makeChildrenEditable(container: HTMLElement): void {\n const children = Array.from(container.children) as HTMLElement[];\n for (const child of children) {\n if (child.tagName === 'TABLE') {\n // For tables, make individual cells editable (not the table itself)\n this.makeTableCellsEditable(child);\n } else {\n child.contentEditable = 'true';\n }\n }\n }\n\n private makeTableCellsEditable(table: HTMLElement): void {\n const cells = table.querySelectorAll('td, th');\n cells.forEach((cell) => {\n (cell as HTMLElement).contentEditable = 'true';\n });\n }\n\n // ----------------------------------------------------------\n // Mutation handling\n // ----------------------------------------------------------\n\n private handleMutations = (_mutations: MutationRecord[]): void => {\n // Debounce to avoid excessive re-pagination\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n this.debounceTimer = setTimeout(() => {\n this.pushUndoSnapshot();\n this.notifyChange();\n }, EditableManager.DEBOUNCE_MS);\n };\n\n // ----------------------------------------------------------\n // Selection / context detection\n // ----------------------------------------------------------\n\n private handleSelectionChange(): void {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0 || !this.container) {\n this.setContext('none');\n return;\n }\n\n const range = selection.getRangeAt(0);\n const node = range.startContainer;\n\n // Walk up from the selection to find context\n let current: Node | null = node;\n while (current && current !== this.container) {\n if (current instanceof HTMLElement) {\n const tag = current.tagName;\n if (tag === 'TD' || tag === 'TH' || tag === 'TABLE') {\n this.setContext('table');\n return;\n }\n if (tag === 'IMG') {\n this.setContext('image');\n return;\n }\n if (current.dataset?.dopecanvasChart) {\n this.setContext('chart');\n return;\n }\n }\n current = current.parentNode;\n }\n\n // Default to text context if inside our container\n if (this.container.contains(node)) {\n this.setContext('text');\n } else {\n this.setContext('none');\n }\n }\n\n private setContext(ctx: ToolbarContext): void {\n if (ctx !== this.currentContext) {\n this.currentContext = ctx;\n this.contextCallbacks.forEach((cb) => cb(ctx));\n }\n }\n\n getContext(): ToolbarContext {\n return this.currentContext;\n }\n\n // ----------------------------------------------------------\n // Undo / Redo\n // ----------------------------------------------------------\n\n private pushUndoSnapshot(): void {\n if (!this.container) return;\n const html = this.container.innerHTML;\n const last = this.undoStack[this.undoStack.length - 1];\n // Don't push duplicates\n if (last && last.html === html) return;\n\n this.undoStack.push({ html, timestamp: Date.now() });\n // Clear redo stack on new change\n this.redoStack = [];\n // Limit stack size\n if (this.undoStack.length > EditableManager.MAX_UNDO_STACK) {\n this.undoStack.shift();\n }\n }\n\n undo(): boolean {\n if (!this.container || this.undoStack.length <= 1) return false;\n\n // Pop current state to redo stack\n const current = this.undoStack.pop()!;\n this.redoStack.push(current);\n\n // Restore previous state\n const previous = this.undoStack[this.undoStack.length - 1];\n this.pauseObserver(() => {\n this.container!.innerHTML = previous.html;\n this.makeChildrenEditable(this.container!);\n });\n this.notifyChange();\n return true;\n }\n\n redo(): boolean {\n if (!this.container || this.redoStack.length === 0) return false;\n\n const next = this.redoStack.pop()!;\n this.undoStack.push(next);\n\n this.pauseObserver(() => {\n this.container!.innerHTML = next.html;\n this.makeChildrenEditable(this.container!);\n });\n this.notifyChange();\n return true;\n }\n\n /** Temporarily disconnect observer to avoid feedback loops */\n private pauseObserver(fn: () => void): void {\n if (this.observer) {\n this.observer.disconnect();\n }\n fn();\n if (this.observer && this.container) {\n this.observer.observe(this.container, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: ['style', 'class'],\n });\n }\n }\n\n // ----------------------------------------------------------\n // Event callbacks\n // ----------------------------------------------------------\n\n onChange(callback: ChangeCallback): Unsubscribe {\n this.changeCallbacks.add(callback);\n return () => {\n this.changeCallbacks.delete(callback);\n };\n }\n\n onContextChange(callback: ContextChangeCallback): Unsubscribe {\n this.contextCallbacks.add(callback);\n return () => {\n this.contextCallbacks.delete(callback);\n };\n }\n\n private notifyChange(): void {\n this.changeCallbacks.forEach((cb) => cb());\n }\n\n // ----------------------------------------------------------\n // Formatting commands\n // ----------------------------------------------------------\n\n /**\n * Execute a formatting command on the current selection.\n * Uses document.execCommand for broad browser support.\n */\n execCommand(command: string, value?: string): boolean {\n return document.execCommand(command, false, value);\n }\n\n /** Check if a command is active for the current selection */\n queryCommandState(command: string): boolean {\n return document.queryCommandState(command);\n }\n\n /** Get the value of a command for the current selection */\n queryCommandValue(command: string): string {\n return document.queryCommandValue(command);\n }\n\n // ----------------------------------------------------------\n // Content access\n // ----------------------------------------------------------\n\n getHTML(): string {\n if (!this.container) return '';\n return this.container.innerHTML;\n }\n\n getPlainText(): string {\n if (!this.container) return '';\n return this.container.innerText || this.container.textContent || '';\n }\n}\n","// ============================================================\n// DopeCanvas — Main canvas component\n// ============================================================\n// The top-level React component that composes the paged view\n// and document engine together.\n//\n// The toolbar is NOT rendered by default. Instead, all toolbar\n// actions are exposed via a ref-based API (DopeCanvasHandle).\n// Consumers can build their own toolbar UI or use the provided\n// Toolbar components separately.\n//\n// CRITICAL DESIGN: Content edits from the user update a ref,\n// NOT state. This prevents React from re-rendering (and thus\n// destroying) the live editable DOM. Only page config changes\n// or external HTML loads trigger re-pagination.\n// ============================================================\n\nimport React, { useMemo, useCallback, useState, useRef, useEffect, useImperativeHandle, forwardRef } from 'react';\nimport { PagedView } from './PagedView';\nimport type { PagedViewHandle } from './PagedView';\nimport { PageLayoutEngine } from '../core/PageLayoutEngine';\nimport { EditableManager } from '../core/EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n} from '../core/types';\nimport { DEFAULT_PAGE_CONFIG } from '../core/types';\n\n// ----------------------------------------------------------\n// Public API handle exposed via ref\n// ----------------------------------------------------------\n\nexport interface DopeCanvasHandle {\n // Text formatting\n /** Execute a formatting command (e.g. 'bold', 'italic', 'fontSize') */\n execCommand: (command: string, value?: string) => boolean;\n /** Check if a command is active for the current selection */\n queryCommandState: (command: string) => boolean;\n /** Get the value of a command for the current selection */\n queryCommandValue: (command: string) => string;\n\n // Page configuration\n /** Get the current page configuration */\n getPageConfig: () => PageConfig;\n /** Update page configuration (size, margins). Triggers re-pagination. */\n setPageConfig: (config: Partial<PageConfig>) => void;\n /** Get the current number of pages */\n getPageCount: () => number;\n\n // Content access\n /** Get the current document HTML (reflects user edits) */\n getHTML: () => string;\n /** Get the document content as plain text */\n getPlainText: () => string;\n\n // Undo / Redo\n /** Undo the last edit. Returns false if nothing to undo. */\n undo: () => boolean;\n /** Redo the last undone edit. Returns false if nothing to redo. */\n redo: () => boolean;\n\n // Page breaks\n /** Insert a page break after the block at the cursor (or at end) */\n insertPageBreak: () => void;\n /** Show or hide visual page break indicators */\n setShowPageBreaks: (show: boolean) => void;\n /** Get the current show-page-breaks state */\n getShowPageBreaks: () => boolean;\n}\n\n// ----------------------------------------------------------\n// Component props\n// ----------------------------------------------------------\n\nexport interface DopeCanvasProps {\n /** Initial HTML content to load */\n html?: string;\n /** Optional CSS to inject alongside the HTML */\n css?: string;\n /** Rendering mode: paginated pages or continuous flow */\n renderMode?: 'paged' | 'flow';\n /** Page configuration */\n pageConfig?: PageConfig;\n /** Callback when content changes */\n onContentChange?: (html: string) => void;\n /** Callback when page config changes */\n onPageConfigChange?: (config: PageConfig) => void;\n /** Style overrides for the root container */\n style?: React.CSSProperties;\n}\n\nexport const DopeCanvas = forwardRef<DopeCanvasHandle, DopeCanvasProps>(({\n html = '',\n css,\n renderMode = 'paged',\n pageConfig: externalPageConfig,\n onContentChange,\n onPageConfigChange,\n style,\n}, ref) => {\n const [internalPageConfig, setInternalPageConfig] = useState<PageConfig>(\n externalPageConfig || DEFAULT_PAGE_CONFIG\n );\n const [paginationResult, setPaginationResult] = useState<PaginationResult>({\n pages: [],\n pageCount: 0,\n });\n // Store the latest HTML in a ref — edits update this without triggering re-render\n const currentHTMLRef = useRef(html);\n // Root container ref — used to read live DOM content in getHTML()\n const rootRef = useRef<HTMLDivElement>(null);\n // Flow-mode editable root\n const flowContentRef = useRef<HTMLDivElement>(null);\n // PagedView ref — used for page break insertion\n const pagedViewRef = useRef<PagedViewHandle>(null);\n\n // Page break visibility toggle\n const [showPageBreaks, setShowPageBreaks] = useState(false);\n\n // Stable callback ref for onContentChange so PagedView doesn't re-render\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n // Use external config if provided, otherwise internal\n const pageConfig = externalPageConfig || internalPageConfig;\n\n // Create engine instances (stable across renders)\n const layoutEngine = useMemo(() => new PageLayoutEngine(pageConfig), []);\n const editableManager = useMemo(() => new EditableManager(), []);\n\n // Update layout engine when config changes\n useEffect(() => {\n layoutEngine.setConfig(pageConfig);\n }, [pageConfig, layoutEngine]);\n\n // Handle content changes from editing — update ref only, no state\n const handleContentChange = useCallback(\n (newHTML: string) => {\n currentHTMLRef.current = newHTML;\n onContentChangeRef.current?.(newHTML);\n },\n []\n );\n\n // Handle page config changes\n const handlePageConfigChange = useCallback(\n (newConfig: Partial<PageConfig>) => {\n const updated = {\n ...pageConfig,\n ...newConfig,\n margins: {\n ...pageConfig.margins,\n ...(newConfig.margins || {}),\n },\n };\n setInternalPageConfig(updated);\n layoutEngine.setConfig(updated);\n onPageConfigChange?.(updated);\n },\n [pageConfig, layoutEngine, onPageConfigChange]\n );\n\n // Handle pagination updates\n const handlePaginationChange = useCallback((result: PaginationResult) => {\n setPaginationResult(result);\n }, []);\n\n // Keep flow-mode DOM in sync when external html changes (e.g. open document)\n useEffect(() => {\n if (renderMode !== 'flow' || !flowContentRef.current) return;\n // Use the latest known edited HTML when switching into flow mode.\n // Fall back to the incoming prop when no live content exists yet.\n const sourceHTML = currentHTMLRef.current || html;\n flowContentRef.current.innerHTML = sourceHTML;\n currentHTMLRef.current = sourceHTML;\n }, [html, renderMode]);\n\n // ----------------------------------------------------------\n // Expose API via ref\n // ----------------------------------------------------------\n\n useImperativeHandle(ref, () => ({\n execCommand: (command: string, value?: string) => {\n return editableManager.execCommand(command, value);\n },\n queryCommandState: (command: string) => {\n return editableManager.queryCommandState(command);\n },\n queryCommandValue: (command: string) => {\n return editableManager.queryCommandValue(command);\n },\n getPageConfig: () => ({ ...pageConfig }),\n setPageConfig: (config: Partial<PageConfig>) => {\n handlePageConfigChange(config);\n },\n getPageCount: () => paginationResult.pageCount,\n getHTML: () => {\n if (renderMode === 'flow' && flowContentRef.current) {\n const live = flowContentRef.current.innerHTML;\n currentHTMLRef.current = live;\n return live;\n }\n\n // Read directly from the live DOM so edits are never missed\n // (the MutationObserver in PagedView debounces updates to the ref,\n // so the ref can be stale if getHTML is called right after an edit)\n if (rootRef.current) {\n const contentDivs = rootRef.current.querySelectorAll(\n '.dopecanvas-block-content'\n );\n if (contentDivs.length > 0) {\n const parts: string[] = [];\n contentDivs.forEach((contentDiv) => {\n const div = contentDiv as HTMLElement;\n const children = div.children;\n \n if (children.length === 0) {\n // Empty block - skip\n return;\n } else if (children.length === 1) {\n // Single child - use its outerHTML (preserves original structure)\n parts.push((children[0] as HTMLElement).outerHTML);\n } else {\n // Multiple children (user pressed Enter and created new lines)\n // Wrap them in a div to keep as one block\n const wrapper = document.createElement('div');\n wrapper.innerHTML = div.innerHTML;\n parts.push(wrapper.outerHTML);\n }\n });\n if (parts.length > 0) {\n const freshHTML = parts.join('\\n');\n currentHTMLRef.current = freshHTML; // keep ref in sync\n return freshHTML;\n }\n }\n }\n return currentHTMLRef.current;\n },\n getPlainText: () => {\n if (renderMode === 'flow' && flowContentRef.current) {\n return (\n flowContentRef.current.innerText ||\n flowContentRef.current.textContent ||\n ''\n );\n }\n\n // Also read from live DOM for consistency\n const liveHTML = rootRef.current\n ? (() => {\n const divs = rootRef.current!.querySelectorAll('.dopecanvas-block-content');\n const parts: string[] = [];\n divs.forEach((contentDiv) => {\n const div = contentDiv as HTMLElement;\n const children = div.children;\n \n if (children.length === 0) {\n return;\n } else if (children.length === 1) {\n parts.push((children[0] as HTMLElement).outerHTML);\n } else {\n const wrapper = document.createElement('div');\n wrapper.innerHTML = div.innerHTML;\n parts.push(wrapper.outerHTML);\n }\n });\n return parts.length > 0 ? parts.join('\\n') : currentHTMLRef.current;\n })()\n : currentHTMLRef.current;\n const tmp = document.createElement('div');\n tmp.innerHTML = liveHTML;\n return tmp.innerText || tmp.textContent || '';\n },\n undo: () => editableManager.undo(),\n redo: () => editableManager.redo(),\n insertPageBreak: () => {\n if (renderMode === 'flow' && flowContentRef.current) {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n if (!flowContentRef.current.contains(range.startContainer)) return;\n const pageBreak = document.createElement('div');\n pageBreak.style.breakBefore = 'page';\n range.insertNode(pageBreak);\n range.setStartAfter(pageBreak);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n const updated = flowContentRef.current.innerHTML;\n currentHTMLRef.current = updated;\n onContentChangeRef.current?.(updated);\n return;\n }\n pagedViewRef.current?.insertPageBreak();\n },\n setShowPageBreaks: (show: boolean) => {\n setShowPageBreaks(show);\n },\n getShowPageBreaks: () => showPageBreaks,\n }), [editableManager, pageConfig, paginationResult.pageCount, handlePageConfigChange, showPageBreaks, renderMode]);\n\n return (\n <div\n ref={rootRef}\n className=\"dopecanvas-root\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n width: '100%',\n fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',\n ...style,\n }}\n >\n {/* Document view */}\n {renderMode === 'paged' ? (\n <PagedView\n ref={pagedViewRef}\n html={html}\n css={css}\n pageConfig={pageConfig}\n layoutEngine={layoutEngine}\n editableManager={editableManager}\n onContentChange={handleContentChange}\n onPaginationChange={handlePaginationChange}\n showPageBreaks={showPageBreaks}\n />\n ) : (\n <div style={flowContainerStyle}>\n {css && <style dangerouslySetInnerHTML={{ __html: css }} />}\n <div\n ref={flowContentRef}\n className=\"dopecanvas-flow-content\"\n contentEditable\n suppressContentEditableWarning\n style={flowContentStyle}\n onInput={() => {\n if (!flowContentRef.current) return;\n const updated = flowContentRef.current.innerHTML;\n currentHTMLRef.current = updated;\n onContentChangeRef.current?.(updated);\n }}\n />\n </div>\n )}\n </div>\n );\n});\n\nconst flowContainerStyle: React.CSSProperties = {\n flex: 1,\n overflow: 'auto',\n backgroundColor: '#f7f7f5',\n padding: '24px',\n};\n\nconst flowContentStyle: React.CSSProperties = {\n minHeight: '100%',\n outline: 'none',\n backgroundColor: '#fff',\n border: '1px solid #e5e7eb',\n borderRadius: 8,\n padding: '20px 24px',\n};\n","// ============================================================\n// DocumentEngine — Core orchestrator\n// ============================================================\n// Ties PageLayoutEngine and EditableManager together.\n// Manages the lifecycle: load → parse → paginate → edit → re-paginate\n// ============================================================\n\nimport { PageLayoutEngine } from './PageLayoutEngine';\nimport { EditableManager } from './EditableManager';\nimport type {\n PageConfig,\n PaginationResult,\n BlockMeasurement,\n Unsubscribe,\n} from './types';\nimport { DEFAULT_PAGE_CONFIG } from './types';\n\nexport class DocumentEngine {\n private layoutEngine: PageLayoutEngine;\n private editableManager: EditableManager;\n private sourceHTML: string = '';\n private sourceCSS: string = '';\n private measureContainer: HTMLElement | null = null;\n private contentContainer: HTMLElement | null = null;\n private paginationResult: PaginationResult = { pages: [], pageCount: 0 };\n private paginationCallbacks: Set<(result: PaginationResult) => void> = new Set();\n private changeCallbacks: Set<(html: string) => void> = new Set();\n\n constructor(config: PageConfig = DEFAULT_PAGE_CONFIG) {\n this.layoutEngine = new PageLayoutEngine(config);\n this.editableManager = new EditableManager();\n }\n\n // ----------------------------------------------------------\n // Accessors\n // ----------------------------------------------------------\n\n getLayoutEngine(): PageLayoutEngine {\n return this.layoutEngine;\n }\n\n getEditableManager(): EditableManager {\n return this.editableManager;\n }\n\n getPaginationResult(): PaginationResult {\n return this.paginationResult;\n }\n\n getSourceHTML(): string {\n return this.sourceHTML;\n }\n\n getPageConfig(): PageConfig {\n return this.layoutEngine.getConfig();\n }\n\n // ----------------------------------------------------------\n // Load HTML content\n // ----------------------------------------------------------\n\n /**\n * Load LLM-generated HTML (and optional CSS) into the engine.\n * This parses the HTML and prepares it for pagination.\n */\n loadHTML(html: string, css?: string): void {\n this.sourceHTML = html;\n this.sourceCSS = css || '';\n }\n\n // ----------------------------------------------------------\n // Attach to DOM containers\n // ----------------------------------------------------------\n\n /**\n * Set the measurement container — a hidden container styled\n * to match the content area width. Used for measuring block heights.\n */\n setMeasureContainer(el: HTMLElement): void {\n this.measureContainer = el;\n }\n\n /**\n * Set the content container — the visible container where\n * editable blocks live. The EditableManager attaches here.\n */\n setContentContainer(el: HTMLElement): void {\n if (this.contentContainer) {\n this.editableManager.detach();\n }\n this.contentContainer = el;\n }\n\n // ----------------------------------------------------------\n // Pagination cycle\n // ----------------------------------------------------------\n\n /**\n * Run the full pagination cycle:\n * 1. Inject HTML into measure container\n * 2. Inject CSS (via <style> tag)\n * 3. Measure all blocks\n * 4. Run pagination algorithm\n * 5. Return result for rendering\n */\n runPagination(): { result: PaginationResult; measurements: BlockMeasurement[] } {\n if (!this.measureContainer) {\n return {\n result: { pages: [{ blockIndices: [] }], pageCount: 1 },\n measurements: [],\n };\n }\n\n // Set up the measure container width to match content area\n const contentWidth = this.layoutEngine.getContentAreaWidth();\n this.measureContainer.style.width = `${contentWidth}px`;\n this.measureContainer.style.position = 'absolute';\n this.measureContainer.style.left = '-9999px';\n this.measureContainer.style.top = '0';\n this.measureContainer.style.visibility = 'hidden';\n\n // Inject CSS if provided\n let styleEl: HTMLStyleElement | null = null;\n if (this.sourceCSS) {\n styleEl = document.createElement('style');\n styleEl.textContent = this.sourceCSS;\n this.measureContainer.appendChild(styleEl);\n }\n\n // Inject HTML content\n const contentWrapper = document.createElement('div');\n contentWrapper.innerHTML = this.sourceHTML;\n this.measureContainer.appendChild(contentWrapper);\n\n // Measure blocks\n const measurements = this.layoutEngine.measureBlocks(contentWrapper);\n\n // Paginate\n this.paginationResult = this.layoutEngine.paginate(measurements);\n\n // Clean up measure container\n this.measureContainer.innerHTML = '';\n\n // Notify listeners\n this.paginationCallbacks.forEach((cb) => cb(this.paginationResult));\n\n return { result: this.paginationResult, measurements };\n }\n\n /**\n * Re-paginate using the current content container's live DOM.\n * Called after user edits.\n */\n rePaginate(): PaginationResult {\n if (!this.contentContainer) {\n return this.paginationResult;\n }\n\n // Measure from the live content\n const measurements = this.layoutEngine.measureBlocks(this.contentContainer);\n this.paginationResult = this.layoutEngine.paginate(measurements);\n\n // Notify\n this.paginationCallbacks.forEach((cb) => cb(this.paginationResult));\n return this.paginationResult;\n }\n\n // ----------------------------------------------------------\n // Attach editing\n // ----------------------------------------------------------\n\n /**\n * Attach the editable manager to the content container\n * and set up change listener for re-pagination.\n */\n attachEditing(container: HTMLElement): void {\n this.contentContainer = container;\n this.editableManager.attach(container);\n\n // Re-paginate on content change\n this.editableManager.onChange(() => {\n // Update source HTML from live DOM\n this.sourceHTML = container.innerHTML;\n this.changeCallbacks.forEach((cb) => cb(this.sourceHTML));\n });\n }\n\n // ----------------------------------------------------------\n // Page config\n // ----------------------------------------------------------\n\n setPageConfig(config: Partial<PageConfig>): void {\n this.layoutEngine.setConfig(config);\n }\n\n // ----------------------------------------------------------\n // Event listeners\n // ----------------------------------------------------------\n\n onPagination(callback: (result: PaginationResult) => void): Unsubscribe {\n this.paginationCallbacks.add(callback);\n return () => {\n this.paginationCallbacks.delete(callback);\n };\n }\n\n onChange(callback: (html: string) => void): Unsubscribe {\n this.changeCallbacks.add(callback);\n return () => {\n this.changeCallbacks.delete(callback);\n };\n }\n\n // ----------------------------------------------------------\n // Content access\n // ----------------------------------------------------------\n\n getHTML(): string {\n if (this.contentContainer) {\n return this.contentContainer.innerHTML;\n }\n return this.sourceHTML;\n }\n\n getPlainText(): string {\n if (this.contentContainer) {\n return this.contentContainer.innerText || this.contentContainer.textContent || '';\n }\n return '';\n }\n\n // ----------------------------------------------------------\n // Cleanup\n // ----------------------------------------------------------\n\n destroy(): void {\n this.editableManager.detach();\n this.paginationCallbacks.clear();\n this.changeCallbacks.clear();\n this.measureContainer = null;\n this.contentContainer = null;\n }\n}\n","// ============================================================\n// DocumentAPI — External programmatic interface\n// ============================================================\n// This class provides a clean API for external systems\n// (LLMs, database sync, parent applications) to interact\n// with a DopeCanvas document.\n// ============================================================\n\nimport type { PageConfig, PaginationResult, Unsubscribe } from '../core/types';\n\n/**\n * DocumentAPI provides a programmatic interface to the DopeCanvas\n * document. It wraps the internal state management and exposes\n * methods for loading, reading, and modifying document content.\n *\n * Usage:\n * const api = new DocumentAPI();\n * // Connect to a DopeCanvas instance (done internally by DopeCanvas component)\n * api.loadHTML('<h1>Hello</h1><p>World</p>');\n * api.onChange((html) => console.log('Content changed:', html));\n */\nexport class DocumentAPI {\n private _html: string = '';\n private _css: string = '';\n private _pageConfig: PageConfig | null = null;\n private _paginationResult: PaginationResult = { pages: [], pageCount: 0 };\n\n // Callbacks\n private _changeCallbacks: Set<(html: string) => void> = new Set();\n private _loadCallbacks: Set<(html: string, css?: string) => void> = new Set();\n private _pageConfigCallbacks: Set<(config: PageConfig) => void> = new Set();\n\n // Connector functions set by DopeCanvas component\n private _getHTMLFn: (() => string) | null = null;\n private _getPlainTextFn: (() => string) | null = null;\n\n // ----------------------------------------------------------\n // Content loading\n // ----------------------------------------------------------\n\n /**\n * Load HTML content into the canvas.\n * Optionally provide CSS to inject alongside.\n */\n loadHTML(html: string, css?: string): void {\n this._html = html;\n this._css = css || '';\n this._loadCallbacks.forEach((cb) => cb(html, css));\n }\n\n /**\n * Get the current document HTML content.\n * Reflects any user edits.\n */\n getHTML(): string {\n if (this._getHTMLFn) {\n return this._getHTMLFn();\n }\n return this._html;\n }\n\n /**\n * Get the document content as plain text.\n */\n getPlainText(): string {\n if (this._getPlainTextFn) {\n return this._getPlainTextFn();\n }\n // Fallback: strip HTML tags\n const tmp = document.createElement('div');\n tmp.innerHTML = this._html;\n return tmp.innerText || tmp.textContent || '';\n }\n\n // ----------------------------------------------------------\n // Event listeners\n // ----------------------------------------------------------\n\n /**\n * Listen for content changes (triggered by user edits).\n * Returns an unsubscribe function.\n */\n onChange(callback: (html: string) => void): Unsubscribe {\n this._changeCallbacks.add(callback);\n return () => {\n this._changeCallbacks.delete(callback);\n };\n }\n\n /**\n * Listen for load events (when loadHTML is called).\n */\n onLoad(callback: (html: string, css?: string) => void): Unsubscribe {\n this._loadCallbacks.add(callback);\n return () => {\n this._loadCallbacks.delete(callback);\n };\n }\n\n /**\n * Listen for page config changes.\n */\n onPageConfigChange(callback: (config: PageConfig) => void): Unsubscribe {\n this._pageConfigCallbacks.add(callback);\n return () => {\n this._pageConfigCallbacks.delete(callback);\n };\n }\n\n // ----------------------------------------------------------\n // Page operations\n // ----------------------------------------------------------\n\n /**\n * Get the current number of pages.\n */\n getPageCount(): number {\n return this._paginationResult.pageCount;\n }\n\n /**\n * Get the current page configuration.\n */\n getPageConfig(): PageConfig | null {\n return this._pageConfig;\n }\n\n /**\n * Set page configuration (size, margins).\n * Triggers re-pagination.\n */\n setPageConfig(config: Partial<PageConfig>): void {\n if (this._pageConfig) {\n this._pageConfig = {\n ...this._pageConfig,\n ...config,\n margins: {\n ...this._pageConfig.margins,\n ...(config.margins || {}),\n },\n };\n this._pageConfigCallbacks.forEach((cb) => cb(this._pageConfig!));\n }\n }\n\n // ----------------------------------------------------------\n // Element access (for future DB sync)\n // ----------------------------------------------------------\n\n /**\n * Query elements within the document by CSS selector.\n * Useful for targeting specific sections for database sync.\n */\n querySelectorAll(selector: string): Element[] {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n return Array.from(tmp.querySelectorAll(selector));\n }\n\n /**\n * Get the innerHTML of a specific element by its ID.\n */\n getElementContent(id: string): string | null {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n const el = tmp.querySelector(`#${id}`);\n return el ? el.innerHTML : null;\n }\n\n /**\n * Set the innerHTML of a specific element by its ID.\n * Re-loads the full document with the modification.\n */\n setElementContent(id: string, html: string): void {\n const tmp = document.createElement('div');\n tmp.innerHTML = this.getHTML();\n const el = tmp.querySelector(`#${id}`);\n if (el) {\n el.innerHTML = html;\n this.loadHTML(tmp.innerHTML, this._css);\n }\n }\n\n // ----------------------------------------------------------\n // Internal connectors (used by DopeCanvas component)\n // ----------------------------------------------------------\n\n /** @internal Called by DopeCanvas to wire up live content access */\n _connectGetHTML(fn: () => string): void {\n this._getHTMLFn = fn;\n }\n\n /** @internal Called by DopeCanvas to wire up plain text access */\n _connectGetPlainText(fn: () => string): void {\n this._getPlainTextFn = fn;\n }\n\n /** @internal Called by DopeCanvas when content changes */\n _notifyChange(html: string): void {\n this._html = html;\n this._changeCallbacks.forEach((cb) => cb(html));\n }\n\n /** @internal Called by DopeCanvas when pagination runs */\n _updatePagination(result: PaginationResult): void {\n this._paginationResult = result;\n }\n\n /** @internal Called by DopeCanvas when page config changes */\n _updatePageConfig(config: PageConfig): void {\n this._pageConfig = config;\n }\n}\n","// ============================================================\n// useSelectionContext — Track what element type is selected\n// ============================================================\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { ToolbarContext, FormattingState } from '../core/types';\nimport type { EditableManager } from '../core/EditableManager';\n\nexport function useSelectionContext(\n editableManager: EditableManager | null\n): ToolbarContext {\n const [context, setContext] = useState<ToolbarContext>('none');\n\n useEffect(() => {\n if (!editableManager) return;\n\n const unsub = editableManager.onContextChange((ctx) => {\n setContext(ctx);\n });\n\n return unsub;\n }, [editableManager]);\n\n return context;\n}\n\n// ----------------------------------------------------------\n// Default formatting state\n// ----------------------------------------------------------\n\nconst DEFAULT_FORMATTING_STATE: FormattingState = {\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n justifyLeft: false,\n justifyCenter: false,\n justifyRight: false,\n justifyFull: false,\n orderedList: false,\n unorderedList: false,\n superscript: false,\n subscript: false,\n fontName: '',\n fontSize: '3',\n foreColor: '#000000',\n backColor: '',\n formatBlock: 'p',\n};\n\n/**\n * Hook to query the current formatting state (bold, italic, font, etc.)\n * from the browser's selection. Updates reactively on every\n * `selectionchange` event.\n *\n * Returns the full `FormattingState` object — consumers can use any\n * fields they need for their own toolbar UI.\n */\nexport function useFormattingState(): FormattingState {\n const [state, setState] = useState<FormattingState>(DEFAULT_FORMATTING_STATE);\n\n const updateState = useCallback(() => {\n try {\n // Normalise the formatBlock value — browsers return inconsistently\n let formatBlock = document.queryCommandValue('formatBlock') || '';\n formatBlock = formatBlock.replace(/^<|>$/g, '').toLowerCase();\n if (!formatBlock || formatBlock === 'div') formatBlock = 'p';\n\n setState({\n // Toggle states\n bold: document.queryCommandState('bold'),\n italic: document.queryCommandState('italic'),\n underline: document.queryCommandState('underline'),\n strikethrough: document.queryCommandState('strikethrough'),\n justifyLeft: document.queryCommandState('justifyLeft'),\n justifyCenter: document.queryCommandState('justifyCenter'),\n justifyRight: document.queryCommandState('justifyRight'),\n justifyFull: document.queryCommandState('justifyFull'),\n orderedList: document.queryCommandState('insertOrderedList'),\n unorderedList: document.queryCommandState('insertUnorderedList'),\n superscript: document.queryCommandState('superscript'),\n subscript: document.queryCommandState('subscript'),\n // Value states\n fontName: document.queryCommandValue('fontName') || '',\n fontSize: document.queryCommandValue('fontSize') || '3',\n foreColor: document.queryCommandValue('foreColor') || '#000000',\n backColor: document.queryCommandValue('backColor') || '',\n formatBlock,\n });\n } catch {\n // queryCommand can throw in some edge cases — ignore\n }\n }, []);\n\n useEffect(() => {\n document.addEventListener('selectionchange', updateState);\n return () => {\n document.removeEventListener('selectionchange', updateState);\n };\n }, [updateState]);\n\n return state;\n}\n","// ============================================================\n// useSelectionSaver — Save / restore browser selection\n// ============================================================\n// Toolbar controls (selects, color pickers, buttons) steal focus\n// from contentEditable areas, clearing the browser selection.\n// This hook provides helpers to save the current range before\n// focus moves and restore it before executing a format command.\n//\n// Usage in a consumer toolbar:\n//\n// const { saveSelection, restoreAndExec } = useSelectionSaver();\n//\n// <select onMouseDown={saveSelection} onChange={(e) => {\n// restoreAndExec(() => canvasRef.current?.execCommand('fontSize', e.target.value));\n// }}>\n// ============================================================\n\nimport { useRef, useCallback } from 'react';\n\nexport interface SelectionSaver {\n /** Call on mouseDown of a toolbar control to snapshot the current selection */\n saveSelection: () => void;\n /** Restore a previously saved selection */\n restoreSelection: () => void;\n /** Restore the selection and then run a callback (convenience) */\n restoreAndExec: (fn: () => void) => void;\n}\n\n/**\n * Hook that provides save/restore helpers for the browser selection.\n * Use `saveSelection` as the `onMouseDown` handler on toolbar controls,\n * then call `restoreAndExec(fn)` inside the control's `onChange` /\n * `onClick` to restore the selection before running the format command.\n */\nexport function useSelectionSaver(): SelectionSaver {\n const savedRangeRef = useRef<Range | null>(null);\n\n const saveSelection = useCallback(() => {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n savedRangeRef.current = sel.getRangeAt(0).cloneRange();\n }\n }, []);\n\n const restoreSelection = useCallback(() => {\n const range = savedRangeRef.current;\n if (!range) return;\n const sel = window.getSelection();\n if (sel) {\n sel.removeAllRanges();\n sel.addRange(range);\n }\n }, []);\n\n const restoreAndExec = useCallback(\n (fn: () => void) => {\n restoreSelection();\n fn();\n },\n [restoreSelection]\n );\n\n return { saveSelection, restoreSelection, restoreAndExec };\n}\n","// ============================================================\n// TextToolbar — Comprehensive text formatting tools\n// ============================================================\n// Provides a full Word-style formatting toolbar using the\n// useFormattingState and useSelectionSaver hooks from the\n// dopecanvas library. Consumers can use this drop-in component\n// or build their own toolbar using the same hooks.\n// ============================================================\n\nimport React, { useCallback } from 'react';\nimport { useFormattingState } from '../../hooks/useSelectionContext';\nimport { useSelectionSaver } from '../../hooks/useSelectionSaver';\n\ninterface TextToolbarProps {\n onExecCommand: (command: string, value?: string) => void;\n}\n\nexport const TextToolbar: React.FC<TextToolbarProps> = ({ onExecCommand }) => {\n const fmt = useFormattingState();\n const { saveSelection, restoreAndExec } = useSelectionSaver();\n\n // ----------------------------------------------------------\n // Handlers\n // ----------------------------------------------------------\n\n /** Toggle command (bold, italic, etc.) — prevent default to keep focus */\n const toggle = useCallback(\n (command: string, value?: string) => (e: React.MouseEvent) => {\n e.preventDefault();\n onExecCommand(command, value);\n },\n [onExecCommand]\n );\n\n /** Select-based command — restore selection before executing */\n const handleSelect = useCallback(\n (command: string) => (e: React.ChangeEvent<HTMLSelectElement>) => {\n restoreAndExec(() => onExecCommand(command, e.target.value));\n },\n [onExecCommand, restoreAndExec]\n );\n\n /** Color input — restore selection before executing */\n const handleColor = useCallback(\n (command: string) => (e: React.ChangeEvent<HTMLInputElement>) => {\n restoreAndExec(() => onExecCommand(command, e.target.value));\n },\n [onExecCommand, restoreAndExec]\n );\n\n /** Insert a link via prompt */\n const handleInsertLink = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n const url = prompt('Enter URL:');\n if (url) onExecCommand('createLink', url);\n },\n [onExecCommand]\n );\n\n return (\n <div style={toolbarSectionStyle}>\n {/* ---- Block format ---- */}\n <select\n value={fmt.formatBlock}\n onChange={handleSelect('formatBlock')}\n style={selectStyle}\n title=\"Block Format\"\n onMouseDown={saveSelection}\n >\n <option value=\"p\">Paragraph</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"h5\">Heading 5</option>\n <option value=\"h6\">Heading 6</option>\n </select>\n\n <Divider />\n\n {/* ---- Font family ---- */}\n <select\n value={fmt.fontName.replace(/['\"]/g, '')}\n onChange={handleSelect('fontName')}\n style={{ ...selectStyle, width: 110 }}\n title=\"Font Family\"\n onMouseDown={saveSelection}\n >\n <option value=\"Arial\">Arial</option>\n <option value=\"Georgia\">Georgia</option>\n <option value=\"Times New Roman\">Times New Roman</option>\n <option value=\"Courier New\">Courier New</option>\n <option value=\"Verdana\">Verdana</option>\n <option value=\"Trebuchet MS\">Trebuchet MS</option>\n <option value=\"Comic Sans MS\">Comic Sans MS</option>\n <option value=\"Impact\">Impact</option>\n <option value=\"system-ui\">System UI</option>\n </select>\n\n {/* ---- Font size ---- */}\n <select\n value={fmt.fontSize}\n onChange={handleSelect('fontSize')}\n style={{ ...selectStyle, width: 56 }}\n title=\"Font Size\"\n onMouseDown={saveSelection}\n >\n <option value=\"1\">8</option>\n <option value=\"2\">10</option>\n <option value=\"3\">12</option>\n <option value=\"4\">14</option>\n <option value=\"5\">18</option>\n <option value=\"6\">24</option>\n <option value=\"7\">36</option>\n </select>\n\n <Divider />\n\n {/* ---- Inline formatting ---- */}\n <Btn icon=\"B\" title=\"Bold (Ctrl+B)\" active={fmt.bold} onMouseDown={toggle('bold')} extraStyle={{ fontWeight: 700 }} />\n <Btn icon=\"I\" title=\"Italic (Ctrl+I)\" active={fmt.italic} onMouseDown={toggle('italic')} extraStyle={{ fontStyle: 'italic' }} />\n <Btn icon=\"U\" title=\"Underline (Ctrl+U)\" active={fmt.underline} onMouseDown={toggle('underline')} extraStyle={{ textDecoration: 'underline' }} />\n <Btn icon=\"S\" title=\"Strikethrough\" active={fmt.strikethrough} onMouseDown={toggle('strikethrough')} extraStyle={{ textDecoration: 'line-through' }} />\n <Btn icon=\"x²\" title=\"Superscript\" active={fmt.superscript} onMouseDown={toggle('superscript')} />\n <Btn icon=\"x₂\" title=\"Subscript\" active={fmt.subscript} onMouseDown={toggle('subscript')} />\n\n <Divider />\n\n {/* ---- Colors ---- */}\n <ColorPicker label=\"A\" title=\"Text Color\" defaultValue=\"#000000\" onChange={handleColor('foreColor')} onMouseDown={saveSelection} />\n <ColorPicker label=\"A\" title=\"Highlight Color\" defaultValue=\"#ffff00\" onChange={handleColor('hiliteColor')} onMouseDown={saveSelection} highlight />\n\n <Divider />\n\n {/* ---- Alignment ---- */}\n <Btn icon=\"≡\" title=\"Align Left\" active={fmt.justifyLeft} onMouseDown={toggle('justifyLeft')} />\n <Btn icon=\"≣\" title=\"Align Center\" active={fmt.justifyCenter} onMouseDown={toggle('justifyCenter')} />\n <Btn icon=\"≢\" title=\"Align Right\" active={fmt.justifyRight} onMouseDown={toggle('justifyRight')} />\n <Btn icon=\"☰\" title=\"Justify\" active={fmt.justifyFull} onMouseDown={toggle('justifyFull')} />\n\n <Divider />\n\n {/* ---- Lists ---- */}\n <Btn icon=\"•\" title=\"Bullet List\" active={fmt.unorderedList} onMouseDown={toggle('insertUnorderedList')} />\n <Btn icon=\"1.\" title=\"Numbered List\" active={fmt.orderedList} onMouseDown={toggle('insertOrderedList')} extraStyle={{ fontSize: '11px', fontWeight: 600 }} />\n\n {/* ---- Indent ---- */}\n <Btn icon=\"⇤\" title=\"Decrease Indent\" onMouseDown={toggle('outdent')} />\n <Btn icon=\"⇥\" title=\"Increase Indent\" onMouseDown={toggle('indent')} />\n\n <Divider />\n\n {/* ---- Extras ---- */}\n <Btn icon=\"🔗\" title=\"Insert Link\" onMouseDown={handleInsertLink} />\n <Btn icon=\"—\" title=\"Horizontal Rule\" onMouseDown={toggle('insertHorizontalRule')} />\n <Btn icon=\"T̸\" title=\"Clear Formatting\" onMouseDown={toggle('removeFormat')} />\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// Sub-components\n// ----------------------------------------------------------\n\ninterface BtnProps {\n icon: string;\n title: string;\n active?: boolean;\n onMouseDown: (e: React.MouseEvent) => void;\n extraStyle?: React.CSSProperties;\n}\n\nconst Btn: React.FC<BtnProps> = ({ icon, title, active, onMouseDown, extraStyle }) => (\n <button\n type=\"button\"\n title={title}\n onMouseDown={onMouseDown}\n style={{\n width: '28px',\n height: '28px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: active ? '#b0b5bd' : 'transparent',\n borderRadius: '3px',\n backgroundColor: active ? '#d0d5dd' : 'transparent',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '13px',\n color: '#333',\n padding: 0,\n fontFamily: 'inherit',\n ...extraStyle,\n }}\n dangerouslySetInnerHTML={{ __html: icon }}\n />\n);\n\nconst Divider: React.FC = () => <div style={dividerStyle} />;\n\ninterface ColorPickerProps {\n label: string;\n title: string;\n defaultValue: string;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n onMouseDown: () => void;\n highlight?: boolean;\n}\n\nconst ColorPicker: React.FC<ColorPickerProps> = ({\n label,\n title,\n defaultValue,\n onChange,\n onMouseDown,\n highlight,\n}) => (\n <label style={colorLabelStyle} title={title} onMouseDown={onMouseDown}>\n {highlight ? (\n <span style={{ backgroundColor: '#ffff00', padding: '0 2px' }}>{label}</span>\n ) : (\n label\n )}\n <input\n type=\"color\"\n defaultValue={defaultValue}\n onChange={onChange}\n style={colorInputStyle}\n />\n </label>\n);\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst toolbarSectionStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n flexWrap: 'wrap',\n};\n\nconst selectStyle: React.CSSProperties = {\n height: '28px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '12px',\n padding: '0 4px',\n cursor: 'pointer',\n backgroundColor: '#fff',\n};\n\nconst dividerStyle: React.CSSProperties = {\n width: '1px',\n height: '20px',\n backgroundColor: '#ddd',\n margin: '0 4px',\n};\n\nconst colorLabelStyle: React.CSSProperties = {\n position: 'relative',\n width: '28px',\n height: '28px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 'bold',\n};\n\nconst colorInputStyle: React.CSSProperties = {\n position: 'absolute',\n bottom: 0,\n left: 0,\n width: '100%',\n height: '4px',\n padding: 0,\n borderWidth: 0,\n borderStyle: 'none',\n cursor: 'pointer',\n};\n","// ============================================================\n// PageSetupToolbar — Page size and margin controls\n// ============================================================\n\nimport React, { useCallback } from 'react';\nimport type { PageConfig, PageSizeName } from '../../core/types';\n\ninterface PageSetupToolbarProps {\n pageConfig: PageConfig;\n pageCount: number;\n onPageConfigChange: (config: Partial<PageConfig>) => void;\n}\n\nexport const PageSetupToolbar: React.FC<PageSetupToolbarProps> = ({\n pageConfig,\n pageCount,\n onPageConfigChange,\n}) => {\n const handleSizeChange = useCallback(\n (e: React.ChangeEvent<HTMLSelectElement>) => {\n const value = e.target.value as PageSizeName;\n onPageConfigChange({ size: value });\n },\n [onPageConfigChange]\n );\n\n const handleMarginChange = useCallback(\n (side: keyof typeof pageConfig.margins) =>\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = Math.max(0, parseInt(e.target.value) || 0);\n onPageConfigChange({\n margins: {\n ...pageConfig.margins,\n [side]: value,\n },\n });\n },\n [pageConfig.margins, onPageConfigChange]\n );\n\n const currentSize =\n typeof pageConfig.size === 'string' ? pageConfig.size : 'custom';\n\n return (\n <div style={sectionStyle}>\n {/* Page size */}\n <label style={labelStyle}>\n Page:\n <select\n value={currentSize}\n onChange={handleSizeChange}\n style={selectStyle}\n >\n <option value=\"letter\">Letter (8.5 x 11)</option>\n <option value=\"a4\">A4 (210 x 297mm)</option>\n <option value=\"legal\">Legal (8.5 x 14)</option>\n </select>\n </label>\n\n <div style={dividerStyle} />\n\n {/* Margins */}\n <span style={{ fontSize: '12px', color: '#666' }}>Margins (px):</span>\n <MarginInput\n label=\"T\"\n value={pageConfig.margins.top}\n onChange={handleMarginChange('top')}\n />\n <MarginInput\n label=\"R\"\n value={pageConfig.margins.right}\n onChange={handleMarginChange('right')}\n />\n <MarginInput\n label=\"B\"\n value={pageConfig.margins.bottom}\n onChange={handleMarginChange('bottom')}\n />\n <MarginInput\n label=\"L\"\n value={pageConfig.margins.left}\n onChange={handleMarginChange('left')}\n />\n\n <div style={dividerStyle} />\n\n {/* Page count */}\n <span style={{ fontSize: '12px', color: '#666' }}>\n {pageCount} {pageCount === 1 ? 'page' : 'pages'}\n </span>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// MarginInput sub-component\n// ----------------------------------------------------------\n\ninterface MarginInputProps {\n label: string;\n value: number;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n}\n\nconst MarginInput: React.FC<MarginInputProps> = ({ label, value, onChange }) => (\n <label style={marginLabelStyle} title={`${label} margin`}>\n {label}:\n <input\n type=\"number\"\n value={value}\n onChange={onChange}\n style={marginInputStyle}\n min={0}\n max={300}\n step={12}\n />\n </label>\n);\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst sectionStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n flexWrap: 'wrap',\n};\n\nconst labelStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n fontSize: '12px',\n color: '#666',\n};\n\nconst selectStyle: React.CSSProperties = {\n height: '26px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '12px',\n padding: '0 4px',\n cursor: 'pointer',\n backgroundColor: '#fff',\n};\n\nconst dividerStyle: React.CSSProperties = {\n width: '1px',\n height: '20px',\n backgroundColor: '#ddd',\n margin: '0 4px',\n};\n\nconst marginLabelStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n fontSize: '11px',\n color: '#666',\n};\n\nconst marginInputStyle: React.CSSProperties = {\n width: '44px',\n height: '24px',\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: '#ccc',\n borderRadius: '3px',\n fontSize: '11px',\n textAlign: 'center',\n padding: '0 2px',\n};\n","// ============================================================\n// Toolbar — Main toolbar container with context switching\n// ============================================================\n\nimport React from 'react';\nimport { TextToolbar } from './TextToolbar';\nimport { PageSetupToolbar } from './PageSetupToolbar';\nimport type { PageConfig } from '../../core/types';\n\ninterface ToolbarProps {\n pageConfig: PageConfig;\n pageCount: number;\n onExecCommand: (command: string, value?: string) => void;\n onPageConfigChange: (config: Partial<PageConfig>) => void;\n}\n\nexport const Toolbar: React.FC<ToolbarProps> = ({\n pageConfig,\n pageCount,\n onExecCommand,\n onPageConfigChange,\n}) => {\n return (\n <div style={toolbarContainerStyle}>\n {/* Top row: Text formatting */}\n <div style={toolbarRowStyle}>\n <TextToolbar onExecCommand={onExecCommand} />\n </div>\n\n {/* Bottom row: Page setup */}\n <div style={toolbarRowStyle}>\n <PageSetupToolbar\n pageConfig={pageConfig}\n pageCount={pageCount}\n onPageConfigChange={onPageConfigChange}\n />\n </div>\n </div>\n );\n};\n\n// ----------------------------------------------------------\n// Styles\n// ----------------------------------------------------------\n\nconst toolbarContainerStyle: React.CSSProperties = {\n borderBottomWidth: '1px',\n borderBottomStyle: 'solid',\n borderBottomColor: '#d0d0d0',\n backgroundColor: '#f8f8f8',\n padding: '4px 8px',\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n flexShrink: 0,\n zIndex: 10,\n};\n\nconst toolbarRowStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n minHeight: '32px',\n};\n","// ============================================================\n// useDocumentEngine — React hook wrapping DocumentEngine\n// ============================================================\n\nimport { useRef, useState, useCallback, useEffect } from 'react';\nimport { DocumentEngine } from '../core/DocumentEngine';\nimport type { PageConfig, PaginationResult } from '../core/types';\nimport { DEFAULT_PAGE_CONFIG } from '../core/types';\n\ninterface UseDocumentEngineOptions {\n initialHTML?: string;\n initialCSS?: string;\n initialConfig?: PageConfig;\n}\n\ninterface UseDocumentEngineReturn {\n engine: DocumentEngine;\n paginationResult: PaginationResult;\n pageConfig: PageConfig;\n loadHTML: (html: string, css?: string) => void;\n setPageConfig: (config: Partial<PageConfig>) => void;\n triggerPagination: () => void;\n getHTML: () => string;\n getPlainText: () => string;\n}\n\nexport function useDocumentEngine(\n options: UseDocumentEngineOptions = {}\n): UseDocumentEngineReturn {\n const {\n initialHTML = '',\n initialCSS = '',\n initialConfig = DEFAULT_PAGE_CONFIG,\n } = options;\n\n const engineRef = useRef<DocumentEngine>(new DocumentEngine(initialConfig));\n const [paginationResult, setPaginationResult] = useState<PaginationResult>({\n pages: [{ blockIndices: [] }],\n pageCount: 1,\n });\n const [pageConfig, setPageConfigState] = useState<PageConfig>(initialConfig);\n\n // Load HTML into engine\n const loadHTML = useCallback((html: string, css?: string) => {\n engineRef.current.loadHTML(html, css);\n }, []);\n\n // Set page configuration\n const setPageConfig = useCallback((config: Partial<PageConfig>) => {\n engineRef.current.setPageConfig(config);\n setPageConfigState(engineRef.current.getPageConfig());\n }, []);\n\n // Trigger re-pagination\n const triggerPagination = useCallback(() => {\n const { result } = engineRef.current.runPagination();\n setPaginationResult(result);\n }, []);\n\n // Get current HTML content\n const getHTML = useCallback(() => {\n return engineRef.current.getHTML();\n }, []);\n\n // Get plain text\n const getPlainText = useCallback(() => {\n return engineRef.current.getPlainText();\n }, []);\n\n // Load initial HTML\n useEffect(() => {\n if (initialHTML) {\n engineRef.current.loadHTML(initialHTML, initialCSS);\n }\n }, [initialHTML, initialCSS]);\n\n // Subscribe to pagination events\n useEffect(() => {\n const unsub = engineRef.current.onPagination((result) => {\n setPaginationResult(result);\n });\n return unsub;\n }, []);\n\n // Cleanup\n useEffect(() => {\n return () => {\n engineRef.current.destroy();\n };\n }, []);\n\n return {\n engine: engineRef.current,\n paginationResult,\n pageConfig,\n loadHTML,\n setPageConfig,\n triggerPagination,\n getHTML,\n getPlainText,\n };\n}\n"],"names":["Page","dimensions","margins","pageNumber","totalPages","children","jsxs","jsx","BlockToolbar","visible","onAddBelow","onEditHTML","onDelete","toolbarStyle","e","Btn","PlusIcon","CodeIcon","TrashIcon","onClick","title","danger","hovered","setHovered","useState","btnStyle","HTMLEditorModal","html","onSave","onCancel","value","setValue","handleKeyDown","ta","start","end","updated","createPortal","overlayStyle","modalStyle","headerStyle","textareaStyle","footerStyle","cancelBtnStyle","saveBtnStyle","PAGE_SIZE_PRESETS","DEFAULT_MARGINS","DEFAULT_PAGE_CONFIG","splitIdCounter","nextSplitId","MIN_SPLIT_HEIGHT","UNSPLITTABLE_TAGS","TEXT_BLOCK_TAGS","trySplitBlock","element","availableHeight","tag","style","result","splitAtChildBoundary","splitAtTextBoundary","recombineSplitBlocks","blockHTMLs","i","splitId","extractSplitId","parts","j","mergeBlockParts","blockTop","splitIndex","childRect","childStyle","marginBottom","firstEl","secondEl","id","textNodes","walker","splitNode","splitOffset","textNode","range","nodeRect","lo","hi","mid","r","text","wordBound","firstRange","secondRange","firstFragment","secondFragment","firstText","fragmentToText","secondText","match","container","innerParts","outerTag","outerAttrs","partHTML","el","clone","tmp","fullTag","closeIdx","fragment","div","activateScripts","activated","orig","fresh","attr","saveCursorPosition","sel","anchorNode","wrappers","blockIndex","w","preRange","textOffset","restoreCursorPosition","state","wrapper","remaining","MemoizedBlockContent","React","isEditable","isTable","isPageBreakBlock","blockHTML","s","PagedView","forwardRef","css","pageConfig","layoutEngine","_editableManager","onContentChange","onPaginationChange","showPageBreaks","ref","measureRef","useRef","pagesContainerRef","mutationObserverRef","onContentChangeRef","pages","setPages","pendingCursorRef","isRePaginatingRef","pagesRef","hoveredBlockIndex","setHoveredBlockIndex","editingBlockIndex","setEditingBlockIndex","editingHTML","setEditingHTML","hideTimeoutRef","collectHTMLFromDOM","useCallback","contentDivs","htmlParts","contentDiv","updatedHTML","paginateHTML","htmlContent","mc","contentWidth","contentHeight","styleEl","queue","m","currentPage","currentHeight","block","remainingSpace","splitResult","tempEl","newElement","rect","marginTop","pageData","blocks","paginationResult","_","idx","__","rePaginateFromDOM","cursor","rawBlockHTMLs","measureWrapper","currentPageHeight","st","mt","mb","newPageData","oldDist","p","newDist","count","rePaginateFromDOMRef","runPagination","parsed","useEffect","collectBlocksFromDOM","child","handleAddBlock","globalIndex","handleDeleteBlock","handleOpenEditor","handleSaveHTML","newHTML","handleCancelEditor","insertPageBreak","PAGE_BREAK_HTML","insertIdx","cursorBlockIdx","useImperativeHandle","cell","activatedScripts","debounceTimer","observer","scrollContainerStyle","pagesWrapperStyle","pageIndex","pageStartIdx","sum","globalIdx","lower","isPageBreak","pageBreakIndicatorStyle","pageBreakLineStyle","pageBreakLabelStyle","pageBreakRemoveBtnStyle","prev","PageLayoutEngine","config","measurements","breakBefore","breakAfter","totalHeight","EditableManager","table","_mutations","selection","node","current","ctx","cb","last","previous","next","fn","callback","command","DopeCanvas","renderMode","externalPageConfig","onPageConfigChange","internalPageConfig","setInternalPageConfig","setPaginationResult","currentHTMLRef","rootRef","flowContentRef","pagedViewRef","setShowPageBreaks","useMemo","editableManager","handleContentChange","handlePageConfigChange","newConfig","handlePaginationChange","sourceHTML","live","freshHTML","liveHTML","divs","pageBreak","show","flowContainerStyle","flowContentStyle","DocumentEngine","contentWrapper","DocumentAPI","selector","useSelectionContext","context","setContext","DEFAULT_FORMATTING_STATE","useFormattingState","setState","updateState","formatBlock","useSelectionSaver","savedRangeRef","saveSelection","restoreSelection","restoreAndExec","TextToolbar","onExecCommand","fmt","toggle","handleSelect","handleColor","handleInsertLink","url","toolbarSectionStyle","selectStyle","Divider","ColorPicker","icon","active","onMouseDown","extraStyle","dividerStyle","label","defaultValue","onChange","highlight","colorLabelStyle","colorInputStyle","PageSetupToolbar","pageCount","handleSizeChange","handleMarginChange","side","currentSize","sectionStyle","labelStyle","MarginInput","marginLabelStyle","marginInputStyle","Toolbar","toolbarContainerStyle","toolbarRowStyle","useDocumentEngine","options","initialHTML","initialCSS","initialConfig","engineRef","setPageConfigState","loadHTML","setPageConfig","triggerPagination","getHTML","getPlainText"],"mappings":"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"}
|