window.MixedNote = (() => { function resultData(result, fallback) { if (!result || !result.ok) throw new Error((result && result.error) || fallback); return result.data; } function canvasOptions(options) { return { async pickPdf() { return resultData(await window.api.reader.pickNotePdf(), '无法选择 PDF 底版'); }, async readPdf(ref) { return resultData(await window.api.reader.notePdfBytes(ref), '无法读取 PDF 底版'); }, async savePdf(bytes, suggestedName) { return resultData( await window.api.reader.saveNotePdf(bytes, suggestedName), '导出 PDF 失败' ); }, onError: options.onError, onChange: options.onChange }; } function mountTyped(host, noteType, initialRich, initialCanvas, options) { host.textContent = ''; const box = document.createElement('div'); box.className = `mixed-note-editor note-editor-${noteType}`; const editorHost = document.createElement('div'); editorHost.className = noteType === 'canvas' ? 'mixed-note-canvas' : 'mixed-note-text'; box.appendChild(editorHost); host.appendChild(box); if (noteType === 'reading') { const rich = window.RichNote.mount(editorHost, initialRich, { placeholder: options.placeholder, onError: options.onError }); return { noteType, ready: async () => {}, richContent: () => rich.content(), canvasContent: () => null, text: () => rich.text(), hasContent: () => window.RichNote.hasContent(rich.content()), focus: () => rich.focus(), setMode: async () => {}, destroy: () => { rich.destroy(); host.textContent = ''; } }; } editorHost.textContent = '正在加载画布...'; let canvas = null; let destroyed = false; const canvasPromise = import('./canvas-note.mjs').then(async (module) => { if (destroyed) return null; canvas = await module.mountCanvasNote(editorHost, initialCanvas, canvasOptions(options)); return canvas; }).catch((error) => { if (typeof options.onError === 'function') options.onError(error.message || String(error)); throw error; }); return { noteType, ready: async () => { await canvasPromise; if (canvas) await canvas.flush(); }, richContent: () => null, canvasContent: () => canvas ? canvas.content() : initialCanvas || null, text: () => '', hasContent: () => !!(canvas ? canvas.hasContent() : initialCanvas), focus: () => { canvasPromise.then((value) => value?.focus()).catch(() => {}); }, setMode: async () => {}, destroy: () => { destroyed = true; if (canvas) canvas.destroy(); host.textContent = ''; } }; } function mount(host, initialRich, initialCanvas, options = {}) { if (options.noteType === 'reading' || options.noteType === 'canvas') { return mountTyped(host, options.noteType, initialRich, initialCanvas, options); } host.textContent = ''; const box = document.createElement('div'); box.className = 'mixed-note-editor'; const modes = document.createElement('div'); modes.className = 'mixed-note-modes'; modes.setAttribute('role', 'tablist'); const textButton = document.createElement('button'); textButton.type = 'button'; textButton.className = 'mixed-note-mode active'; textButton.textContent = '文本'; textButton.setAttribute('role', 'tab'); textButton.setAttribute('aria-selected', 'true'); const canvasButton = document.createElement('button'); canvasButton.type = 'button'; canvasButton.className = 'mixed-note-mode'; canvasButton.textContent = '自由画布'; canvasButton.setAttribute('role', 'tab'); canvasButton.setAttribute('aria-selected', 'false'); modes.append(textButton, canvasButton); const textHost = document.createElement('div'); textHost.className = 'mixed-note-text'; const canvasHost = document.createElement('div'); canvasHost.className = 'mixed-note-canvas hidden'; box.append(modes, textHost, canvasHost); host.appendChild(box); const rich = window.RichNote.mount(textHost, initialRich, { placeholder: options.placeholder, onError: options.onError }); let canvas = null; let canvasPromise = null; let destroyed = false; function legacyCanvasOptions() { return { async pickPdf() { const value = resultData(await window.api.reader.pickNotePdf(), '无法选择 PDF 底版'); return value; }, async readPdf(ref) { return resultData(await window.api.reader.notePdfBytes(ref), '无法读取 PDF 底版'); }, async savePdf(bytes, suggestedName) { return resultData( await window.api.reader.saveNotePdf(bytes, suggestedName), '导出 PDF 失败' ); }, onError: options.onError, onChange: options.onChange }; } async function ensureCanvas() { if (canvas) return canvas; if (!canvasPromise) { canvasPromise = import('./canvas-note.mjs').then(async (module) => { if (destroyed) return null; canvas = await module.mountCanvasNote(canvasHost, initialCanvas, legacyCanvasOptions()); return canvas; }).catch((error) => { canvasPromise = null; if (typeof options.onError === 'function') options.onError(error.message || String(error)); throw error; }); } return canvasPromise; } async function setMode(mode) { const showCanvas = mode === 'canvas'; if (showCanvas) await ensureCanvas(); textHost.classList.toggle('hidden', showCanvas); canvasHost.classList.toggle('hidden', !showCanvas); textButton.classList.toggle('active', !showCanvas); canvasButton.classList.toggle('active', showCanvas); textButton.setAttribute('aria-selected', String(!showCanvas)); canvasButton.setAttribute('aria-selected', String(showCanvas)); if (showCanvas && canvas) canvas.focus(); else rich.focus(); } textButton.onclick = () => { setMode('text'); }; canvasButton.onclick = () => { setMode('canvas'); }; if (initialCanvas) ensureCanvas(); return { ready: async () => { if (canvasPromise) await canvasPromise; if (canvas) await canvas.flush(); }, richContent: () => rich.content(), canvasContent: () => canvas ? canvas.content() : initialCanvas || null, text: () => rich.text(), hasContent: () => ( window.RichNote.hasContent(rich.content()) || !!((canvas ? canvas.content() : initialCanvas) && (canvas ? canvas.hasContent() : true)) ), focus: () => rich.focus(), setMode, destroy: () => { destroyed = true; rich.destroy(); if (canvas) canvas.destroy(); host.textContent = ''; } }; } return { mount }; })();