open-edit 0.1.0
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/LICENSE +21 -0
- package/README.md +595 -0
- package/dist/core/commands.d.ts +33 -0
- package/dist/core/history.d.ts +18 -0
- package/dist/core/model.d.ts +25 -0
- package/dist/core/types.d.ts +229 -0
- package/dist/editor.d.ts +64 -0
- package/dist/index.d.ts +61 -0
- package/dist/io/deserializer.d.ts +3 -0
- package/dist/io/markdown.d.ts +3 -0
- package/dist/io/serializer.d.ts +4 -0
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en.d.ts +2 -0
- package/dist/locales/types.d.ts +77 -0
- package/dist/open-edit.cjs.js +783 -0
- package/dist/open-edit.cjs.js.map +1 -0
- package/dist/open-edit.esm.js +3333 -0
- package/dist/open-edit.esm.js.map +1 -0
- package/dist/open-edit.umd.js +783 -0
- package/dist/open-edit.umd.js.map +1 -0
- package/dist/plugins/ai.d.ts +16 -0
- package/dist/plugins/callout.d.ts +5 -0
- package/dist/plugins/emoji.d.ts +2 -0
- package/dist/plugins/highlight.d.ts +24 -0
- package/dist/plugins/slash-commands.d.ts +22 -0
- package/dist/plugins/template-tags.d.ts +2 -0
- package/dist/view/bubble-toolbar.d.ts +16 -0
- package/dist/view/code-lang-picker.d.ts +20 -0
- package/dist/view/icons.d.ts +1 -0
- package/dist/view/image-resize.d.ts +15 -0
- package/dist/view/renderer.d.ts +11 -0
- package/dist/view/selection.d.ts +17 -0
- package/dist/view/styles.d.ts +1 -0
- package/dist/view/toolbar.d.ts +31 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-edit.esm.js","names":[],"sources":["../src/core/history.ts","../src/core/model.ts","../src/io/deserializer.ts","../src/io/serializer.ts","../src/io/markdown.ts","../src/view/renderer.ts","../src/core/commands.ts","../src/view/selection.ts","../src/locales/en.ts","../src/view/icons.ts","../src/view/toolbar.ts","../src/view/bubble-toolbar.ts","../src/view/styles.ts","../src/view/image-resize.ts","../src/view/code-lang-picker.ts","../src/locales/de.ts","../src/editor.ts","../src/plugins/highlight.ts","../src/plugins/emoji.ts","../src/plugins/template-tags.ts","../src/plugins/ai.ts","../src/plugins/callout.ts","../src/plugins/slash-commands.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Undo/Redo History\n// Approach: Model snapshots (full document state after each command)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorDocument } from './types.js';\n\nconst MAX_HISTORY = 200;\n\nexport class History {\n private undoStack: EditorDocument[] = [];\n private redoStack: EditorDocument[] = [];\n private _paused = false;\n\n /** Push a snapshot onto the undo stack (clears redo) */\n push(doc: EditorDocument): void {\n if (this._paused) return;\n this.undoStack.push(deepClone(doc));\n if (this.undoStack.length > MAX_HISTORY) this.undoStack.shift();\n this.redoStack = [];\n }\n\n /** Undo: returns the previous state (or null if nothing to undo) */\n undo(current: EditorDocument): EditorDocument | null {\n if (this.undoStack.length === 0) return null;\n this.redoStack.push(deepClone(current));\n return this.undoStack.pop()!;\n }\n\n /** Redo: returns the next state (or null if nothing to redo) */\n redo(current: EditorDocument): EditorDocument | null {\n if (this.redoStack.length === 0) return null;\n this.undoStack.push(deepClone(current));\n return this.redoStack.pop()!;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n /** Pause recording (e.g. during undo/redo itself) */\n pause(): void {\n this._paused = true;\n }\n\n resume(): void {\n this._paused = false;\n }\n\n clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n }\n}\n\nfunction deepClone<T>(val: T): T {\n // Structured clone is available in all modern browsers\n return structuredClone(val);\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Document Model Operations\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n BlockNode,\n InlineNode,\n TextNode,\n Mark,\n MarkType,\n ParagraphNode,\n HeadingNode,\n ListItemNode,\n BulletListNode,\n OrderedListNode,\n BlockquoteNode,\n CodeBlockNode,\n CalloutNode,\n CalloutVariant,\n} from './types.js';\n\n// ── Factories ─────────────────────────────────────────────────────────────────\n\nexport function createDocument(children?: BlockNode[]): EditorDocument {\n return { children: children ?? [createParagraph()] };\n}\n\nexport function createParagraph(text = ''): ParagraphNode {\n return { type: 'paragraph', children: text ? [createText(text)] : [createText('')] };\n}\n\nexport function createHeading(level: 1 | 2 | 3 | 4 | 5 | 6, text = ''): HeadingNode {\n return { type: 'heading', level, children: [createText(text)] };\n}\n\nexport function createText(text: string, marks: Mark[] = []): TextNode {\n return { type: 'text', text, marks };\n}\n\nexport function createListItem(text = ''): ListItemNode {\n return { type: 'list_item', children: [createText(text)] };\n}\n\nexport function createBulletList(items: string[] = ['']): BulletListNode {\n return { type: 'bullet_list', children: items.map(createListItem) };\n}\n\nexport function createOrderedList(items: string[] = ['']): OrderedListNode {\n return { type: 'ordered_list', children: items.map(createListItem) };\n}\n\nexport function createBlockquote(text = ''): BlockquoteNode {\n return { type: 'blockquote', children: [createParagraph(text)] };\n}\n\nexport function createCodeBlock(code = '', lang?: string): CodeBlockNode {\n return { type: 'code_block', lang, children: [createText(code)] };\n}\n\nexport function createCallout(text = '', variant: CalloutVariant = 'info'): CalloutNode {\n return { type: 'callout', variant, children: [createText(text)] };\n}\n\n// ── Mark helpers ─────────────────────────────────────────────────────────────\n\nexport function hasMark(node: TextNode, type: MarkType): boolean {\n return node.marks.some((m) => m.type === type);\n}\n\nexport function addMark(node: TextNode, mark: Mark): TextNode {\n const filtered = node.marks.filter((m) => m.type !== mark.type);\n return { ...node, marks: [...filtered, mark] };\n}\n\nexport function removeMark(node: TextNode, type: MarkType): TextNode {\n return { ...node, marks: node.marks.filter((m) => m.type !== type) };\n}\n\nexport function toggleMark(node: TextNode, mark: Mark): TextNode {\n return hasMark(node, mark.type) ? removeMark(node, mark.type) : addMark(node, mark);\n}\n\n// ── Inline content helpers ────────────────────────────────────────────────────\n\n/** Get plain text from inline nodes */\nexport function getPlainText(inlines: InlineNode[]): string {\n return inlines\n .map((n) => (n.type === 'text' ? n.text : '\\n'))\n .join('');\n}\n\n/** Split inline content at a character offset, returns [before, after] */\nexport function splitInlinesAt(\n inlines: InlineNode[],\n offset: number,\n): [InlineNode[], InlineNode[]] {\n const before: InlineNode[] = [];\n const after: InlineNode[] = [];\n let pos = 0;\n\n for (const node of inlines) {\n if (node.type === 'hardbreak') {\n if (pos < offset) before.push(node);\n else after.push(node);\n pos += 1;\n continue;\n }\n\n const end = pos + node.text.length;\n if (end <= offset) {\n before.push(node);\n } else if (pos >= offset) {\n after.push(node);\n } else {\n // split this text node\n const cut = offset - pos;\n before.push(createText(node.text.slice(0, cut), node.marks));\n after.push(createText(node.text.slice(cut), node.marks));\n }\n pos = end;\n }\n\n return [before, after];\n}\n\n/** Merge adjacent text nodes with identical marks */\nexport function normalizeInlines(inlines: InlineNode[]): InlineNode[] {\n const result: InlineNode[] = [];\n for (const node of inlines) {\n if (node.type !== 'text' || node.text === '') {\n if (node.type === 'hardbreak') result.push(node);\n continue;\n }\n const prev = result[result.length - 1];\n if (prev && prev.type === 'text' && marksEqual(prev.marks, node.marks)) {\n result[result.length - 1] = createText(prev.text + node.text, prev.marks);\n } else {\n result.push(node);\n }\n }\n // Always have at least one text node\n if (result.length === 0) result.push(createText(''));\n return result;\n}\n\nfunction marksEqual(a: Mark[], b: Mark[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((m, i) => m.type === b[i].type);\n}\n\n// ── Document helpers ──────────────────────────────────────────────────────────\n\n/** Get total character length of a block's inline content */\nexport function blockLength(block: BlockNode): number {\n if (\n block.type === 'paragraph' ||\n block.type === 'heading' ||\n block.type === 'list_item' ||\n block.type === 'code_block'\n ) {\n return getPlainText(block.children as InlineNode[]).length;\n }\n if (block.type === 'bullet_list' || block.type === 'ordered_list') {\n return block.children.reduce((s, li) => s + blockLength(li), 0);\n }\n if (block.type === 'blockquote') {\n return block.children.reduce((s, b) => s + blockLength(b), 0);\n }\n return 0;\n}\n\n/** Create a fresh empty document */\nexport function emptyDocument(): EditorDocument {\n return createDocument([createParagraph('')]);\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — HTML Deserializer (HTML string → Document Model)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n BlockNode,\n InlineNode,\n TextNode,\n Mark,\n LinkMark,\n ParagraphNode,\n HeadingNode,\n ListItemNode,\n CalloutNode,\n CalloutVariant,\n} from '../core/types.js';\nimport { createParagraph, createText, emptyDocument } from '../core/model.js';\n\nconst CALLOUT_VARIANTS: CalloutVariant[] = ['info', 'success', 'warning', 'danger'];\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport function deserializeHTML(html: string): EditorDocument {\n if (!html || html.trim() === '') return emptyDocument();\n\n const container = document.createElement('div');\n container.innerHTML = sanitizeInput(html);\n\n const blocks = parseChildren(container);\n\n if (blocks.length === 0) return emptyDocument();\n return { children: blocks };\n}\n\n// ── Sanitize / normalize input HTML ──────────────────────────────────────────\n\nfunction sanitizeInput(html: string): string {\n // Basic: remove script tags and event handlers\n return html\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/\\s+on\\w+=\"[^\"]*\"/gi, '')\n .replace(/\\s+on\\w+='[^']*'/gi, '');\n}\n\n// ── Block-level parsing ───────────────────────────────────────────────────────\n\nfunction parseChildren(el: Element): BlockNode[] {\n const blocks: BlockNode[] = [];\n\n for (const child of Array.from(el.childNodes)) {\n if (child.nodeType === Node.TEXT_NODE) {\n const text = (child.textContent ?? '').trim();\n if (text) {\n blocks.push(createParagraph(text));\n }\n continue;\n }\n\n if (child.nodeType !== Node.ELEMENT_NODE) continue;\n\n const node = child as Element;\n const tag = node.tagName.toLowerCase();\n const block = parseBlockNode(node, tag);\n if (block) blocks.push(block);\n }\n\n return blocks;\n}\n\nfunction parseBlockNode(node: Element, tag: string): BlockNode | null {\n switch (tag) {\n case 'p':\n return parseParagraph(node);\n\n case 'h1': case 'h2': case 'h3':\n case 'h4': case 'h5': case 'h6':\n return parseHeading(node, parseInt(tag[1]) as 1|2|3|4|5|6);\n\n case 'blockquote':\n return parseBlockquote(node);\n\n case 'pre':\n return parseCodeBlock(node);\n\n case 'ul':\n return parseBulletList(node);\n\n case 'ol':\n return parseOrderedList(node);\n\n case 'img':\n return parseImage(node);\n\n case 'hr':\n return { type: 'hr' };\n\n case 'div':\n // Recognize callout blocks before generic unwrapping\n if (node.classList.contains('oe-callout')) {\n return parseCallout(node);\n }\n // Unwrap generic containers\n return parseChildren(node)[0] ?? null;\n\n case 'section': case 'article': case 'main':\n // Unwrap generic containers\n return parseChildren(node)[0] ?? null;\n\n case 'br':\n return createParagraph('');\n\n default:\n // Treat unknown block-ish elements as paragraphs\n return parseParagraph(node);\n }\n}\n\nfunction parseParagraph(node: Element): ParagraphNode {\n const align = parseAlign(node);\n const children = parseInlineNodes(node);\n return { type: 'paragraph', ...(align ? { align } : {}), children };\n}\n\nfunction parseHeading(node: Element, level: 1|2|3|4|5|6): HeadingNode {\n const align = parseAlign(node);\n const children = parseInlineNodes(node);\n return { type: 'heading', level, ...(align ? { align } : {}), children };\n}\n\nfunction parseBlockquote(node: Element): BlockNode {\n const children = parseChildren(node);\n if (children.length === 0) children.push(createParagraph(''));\n return { type: 'blockquote', children };\n}\n\nfunction parseCodeBlock(node: Element): BlockNode {\n const codeEl = node.querySelector('code');\n const rawText = codeEl ? codeEl.textContent ?? '' : node.textContent ?? '';\n // Try to detect language from class\n const lang = codeEl?.className.match(/language-(\\w+)/)?.[1];\n return {\n type: 'code_block',\n ...(lang ? { lang } : {}),\n children: [createText(rawText)],\n };\n}\n\nfunction parseBulletList(node: Element): BlockNode {\n const items = Array.from(node.querySelectorAll(':scope > li')).map(parseListItem);\n if (items.length === 0) items.push({ type: 'list_item', children: [createText('')] });\n return { type: 'bullet_list', children: items };\n}\n\nfunction parseOrderedList(node: Element): BlockNode {\n const start = parseInt(node.getAttribute('start') ?? '1');\n const items = Array.from(node.querySelectorAll(':scope > li')).map(parseListItem);\n if (items.length === 0) items.push({ type: 'list_item', children: [createText('')] });\n return { type: 'ordered_list', ...(start !== 1 ? { start } : {}), children: items };\n}\n\nfunction parseListItem(node: Element): ListItemNode {\n // Only take inline content, ignore nested lists for now\n const children = parseInlineNodes(node);\n return { type: 'list_item', children };\n}\n\nfunction parseCallout(node: Element): CalloutNode {\n const dataVariant = node.getAttribute('data-callout-variant');\n const variant: CalloutVariant =\n CALLOUT_VARIANTS.includes(dataVariant as CalloutVariant)\n ? (dataVariant as CalloutVariant)\n : (CALLOUT_VARIANTS.find((v) => node.classList.contains(`oe-callout-${v}`)) ?? 'info');\n const children = parseInlineNodes(node);\n return { type: 'callout', variant, children };\n}\n\nfunction parseImage(node: Element): BlockNode {\n return {\n type: 'image',\n src: node.getAttribute('src') ?? '',\n alt: node.getAttribute('alt') ?? undefined,\n width: node.getAttribute('width') ? parseInt(node.getAttribute('width')!) : undefined,\n height: node.getAttribute('height') ? parseInt(node.getAttribute('height')!) : undefined,\n };\n}\n\n// ── Inline parsing ────────────────────────────────────────────────────────────\n\nfunction parseInlineNodes(el: Element | ChildNode, marks: Mark[] = []): InlineNode[] {\n const result: InlineNode[] = [];\n\n for (const child of Array.from(el.childNodes)) {\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent ?? '';\n if (text) result.push(createText(text, [...marks]));\n continue;\n }\n\n if (child.nodeType !== Node.ELEMENT_NODE) continue;\n\n const node = child as Element;\n const tag = node.tagName.toLowerCase();\n const newMarks = marksForTag(tag, node, marks);\n\n if (tag === 'br') {\n result.push({ type: 'hardbreak' });\n continue;\n }\n\n if (tag === 'img') {\n // Inline image — treat as text placeholder for now\n result.push(createText('[image]', marks));\n continue;\n }\n\n result.push(...parseInlineNodes(node, newMarks));\n }\n\n // Ensure at least empty text\n if (result.length === 0) result.push(createText('', marks));\n return result;\n}\n\nfunction marksForTag(tag: string, node: Element, existing: Mark[]): Mark[] {\n const marks = [...existing];\n\n switch (tag) {\n case 'strong': case 'b':\n if (!marks.some((m) => m.type === 'bold')) marks.push({ type: 'bold' });\n break;\n case 'em': case 'i':\n if (!marks.some((m) => m.type === 'italic')) marks.push({ type: 'italic' });\n break;\n case 'u':\n if (!marks.some((m) => m.type === 'underline')) marks.push({ type: 'underline' });\n break;\n case 's': case 'del': case 'strike':\n if (!marks.some((m) => m.type === 'strikethrough')) marks.push({ type: 'strikethrough' });\n break;\n case 'code':\n if (!marks.some((m) => m.type === 'code')) marks.push({ type: 'code' });\n break;\n case 'a': {\n const href = node.getAttribute('href') ?? '';\n const target = node.getAttribute('target') === '_blank' ? '_blank' : '_self';\n const lm: LinkMark = { type: 'link', href, target };\n if (!marks.some((m) => m.type === 'link')) marks.push(lm);\n break;\n }\n case 'mark':\n // Treat highlight as no mark (preserve text)\n break;\n }\n\n return marks;\n}\n\n// ── Alignment helper ──────────────────────────────────────────────────────────\n\nfunction parseAlign(node: Element): 'left' | 'center' | 'right' | 'justify' | undefined {\n const style = node.getAttribute('style') ?? '';\n const match = style.match(/text-align\\s*:\\s*(left|center|right|justify)/);\n return match ? (match[1] as 'left' | 'center' | 'right' | 'justify') : undefined;\n}\n\n// ── Unused export kept for type compatibility ──────────────────────────────────\nexport type { TextNode };\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — HTML Serializer (Document Model → Clean HTML)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n BlockNode,\n InlineNode,\n TextNode,\n Mark,\n MarkType,\n LinkMark,\n} from '../core/types.js';\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport function serializeToHTML(doc: EditorDocument): string {\n return doc.children.map(serializeBlock).join('\\n');\n}\n\n// ── Block serialization ───────────────────────────────────────────────────────\n\nfunction serializeBlock(node: BlockNode): string {\n switch (node.type) {\n case 'paragraph': {\n const content = serializeInlines(node.children);\n const align = node.align && node.align !== 'left' ? ` style=\"text-align:${node.align}\"` : '';\n return `<p${align}>${content || '<br>'}</p>`;\n }\n\n case 'heading': {\n const content = serializeInlines(node.children);\n const align = node.align && node.align !== 'left' ? ` style=\"text-align:${node.align}\"` : '';\n return `<h${node.level}${align}>${content}</h${node.level}>`;\n }\n\n case 'blockquote': {\n const inner = node.children.map(serializeBlock).join('\\n');\n return `<blockquote>\\n${inner}\\n</blockquote>`;\n }\n\n case 'code_block': {\n const code = escapeHTML(node.children.map((t) => t.text).join(''));\n const lang = node.lang ? ` class=\"language-${node.lang}\"` : '';\n return `<pre><code${lang}>${code}</code></pre>`;\n }\n\n case 'bullet_list': {\n const items = node.children\n .map((li) => ` <li>${serializeInlines(li.children)}</li>`)\n .join('\\n');\n return `<ul>\\n${items}\\n</ul>`;\n }\n\n case 'ordered_list': {\n const start = node.start && node.start !== 1 ? ` start=\"${node.start}\"` : '';\n const items = node.children\n .map((li) => ` <li>${serializeInlines(li.children)}</li>`)\n .join('\\n');\n return `<ol${start}>\\n${items}\\n</ol>`;\n }\n\n case 'image': {\n const alt = node.alt ? ` alt=\"${escapeAttr(node.alt)}\"` : '';\n const w = node.width ? ` width=\"${node.width}\"` : '';\n const h = node.height ? ` height=\"${node.height}\"` : '';\n return `<img src=\"${escapeAttr(node.src)}\"${alt}${w}${h}>`;\n }\n\n case 'hr':\n return '<hr>';\n\n case 'callout': {\n const content = serializeInlines(node.children);\n return `<div class=\"oe-callout oe-callout-${node.variant}\" data-callout-variant=\"${node.variant}\">${content || '<br>'}</div>`;\n }\n\n default:\n return '';\n }\n}\n\n// ── Inline serialization ──────────────────────────────────────────────────────\n\nfunction serializeInlines(nodes: InlineNode[]): string {\n // Group consecutive text nodes by their marks to minimize nesting\n return nodes.map(serializeInline).join('');\n}\n\nfunction serializeInline(node: InlineNode): string {\n if (node.type === 'hardbreak') return '<br>';\n\n let text = escapeHTML(node.text);\n if (text === '' && node.marks.length === 0) return '';\n\n // Apply marks from inside out: innermost = last mark applied\n // Order: code > link > bold > italic > underline > strikethrough\n const marks = [...node.marks].sort(markOrder);\n\n for (const mark of marks) {\n text = applyMark(text, mark);\n }\n return text;\n}\n\nfunction markOrder(a: Mark, b: Mark): number {\n const order: MarkType[] = ['strikethrough', 'underline', 'italic', 'bold', 'link', 'code'];\n return order.indexOf(a.type) - order.indexOf(b.type);\n}\n\nfunction applyMark(text: string, mark: Mark): string {\n switch (mark.type) {\n case 'bold': return `<strong>${text}</strong>`;\n case 'italic': return `<em>${text}</em>`;\n case 'underline': return `<u>${text}</u>`;\n case 'code': return `<code>${text}</code>`;\n case 'strikethrough': return `<s>${text}</s>`;\n case 'link': {\n const lm = mark as LinkMark;\n const target = lm.target === '_blank' ? ' target=\"_blank\" rel=\"noopener noreferrer\"' : '';\n const title = lm.title ? ` title=\"${escapeAttr(lm.title)}\"` : '';\n return `<a href=\"${escapeAttr(lm.href)}\"${target}${title}>${text}</a>`;\n }\n default:\n return text;\n }\n}\n\n// ── Escape helpers ────────────────────────────────────────────────────────────\n\nexport function escapeHTML(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\nexport function escapeAttr(str: string): string {\n return str.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<');\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Markdown Serializer & Deserializer (K9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n BlockNode,\n InlineNode,\n ListItemNode,\n Mark,\n LinkMark,\n CalloutVariant,\n} from '../core/types.js';\nimport { createText, createParagraph, emptyDocument } from '../core/model.js';\n\n// ── Markdown Serializer (Model → Markdown) ────────────────────────────────────\n\nexport function serializeToMarkdown(doc: EditorDocument): string {\n return doc.children.map(serializeMdBlock).filter(Boolean).join('\\n\\n');\n}\n\nfunction serializeMdBlock(node: BlockNode): string {\n switch (node.type) {\n case 'paragraph':\n return serializeMdInlines(node.children);\n\n case 'heading':\n return `${'#'.repeat(node.level)} ${serializeMdInlines(node.children)}`;\n\n case 'blockquote': {\n const inner = node.children.map(serializeMdBlock).join('\\n\\n');\n return inner.split('\\n').map((l) => `> ${l}`).join('\\n');\n }\n\n case 'code_block': {\n const lang = node.lang ?? '';\n const code = node.children.map((t) => t.text).join('');\n return `\\`\\`\\`${lang}\\n${code}\\n\\`\\`\\``;\n }\n\n case 'bullet_list':\n return node.children.map((li) => `- ${serializeMdInlines(li.children)}`).join('\\n');\n\n case 'ordered_list': {\n const start = node.start ?? 1;\n return node.children\n .map((li, i) => `${start + i}. ${serializeMdInlines(li.children)}`)\n .join('\\n');\n }\n\n case 'image':\n return ``;\n\n case 'hr':\n return '---';\n\n case 'callout': {\n const variant = node.variant ?? 'info';\n const text = serializeMdInlines(node.children);\n // Multi-line: prefix every line with \"> \" (empty lines get bare \">\")\n const body = text.split('\\n').map((l) => l ? `> ${l}` : '>').join('\\n');\n return `> [!${variant}]\\n${body}`;\n }\n\n default:\n return '';\n }\n}\n\nfunction serializeMdInlines(nodes: InlineNode[]): string {\n return nodes.map(serializeMdInline).join('');\n}\n\nfunction serializeMdInline(node: InlineNode): string {\n if (node.type === 'hardbreak') return '\\n';\n\n let text = node.text;\n\n // Sort marks for consistent output (outermost → innermost)\n const marks = [...node.marks].sort((a, b) => {\n const order: Mark['type'][] = ['link', 'bold', 'italic', 'strikethrough', 'underline', 'code'];\n return order.indexOf(a.type) - order.indexOf(b.type);\n });\n\n for (const mark of marks) {\n switch (mark.type) {\n case 'bold': text = `**${text}**`; break;\n case 'italic': text = `*${text}*`; break;\n case 'code': text = `\\`${text}\\``; break;\n case 'strikethrough': text = `~~${text}~~`; break;\n case 'underline': text = `<u>${text}</u>`; break;\n case 'link': {\n const lm = mark as LinkMark;\n const title = lm.title ? ` \"${lm.title}\"` : '';\n text = `[${text}](${lm.href}${title})`;\n break;\n }\n }\n }\n\n return text;\n}\n\n// ── Markdown Deserializer (Markdown → Model) ──────────────────────────────────\n\nexport function deserializeMarkdown(md: string): EditorDocument {\n if (!md || md.trim() === '') return emptyDocument();\n const blocks = parseMdBlocks(md);\n return blocks.length > 0 ? { children: blocks } : emptyDocument();\n}\n\nfunction parseMdBlocks(md: string): BlockNode[] {\n const lines = md.split('\\n');\n const blocks: BlockNode[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n // Empty line: skip\n if (line.trim() === '') { i++; continue; }\n\n // Code fence\n const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)/);\n if (fenceMatch) {\n const fence = fenceMatch[1];\n const lang = fenceMatch[2].trim();\n i++;\n const codeLines: string[] = [];\n while (i < lines.length && !lines[i].startsWith(fence)) {\n codeLines.push(lines[i]);\n i++;\n }\n if (i < lines.length) i++; // skip closing fence\n blocks.push({\n type: 'code_block',\n ...(lang ? { lang } : {}),\n children: [createText(codeLines.join('\\n'))],\n });\n continue;\n }\n\n // ATX Heading\n const headingMatch = line.match(/^(#{1,6})\\s+(.*)/);\n if (headingMatch) {\n const level = headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6;\n blocks.push({\n type: 'heading',\n level,\n children: parseMdInlines(headingMatch[2].trim()),\n });\n i++;\n continue;\n }\n\n // HR\n if (/^[-*_]{3,}\\s*$/.test(line.trim())) {\n blocks.push({ type: 'hr' });\n i++;\n continue;\n }\n\n // Blockquote / Callout (> [!variant] syntax)\n if (line.startsWith('>')) {\n const quoteLines: string[] = [];\n while (i < lines.length && lines[i].startsWith('>')) {\n quoteLines.push(lines[i].slice(1).trimStart());\n i++;\n }\n\n // Callout: first line is [!variant]\n const calloutMatch = quoteLines[0]?.match(/^\\[!(info|success|warning|danger)\\]$/i);\n if (calloutMatch) {\n const variant = calloutMatch[1].toLowerCase() as CalloutVariant;\n const contentText = quoteLines.slice(1).join('\\n');\n blocks.push({\n type: 'callout',\n variant,\n children: parseMdInlines(contentText),\n });\n continue;\n }\n\n const innerBlocks = parseMdBlocks(quoteLines.join('\\n'));\n blocks.push({\n type: 'blockquote',\n children: innerBlocks.length > 0 ? innerBlocks : [createParagraph('')],\n });\n continue;\n }\n\n // Unordered list\n if (/^[-*+] /.test(line)) {\n const items: ListItemNode[] = [];\n while (i < lines.length && /^[-*+] /.test(lines[i])) {\n items.push({\n type: 'list_item',\n children: parseMdInlines(lines[i].replace(/^[-*+]\\s+/, '')),\n });\n i++;\n }\n blocks.push({ type: 'bullet_list', children: items });\n continue;\n }\n\n // Ordered list\n if (/^\\d+\\. /.test(line)) {\n const items: ListItemNode[] = [];\n const startMatch = line.match(/^(\\d+)\\./);\n const start = startMatch ? parseInt(startMatch[1]) : 1;\n while (i < lines.length && /^\\d+\\. /.test(lines[i])) {\n items.push({\n type: 'list_item',\n children: parseMdInlines(lines[i].replace(/^\\d+\\.\\s+/, '')),\n });\n i++;\n }\n blocks.push({\n type: 'ordered_list',\n ...(start !== 1 ? { start } : {}),\n children: items,\n });\n continue;\n }\n\n // Standalone image\n const imgMatch = line.match(/^!\\[([^\\]]*)\\]\\(([^)]+)\\)\\s*$/);\n if (imgMatch) {\n blocks.push({ type: 'image', alt: imgMatch[1] || undefined, src: imgMatch[2] });\n i++;\n continue;\n }\n\n // Paragraph: accumulate until blank line or block start\n const paraLines: string[] = [];\n while (i < lines.length && lines[i].trim() !== '' && !isMdBlockStart(lines[i])) {\n paraLines.push(lines[i]);\n i++;\n }\n if (paraLines.length > 0) {\n blocks.push({ type: 'paragraph', children: parseMdInlines(paraLines.join(' ')) });\n }\n }\n\n return blocks;\n}\n\nfunction isMdBlockStart(line: string): boolean {\n return (\n /^#{1,6}\\s/.test(line) ||\n /^(`{3,}|~{3,})/.test(line) ||\n line.startsWith('>') ||\n /^[-*+] /.test(line) ||\n /^\\d+\\. /.test(line) ||\n /^[-*_]{3,}\\s*$/.test(line.trim())\n );\n}\n\n// ── Inline Parsing ────────────────────────────────────────────────────────────\n\ntype InlinePattern = {\n re: RegExp;\n handler: (match: RegExpExecArray) => InlineNode[];\n priority: number; // lower = higher priority when same index\n};\n\nconst INLINE_PATTERNS: InlinePattern[] = [\n // Code (highest priority — never parse inside)\n {\n re: /`([^`]+)`/,\n priority: 0,\n handler: (m) => [createText(m[1], [{ type: 'code' }])],\n },\n // Link: [text](url \"title\")\n {\n re: /\\[([^\\]]+)\\]\\(([^) \"]+)(?:\\s+\"([^\"]*)\")?\\)/,\n priority: 1,\n handler: (m) => {\n const inner = parseMdInlines(m[1]);\n const lm: LinkMark = { type: 'link', href: m[2], target: '_self', ...(m[3] ? { title: m[3] } : {}) };\n return inner.map((n) => {\n if (n.type === 'text') return createText(n.text, [...n.marks, lm]);\n return n;\n });\n },\n },\n // Image (inline)\n {\n re: /!\\[([^\\]]*)\\]\\(([^)]+)\\)/,\n priority: 1,\n handler: (m) => [createText(`[${m[1] || 'image'}]`, [])],\n },\n // Bold **text** or __text__\n {\n re: /\\*\\*([^*]+)\\*\\*|__([^_]+)__/,\n priority: 2,\n handler: (m) => {\n const text = m[1] ?? m[2];\n return parseMdInlinesWithMark(text, { type: 'bold' });\n },\n },\n // Italic *text* or _text_ (must come after bold)\n {\n re: /\\*([^*]+)\\*|_([^_]+)_/,\n priority: 3,\n handler: (m) => {\n const text = m[1] ?? m[2];\n return parseMdInlinesWithMark(text, { type: 'italic' });\n },\n },\n // Strikethrough ~~text~~\n {\n re: /~~([^~]+)~~/,\n priority: 2,\n handler: (m) => parseMdInlinesWithMark(m[1], { type: 'strikethrough' }),\n },\n // Underline <u>text</u>\n {\n re: /<u>([^<]+)<\\/u>/,\n priority: 2,\n handler: (m) => parseMdInlinesWithMark(m[1], { type: 'underline' }),\n },\n];\n\nfunction parseMdInlines(text: string): InlineNode[] {\n if (!text) return [createText('', [])];\n\n const result: InlineNode[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n // Find the earliest matching pattern\n let best: { index: number; match: RegExpExecArray; pattern: InlinePattern } | null = null;\n\n for (const pattern of INLINE_PATTERNS) {\n const re = new RegExp(pattern.re.source, pattern.re.flags.replace('g', ''));\n const match = re.exec(remaining);\n if (match === null) continue;\n\n const idx = match.index;\n if (\n !best ||\n idx < best.index ||\n (idx === best.index && pattern.priority < best.pattern.priority) ||\n (idx === best.index && pattern.priority === best.pattern.priority && match[0].length > best.match[0].length)\n ) {\n best = { index: idx, match, pattern };\n }\n }\n\n if (!best) {\n // No more patterns: rest is plain text\n result.push(createText(remaining, []));\n break;\n }\n\n // Plain text before match\n if (best.index > 0) {\n result.push(createText(remaining.slice(0, best.index), []));\n }\n\n // Matched nodes\n result.push(...best.pattern.handler(best.match));\n remaining = remaining.slice(best.index + best.match[0].length);\n }\n\n return result.length > 0 ? result : [createText(text, [])];\n}\n\nfunction parseMdInlinesWithMark(text: string, mark: Mark): InlineNode[] {\n const inner = parseMdInlines(text);\n return inner.map((node) => {\n if (node.type === 'text') {\n return createText(node.text, [...node.marks, mark]);\n }\n return node;\n });\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — DOM Renderer (Document Model → DOM)\n// Strategy: Keyed rendering — each block gets a stable data-oe-key attribute.\n// On re-render we diff by key and patch only changed blocks.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n BlockNode,\n InlineNode,\n Mark,\n LinkMark,\n} from '../core/types.js';\n\nlet _keyCounter = 0;\nfunction nextKey(): string {\n return `oe-${++_keyCounter}`;\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Full render: replace the content of `root` with a fresh render.\n * Attaches data-oe-key to each block element for future diffing.\n */\nexport function renderDocument(doc: EditorDocument, root: HTMLElement): void {\n // Build new DOM fragment\n const frag = document.createDocumentFragment();\n for (const block of doc.children) {\n const el = renderBlock(block);\n frag.appendChild(el);\n }\n\n // Replace all children\n root.innerHTML = '';\n root.appendChild(frag);\n}\n\n/**\n * Patch render: compare existing block elements by key and update only changed ones.\n * Blocks without a key get a fresh render.\n */\nexport function patchDocument(\n doc: EditorDocument,\n root: HTMLElement,\n keys: string[],\n): string[] {\n const existing = Array.from(root.children) as HTMLElement[];\n const newKeys: string[] = [];\n\n for (let i = 0; i < doc.children.length; i++) {\n const block = doc.children[i];\n const key = keys[i] ?? nextKey();\n newKeys.push(key);\n\n const oldEl = existing.find((el) => el.dataset.oeKey === key);\n const newEl = renderBlock(block);\n newEl.dataset.oeKey = key;\n\n if (!oldEl) {\n // New block — insert at position i\n const refEl = root.children[i];\n if (refEl) root.insertBefore(newEl, refEl);\n else root.appendChild(newEl);\n } else {\n // Replace if changed\n if (oldEl.outerHTML !== newEl.outerHTML) {\n root.replaceChild(newEl, oldEl);\n }\n }\n }\n\n // Remove blocks that no longer exist\n Array.from(root.children).forEach((el) => {\n const key = (el as HTMLElement).dataset.oeKey ?? '';\n if (!newKeys.includes(key)) el.remove();\n });\n\n return newKeys;\n}\n\n// ── Block rendering ───────────────────────────────────────────────────────────\n\nfunction renderBlock(node: BlockNode): HTMLElement {\n switch (node.type) {\n case 'paragraph': {\n const p = document.createElement('p');\n if (node.align && node.align !== 'left') p.style.textAlign = node.align;\n renderInlinesInto(node.children, p);\n return p;\n }\n\n case 'heading': {\n const h = document.createElement(`h${node.level}`);\n if (node.align && node.align !== 'left') h.style.textAlign = node.align;\n renderInlinesInto(node.children, h);\n return h;\n }\n\n case 'blockquote': {\n const bq = document.createElement('blockquote');\n for (const child of node.children) {\n bq.appendChild(renderBlock(child));\n }\n return bq;\n }\n\n case 'code_block': {\n const pre = document.createElement('pre');\n // Language badge — contenteditable=false so it doesn't interfere with cursor\n const badge = document.createElement('span');\n badge.className = 'oe-code-lang-badge';\n badge.contentEditable = 'false';\n badge.textContent = node.lang || 'plain';\n pre.appendChild(badge);\n const code = document.createElement('code');\n if (node.lang) code.className = `language-${node.lang}`;\n code.textContent = node.children.map((t) => t.text).join('');\n pre.appendChild(code);\n return pre;\n }\n\n case 'bullet_list': {\n const ul = document.createElement('ul');\n for (const li of node.children) {\n const liEl = document.createElement('li');\n renderInlinesInto(li.children, liEl);\n ul.appendChild(liEl);\n }\n return ul;\n }\n\n case 'ordered_list': {\n const ol = document.createElement('ol');\n if (node.start && node.start !== 1) ol.setAttribute('start', String(node.start));\n for (const li of node.children) {\n const liEl = document.createElement('li');\n renderInlinesInto(li.children, liEl);\n ol.appendChild(liEl);\n }\n return ol;\n }\n\n case 'image': {\n const div = document.createElement('div');\n div.className = 'oe-image-wrapper';\n const img = document.createElement('img');\n img.src = node.src;\n if (node.alt) img.alt = node.alt;\n if (node.width) img.width = node.width;\n if (node.height) img.height = node.height;\n img.draggable = false;\n div.appendChild(img);\n return div;\n }\n\n case 'hr': {\n const hr = document.createElement('hr');\n return hr;\n }\n\n case 'callout': {\n const div = document.createElement('div');\n div.className = `oe-callout oe-callout-${node.variant}`;\n div.dataset.calloutVariant = node.variant;\n renderInlinesInto(node.children, div);\n return div;\n }\n\n default: {\n const p = document.createElement('p');\n p.textContent = '';\n return p;\n }\n }\n}\n\n// ── Inline rendering ──────────────────────────────────────────────────────────\n\nfunction renderInlinesInto(nodes: InlineNode[], parent: HTMLElement): void {\n if (nodes.length === 0 || (nodes.length === 1 && nodes[0].type === 'text' && nodes[0].text === '')) {\n // Empty — add BR so the block is focusable\n parent.appendChild(document.createElement('br'));\n return;\n }\n\n for (const node of nodes) {\n if (node.type === 'hardbreak') {\n parent.appendChild(document.createElement('br'));\n continue;\n }\n\n // Build inline DOM with marks\n let el: Node = document.createTextNode(node.text);\n\n // Apply marks from outermost to innermost (reverse of serializer order)\n const marks = [...node.marks];\n for (const mark of marks.reverse()) {\n el = applyMarkToDOM(el, mark);\n }\n\n parent.appendChild(el);\n }\n}\n\nfunction applyMarkToDOM(inner: Node, mark: Mark): HTMLElement {\n let wrapper: HTMLElement;\n\n switch (mark.type) {\n case 'bold': wrapper = document.createElement('strong'); break;\n case 'italic': wrapper = document.createElement('em'); break;\n case 'underline': wrapper = document.createElement('u'); break;\n case 'strikethrough': wrapper = document.createElement('s'); break;\n case 'code': wrapper = document.createElement('code'); break;\n case 'link': {\n const lm = mark as LinkMark;\n const a = document.createElement('a');\n a.href = lm.href;\n if (lm.target === '_blank') {\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n }\n if (lm.title) a.title = lm.title;\n wrapper = a;\n break;\n }\n default:\n wrapper = document.createElement('span');\n }\n\n wrapper.appendChild(inner);\n return wrapper;\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Commands\n// Each command receives the current document + the editor's contentEditable div\n// and returns a new document (or null if nothing changed).\n// Commands use execCommand for inline formatting (isolated to this file) while\n// block-level operations manipulate the model directly.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorDocument, BlockNode, MarkType, ListItemNode, CalloutVariant } from './types.js';\nimport { deserializeHTML } from '../io/deserializer.js';\nimport {\n createParagraph,\n createHeading,\n createBulletList,\n createOrderedList,\n createBlockquote,\n createCodeBlock,\n createCallout,\n} from './model.js';\n\n// ── Read DOM state → Model ────────────────────────────────────────────────────\n\n/**\n * Re-sync the document model from the current DOM state.\n * Called after browser-native text input events.\n */\nexport function syncFromDOM(root: HTMLElement): EditorDocument {\n const html = root.innerHTML;\n return deserializeHTML(html);\n}\n\n// ── Inline Formatting ─────────────────────────────────────────────────────────\n// We use a hybrid: execCommand for inline formatting (still works in all browsers\n// for contentEditable), but we re-sync the model immediately after.\n\nexport function execInlineCommand(command: string, value?: string): void {\n document.execCommand(command, false, value ?? undefined);\n}\n\n// ── Block-level commands ──────────────────────────────────────────────────────\n\n/**\n * Set the block type of the block containing the cursor.\n * Returns the updated document.\n */\nexport function setBlockType(\n doc: EditorDocument,\n blockIndex: number,\n newType: string,\n attrs?: Record<string, unknown>,\n): EditorDocument {\n const blocks = [...doc.children];\n const block = blocks[blockIndex];\n if (!block) return doc;\n\n // Extract inline text from current block\n const inlineText = getBlockInlineText(block);\n\n let newBlock: BlockNode;\n switch (newType) {\n case 'paragraph':\n newBlock = { ...createParagraph(inlineText), ...(attrs ?? {}) } as BlockNode;\n break;\n case 'heading': {\n const level = (attrs?.level as 1 | 2 | 3 | 4 | 5 | 6) ?? 1;\n newBlock = createHeading(level, inlineText);\n break;\n }\n case 'h1': case 'H1':\n newBlock = createHeading(1, inlineText);\n break;\n case 'h2': case 'H2':\n newBlock = createHeading(2, inlineText);\n break;\n case 'h3': case 'H3':\n newBlock = createHeading(3, inlineText);\n break;\n case 'h4': case 'H4':\n newBlock = createHeading(4, inlineText);\n break;\n case 'h5': case 'H5':\n newBlock = createHeading(5, inlineText);\n break;\n case 'h6': case 'H6':\n newBlock = createHeading(6, inlineText);\n break;\n case 'blockquote': case 'BLOCKQUOTE':\n newBlock = createBlockquote(inlineText);\n break;\n case 'code_block': case 'PRE':\n newBlock = createCodeBlock(inlineText, attrs?.lang as string | undefined);\n break;\n case 'bullet_list':\n newBlock = createBulletList([inlineText]);\n break;\n case 'ordered_list':\n newBlock = createOrderedList([inlineText]);\n break;\n case 'callout': {\n const variant = (attrs?.variant as CalloutVariant) ?? 'info';\n newBlock = createCallout(inlineText, variant);\n break;\n }\n // CALLOUT-{variant} values from the block-type select\n case 'CALLOUT-info':\n case 'CALLOUT-success':\n case 'CALLOUT-warning':\n case 'CALLOUT-danger': {\n const variant = newType.split('-')[1] as CalloutVariant;\n newBlock = createCallout(inlineText, variant);\n break;\n }\n default:\n newBlock = createParagraph(inlineText);\n }\n\n blocks[blockIndex] = newBlock;\n return { children: blocks };\n}\n\n/**\n * Toggle a list type on the block at blockIndex.\n * If already that list type, convert back to paragraph.\n */\nexport function toggleList(\n doc: EditorDocument,\n blockIndex: number,\n listType: 'bullet_list' | 'ordered_list',\n): EditorDocument {\n const blocks = [...doc.children];\n const block = blocks[blockIndex];\n if (!block) return doc;\n\n if (block.type === listType) {\n // Unwrap: convert list items back to paragraphs\n const listBlock = block as { children: ListItemNode[] };\n const newBlocks = listBlock.children.map((li) =>\n createParagraph(li.children.map((n) => (n.type === 'text' ? n.text : '')).join(''))\n );\n blocks.splice(blockIndex, 1, ...newBlocks);\n } else if (block.type === 'bullet_list' || block.type === 'ordered_list') {\n // Switch list type\n const listBlock = block as { children: ListItemNode[] };\n if (listType === 'bullet_list') {\n blocks[blockIndex] = { type: 'bullet_list', children: listBlock.children };\n } else {\n blocks[blockIndex] = { type: 'ordered_list', children: listBlock.children };\n }\n } else {\n // Convert to list\n const text = getBlockInlineText(block);\n blocks[blockIndex] = listType === 'bullet_list'\n ? createBulletList([text])\n : createOrderedList([text]);\n }\n\n return { children: blocks };\n}\n\n/**\n * Set text alignment on the block at blockIndex.\n */\nexport function setAlignment(\n doc: EditorDocument,\n blockIndex: number,\n align: 'left' | 'center' | 'right' | 'justify',\n): EditorDocument {\n const blocks = [...doc.children];\n const block = blocks[blockIndex];\n if (!block) return doc;\n\n if (block.type === 'paragraph' || block.type === 'heading') {\n blocks[blockIndex] = { ...block, align };\n }\n\n return { children: blocks };\n}\n\n/**\n * Insert an image node after the block at blockIndex.\n */\nexport function insertImage(\n doc: EditorDocument,\n blockIndex: number,\n src: string,\n alt?: string,\n): EditorDocument {\n const blocks = [...doc.children];\n const imageNode: BlockNode = { type: 'image', src, alt };\n // Insert after current block, then add empty paragraph\n blocks.splice(blockIndex + 1, 0, imageNode, createParagraph(''));\n return { children: blocks };\n}\n\n/**\n * Insert a horizontal rule after the block at blockIndex.\n */\nexport function insertHr(doc: EditorDocument, blockIndex: number): EditorDocument {\n const blocks = [...doc.children];\n blocks.splice(blockIndex + 1, 0, { type: 'hr' }, createParagraph(''));\n return { children: blocks };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction getBlockInlineText(block: BlockNode): string {\n if (\n block.type === 'paragraph' ||\n block.type === 'heading' ||\n block.type === 'list_item' ||\n block.type === 'code_block' ||\n block.type === 'callout'\n ) {\n return block.children\n .map((n) => (n.type === 'text' ? n.text : ''))\n .join('');\n }\n if (block.type === 'bullet_list' || block.type === 'ordered_list') {\n return block.children\n .map((li) => li.children.map((n) => (n.type === 'text' ? n.text : '')).join(''))\n .join(' ');\n }\n if (block.type === 'blockquote') {\n return block.children.map(getBlockInlineText).join(' ');\n }\n return '';\n}\n\n// ── Active state queries ──────────────────────────────────────────────────────\n\nexport function isMarkActiveInDOM(markType: MarkType): boolean {\n const commandMap: Partial<Record<MarkType, string>> = {\n bold: 'bold',\n italic: 'italic',\n underline: 'underline',\n strikethrough: 'strikethrough',\n };\n const cmd = commandMap[markType];\n if (!cmd) return false;\n try {\n return document.queryCommandState(cmd);\n } catch {\n return false;\n }\n}\n\nexport function getBlockTypeFromDOM(root: HTMLElement): string {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return 'p';\n\n let node: Node | null = sel.anchorNode;\n while (node && node.parentNode !== root) {\n node = node.parentNode;\n }\n\n if (!node || node.nodeType !== Node.ELEMENT_NODE) return 'p';\n const tag = (node as Element).tagName.toLowerCase();\n\n // Map tag to select option value\n if (tag === 'h1') return 'H1';\n if (tag === 'h2') return 'H2';\n if (tag === 'h3') return 'H3';\n if (tag === 'h4') return 'H4';\n if (tag === 'blockquote') return 'BLOCKQUOTE';\n if (tag === 'pre') return 'PRE';\n if (tag === 'ul') return 'bullet_list';\n if (tag === 'ol') return 'ordered_list';\n if (tag === 'div' && (node as Element).classList?.contains('oe-callout')) {\n const variant = (node as HTMLElement).dataset?.calloutVariant ?? 'info';\n return `CALLOUT-${variant}`;\n }\n return 'P';\n}\n\nexport function getAlignmentFromDOM(root: HTMLElement): string {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return 'left';\n\n let node: Node | null = sel.anchorNode;\n while (node && node.parentNode !== root) {\n node = node.parentNode;\n }\n\n if (!node || node.nodeType !== Node.ELEMENT_NODE) return 'left';\n const style = (node as HTMLElement).style.textAlign;\n return style || 'left';\n}\n\n// ── Link command ──────────────────────────────────────────────────────────────\n\nexport function insertLink(href: string, target: string): void {\n document.execCommand('createLink', false, href);\n // Set target on the newly created link\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const range = sel.getRangeAt(0);\n const link = range.startContainer.parentElement?.closest('a');\n if (link) {\n link.target = target;\n if (target === '_blank') link.rel = 'noopener noreferrer';\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Selection utilities\n// Maps between DOM selections and the editor's model positions.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorDocument, ModelSelection, ModelPosition } from '../core/types.js';\n\n// ── DOM → Model ───────────────────────────────────────────────────────────────\n\n/**\n * Convert the current browser selection into a ModelSelection.\n * Returns null if there's no valid selection within the editor root.\n */\nexport function domSelectionToModel(\n root: HTMLElement,\n _doc: EditorDocument,\n): ModelSelection | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n\n const range = sel.getRangeAt(0);\n if (!root.contains(range.commonAncestorContainer)) return null;\n\n const anchor = domPointToModel(root, sel.anchorNode!, sel.anchorOffset);\n const focus = domPointToModel(root, sel.focusNode!, sel.focusOffset);\n\n if (!anchor || !focus) return null;\n\n return {\n anchor,\n focus,\n isCollapsed: sel.isCollapsed,\n };\n}\n\nfunction domPointToModel(\n root: HTMLElement,\n node: Node,\n offset: number,\n): ModelPosition | null {\n // Walk up to find the top-level block element\n let blockEl: Element | null = null;\n let current: Node | null = node;\n\n while (current && current !== root) {\n if (current.parentNode === root && current.nodeType === Node.ELEMENT_NODE) {\n blockEl = current as Element;\n break;\n }\n current = current.parentNode;\n }\n\n if (!blockEl) return null;\n\n const blockIndex = Array.from(root.children).indexOf(blockEl);\n if (blockIndex < 0) return null;\n\n // Calculate char offset within the block's text content\n const charOffset = getCharOffsetInBlock(blockEl, node, offset);\n\n // Check if it's inside a list item\n const liEl = findAncestorLi(node, blockEl);\n if (liEl) {\n const ul = liEl.parentElement!;\n const itemIndex = Array.from(ul.children).indexOf(liEl);\n return { blockIndex, itemIndex, offset: charOffset };\n }\n\n return { blockIndex, offset: charOffset };\n}\n\nfunction findAncestorLi(node: Node, stopAt: Element): HTMLElement | null {\n let current: Node | null = node;\n while (current && current !== stopAt) {\n if (current.nodeType === Node.ELEMENT_NODE && (current as Element).tagName === 'LI') {\n return current as HTMLElement;\n }\n current = current.parentNode;\n }\n return null;\n}\n\n/** Get character offset of a DOM point within a container element */\nfunction getCharOffsetInBlock(container: Element, targetNode: Node, targetOffset: number): number {\n let total = 0;\n const found = walkForOffset(container, targetNode, targetOffset, { count: 0, done: false, result: 0 });\n return found.result;\n}\n\ninterface WalkState {\n count: number;\n done: boolean;\n result: number;\n}\n\nfunction walkForOffset(node: Node, target: Node, targetOffset: number, state: WalkState): WalkState {\n if (state.done) return state;\n\n if (node === target) {\n state.result = state.count + (node.nodeType === Node.TEXT_NODE ? targetOffset : 0);\n state.done = true;\n return state;\n }\n\n if (node.nodeType === Node.TEXT_NODE) {\n state.count += (node.textContent ?? '').length;\n return state;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n const tag = (node as Element).tagName.toLowerCase();\n if (tag === 'br') {\n // BR at the end of a block = cursor position there\n if (node === target) {\n state.result = state.count;\n state.done = true;\n }\n return state;\n }\n }\n\n for (const child of Array.from(node.childNodes)) {\n state = walkForOffset(child, target, targetOffset, state);\n if (state.done) break;\n }\n\n return state;\n}\n\n// ── Model → DOM (restore selection after re-render) ──────────────────────────\n\n/**\n * Restore a selection in the DOM after a re-render.\n * Uses character offsets to find the correct text node.\n */\nexport function modelSelectionToDOM(\n root: HTMLElement,\n sel: ModelSelection,\n): void {\n try {\n const anchorPoint = modelPointToDOM(root, sel.anchor);\n const focusPoint = modelPointToDOM(root, sel.focus);\n\n if (!anchorPoint || !focusPoint) return;\n\n const domSel = window.getSelection();\n if (!domSel) return;\n\n const range = document.createRange();\n range.setStart(anchorPoint.node, anchorPoint.offset);\n range.collapse(sel.isCollapsed);\n\n domSel.removeAllRanges();\n domSel.addRange(range);\n\n if (!sel.isCollapsed) {\n domSel.extend(focusPoint.node, focusPoint.offset);\n }\n } catch {\n // Selection restoration can fail — ignore silently\n }\n}\n\ninterface DOMPoint {\n node: Node;\n offset: number;\n}\n\nfunction modelPointToDOM(\n root: HTMLElement,\n pos: ModelPosition,\n): DOMPoint | null {\n const blockEl = root.children[pos.blockIndex];\n if (!blockEl) return null;\n\n let targetEl: Element = blockEl;\n\n // If it's a list and we have an itemIndex\n if (pos.itemIndex !== undefined) {\n const li = blockEl.children[pos.itemIndex];\n if (li) targetEl = li;\n }\n\n return findDOMPoint(targetEl, pos.offset);\n}\n\nfunction findDOMPoint(container: Element, targetOffset: number): DOMPoint | null {\n let remaining = targetOffset;\n\n const result = walkDOMForPoint(container, remaining);\n if (result) return result;\n\n // Fallback: end of container\n const lastChild = container.lastChild;\n if (lastChild) {\n if (lastChild.nodeType === Node.TEXT_NODE) {\n return { node: lastChild, offset: (lastChild.textContent ?? '').length };\n }\n return { node: container, offset: container.childNodes.length };\n }\n\n return { node: container, offset: 0 };\n}\n\nfunction walkDOMForPoint(node: Node, remaining: number): DOMPoint | null {\n if (node.nodeType === Node.TEXT_NODE) {\n const len = (node.textContent ?? '').length;\n if (remaining <= len) {\n return { node, offset: remaining };\n }\n return null; // Not in this node\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n const tag = (node as Element).tagName.toLowerCase();\n if (tag === 'br') {\n if (remaining === 0) {\n return { node: node.parentNode!, offset: Array.from(node.parentNode!.childNodes).indexOf(node as ChildNode) };\n }\n return null;\n }\n }\n\n let consumed = 0;\n for (const child of Array.from(node.childNodes)) {\n const childLen = getNodeTextLength(child);\n if (consumed + childLen >= remaining) {\n const result = walkDOMForPoint(child, remaining - consumed);\n if (result) return result;\n }\n consumed += childLen;\n }\n\n return null;\n}\n\nfunction getNodeTextLength(node: Node): number {\n if (node.nodeType === Node.TEXT_NODE) return (node.textContent ?? '').length;\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName === 'BR') return 0;\n let total = 0;\n for (const child of Array.from(node.childNodes)) total += getNodeTextLength(child);\n return total;\n}\n\n// ── Query helpers ─────────────────────────────────────────────────────────────\n\n/** Get the block index of the element containing the current cursor */\nexport function getActiveBlockIndex(root: HTMLElement): number {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return 0;\n\n let node: Node | null = sel.anchorNode;\n while (node && node.parentNode !== root) {\n node = node.parentNode;\n }\n\n if (!node) return 0;\n return Array.from(root.children).indexOf(node as Element);\n}\n\n/** Get the tag name of the block element containing the cursor */\nexport function getActiveBlockTag(root: HTMLElement): string {\n const idx = getActiveBlockIndex(root);\n const el = root.children[idx];\n return el ? el.tagName.toLowerCase() : 'p';\n}\n\n/** Collect all mark types active on every character in the current selection */\nexport function getActiveMarks(root: HTMLElement): Set<string> {\n const sel = window.getSelection();\n const active = new Set<string>();\n if (!sel || sel.rangeCount === 0) return active;\n\n const range = sel.getRangeAt(0);\n if (!root.contains(range.commonAncestorContainer)) return active;\n\n // Walk ancestors of anchor node collecting formatting elements\n let node: Node | null = sel.anchorNode;\n while (node && node !== root) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const tag = (node as Element).tagName.toLowerCase();\n if (tag === 'strong' || tag === 'b') active.add('bold');\n if (tag === 'em' || tag === 'i') active.add('italic');\n if (tag === 'u') active.add('underline');\n if (tag === 's' || tag === 'del') active.add('strikethrough');\n if (tag === 'code') active.add('code');\n if (tag === 'a') active.add('link');\n }\n node = node.parentNode;\n }\n\n return active;\n}\n","import type { EditorLocale } from './types.js';\n\nexport const en: EditorLocale = {\n toolbar: {\n undo: 'Undo (Ctrl+Z)',\n redo: 'Redo (Ctrl+Y)',\n textFormat: 'Text Format',\n paragraph: 'Paragraph',\n heading: (level) => `Heading ${level}`,\n quote: 'Quote',\n codeBlock: 'Code Block',\n bold: 'Bold (Ctrl+B)',\n italic: 'Italic (Ctrl+I)',\n underline: 'Underline (Ctrl+U)',\n inlineCode: 'Inline Code (Ctrl+`)',\n alignLeft: 'Align Left',\n alignCenter: 'Align Center',\n alignRight: 'Align Right',\n justify: 'Justify',\n bulletList: 'Bullet List',\n orderedList: 'Ordered List',\n insertLink: 'Insert Link',\n insertImage: 'Insert Image',\n blockquote: 'Blockquote',\n horizontalRule: 'Horizontal Rule',\n htmlSource: 'Toggle HTML Source',\n calloutInfo: 'Callout: Info',\n calloutSuccess: 'Callout: Success',\n calloutWarning: 'Callout: Warning',\n calloutDanger: 'Callout: Danger',\n insertCallout: 'Insert Callout',\n },\n statusBar: {\n words: 'Words',\n characters: 'Characters',\n htmlSource: 'HTML',\n },\n dialogs: {\n linkUrl: 'Link URL:',\n openInNewTab: 'Open in new tab?',\n imageUrl: 'Image URL:',\n imageAlt: 'Alt text (optional):',\n },\n plugins: {\n ai: {\n panelTitle: 'AI Assistant',\n noSelection: '(No text selected — entire document will be used)',\n customPromptPlaceholder: 'Enter custom instruction…',\n runButton: 'Run',\n applyButton: 'Apply',\n generating: '⏳ Generating…',\n noApiKey: '⚠️ No API key configured. Pass apiKey or endpoint when creating the plugin.',\n errorPrefix: '❌ Error: ',\n actions: {\n improve: 'Improve',\n shorten: 'Shorten',\n expand: 'Expand',\n summarize: 'Summarize',\n toGerman: '🇩🇪 To German',\n toEnglish: '🇬🇧 To English',\n },\n },\n emoji: {\n buttonTitle: 'Insert emoji',\n categories: {\n faces: 'Faces',\n hearts: 'Hearts',\n gestures: 'Gestures',\n nature: 'Nature',\n food: 'Food',\n objects: 'Objects',\n symbols: 'Symbols',\n },\n },\n },\n};\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — SVG Icon Library\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SVG_ATTRS = `width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"`;\n\nfunction svg(inner: string): string {\n return `<svg ${SVG_ATTRS}>${inner}</svg>`;\n}\n\nexport const ICONS: Record<string, string> = {\n undo: svg(`<path d=\"M3 7h10a6 6 0 0 1 0 12H9\"/><path d=\"M3 7l4-4M3 7l4 4\"/>`),\n redo: svg(`<path d=\"M21 7H11a6 6 0 0 0 0 12h4\"/><path d=\"M21 7l-4-4M21 7l-4 4\"/>`),\n bold: svg(`<path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/>`),\n italic: svg(`<line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/>`),\n underline: svg(`<path d=\"M6 4v6a6 6 0 0 0 12 0V4\"/><line x1=\"4\" y1=\"20\" x2=\"20\" y2=\"20\"/>`),\n strikethrough: svg(`<path d=\"M17.3 12H12m-6 0h3\"/><line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/><path d=\"M7 7.5C7 5.6 9 4 12 4s5 1.6 5 3.5c0 .7-.3 1.4-.8 2\"/><path d=\"M17 16.5C17 18.4 15 20 12 20s-5-1.6-5-3.5\"/>`),\n code_inline: svg(`<polyline points=\"16,18 22,12 16,6\"/><polyline points=\"8,6 2,12 8,18\"/>`),\n link: svg(`<path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/>`),\n image: svg(`<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21,15 16,10 5,21\"/>`),\n blockquote: svg(`<path d=\"M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z\"/><path d=\"M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z\"/>`),\n hr: svg(`<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>`),\n alignLeft: svg(`<line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"15\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"18\" y2=\"18\"/>`),\n alignCenter: svg(`<line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"6\" y1=\"12\" x2=\"18\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>`),\n alignRight: svg(`<line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"9\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"6\" y1=\"18\" x2=\"21\" y2=\"18\"/>`),\n alignJustify: svg(`<line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/>`),\n bulletList: svg(`<circle cx=\"4\" cy=\"7\" r=\"1.5\" fill=\"currentColor\"/><line x1=\"8\" y1=\"7\" x2=\"21\" y2=\"7\"/><circle cx=\"4\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><circle cx=\"4\" cy=\"17\" r=\"1.5\" fill=\"currentColor\"/><line x1=\"8\" y1=\"17\" x2=\"21\" y2=\"17\"/>`),\n orderedList: svg(`<line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\"/><path d=\"M4 6h1v4\"/><path d=\"M4 10h2\"/><path d=\"M6 18H4c0-1 2-2 2-3s-1-1.5-2-1\"/>`),\n htmlToggle: svg(`<path d=\"M10 9l-3 3 3 3\"/><path d=\"M14 15l3-3-3-3\"/><rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\"/>`),\n maximize: svg(`<path d=\"M8 3H5a2 2 0 0 0-2 2v3\"/><path d=\"M21 8V5a2 2 0 0 0-2-2h-3\"/><path d=\"M3 16v3a2 2 0 0 0 2 2h3\"/><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\"/>`),\n callout: svg(`<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>`),\n};\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Toolbar (Design A style, configurable)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface, ToolbarItemConfig } from '../core/types.js';\nimport type { EditorLocale } from '../locales/types.js';\nimport { en } from '../locales/en.js';\nimport { getActiveMarks } from './selection.js';\nimport { ICONS } from './icons.js';\n\n// ── Default toolbar item IDs (for filtering reference) ────────────────────────\n\n/** All item IDs available in the default toolbar, in order. */\nexport const TOOLBAR_ITEM_IDS = [\n 'undo', 'redo', 'blockType', 'bold', 'italic', 'underline', 'code',\n 'alignLeft', 'alignCenter', 'alignRight', 'alignJustify',\n 'bulletList', 'orderedList', 'link', 'image', 'blockquote', 'hr', 'callout',\n] as const;\n\n// ── Build locale-aware default toolbar ────────────────────────────────────────\n\nfunction buildDefaultToolbar(locale: EditorLocale): ToolbarItemConfig[] {\n const t = locale.toolbar;\n return [\n { type: 'button', id: 'undo', icon: 'undo', title: t.undo, command: 'undo' },\n { type: 'button', id: 'redo', icon: 'redo', title: t.redo, command: 'redo' },\n { type: 'separator' },\n {\n type: 'select',\n id: 'blockType',\n title: t.textFormat,\n command: 'setBlock',\n options: [\n { label: t.paragraph, value: 'P' },\n { label: t.heading(1), value: 'H1' },\n { label: t.heading(2), value: 'H2' },\n { label: t.heading(3), value: 'H3' },\n { label: t.heading(4), value: 'H4' },\n { label: t.quote, value: 'BLOCKQUOTE' },\n { label: t.codeBlock, value: 'PRE' },\n { label: t.calloutInfo, value: 'CALLOUT-info' },\n { label: t.calloutSuccess, value: 'CALLOUT-success' },\n { label: t.calloutWarning, value: 'CALLOUT-warning' },\n { label: t.calloutDanger, value: 'CALLOUT-danger' },\n ],\n getValue: (editor) => editor.getActiveBlockType(),\n },\n { type: 'separator' },\n { type: 'button', id: 'bold', icon: 'bold', title: t.bold, command: 'bold', isActive: (e) => e.isMarkActive('bold') },\n { type: 'button', id: 'italic', icon: 'italic', title: t.italic, command: 'italic', isActive: (e) => e.isMarkActive('italic') },\n { type: 'button', id: 'underline', icon: 'underline', title: t.underline, command: 'underline', isActive: (e) => e.isMarkActive('underline') },\n { type: 'button', id: 'code', icon: 'code_inline', title: t.inlineCode, command: 'code', isActive: (e) => e.isMarkActive('code') },\n { type: 'separator' },\n { type: 'button', id: 'alignLeft', icon: 'alignLeft', title: t.alignLeft, command: 'alignLeft' },\n { type: 'button', id: 'alignCenter', icon: 'alignCenter', title: t.alignCenter, command: 'alignCenter' },\n { type: 'button', id: 'alignRight', icon: 'alignRight', title: t.alignRight, command: 'alignRight' },\n { type: 'button', id: 'alignJustify', icon: 'alignJustify', title: t.justify, command: 'alignJustify' },\n { type: 'separator' },\n { type: 'button', id: 'bulletList', icon: 'bulletList', title: t.bulletList, command: 'bulletList', isActive: (e) => e.getActiveBlockType() === 'bullet_list' },\n { type: 'button', id: 'orderedList', icon: 'orderedList', title: t.orderedList, command: 'orderedList', isActive: (e) => e.getActiveBlockType() === 'ordered_list' },\n { type: 'separator' },\n { type: 'button', id: 'link', icon: 'link', title: t.insertLink, command: 'link' },\n { type: 'button', id: 'image', icon: 'image', title: t.insertImage, command: 'image' },\n { type: 'button', id: 'blockquote', icon: 'blockquote', title: t.blockquote, command: 'blockquote' },\n { type: 'button', id: 'hr', icon: 'hr', title: t.horizontalRule, command: 'hr' },\n { type: 'button', id: 'callout', icon: 'callout', title: t.insertCallout, command: 'callout', isActive: (e) => e.getActiveBlockType().startsWith('CALLOUT-') },\n ];\n}\n\n/** English default toolbar (backwards-compatible export). */\nexport const DEFAULT_TOOLBAR: ToolbarItemConfig[] = buildDefaultToolbar(en);\n\n// ── Toolbar filter ────────────────────────────────────────────────────────────\n\n/**\n * Filter the default toolbar by a list of item IDs.\n * Orphaned separators (leading, trailing, consecutive) are removed automatically.\n */\nexport function filterToolbar(ids: string[], locale: EditorLocale = en): ToolbarItemConfig[] {\n const source = buildDefaultToolbar(locale);\n const filtered = source.filter((item) => {\n if (item.type === 'separator' || item.type === 'spacer') return true;\n return 'id' in item && ids.includes(item.id);\n });\n\n const result: ToolbarItemConfig[] = [];\n for (const item of filtered) {\n if (item.type === 'separator') {\n const last = result[result.length - 1];\n if (!last || last.type === 'separator' || last.type === 'spacer') continue;\n result.push(item);\n } else {\n result.push(item);\n }\n }\n while (result.length > 0) {\n const last = result[result.length - 1];\n if (last.type === 'separator' || last.type === 'spacer') result.pop();\n else break;\n }\n return result;\n}\n\n// ── Toolbar class ─────────────────────────────────────────────────────────────\n\n// ── Callout variant picker ────────────────────────────────────────────────────\n\nconst CALLOUT_PICKER_STYLE_ID = 'oe-callout-picker-styles';\n\nfunction injectCalloutPickerStyles(): void {\n if (document.getElementById(CALLOUT_PICKER_STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = CALLOUT_PICKER_STYLE_ID;\n style.textContent = `\n.oe-callout-picker {\n position: fixed;\n z-index: 10000;\n background: var(--oe-bg, #ffffff);\n border: 1px solid var(--oe-border, #e7e5e4);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.12);\n padding: 5px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 160px;\n font-family: var(--oe-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n}\n.oe-callout-picker-item {\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 7px 10px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--oe-text, #1c1917);\n user-select: none;\n transition: background 80ms;\n}\n.oe-callout-picker-item:hover {\n background: var(--oe-btn-hover-bg, #f5f5f4);\n}\n.oe-callout-picker-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n`;\n document.head.appendChild(style);\n}\n\nconst CALLOUT_VARIANTS_PICKER = [\n { variant: 'info', label: 'Info', color: '#3b82f6' },\n { variant: 'success', label: 'Success', color: '#22c55e' },\n { variant: 'warning', label: 'Warning', color: '#f59e0b' },\n { variant: 'danger', label: 'Danger', color: '#ef4444' },\n] as const;\n\n// ── Toolbar class ─────────────────────────────────────────────────────────────\n\nexport class Toolbar {\n private el: HTMLElement;\n private editor: EditorInterface;\n private config: ToolbarItemConfig[];\n private itemEls: Map<string, HTMLElement> = new Map();\n private disabled = false;\n private locale: EditorLocale;\n private calloutPickerEl: HTMLElement | null = null;\n\n constructor(\n el: HTMLElement,\n editor: EditorInterface,\n config?: ToolbarItemConfig[],\n toolbarItems?: string[],\n locale: EditorLocale = en,\n ) {\n this.el = el;\n this.editor = editor;\n this.locale = locale;\n if (config) {\n this.config = config;\n } else if (toolbarItems) {\n this.config = filterToolbar(toolbarItems, locale);\n } else {\n this.config = buildDefaultToolbar(locale);\n }\n this.render();\n }\n\n private render(): void {\n this.el.innerHTML = '';\n this.el.className = 'oe-toolbar';\n\n for (const item of this.config) {\n const itemEl = this.renderItem(item);\n this.el.appendChild(itemEl);\n }\n }\n\n private renderItem(item: ToolbarItemConfig): HTMLElement {\n switch (item.type) {\n case 'separator': {\n const sep = document.createElement('div');\n sep.className = 'oe-toolbar-sep';\n return sep;\n }\n\n case 'spacer': {\n const sp = document.createElement('div');\n sp.className = 'oe-toolbar-spacer';\n return sp;\n }\n\n case 'select': {\n const wrap = document.createElement('div');\n wrap.className = 'oe-toolbar-select-wrap';\n const sel = document.createElement('select');\n sel.className = 'oe-toolbar-select';\n sel.title = item.title;\n\n for (const opt of item.options) {\n const o = document.createElement('option');\n o.value = opt.value;\n o.textContent = opt.label;\n sel.appendChild(o);\n }\n\n sel.addEventListener('change', () => {\n const val = sel.value;\n this.handleBlockTypeChange(val);\n // Return focus to editor after selection\n setTimeout(() => this.editor.focus(), 0);\n });\n\n wrap.appendChild(sel);\n this.itemEls.set(item.id, sel);\n return wrap;\n }\n\n case 'button': {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'oe-toolbar-btn';\n btn.title = item.title;\n btn.innerHTML = ICONS[item.icon as keyof typeof ICONS] ?? item.icon;\n\n btn.addEventListener('mousedown', (e) => {\n e.preventDefault(); // Don't blur editor\n });\n btn.addEventListener('click', () => {\n this.handleCommand(item.command, item as { id: string; command: string });\n });\n\n this.itemEls.set(item.id, btn);\n return btn;\n }\n }\n }\n\n private handleCommand(command: string, item?: { id: string }): void {\n const editor = this.editor as EditorInterface & {\n chain: () => ReturnType<EditorInterface['chain']>;\n toggleHTMLMode?: () => void;\n options?: { onImageUpload?: (f: File) => Promise<string> };\n };\n\n switch (command) {\n case 'bold': editor.chain().toggleMark('bold').run(); break;\n case 'italic': editor.chain().toggleMark('italic').run(); break;\n case 'underline': editor.chain().toggleMark('underline').run(); break;\n case 'code': editor.chain().toggleMark('code').run(); break;\n case 'bulletList': editor.chain().toggleList('bullet_list').run(); break;\n case 'orderedList': editor.chain().toggleList('ordered_list').run(); break;\n case 'blockquote': editor.chain().setBlock('blockquote').run(); break;\n case 'hr': editor.chain().insertHr().run(); break;\n case 'callout': {\n const btnEl = item ? this.itemEls.get(item.id) : null;\n if (btnEl) this.toggleCalloutPicker(btnEl);\n break;\n }\n case 'undo': editor.chain().undo().run(); break;\n case 'redo': editor.chain().redo().run(); break;\n case 'alignLeft': editor.chain().setAlign('left').run(); break;\n case 'alignCenter': editor.chain().setAlign('center').run(); break;\n case 'alignRight': editor.chain().setAlign('right').run(); break;\n case 'alignJustify':editor.chain().setAlign('justify').run(); break;\n case 'link': this.handleLinkCommand(); break;\n case 'image': this.handleImageCommand(); break;\n case 'htmlToggle': (editor as unknown as { toggleHTMLMode?: () => void }).toggleHTMLMode?.(); break;\n }\n }\n\n private toggleCalloutPicker(anchorEl: HTMLElement): void {\n if (this.calloutPickerEl) {\n this.closeCalloutPicker();\n return;\n }\n\n injectCalloutPickerStyles();\n\n const picker = document.createElement('div');\n picker.className = 'oe-callout-picker';\n this.calloutPickerEl = picker;\n\n for (const { variant, label, color } of CALLOUT_VARIANTS_PICKER) {\n const item = document.createElement('div');\n item.className = 'oe-callout-picker-item';\n item.innerHTML = `<span class=\"oe-callout-picker-dot\" style=\"background:${color}\"></span>${label}`;\n item.addEventListener('mousedown', (e) => {\n e.preventDefault(); // keep editor focus\n this.closeCalloutPicker();\n this.editor.chain().setBlock('callout', { variant }).run();\n });\n picker.appendChild(item);\n }\n\n document.body.appendChild(picker);\n\n // Position below the anchor button\n const rect = anchorEl.getBoundingClientRect();\n picker.style.top = `${rect.bottom + 4}px`;\n picker.style.left = `${rect.left}px`;\n\n // Clamp to viewport\n requestAnimationFrame(() => {\n if (!picker.isConnected) return;\n const vw = window.innerWidth;\n const right = rect.left + picker.offsetWidth;\n if (right > vw - 8) picker.style.left = `${vw - picker.offsetWidth - 8}px`;\n });\n\n // Close on outside click\n const onOutside = (e: MouseEvent): void => {\n if (!picker.contains(e.target as Node) && e.target !== anchorEl) {\n this.closeCalloutPicker();\n document.removeEventListener('mousedown', onOutside, true);\n }\n };\n // Delay so this click doesn't immediately close the picker\n setTimeout(() => document.addEventListener('mousedown', onOutside, true), 0);\n }\n\n private closeCalloutPicker(): void {\n this.calloutPickerEl?.remove();\n this.calloutPickerEl = null;\n }\n\n private handleBlockTypeChange(val: string): void {\n switch (val) {\n case 'P':\n this.editor.chain().setBlock('paragraph').run();\n return;\n case 'H1':\n case 'H2':\n case 'H3':\n case 'H4':\n case 'H5':\n case 'H6':\n this.editor.chain().setBlock('heading', { level: Number(val[1]) }).run();\n return;\n case 'BLOCKQUOTE':\n this.editor.chain().setBlock('blockquote').run();\n return;\n case 'PRE':\n this.editor.chain().setBlock('code_block').run();\n return;\n case 'bullet_list':\n case 'ordered_list':\n this.editor.chain().setBlock(val).run();\n return;\n case 'CALLOUT-info':\n case 'CALLOUT-success':\n case 'CALLOUT-warning':\n case 'CALLOUT-danger': {\n const variant = val.split('-')[1];\n this.editor.chain().setBlock('callout', { variant }).run();\n return;\n }\n default:\n this.editor.chain().setBlock('paragraph').run();\n }\n }\n\n private handleLinkCommand(): void {\n const sel = window.getSelection();\n const selectedText = sel && !sel.isCollapsed ? sel.toString() : '';\n const d = this.locale.dialogs;\n\n const href = window.prompt(d.linkUrl, selectedText.startsWith('http') ? selectedText : 'https://');\n if (!href) return;\n\n const target = window.confirm(d.openInNewTab) ? '_blank' : '_self';\n this.editor.chain().toggleMark('link', { href, target }).run();\n }\n\n private handleImageCommand(): void {\n const d = this.locale.dialogs;\n const url = window.prompt(d.imageUrl, 'https://');\n if (!url) return;\n const alt = window.prompt(d.imageAlt, '') ?? '';\n this.editor.chain().insertImage(url, alt || undefined).run();\n }\n\n updateActiveState(): void {\n if (this.disabled) return;\n\n const marks = getActiveMarks(\n (this.editor as unknown as { editorEl: HTMLElement }).editorEl\n );\n\n const blockType = this.editor.getActiveBlockType();\n\n for (const [id, el] of this.itemEls) {\n const item = this.config.find(\n (i) => (i.type === 'button' || i.type === 'select') && 'id' in i && i.id === id\n );\n if (!item) continue;\n\n if (item.type === 'select') {\n (el as HTMLSelectElement).value = blockType;\n continue;\n }\n\n if (item.type === 'button') {\n const active = item.isActive ? item.isActive(this.editor) :\n (id === 'bold' && marks.has('bold')) ||\n (id === 'italic' && marks.has('italic')) ||\n (id === 'underline' && marks.has('underline')) ||\n (id === 'code' && marks.has('code')) ||\n (id === 'link' && marks.has('link')) ||\n (id === 'bulletList' && blockType === 'bullet_list') ||\n (id === 'orderedList' && blockType === 'ordered_list');\n\n el.classList.toggle('oe-active', active);\n }\n }\n }\n\n setDisabled(disabled: boolean): void {\n this.disabled = disabled;\n this.el.classList.toggle('oe-toolbar-disabled', disabled);\n for (const el of this.itemEls.values()) {\n if (el instanceof HTMLButtonElement) el.disabled = disabled;\n if (el instanceof HTMLSelectElement) el.disabled = disabled;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Bubble Toolbar (S5: floating toolbar on text selection)\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface } from '../core/types.js';\nimport type { EditorLocale } from '../locales/types.js';\nimport { en } from '../locales/en.js';\nimport { getActiveMarks } from './selection.js';\nimport { ICONS } from './icons.js';\n\ninterface BubbleButton {\n id: string;\n icon: string;\n title: string;\n action: () => void;\n isActive?: () => boolean;\n}\n\nexport class BubbleToolbar {\n private el: HTMLElement;\n private editor: EditorInterface;\n private locale: EditorLocale;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(el: HTMLElement, editor: EditorInterface, locale: EditorLocale = en) {\n this.el = el;\n this.editor = editor;\n this.locale = locale;\n this.render();\n }\n\n private get editorEl(): HTMLElement {\n return (this.editor as unknown as { editorEl: HTMLElement }).editorEl;\n }\n\n private render(): void {\n this.el.className = 'oe-bubble-toolbar';\n\n const t = this.locale.toolbar;\n const d = this.locale.dialogs;\n const buttons: BubbleButton[] = [\n {\n id: 'bold', icon: 'bold', title: t.bold,\n action: () => this.editor.chain().toggleMark('bold').run(),\n isActive: () => this.editor.isMarkActive('bold'),\n },\n {\n id: 'italic', icon: 'italic', title: t.italic,\n action: () => this.editor.chain().toggleMark('italic').run(),\n isActive: () => this.editor.isMarkActive('italic'),\n },\n {\n id: 'underline', icon: 'underline', title: t.underline,\n action: () => this.editor.chain().toggleMark('underline').run(),\n isActive: () => this.editor.isMarkActive('underline'),\n },\n {\n id: 'code_inline', icon: 'code_inline', title: t.inlineCode,\n action: () => this.editor.chain().toggleMark('code').run(),\n isActive: () => this.editor.isMarkActive('code'),\n },\n {\n id: 'link', icon: 'link', title: t.insertLink,\n action: () => {\n const href = window.prompt(d.linkUrl, 'https://');\n if (!href) return;\n const target = window.confirm(d.openInNewTab) ? '_blank' : '_self';\n this.editor.chain().toggleMark('link', { href, target }).run();\n },\n isActive: () => this.editor.isMarkActive('link'),\n },\n ];\n\n for (const btn of buttons) {\n const el = document.createElement('button');\n el.type = 'button';\n el.className = 'oe-bubble-btn';\n el.title = btn.title;\n el.innerHTML = ICONS[btn.icon] ?? btn.icon;\n el.dataset.btnId = btn.id;\n\n el.addEventListener('mousedown', (e) => {\n e.preventDefault(); // Don't lose selection\n });\n el.addEventListener('click', () => {\n btn.action();\n this.updateActiveState();\n });\n\n this.el.appendChild(el);\n }\n }\n\n onSelectionChange(): void {\n const sel = window.getSelection();\n\n if (!sel || sel.isCollapsed || !this.editorEl.contains(sel.anchorNode)) {\n this.hide();\n return;\n }\n\n // Show after a brief delay to avoid flicker during click\n if (this.hideTimer) clearTimeout(this.hideTimer);\n setTimeout(() => {\n const currentSel = window.getSelection();\n if (!currentSel || currentSel.isCollapsed) { this.hide(); return; }\n this.showAtSelection();\n this.updateActiveState();\n }, 100);\n }\n\n private showAtSelection(): void {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n\n if (!rect.width && !rect.height) { this.hide(); return; }\n\n const toolbarWidth = 280; // approximate\n const toolbarHeight = 44;\n const margin = 8;\n const scrollY = window.scrollY;\n const scrollX = window.scrollX;\n\n let top = rect.top + scrollY - toolbarHeight - margin;\n let left = rect.left + scrollX + rect.width / 2 - toolbarWidth / 2;\n\n // Keep within viewport\n if (top < scrollY + margin) top = rect.bottom + scrollY + margin;\n if (left < margin) left = margin;\n if (left + toolbarWidth > window.innerWidth - margin) {\n left = window.innerWidth - toolbarWidth - margin;\n }\n\n this.el.style.top = `${top}px`;\n this.el.style.left = `${left}px`;\n this.el.classList.add('oe-bubble-visible');\n }\n\n private hide(): void {\n this.hideTimer = setTimeout(() => {\n this.el.classList.remove('oe-bubble-visible');\n }, 200);\n }\n\n private updateActiveState(): void {\n const marks = getActiveMarks(this.editorEl);\n const btns = this.el.querySelectorAll<HTMLElement>('.oe-bubble-btn');\n btns.forEach((btn) => {\n const id = btn.dataset.btnId ?? '';\n const active =\n (id === 'bold' && marks.has('bold')) ||\n (id === 'italic' && marks.has('italic')) ||\n (id === 'underline' && marks.has('underline')) ||\n (id === 'code_inline' && marks.has('code')) ||\n (id === 'link' && marks.has('link'));\n btn.classList.toggle('oe-active', active);\n });\n }\n\n destroy(): void {\n if (this.hideTimer) clearTimeout(this.hideTimer);\n this.el.remove();\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — CSS injection (M13: CSS Custom Properties, Dark/Light Mode)\n// All styles are scoped to .oe-container — no global pollution.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst STYLE_ID = 'openedit-styles';\n\nexport function injectStyles(theme: 'light' | 'dark' | 'auto'): void {\n if (document.getElementById(STYLE_ID)) return;\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = getCSS();\n document.head.appendChild(style);\n}\n\nfunction getCSS(): string {\n return `\n/* ── CSS Custom Properties (Light Mode defaults) ────────────────────── */\n:root {\n --oe-bg: #ffffff;\n --oe-bg-toolbar: #fafaf9;\n --oe-bg-statusbar: #fafaf9;\n --oe-bg-code: #1c1917;\n --oe-bg-html: #1c1917;\n --oe-border: #e7e5e4;\n --oe-border-inner: #e7e5e4;\n --oe-text: #1c1917;\n --oe-text-muted: #78716c;\n --oe-text-light: #a8a29e;\n --oe-text-code: #f5f5f4;\n --oe-btn-active-bg: #1c1917;\n --oe-btn-active-fg: #ffffff;\n --oe-btn-hover-bg: #f5f5f4;\n --oe-shadow: 0 4px 16px rgba(0,0,0,0.07);\n --oe-radius: 16px;\n --oe-radius-sm: 8px;\n --oe-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --oe-font-mono: \"Fira Code\", \"Cascadia Code\", \"Courier New\", monospace;\n --oe-focus-ring: 0 0 0 4px rgba(28,25,23,0.05);\n --oe-link-color: #2563eb;\n --oe-mark-bg: rgba(251,191,36,0.4);\n --oe-blockquote-border: #d6d3d1;\n}\n\n/* Dark mode via attribute */\n[data-oe-theme=\"dark\"] .oe-container,\n.oe-container[data-oe-theme=\"dark\"] {\n --oe-bg: #1c1917;\n --oe-bg-toolbar: #1c1917;\n --oe-bg-statusbar: #161412;\n --oe-bg-code: #0c0a09;\n --oe-bg-html: #0c0a09;\n --oe-border: #3a3330;\n --oe-border-inner: #2c2a28;\n --oe-text: #e7e5e4;\n --oe-text-muted: #a8a29e;\n --oe-text-light: #78716c;\n --oe-btn-active-bg: #e7e5e4;\n --oe-btn-active-fg: #1c1917;\n --oe-btn-hover-bg: #292524;\n --oe-shadow: 0 4px 16px rgba(0,0,0,0.4);\n --oe-link-color: #60a5fa;\n --oe-mark-bg: rgba(180,130,0,0.3);\n --oe-blockquote-border: #57534e;\n}\n\n/* Auto dark mode (follows OS preference) */\n@media (prefers-color-scheme: dark) {\n [data-oe-theme=\"auto\"] .oe-container,\n .oe-container[data-oe-theme=\"auto\"] {\n --oe-bg: #1c1917;\n --oe-bg-toolbar: #1c1917;\n --oe-bg-statusbar: #161412;\n --oe-bg-code: #0c0a09;\n --oe-bg-html: #0c0a09;\n --oe-border: #3a3330;\n --oe-border-inner: #2c2a28;\n --oe-text: #e7e5e4;\n --oe-text-muted: #a8a29e;\n --oe-text-light: #78716c;\n --oe-btn-active-bg: #e7e5e4;\n --oe-btn-active-fg: #1c1917;\n --oe-btn-hover-bg: #292524;\n --oe-shadow: 0 4px 16px rgba(0,0,0,0.4);\n --oe-link-color: #60a5fa;\n --oe-mark-bg: rgba(180,130,0,0.3);\n --oe-blockquote-border: #57534e;\n }\n}\n\n/* ── Container ──────────────────────────────────────────────────────── */\n.oe-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--oe-border);\n border-radius: var(--oe-radius);\n overflow: hidden;\n background: var(--oe-bg);\n box-shadow: var(--oe-shadow);\n font-family: var(--oe-font);\n color: var(--oe-text);\n box-sizing: border-box;\n transition: box-shadow 0.15s, border-color 0.15s;\n}\n\n.oe-container.oe-focused {\n box-shadow: var(--oe-shadow), var(--oe-focus-ring);\n border-color: #a8a29e;\n}\n\n/* ── Toolbar ────────────────────────────────────────────────────────── */\n.oe-toolbar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 2px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--oe-border);\n background: var(--oe-bg-toolbar);\n border-radius: var(--oe-radius) var(--oe-radius) 0 0;\n min-height: 50px;\n box-sizing: border-box;\n}\n\n.oe-toolbar-disabled {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.oe-toolbar-sep {\n width: 1px;\n height: 24px;\n background: var(--oe-border);\n margin: 0 6px;\n flex-shrink: 0;\n}\n\n.oe-toolbar-spacer {\n flex: 1;\n}\n\n.oe-toolbar-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: var(--oe-radius-sm);\n background: transparent;\n color: var(--oe-text-muted);\n cursor: pointer;\n flex-shrink: 0;\n transition: background 0.1s, color 0.1s;\n}\n\n.oe-toolbar-btn:hover {\n background: var(--oe-btn-hover-bg);\n color: var(--oe-text);\n}\n\n.oe-toolbar-btn.oe-active {\n background: var(--oe-btn-active-bg);\n color: var(--oe-btn-active-fg);\n}\n\n.oe-toolbar-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.oe-toolbar-select-wrap {\n position: relative;\n}\n\n.oe-toolbar-select {\n height: 32px;\n padding: 0 8px;\n border: 1px solid var(--oe-border);\n border-radius: var(--oe-radius-sm);\n background: var(--oe-bg);\n color: var(--oe-text);\n font-family: var(--oe-font);\n font-size: 13px;\n cursor: pointer;\n outline: none;\n min-width: 130px;\n transition: border-color 0.1s;\n}\n\n.oe-toolbar-select:hover {\n border-color: var(--oe-text-light);\n}\n\n.oe-toolbar-select:focus {\n border-color: var(--oe-text-muted);\n}\n\n/* ── Content wrapper ────────────────────────────────────────────────── */\n.oe-content-wrap {\n flex: 1;\n position: relative;\n min-height: 300px;\n display: flex;\n flex-direction: column;\n}\n\n/* ── Editor (contenteditable) ───────────────────────────────────────── */\n.oe-editor {\n flex: 1;\n padding: 28px 32px;\n outline: none;\n min-height: 300px;\n font-size: 15px;\n line-height: 1.7;\n color: var(--oe-text);\n background: var(--oe-bg);\n word-wrap: break-word;\n overflow-wrap: break-word;\n box-sizing: border-box;\n}\n\n/* Placeholder */\n.oe-editor.oe-empty::before {\n content: attr(data-placeholder);\n color: var(--oe-text-light);\n pointer-events: none;\n position: absolute;\n top: 28px;\n left: 32px;\n}\n\n/* Content styles */\n.oe-editor h1 { font-size: 1.875rem; font-weight: 700; margin: 0 0 1rem; color: var(--oe-text); }\n.oe-editor h2 { font-size: 1.5rem; font-weight: 600; margin: 1.4rem 0 0.75rem; color: var(--oe-text); }\n.oe-editor h3 { font-size: 1.25rem; font-weight: 600; margin: 1.2rem 0 0.6rem; color: var(--oe-text); }\n.oe-editor h4 { font-size: 1.125rem; font-weight: 600; margin: 1rem 0 0.5rem; color: var(--oe-text); }\n.oe-editor h1:first-child,\n.oe-editor h2:first-child { margin-top: 0; }\n.oe-editor p { margin: 0 0 0.875rem; }\n.oe-editor p:last-child { margin-bottom: 0; }\n.oe-editor strong, .oe-editor b { font-weight: 600; color: var(--oe-text); }\n.oe-editor em, .oe-editor i { font-style: italic; }\n.oe-editor u { text-decoration: underline; text-underline-offset: 2px; }\n.oe-editor s, .oe-editor del { text-decoration: line-through; color: var(--oe-text-muted); }\n.oe-editor code {\n background: rgba(0,0,0,0.06);\n color: #c2410c;\n border-radius: 4px;\n padding: 1px 5px;\n font-family: var(--oe-font-mono);\n font-size: 0.875em;\n}\n.oe-editor pre {\n background: var(--oe-bg-code);\n color: var(--oe-text-code);\n border-radius: 10px;\n padding: 16px 20px;\n padding-top: 32px;\n font-family: var(--oe-font-mono);\n font-size: 0.875rem;\n overflow-x: auto;\n margin: 0.875rem 0;\n line-height: 1.6;\n position: relative;\n}\n.oe-editor pre code { background: transparent; color: inherit; padding: 0; border-radius: 0; }\n/* Prevent highlight.js from overriding the <pre> background */\n.oe-editor pre code.hljs { background: transparent; }\n\n/* ── Code language badge ─────────────────────────────────────────────────── */\n.oe-code-lang-badge {\n position: absolute;\n top: 8px;\n right: 12px;\n font-size: 11px;\n font-family: var(--oe-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-weight: 500;\n color: rgba(255,255,255,0.35);\n background: transparent;\n cursor: pointer;\n user-select: none;\n padding: 2px 7px;\n border-radius: 4px;\n border: 1px solid transparent;\n transition: color 120ms, border-color 120ms;\n line-height: 1.5;\n letter-spacing: 0.02em;\n}\n.oe-code-lang-badge:hover {\n color: rgba(255,255,255,0.7);\n border-color: rgba(255,255,255,0.2);\n}\n\n/* ── Code language picker ────────────────────────────────────────────────── */\n.oe-code-lang-picker {\n position: fixed;\n z-index: 10000;\n background: var(--oe-bg, #ffffff);\n border: 1px solid var(--oe-border, #e7e5e4);\n border-radius: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.13), 0 2px 8px rgba(0,0,0,0.07);\n padding: 6px;\n min-width: 180px;\n max-height: 280px;\n display: flex;\n flex-direction: column;\n font-family: var(--oe-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n}\n.oe-code-lang-picker-input {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--oe-border, #e7e5e4);\n border-radius: 6px;\n padding: 6px 9px;\n font-size: 13px;\n font-family: inherit;\n background: var(--oe-bg, #fff);\n color: var(--oe-text, #1c1917);\n outline: none;\n margin-bottom: 4px;\n flex-shrink: 0;\n}\n.oe-code-lang-picker-input:focus {\n border-color: var(--oe-btn-active-bg, #1c1917);\n}\n.oe-code-lang-picker-list {\n overflow-y: auto;\n flex: 1;\n}\n.oe-code-lang-picker-item {\n padding: 6px 10px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--oe-text, #1c1917);\n font-family: var(--oe-font-mono, monospace);\n user-select: none;\n transition: background 80ms;\n}\n.oe-code-lang-picker-item:hover,\n.oe-code-lang-picker-item.oe-active {\n background: var(--oe-btn-hover-bg, #f5f5f4);\n}\n.oe-code-lang-picker-item.oe-active {\n background: var(--oe-btn-active-bg, #1c1917);\n color: var(--oe-btn-active-fg, #ffffff);\n}\n.oe-editor blockquote {\n border-left: 4px solid var(--oe-blockquote-border);\n padding: 4px 0 4px 16px;\n margin: 0.875rem 0;\n color: var(--oe-text-muted);\n font-style: italic;\n background: rgba(0,0,0,0.02);\n border-radius: 0 6px 6px 0;\n}\n.oe-editor ul { list-style: disc; padding-left: 1.5rem; margin: 0.875rem 0; }\n.oe-editor ol { list-style: decimal; padding-left: 1.5rem; margin: 0.875rem 0; }\n.oe-editor li { margin-bottom: 0.25rem; }\n.oe-editor a { color: var(--oe-link-color); text-decoration: underline; text-underline-offset: 2px; }\n.oe-editor mark { background: var(--oe-mark-bg); border-radius: 3px; padding: 0 3px; color: var(--oe-text); }\n.oe-editor hr { border: none; border-top: 2px solid var(--oe-border); margin: 1.5rem 0; }\n.oe-editor img { max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--oe-border); cursor: pointer; transition: box-shadow 0.15s; }\n.oe-editor img:hover { box-shadow: 0 0 0 3px rgba(59,130,246,0.35); }\n.oe-image-wrapper { margin: 0.875rem 0; display: block; }\n\n/* ── Callout Blocks ─────────────────────────────────────────────────── */\n.oe-editor .oe-callout {\n display: block;\n position: relative;\n padding: 12px 16px 12px 20px;\n margin: 0.875rem 0;\n border-left: 4px solid var(--oe-callout-border, #3b82f6);\n border-radius: 0 8px 8px 0;\n background: var(--oe-callout-bg, rgba(59,130,246,0.07));\n color: var(--oe-text);\n font-style: normal;\n line-height: 1.6;\n}\n\n/* Info */\n.oe-editor .oe-callout-info {\n --oe-callout-border: #3b82f6;\n --oe-callout-bg: rgba(59,130,246,0.07);\n --oe-callout-icon-color: #3b82f6;\n}\n.oe-editor .oe-callout-info::before {\n content: 'ℹ';\n position: absolute;\n left: -1.45rem;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.85em;\n color: var(--oe-callout-icon-color);\n line-height: 1;\n}\n\n/* Success */\n.oe-editor .oe-callout-success {\n --oe-callout-border: #22c55e;\n --oe-callout-bg: rgba(34,197,94,0.07);\n --oe-callout-icon-color: #22c55e;\n}\n.oe-editor .oe-callout-success::before {\n content: '✓';\n position: absolute;\n left: -1.3rem;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.85em;\n font-weight: 700;\n color: var(--oe-callout-icon-color);\n line-height: 1;\n}\n\n/* Warning */\n.oe-editor .oe-callout-warning {\n --oe-callout-border: #f59e0b;\n --oe-callout-bg: rgba(245,158,11,0.07);\n --oe-callout-icon-color: #f59e0b;\n}\n.oe-editor .oe-callout-warning::before {\n content: '⚠';\n position: absolute;\n left: -1.45rem;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.8em;\n color: var(--oe-callout-icon-color);\n line-height: 1;\n}\n\n/* Danger */\n.oe-editor .oe-callout-danger {\n --oe-callout-border: #ef4444;\n --oe-callout-bg: rgba(239,68,68,0.07);\n --oe-callout-icon-color: #ef4444;\n}\n.oe-editor .oe-callout-danger::before {\n content: '✕';\n position: absolute;\n left: -1.3rem;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.8em;\n font-weight: 700;\n color: var(--oe-callout-icon-color);\n line-height: 1;\n}\n\n/* Dark mode adjustments */\n[data-oe-theme=\"dark\"] .oe-editor .oe-callout-info,\n.oe-container[data-oe-theme=\"dark\"] .oe-editor .oe-callout-info {\n --oe-callout-bg: rgba(59,130,246,0.12);\n}\n[data-oe-theme=\"dark\"] .oe-editor .oe-callout-success,\n.oe-container[data-oe-theme=\"dark\"] .oe-editor .oe-callout-success {\n --oe-callout-bg: rgba(34,197,94,0.12);\n}\n[data-oe-theme=\"dark\"] .oe-editor .oe-callout-warning,\n.oe-container[data-oe-theme=\"dark\"] .oe-editor .oe-callout-warning {\n --oe-callout-bg: rgba(245,158,11,0.12);\n}\n[data-oe-theme=\"dark\"] .oe-editor .oe-callout-danger,\n.oe-container[data-oe-theme=\"dark\"] .oe-editor .oe-callout-danger {\n --oe-callout-bg: rgba(239,68,68,0.12);\n}\n@media (prefers-color-scheme: dark) {\n [data-oe-theme=\"auto\"] .oe-editor .oe-callout-info { --oe-callout-bg: rgba(59,130,246,0.12); }\n [data-oe-theme=\"auto\"] .oe-editor .oe-callout-success { --oe-callout-bg: rgba(34,197,94,0.12); }\n [data-oe-theme=\"auto\"] .oe-editor .oe-callout-warning { --oe-callout-bg: rgba(245,158,11,0.12); }\n [data-oe-theme=\"auto\"] .oe-editor .oe-callout-danger { --oe-callout-bg: rgba(239,68,68,0.12); }\n}\n\n/* ── Template Tags (K13) ────────────────────────────────────────────── */\n.oe-template-tag {\n display: inline-flex;\n align-items: center;\n background: rgba(99, 102, 241, 0.12);\n color: #4f46e5;\n border: 1px solid rgba(99, 102, 241, 0.25);\n border-radius: 6px;\n padding: 0 6px;\n font-size: 0.8em;\n font-family: var(--oe-font-mono);\n font-weight: 500;\n cursor: default;\n user-select: none;\n white-space: nowrap;\n vertical-align: baseline;\n line-height: 1.6;\n}\n[data-oe-theme=\"dark\"] .oe-template-tag {\n background: rgba(129, 140, 248, 0.15);\n color: #a5b4fc;\n border-color: rgba(129, 140, 248, 0.3);\n}\n\n/* ── HTML Source View ───────────────────────────────────────────────── */\n.oe-html-source {\n flex: 1;\n min-height: 300px;\n display: flex;\n flex-direction: column;\n}\n\n.oe-html-textarea {\n flex: 1;\n min-height: 300px;\n padding: 28px 32px;\n background: var(--oe-bg-html);\n color: #a5f3fc;\n font-family: var(--oe-font-mono);\n font-size: 13px;\n line-height: 1.7;\n border: none;\n outline: none;\n resize: none;\n tab-size: 2;\n box-sizing: border-box;\n}\n\n/* ── Status bar ─────────────────────────────────────────────────────── */\n.oe-statusbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 14px;\n background: var(--oe-bg-statusbar);\n border-top: 1px solid var(--oe-border);\n border-radius: 0 0 var(--oe-radius) var(--oe-radius);\n font-size: 12px;\n color: var(--oe-text-muted);\n min-height: 38px;\n box-sizing: border-box;\n}\n\n.oe-statusbar-path {\n font-family: var(--oe-font-mono);\n font-size: 11px;\n color: var(--oe-text-light);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 40%;\n}\n\n.oe-statusbar-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n}\n\n.oe-word-count,\n.oe-char-count {\n white-space: nowrap;\n}\n\n.oe-statusbar-divider {\n width: 1px;\n height: 14px;\n background: var(--oe-border);\n}\n\n.oe-html-toggle {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 0;\n background: none;\n border: none;\n color: var(--oe-text-muted);\n cursor: pointer;\n font-size: 12px;\n font-family: var(--oe-font);\n font-weight: 500;\n transition: color 0.1s;\n}\n\n.oe-html-toggle:hover {\n color: var(--oe-text);\n text-decoration: underline;\n}\n\n.oe-html-toggle.oe-active {\n color: var(--oe-text);\n}\n\n/* ── Bubble toolbar ─────────────────────────────────────────────────── */\n.oe-bubble-toolbar {\n position: absolute;\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 6px 8px;\n background: var(--oe-text);\n border-radius: 10px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.25);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s;\n z-index: 9999;\n white-space: nowrap;\n}\n\n.oe-bubble-toolbar.oe-bubble-visible {\n opacity: 1;\n pointer-events: all;\n}\n\n.oe-bubble-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n padding: 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--oe-btn-active-fg);\n cursor: pointer;\n transition: background 0.1s;\n}\n\n.oe-bubble-btn:hover {\n background: rgba(255,255,255,0.15);\n}\n\n.oe-bubble-btn.oe-active {\n background: rgba(255,255,255,0.25);\n}\n`;\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Image Resize Handler\n// Click on an image to select it, then drag the corner handles to resize.\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype Handle = 'nw' | 'ne' | 'sw' | 'se';\n\nexport class ImageResizer {\n private editorEl: HTMLElement;\n private syncCallback: () => void;\n private overlay: HTMLDivElement | null = null;\n private selectedImg: HTMLImageElement | null = null;\n private scrollParents: Element[] = [];\n private boundUpdatePos: (() => void) | null = null;\n\n constructor(editorEl: HTMLElement, syncCallback: () => void) {\n this.editorEl = editorEl;\n this.syncCallback = syncCallback;\n\n editorEl.addEventListener('click', this.onEditorClick);\n document.addEventListener('mousedown', this.onDocMousedown, true);\n }\n\n destroy(): void {\n this.editorEl.removeEventListener('click', this.onEditorClick);\n document.removeEventListener('mousedown', this.onDocMousedown, true);\n this.hideOverlay();\n }\n\n // ── Event handlers ──────────────────────────────────────────────────────────\n\n private readonly onEditorClick = (e: MouseEvent): void => {\n if (!(e.target instanceof HTMLImageElement)) return;\n e.stopPropagation();\n this.selectImage(e.target);\n };\n\n private readonly onDocMousedown = (e: MouseEvent): void => {\n if (!this.overlay) return;\n const t = e.target as Node;\n // Keep overlay when clicking handles or the selected image itself\n if (this.overlay.contains(t) || t === this.selectedImg) return;\n this.hideOverlay();\n };\n\n // ── Selection ───────────────────────────────────────────────────────────────\n\n private selectImage(img: HTMLImageElement): void {\n this.hideOverlay();\n this.selectedImg = img;\n\n const overlay = document.createElement('div');\n overlay.className = 'oe-img-overlay';\n // Overlay is transparent and non-blocking, handles do the work\n Object.assign(overlay.style, {\n position: 'fixed',\n border: '2px solid #3b82f6',\n borderRadius: '3px',\n zIndex: '9900',\n pointerEvents: 'none',\n boxSizing: 'border-box',\n });\n\n // Corner resize handles\n for (const pos of ['nw', 'ne', 'sw', 'se'] as Handle[]) {\n const handle = document.createElement('div');\n handle.className = `oe-img-handle oe-img-handle-${pos}`;\n Object.assign(handle.style, {\n position: 'absolute',\n width: '10px',\n height: '10px',\n background: '#ffffff',\n border: '2px solid #3b82f6',\n borderRadius: '2px',\n boxSizing: 'border-box',\n pointerEvents: 'all',\n cursor: `${pos}-resize`,\n ...handleOffset(pos),\n });\n handle.addEventListener('mousedown', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this.startResize(e, img, pos);\n });\n overlay.appendChild(handle);\n }\n\n // Dimension label (bottom-right)\n const label = document.createElement('div');\n label.className = 'oe-img-label';\n Object.assign(label.style, {\n position: 'absolute',\n bottom: '-26px',\n right: '0',\n background: 'rgba(28,25,23,0.85)',\n color: '#fff',\n fontSize: '11px',\n fontFamily: 'monospace',\n padding: '2px 6px',\n borderRadius: '4px',\n whiteSpace: 'nowrap',\n pointerEvents: 'none',\n });\n overlay.appendChild(label);\n\n document.body.appendChild(overlay);\n this.overlay = overlay;\n\n const updatePos = (): void => {\n if (!img.isConnected) { this.hideOverlay(); return; }\n const r = img.getBoundingClientRect();\n Object.assign(overlay.style, {\n top: `${r.top}px`,\n left: `${r.left}px`,\n width: `${r.width}px`,\n height: `${r.height}px`,\n });\n label.textContent = `${Math.round(r.width)} × ${Math.round(r.height)}`;\n };\n\n updatePos();\n this.boundUpdatePos = updatePos;\n window.addEventListener('scroll', updatePos, { passive: true, capture: true });\n window.addEventListener('resize', updatePos, { passive: true });\n }\n\n private hideOverlay(): void {\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n if (this.boundUpdatePos) {\n window.removeEventListener('scroll', this.boundUpdatePos, true);\n window.removeEventListener('resize', this.boundUpdatePos);\n this.boundUpdatePos = null;\n }\n this.selectedImg = null;\n }\n\n // ── Resize drag ─────────────────────────────────────────────────────────────\n\n private startResize(e: MouseEvent, img: HTMLImageElement, handle: Handle): void {\n const startX = e.clientX;\n const startY = e.clientY;\n const startW = img.getBoundingClientRect().width;\n const startH = img.getBoundingClientRect().height;\n const aspect = startW / (startH || 1);\n\n // Disable text selection while dragging\n const prev = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n\n const onMove = (ev: MouseEvent): void => {\n const dx = ev.clientX - startX;\n const dy = ev.clientY - startY;\n\n // Determine delta based on handle position\n const rightSide = handle === 'ne' || handle === 'se';\n const bottomSide = handle === 'sw' || handle === 'se';\n\n const wDelta = rightSide ? dx : -dx;\n const hDelta = bottomSide ? dy : -dy;\n\n // Use the larger delta to drive resize, maintain aspect ratio\n const delta = Math.abs(wDelta) >= Math.abs(hDelta) ? wDelta : hDelta * aspect;\n const newW = Math.max(40, Math.round(startW + delta));\n const newH = Math.max(20, Math.round(newW / aspect));\n\n // Apply directly to img element\n img.style.width = `${newW}px`;\n img.style.height = 'auto';\n img.setAttribute('width', String(newW));\n img.setAttribute('height', String(newH));\n\n this.boundUpdatePos?.();\n };\n\n const onUp = (): void => {\n document.removeEventListener('mousemove', onMove);\n document.removeEventListener('mouseup', onUp);\n document.body.style.userSelect = prev;\n this.syncCallback();\n };\n\n document.addEventListener('mousemove', onMove);\n document.addEventListener('mouseup', onUp);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction handleOffset(pos: Handle): Partial<CSSStyleDeclaration> {\n const offset = '-6px';\n switch (pos) {\n case 'nw': return { top: offset, left: offset };\n case 'ne': return { top: offset, right: offset };\n case 'sw': return { bottom: offset, left: offset };\n case 'se': return { bottom: offset, right: offset };\n }\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Code Language Picker\n//\n// Renders a clickable language badge on each <pre> block and shows a\n// floating dropdown with a filter input when the badge is clicked.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface } from '../core/types.js';\n\n// ── Language list ─────────────────────────────────────────────────────────────\n\nconst LANGUAGES: string[] = [\n 'plain',\n 'bash', 'c', 'cpp', 'csharp', 'css', 'dockerfile',\n 'go', 'graphql', 'html', 'java', 'javascript', 'json',\n 'kotlin', 'markdown', 'php', 'python', 'ruby', 'rust',\n 'scss', 'shell', 'sql', 'swift', 'typescript', 'xml', 'yaml',\n];\n\n// ── CodeLangPicker ────────────────────────────────────────────────────────────\n\nexport class CodeLangPicker {\n private editorEl: HTMLElement;\n private editor: EditorInterface;\n private pickerEl: HTMLElement | null = null;\n private activeIndex = 0;\n private filtered: string[] = [];\n private targetPreEl: HTMLElement | null = null;\n private onOutside: ((e: MouseEvent) => void) | null = null;\n private onOutsideTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(editorEl: HTMLElement, editor: EditorInterface) {\n this.editorEl = editorEl;\n this.editor = editor;\n\n // Event delegation: one listener for all badges, present + future\n this.editorEl.addEventListener('mousedown', this.onBadgeMousedown, true);\n this.editorEl.addEventListener('click', this.onBadgeClick, true);\n }\n\n // ── Event handlers ──────────────────────────────────────────────────────────\n\n private readonly onBadgeMousedown = (e: MouseEvent): void => {\n const target = e.target as Element;\n if (!target.classList.contains('oe-code-lang-badge')) return;\n // Prevent focus loss from the editor\n e.preventDefault();\n e.stopPropagation();\n };\n\n private readonly onBadgeClick = (e: MouseEvent): void => {\n const target = e.target as Element;\n if (!target.classList.contains('oe-code-lang-badge')) return;\n e.preventDefault();\n e.stopPropagation();\n\n const preEl = target.closest('pre') as HTMLElement | null;\n if (!preEl) return;\n\n if (this.pickerEl && this.targetPreEl === preEl) {\n this.closePicker();\n return;\n }\n\n this.closePicker();\n this.targetPreEl = preEl;\n this.openPicker(target as HTMLElement, preEl);\n };\n\n // ── Picker open / close ────────────────────────────────────────────────────\n\n private openPicker(badgeEl: HTMLElement, preEl: HTMLElement): void {\n const picker = document.createElement('div');\n picker.className = 'oe-code-lang-picker';\n this.pickerEl = picker;\n\n // Filter input\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'oe-code-lang-picker-input';\n input.placeholder = 'Filter…';\n picker.appendChild(input);\n\n // List container\n const list = document.createElement('div');\n list.className = 'oe-code-lang-picker-list';\n picker.appendChild(list);\n\n document.body.appendChild(picker);\n this.renderList(list, '');\n\n // Position below the badge\n const rect = badgeEl.getBoundingClientRect();\n picker.style.top = `${rect.bottom + 4}px`;\n picker.style.left = `${rect.left}px`;\n\n requestAnimationFrame(() => {\n if (!picker.isConnected) return;\n const vw = window.innerWidth;\n const right = rect.left + picker.offsetWidth;\n if (right > vw - 8) picker.style.left = `${vw - picker.offsetWidth - 8}px`;\n input.focus();\n });\n\n // Keyboard navigation inside the input\n input.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.activeIndex = Math.min(this.activeIndex + 1, this.filtered.length - 1);\n this.updateActive(list);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.activeIndex = Math.max(this.activeIndex - 1, 0);\n this.updateActive(list);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n const lang = this.filtered[this.activeIndex];\n if (lang !== undefined) this.selectLang(lang, preEl);\n } else if (e.key === 'Escape') {\n this.closePicker();\n }\n });\n\n input.addEventListener('input', () => {\n this.activeIndex = 0;\n this.renderList(list, input.value.trim().toLowerCase());\n });\n\n // Close on outside mousedown\n this.onOutside = (e: MouseEvent): void => {\n if (!picker.contains(e.target as Node)) {\n this.closePicker();\n }\n };\n this.onOutsideTimer = setTimeout(() => {\n this.onOutsideTimer = null;\n if (this.onOutside) document.addEventListener('mousedown', this.onOutside, true);\n }, 0);\n }\n\n private renderList(list: HTMLElement, query: string): void {\n this.filtered = query\n ? LANGUAGES.filter((l) => l.includes(query))\n : LANGUAGES;\n\n list.innerHTML = '';\n this.filtered.forEach((lang, i) => {\n const item = document.createElement('div');\n item.className = 'oe-code-lang-picker-item' + (i === this.activeIndex ? ' oe-active' : '');\n item.textContent = lang;\n item.addEventListener('mousedown', (e) => {\n e.preventDefault();\n this.selectLang(lang, this.targetPreEl!);\n });\n list.appendChild(item);\n });\n }\n\n private updateActive(list: HTMLElement): void {\n list.querySelectorAll('.oe-code-lang-picker-item').forEach((el, i) => {\n el.classList.toggle('oe-active', i === this.activeIndex);\n if (i === this.activeIndex) (el as HTMLElement).scrollIntoView({ block: 'nearest' });\n });\n }\n\n private selectLang(lang: string, preEl: HTMLElement): void {\n this.closePicker();\n\n // Place selection inside the <code> element of this block so\n // getActiveBlockIndex() targets the right block\n const codeEl = preEl.querySelector('code');\n if (codeEl) {\n const sel = window.getSelection();\n if (sel) {\n const range = document.createRange();\n range.setStart(codeEl, 0);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n }\n }\n\n const finalLang = lang === 'plain' ? undefined : lang;\n this.editor.chain().setBlock('code_block', { lang: finalLang }).run();\n }\n\n private closePicker(): void {\n if (this.onOutsideTimer !== null) {\n clearTimeout(this.onOutsideTimer);\n this.onOutsideTimer = null;\n }\n if (this.onOutside) {\n document.removeEventListener('mousedown', this.onOutside, true);\n this.onOutside = null;\n }\n this.pickerEl?.remove();\n this.pickerEl = null;\n this.targetPreEl = null;\n this.activeIndex = 0;\n this.filtered = [];\n }\n\n // ── Cleanup ────────────────────────────────────────────────────────────────\n\n destroy(): void {\n this.closePicker();\n this.editorEl.removeEventListener('mousedown', this.onBadgeMousedown, true);\n this.editorEl.removeEventListener('click', this.onBadgeClick, true);\n }\n}\n","import type { EditorLocale } from './types.js';\n\nexport const de: EditorLocale = {\n toolbar: {\n undo: 'Rückgängig (Ctrl+Z)',\n redo: 'Wiederholen (Ctrl+Y)',\n textFormat: 'Textformat',\n paragraph: 'Absatz',\n heading: (level) => `Überschrift ${level}`,\n quote: 'Zitat',\n codeBlock: 'Code-Block',\n bold: 'Fett (Ctrl+B)',\n italic: 'Kursiv (Ctrl+I)',\n underline: 'Unterstrichen (Ctrl+U)',\n inlineCode: 'Inline-Code (Ctrl+`)',\n alignLeft: 'Linksbündig',\n alignCenter: 'Zentriert',\n alignRight: 'Rechtsbündig',\n justify: 'Blocksatz',\n bulletList: 'Aufzählungsliste',\n orderedList: 'Nummerierte Liste',\n insertLink: 'Link einfügen',\n insertImage: 'Bild einfügen',\n blockquote: 'Zitat',\n horizontalRule: 'Trennlinie',\n htmlSource: 'HTML-Quellcode anzeigen',\n calloutInfo: 'Hinweis: Info',\n calloutSuccess: 'Hinweis: Erfolg',\n calloutWarning: 'Hinweis: Warnung',\n calloutDanger: 'Hinweis: Gefahr',\n insertCallout: 'Hinweisbox einfügen',\n },\n statusBar: {\n words: 'Wörter',\n characters: 'Zeichen',\n htmlSource: 'HTML',\n },\n dialogs: {\n linkUrl: 'Link-URL:',\n openInNewTab: 'In neuem Tab öffnen?',\n imageUrl: 'Bild-URL:',\n imageAlt: 'Alternativtext (optional):',\n },\n plugins: {\n ai: {\n panelTitle: 'KI-Assistent',\n noSelection: '(Kein Text ausgewählt — gesamtes Dokument wird verwendet)',\n customPromptPlaceholder: 'Eigene Anweisung eingeben…',\n runButton: 'Ausführen',\n applyButton: 'Übernehmen',\n generating: '⏳ Wird generiert…',\n noApiKey: '⚠️ Kein API-Key konfiguriert. Übergib apiKey oder endpoint beim Erstellen des Plugins.',\n errorPrefix: '❌ Fehler: ',\n actions: {\n improve: 'Verbessern',\n shorten: 'Kürzen',\n expand: 'Erweitern',\n summarize: 'Zusammenfassen',\n toGerman: '🇩🇪 Auf Deutsch',\n toEnglish: '🇬🇧 To English',\n },\n },\n emoji: {\n buttonTitle: 'Emoji einfügen',\n categories: {\n faces: 'Gesichter',\n hearts: 'Herzen',\n gestures: 'Gesten',\n nature: 'Natur',\n food: 'Essen',\n objects: 'Objekte',\n symbols: 'Symbole',\n },\n },\n },\n};\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Main Editor Class\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type {\n EditorDocument,\n EditorOptions,\n EditorInterface,\n EditorPlugin,\n EditorEventMap,\n EditorEventListener,\n ModelSelection,\n MarkType,\n ToolbarItemConfig,\n ChainInterface,\n} from './core/types.js';\nimport { History } from './core/history.js';\nimport { deserializeHTML } from './io/deserializer.js';\nimport { serializeToHTML } from './io/serializer.js';\nimport { serializeToMarkdown, deserializeMarkdown } from './io/markdown.js';\nimport { renderDocument } from './view/renderer.js';\nimport {\n syncFromDOM,\n execInlineCommand,\n setBlockType,\n toggleList,\n setAlignment,\n insertImage as insertImageCmd,\n insertHr as insertHrCmd,\n getBlockTypeFromDOM,\n getAlignmentFromDOM,\n insertLink,\n} from './core/commands.js';\nimport { domSelectionToModel, getActiveBlockIndex, getActiveMarks } from './view/selection.js';\nimport { Toolbar } from './view/toolbar.js';\nimport { BubbleToolbar } from './view/bubble-toolbar.js';\nimport { injectStyles } from './view/styles.js';\nimport { ImageResizer } from './view/image-resize.js';\nimport { CodeLangPicker } from './view/code-lang-picker.js';\nimport { emptyDocument } from './core/model.js';\nimport type { EditorLocale } from './locales/types.js';\nimport { en } from './locales/en.js';\nimport { de } from './locales/de.js';\n\n/** Built-in locales available for auto-detection. Add entries here as new languages are added. */\nconst BUILT_IN_LOCALES: Record<string, EditorLocale> = { de };\n\n/** Detect locale from navigator.language, fall back to English. */\nfunction detectLocale(): EditorLocale {\n const lang = (typeof navigator !== 'undefined' ? navigator.language : '').split('-')[0].toLowerCase();\n return BUILT_IN_LOCALES[lang] ?? en;\n}\n\nfunction mergeLocale(overrides: Partial<EditorLocale>): EditorLocale {\n return {\n toolbar: { ...en.toolbar, ...overrides.toolbar },\n statusBar: { ...en.statusBar, ...overrides.statusBar },\n dialogs: { ...en.dialogs, ...overrides.dialogs },\n plugins: {\n ai: { ...en.plugins.ai, actions: { ...en.plugins.ai.actions, ...overrides.plugins?.ai?.actions }, ...overrides.plugins?.ai },\n emoji: { ...en.plugins.emoji, categories: { ...en.plugins.emoji.categories, ...overrides.plugins?.emoji?.categories }, ...overrides.plugins?.emoji },\n },\n };\n}\n\ntype EventListeners = {\n [K in keyof EditorEventMap]: Set<EditorEventListener<K>>;\n};\n\nexport class Editor implements EditorInterface {\n private doc: EditorDocument;\n private history: History;\n private root: HTMLElement;\n private editorEl: HTMLElement; // contentEditable div\n private containerEl: HTMLElement; // outer wrapper\n private toolbar: Toolbar | null = null;\n private bubbleToolbar: BubbleToolbar | null = null;\n private imageResizer: ImageResizer | null = null;\n private codeLangPicker: CodeLangPicker | null = null;\n private plugins: EditorPlugin[] = [];\n private options: EditorOptions;\n readonly locale: EditorLocale;\n private listeners: EventListeners = {\n change: new Set(),\n selectionchange: new Set(),\n focus: new Set(),\n blur: new Set(),\n };\n private isComposing = false;\n private _isFocused = false;\n private isUpdating = false;\n // Debounce timer for syncing DOM → model after typing\n private syncTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(options: EditorOptions) {\n this.options = options;\n this.locale = options.locale ? mergeLocale(options.locale as Partial<EditorLocale>) : detectLocale();\n this.history = new History();\n\n // Resolve mount element\n const mount =\n typeof options.element === 'string'\n ? document.querySelector<HTMLElement>(options.element)\n : options.element;\n\n if (!mount) throw new Error(`[OpenEdit] Element not found: ${options.element}`);\n this.root = mount;\n\n // Initial document\n this.doc = options.content ? deserializeHTML(options.content) : emptyDocument();\n\n // Build DOM structure\n this.containerEl = this.buildContainer();\n this.editorEl = this.buildEditorEl();\n this.root.appendChild(this.containerEl);\n\n // Inject CSS\n injectStyles(options.theme ?? 'auto');\n\n // Initial render\n renderDocument(this.doc, this.editorEl);\n\n // Toolbar\n const toolbarEl = this.containerEl.querySelector<HTMLElement>('.oe-toolbar')!;\n this.toolbar = new Toolbar(toolbarEl, this, options.toolbar, options.toolbarItems, this.locale);\n\n // Bubble toolbar\n const bubbleEl = document.createElement('div');\n bubbleEl.className = 'oe-bubble-toolbar';\n document.body.appendChild(bubbleEl);\n this.bubbleToolbar = new BubbleToolbar(bubbleEl, this, this.locale);\n\n // Image resizer\n this.imageResizer = new ImageResizer(this.editorEl, () => this.scheduleSyncFromDOM());\n\n // Code language picker\n this.codeLangPicker = new CodeLangPicker(this.editorEl, this);\n\n // Placeholder\n this.updatePlaceholder();\n\n // Attach events\n this.attachEvents();\n\n // Apply theme\n this.applyTheme(options.theme ?? 'auto');\n }\n\n // ── DOM Structure ────────────────────────────────────────────────────────────\n\n private buildContainer(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'oe-container';\n\n el.innerHTML = `\n <div class=\"oe-toolbar\"></div>\n <div class=\"oe-content-wrap\">\n <div class=\"oe-editor\" contenteditable=\"true\" spellcheck=\"true\"></div>\n <div class=\"oe-html-source\" style=\"display:none\">\n <textarea class=\"oe-html-textarea\" spellcheck=\"false\"></textarea>\n </div>\n </div>\n <div class=\"oe-statusbar\">\n <div class=\"oe-statusbar-path\"></div>\n <div class=\"oe-statusbar-right\">\n <span class=\"oe-word-count\">${this.locale.statusBar.words}: 0</span>\n <span class=\"oe-char-count\">${this.locale.statusBar.characters}: 0</span>\n <div class=\"oe-statusbar-divider\"></div>\n <button type=\"button\" class=\"oe-html-toggle\" title=\"${this.locale.toolbar.htmlSource}\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10 9l-3 3 3 3\"/><path d=\"M14 15l3-3-3-3\"/><rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\"/></svg>\n ${this.locale.statusBar.htmlSource}\n </button>\n </div>\n </div>\n `;\n\n // Apply statusBar option\n const sb = this.options.statusBar;\n if (sb === false) {\n el.querySelector('.oe-statusbar')?.remove();\n } else if (sb !== undefined && typeof sb === 'object') {\n if (sb.wordCount === false) el.querySelector('.oe-word-count')?.remove();\n if (sb.charCount === false) el.querySelector('.oe-char-count')?.remove();\n if (sb.elementPath === false) el.querySelector('.oe-statusbar-path')?.remove();\n if (sb.htmlToggle === false) {\n el.querySelector('.oe-html-toggle')?.remove();\n el.querySelector('.oe-statusbar-divider')?.remove();\n }\n }\n\n return el;\n }\n\n private buildEditorEl(): HTMLElement {\n const el = this.containerEl.querySelector<HTMLElement>('.oe-editor')!;\n if (this.options.placeholder) {\n el.dataset.placeholder = this.options.placeholder;\n }\n if (this.options.readOnly) {\n el.contentEditable = 'false';\n }\n return el;\n }\n\n // ── Event wiring ─────────────────────────────────────────────────────────────\n\n private attachEvents(): void {\n const ed = this.editorEl;\n\n // Input: after character typed — defer sync to avoid mid-composition issues\n ed.addEventListener('input', this.onInput);\n ed.addEventListener('compositionstart', this.onCompositionStart);\n ed.addEventListener('compositionend', this.onCompositionEnd);\n\n // Selection\n document.addEventListener('selectionchange', this.onSelectionChange);\n\n // Focus / blur\n ed.addEventListener('focus', this.onFocus);\n ed.addEventListener('blur', this.onBlur);\n\n // Keyboard shortcuts\n ed.addEventListener('keydown', this.onKeydown);\n\n // Tab in code blocks\n ed.addEventListener('keydown', this.onTabInCodeBlock);\n\n // HTML source toggle\n const htmlToggle = this.containerEl.querySelector<HTMLElement>('.oe-html-toggle')!;\n htmlToggle.addEventListener('click', this.onHTMLToggleClick);\n\n // Paste: sanitize\n ed.addEventListener('paste', this.onPaste);\n\n // Drop images\n ed.addEventListener('drop', this.onDrop);\n }\n\n private readonly onInput = (): void => {\n if (this.isComposing || this.isUpdating) return;\n this.scheduleSyncFromDOM();\n };\n\n private readonly onCompositionStart = (): void => {\n this.isComposing = true;\n };\n\n private readonly onCompositionEnd = (): void => {\n this.isComposing = false;\n this.scheduleSyncFromDOM();\n };\n\n private readonly onHTMLToggleClick = (): void => {\n this.toggleHTMLMode();\n };\n\n private scheduleSyncFromDOM(): void {\n if (this.syncTimer) clearTimeout(this.syncTimer);\n this.syncTimer = setTimeout(() => {\n this.history.push(this.doc);\n this.doc = syncFromDOM(this.editorEl);\n this.updatePlaceholder();\n this.updateStatusBar();\n this.emit('change', this.doc);\n this.options.onChange?.(this.getHTML());\n }, 0);\n }\n\n /** Sync model from DOM without a full rerender (used after inline execCommands) */\n syncModelFromDOM(): void {\n if (this.syncTimer) clearTimeout(this.syncTimer);\n this.syncTimer = setTimeout(() => {\n this.doc = syncFromDOM(this.editorEl);\n this.updatePlaceholder();\n this.updateStatusBar();\n }, 0);\n }\n\n private readonly onSelectionChange = (): void => {\n if (!this.editorEl.contains(document.activeElement) &&\n document.activeElement !== this.editorEl) return;\n\n this.toolbar?.updateActiveState();\n this.bubbleToolbar?.onSelectionChange();\n this.updateElementPath();\n\n const sel = domSelectionToModel(this.editorEl, this.doc);\n this.emit('selectionchange', sel);\n };\n\n private readonly onFocus = (): void => {\n this._isFocused = true;\n this.containerEl.classList.add('oe-focused');\n this.emit('focus', undefined as void);\n };\n\n private readonly onBlur = (): void => {\n this._isFocused = false;\n this.containerEl.classList.remove('oe-focused');\n this.emit('blur', undefined as void);\n };\n\n private readonly onKeydown = (e: KeyboardEvent): void => {\n const ctrl = e.ctrlKey || e.metaKey;\n\n if (ctrl && e.key === 'z') { e.preventDefault(); this.chain().undo().run(); return; }\n if (ctrl && (e.key === 'y' || e.key === 'Z')) { e.preventDefault(); this.chain().redo().run(); return; }\n if (ctrl && e.key === 'b') { e.preventDefault(); this.chain().toggleMark('bold').run(); return; }\n if (ctrl && e.key === 'i') { e.preventDefault(); this.chain().toggleMark('italic').run(); return; }\n if (ctrl && e.key === 'u') { e.preventDefault(); this.chain().toggleMark('underline').run(); return; }\n if (ctrl && e.key === '`') { e.preventDefault(); this.chain().toggleMark('code').run(); return; }\n\n // Plugin keymaps\n for (const plugin of this.plugins) {\n if (plugin.keymaps) {\n for (const [shortcut, handler] of Object.entries(plugin.keymaps)) {\n if (matchesShortcut(shortcut, e)) {\n e.preventDefault();\n handler(this);\n return;\n }\n }\n }\n }\n };\n\n private readonly onTabInCodeBlock = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') return;\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n\n let node: Node | null = sel.anchorNode;\n while (node && node !== this.editorEl) {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName === 'PRE') {\n e.preventDefault();\n execInlineCommand('insertText', e.shiftKey ? '' : ' ');\n return;\n }\n node = node.parentNode;\n }\n };\n\n private readonly onPaste = (e: ClipboardEvent): void => {\n e.preventDefault();\n const html = e.clipboardData?.getData('text/html');\n const text = e.clipboardData?.getData('text/plain') ?? '';\n\n this.history.push(this.doc);\n\n if (html && html.trim()) {\n // HTML paste: sanitize through model and re-serialize\n const pastedDoc = deserializeHTML(html);\n const pastedHTML = serializeToHTML(pastedDoc);\n execInlineCommand('insertHTML', pastedHTML);\n } else if (text) {\n const trimmed = text.trim();\n\n // N6: URL → link\n if (isURL(trimmed)) {\n const sel = window.getSelection();\n const selText = sel && !sel.isCollapsed ? sel.toString() : '';\n if (selText) {\n // Wrap selected text in a link\n const linkHTML = `<a href=\"${escapeAttrValue(trimmed)}\" target=\"_blank\" rel=\"noopener noreferrer\">${selText}</a>`;\n execInlineCommand('insertHTML', linkHTML);\n } else {\n // Insert URL as a clickable link\n const linkHTML = `<a href=\"${escapeAttrValue(trimmed)}\" target=\"_blank\" rel=\"noopener noreferrer\">${trimmed}</a>`;\n execInlineCommand('insertHTML', linkHTML);\n }\n } else if (looksLikeMarkdown(text)) {\n // N6: Markdown → convert and insert\n const mdDoc = deserializeMarkdown(text);\n const mdHTML = serializeToHTML(mdDoc);\n execInlineCommand('insertHTML', mdHTML);\n } else {\n execInlineCommand('insertText', text);\n }\n }\n\n this.scheduleSyncFromDOM();\n };\n\n private readonly onDrop = (e: DragEvent): void => {\n const files = e.dataTransfer?.files;\n if (!files || files.length === 0) return;\n\n const imageFile = Array.from(files).find((f) => f.type.startsWith('image/'));\n if (!imageFile) return;\n\n if (this.options.onImageUpload) {\n e.preventDefault();\n this.options.onImageUpload(imageFile).then((url) => {\n const idx = getActiveBlockIndex(this.editorEl);\n this.history.push(this.doc);\n this.doc = insertImageCmd(this.doc, idx, url, imageFile.name);\n this.rerender();\n }).catch(() => { /* ignore */ });\n }\n };\n\n // ── HTML Source Mode ──────────────────────────────────────────────────────────\n\n private isHTMLMode = false;\n\n toggleHTMLMode(): void {\n const contentWrap = this.containerEl.querySelector<HTMLElement>('.oe-content-wrap')!;\n const editorEl = this.editorEl;\n const sourceEl = this.containerEl.querySelector<HTMLElement>('.oe-html-source')!;\n const textarea = this.containerEl.querySelector<HTMLTextAreaElement>('.oe-html-textarea')!;\n const toggleBtn = this.containerEl.querySelector<HTMLElement>('.oe-html-toggle')!;\n\n if (!this.isHTMLMode) {\n // Switch to HTML mode\n textarea.value = this.getHTML();\n editorEl.style.display = 'none';\n sourceEl.style.display = '';\n this.isHTMLMode = true;\n toggleBtn.classList.add('oe-active');\n this.toolbar?.setDisabled(true);\n } else {\n // Switch back to WYSIWYG\n const newHTML = textarea.value;\n this.history.push(this.doc);\n this.doc = deserializeHTML(newHTML);\n editorEl.style.display = '';\n sourceEl.style.display = 'none';\n this.isHTMLMode = false;\n toggleBtn.classList.remove('oe-active');\n this.toolbar?.setDisabled(false);\n this.rerender();\n }\n }\n\n // ── Chain API ─────────────────────────────────────────────────────────────────\n\n chain(): ChainInterface {\n const operations: Array<() => void> = [];\n // Track whether any operation requires a full model→DOM rerender.\n // Inline mark commands (execCommand) mutate the DOM directly —\n // rerendering from the old model would undo their changes.\n let needsRerender = false;\n const editor = this;\n\n const chainObj: ChainInterface = {\n toggleMark(type: MarkType, attrs?: Record<string, unknown>) {\n // Inline op: no rerender — execCommand mutates DOM, then we sync model from DOM\n operations.push(() => {\n editor.editorEl.focus();\n const cmdMap: Partial<Record<MarkType, string>> = {\n bold: 'bold',\n italic: 'italic',\n underline: 'underline',\n strikethrough: 'strikethrough',\n };\n if (type === 'code') {\n const activeMarks = getActiveMarks(editor.editorEl);\n if (activeMarks.has('code')) {\n // Remove: unwrap the <code> element around selection\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const range = sel.getRangeAt(0);\n const codeEl = (range.startContainer.parentElement as Element).closest('code');\n if (codeEl) {\n const text = codeEl.textContent ?? '';\n codeEl.replaceWith(document.createTextNode(text));\n }\n }\n } else {\n const sel = window.getSelection();\n if (sel && !sel.isCollapsed) {\n const range = sel.getRangeAt(0);\n const code = document.createElement('code');\n range.surroundContents(code);\n }\n }\n } else if (type === 'link') {\n if (attrs?.href) {\n const target = (attrs.target as string) ?? '_self';\n insertLink(attrs.href as string, target);\n }\n } else {\n const cmd = cmdMap[type];\n if (cmd) execInlineCommand(cmd);\n }\n });\n return chainObj;\n },\n\n setBlock(type: string, attrs?: Record<string, unknown>) {\n needsRerender = true;\n operations.push(() => {\n // Sync current DOM state into model before mutating\n editor.doc = syncFromDOM(editor.editorEl);\n const idx = getActiveBlockIndex(editor.editorEl);\n editor.history.push(editor.doc);\n editor.doc = setBlockType(editor.doc, idx, type, attrs);\n });\n return chainObj;\n },\n\n setAlign(align: 'left' | 'center' | 'right' | 'justify') {\n needsRerender = true;\n operations.push(() => {\n editor.doc = syncFromDOM(editor.editorEl);\n const idx = getActiveBlockIndex(editor.editorEl);\n editor.history.push(editor.doc);\n editor.doc = setAlignment(editor.doc, idx, align);\n });\n return chainObj;\n },\n\n insertImage(src: string, alt?: string) {\n needsRerender = true;\n operations.push(() => {\n editor.doc = syncFromDOM(editor.editorEl);\n const idx = getActiveBlockIndex(editor.editorEl);\n editor.history.push(editor.doc);\n editor.doc = insertImageCmd(editor.doc, idx, src, alt);\n });\n return chainObj;\n },\n\n insertHr() {\n needsRerender = true;\n operations.push(() => {\n editor.doc = syncFromDOM(editor.editorEl);\n const idx = getActiveBlockIndex(editor.editorEl);\n editor.history.push(editor.doc);\n editor.doc = insertHrCmd(editor.doc, idx);\n });\n return chainObj;\n },\n\n toggleList(type: 'bullet_list' | 'ordered_list') {\n needsRerender = true;\n operations.push(() => {\n editor.doc = syncFromDOM(editor.editorEl);\n const idx = getActiveBlockIndex(editor.editorEl);\n editor.history.push(editor.doc);\n editor.doc = toggleList(editor.doc, idx, type);\n });\n return chainObj;\n },\n\n undo() {\n needsRerender = true;\n operations.push(() => {\n const prev = editor.history.undo(editor.doc);\n if (prev) editor.doc = prev;\n });\n return chainObj;\n },\n\n redo() {\n needsRerender = true;\n operations.push(() => {\n const next = editor.history.redo(editor.doc);\n if (next) editor.doc = next;\n });\n return chainObj;\n },\n\n run() {\n editor.isUpdating = true;\n for (const op of operations) op();\n editor.isUpdating = false;\n\n if (needsRerender) {\n // Block-level change: model is updated, push DOM from model\n editor.rerender();\n } else {\n // Inline change: DOM was mutated directly by execCommand,\n // sync model from DOM asynchronously (don't overwrite DOM)\n editor.syncModelFromDOM();\n }\n\n editor.toolbar?.updateActiveState();\n editor.emit('change', editor.doc);\n editor.options.onChange?.(editor.getHTML());\n },\n };\n\n return chainObj;\n }\n\n // ── Re-render ─────────────────────────────────────────────────────────────────\n\n private rerender(): void {\n // Save selection\n const sel = domSelectionToModel(this.editorEl, this.doc);\n\n renderDocument(this.doc, this.editorEl);\n this.updatePlaceholder();\n this.updateStatusBar();\n\n // Restore focus (don't restore selection — can cause issues after block changes)\n this.editorEl.focus();\n }\n\n // ── Status bar ────────────────────────────────────────────────────────────────\n\n private updateStatusBar(): void {\n const text = this.editorEl.innerText ?? '';\n const words = text.trim().split(/\\s+/).filter((w) => w.length > 0);\n const wordCount = this.containerEl.querySelector('.oe-word-count');\n const charCount = this.containerEl.querySelector('.oe-char-count');\n if (wordCount) wordCount.textContent = `${this.locale.statusBar.words}: ${words.length}`;\n if (charCount) charCount.textContent = `${this.locale.statusBar.characters}: ${text.length}`;\n }\n\n private updateElementPath(): void {\n const pathEl = this.containerEl.querySelector('.oe-statusbar-path');\n if (!pathEl) return;\n\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n\n const path: string[] = [];\n let node: Node | null = sel.anchorNode;\n\n while (node && node !== this.editorEl) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n path.unshift((node as Element).tagName.toLowerCase());\n }\n node = node.parentNode;\n }\n\n pathEl.textContent = path.join(' › ');\n }\n\n private updatePlaceholder(): void {\n if (!this.options.placeholder) return;\n const isEmpty =\n this.editorEl.innerText.trim() === '' &&\n this.editorEl.querySelectorAll('img, hr').length === 0;\n this.editorEl.classList.toggle('oe-empty', isEmpty);\n }\n\n // ── Public API ────────────────────────────────────────────────────────────────\n\n getHTML(): string {\n return serializeToHTML(this.doc);\n }\n\n setHTML(html: string): void {\n this.history.push(this.doc);\n this.doc = deserializeHTML(html);\n this.rerender();\n }\n\n getMarkdown(): string {\n return serializeToMarkdown(this.doc);\n }\n\n setMarkdown(md: string): void {\n this.history.push(this.doc);\n this.doc = deserializeMarkdown(md);\n this.rerender();\n }\n\n getDocument(): EditorDocument {\n return this.doc;\n }\n\n use(plugin: EditorPlugin): EditorInterface {\n this.plugins.push(plugin);\n plugin.onInit?.(this);\n return this;\n }\n\n on<K extends keyof EditorEventMap>(event: K, listener: EditorEventListener<K>): void {\n (this.listeners[event] as Set<EditorEventListener<K>>).add(listener);\n }\n\n off<K extends keyof EditorEventMap>(event: K, listener: EditorEventListener<K>): void {\n (this.listeners[event] as Set<EditorEventListener<K>>).delete(listener);\n }\n\n private emit<K extends keyof EditorEventMap>(event: K, payload: EditorEventMap[K]): void {\n (this.listeners[event] as Set<EditorEventListener<K>>).forEach((fn) => fn(payload));\n }\n\n destroy(): void {\n if (this.syncTimer) clearTimeout(this.syncTimer);\n document.removeEventListener('selectionchange', this.onSelectionChange);\n this.editorEl.removeEventListener('input', this.onInput);\n this.editorEl.removeEventListener('compositionstart', this.onCompositionStart);\n this.editorEl.removeEventListener('compositionend', this.onCompositionEnd);\n this.editorEl.removeEventListener('focus', this.onFocus);\n this.editorEl.removeEventListener('blur', this.onBlur);\n this.editorEl.removeEventListener('keydown', this.onKeydown);\n this.editorEl.removeEventListener('keydown', this.onTabInCodeBlock);\n this.editorEl.removeEventListener('paste', this.onPaste);\n this.editorEl.removeEventListener('drop', this.onDrop);\n const htmlToggle = this.containerEl.querySelector<HTMLElement>('.oe-html-toggle');\n htmlToggle?.removeEventListener('click', this.onHTMLToggleClick);\n this.bubbleToolbar?.destroy();\n this.imageResizer?.destroy();\n this.codeLangPicker?.destroy();\n this.plugins.forEach((p) => p.onDestroy?.(this));\n this.root.innerHTML = '';\n }\n\n focus(): void { this.editorEl.focus(); }\n blur(): void { this.editorEl.blur(); }\n isFocused(): boolean { return this._isFocused; }\n\n getSelection(): ModelSelection | null {\n return domSelectionToModel(this.editorEl, this.doc);\n }\n\n isMarkActive(type: MarkType): boolean {\n return getActiveMarks(this.editorEl).has(type);\n }\n\n getActiveBlockType(): string {\n return getBlockTypeFromDOM(this.editorEl);\n }\n\n isEmpty(): boolean {\n return this.editorEl.innerText.trim() === '' &&\n this.editorEl.querySelectorAll('img, hr').length === 0;\n }\n\n // ── Theme ─────────────────────────────────────────────────────────────────────\n\n private applyTheme(theme: 'light' | 'dark' | 'auto'): void {\n this.containerEl.dataset.oeTheme = theme;\n }\n}\n\n// ── N6: Clipboard Intelligence helpers ────────────────────────────────────────\n\nfunction isURL(text: string): boolean {\n return /^https?:\\/\\/[^\\s]{4,}$/.test(text);\n}\n\nfunction looksLikeMarkdown(text: string): boolean {\n return (\n /^#{1,6} /m.test(text) ||\n /\\*\\*[^*]+\\*\\*/.test(text) ||\n /^```/m.test(text) ||\n /^[-*+] /m.test(text) ||\n /^\\d+\\. /m.test(text) ||\n /^> /m.test(text) ||\n /~~[^~]+~~/.test(text)\n );\n}\n\nfunction escapeAttrValue(str: string): string {\n return str.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<');\n}\n\n// ── Keyboard shortcut matcher ─────────────────────────────────────────────────\n\nfunction matchesShortcut(shortcut: string, e: KeyboardEvent): boolean {\n const parts = shortcut.toLowerCase().split('+');\n const key = parts[parts.length - 1];\n const ctrl = parts.includes('ctrl') || parts.includes('meta');\n const shift = parts.includes('shift');\n const alt = parts.includes('alt');\n\n return (\n e.key.toLowerCase() === key &&\n (e.ctrlKey || e.metaKey) === ctrl &&\n e.shiftKey === shift &&\n e.altKey === alt\n );\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit Plugin — Syntax Highlighting (K1)\n// Loads highlight.js from CDN and applies it to <pre><code> blocks.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface, EditorPlugin } from '../core/types.js';\n\ndeclare global {\n interface Window {\n hljs?: {\n highlightElement(el: HTMLElement): void;\n configure(opts: Record<string, unknown>): void;\n };\n }\n}\n\nlet hljsPromise: Promise<Window['hljs']> | null = null;\n\nfunction loadHljs(): Promise<Window['hljs']> {\n if (hljsPromise) return hljsPromise;\n\n hljsPromise = new Promise((resolve, reject) => {\n if (window.hljs) {\n resolve(window.hljs);\n return;\n }\n const script = document.createElement('script');\n script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js';\n script.crossOrigin = 'anonymous';\n script.onload = () => resolve(window.hljs);\n script.onerror = reject;\n document.head.appendChild(script);\n });\n\n return hljsPromise;\n}\n\nconst BASE = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/';\nconst DEFAULT_DARK = `${BASE}atom-one-dark.min.css`;\nconst DEFAULT_LIGHT = `${BASE}atom-one-light.min.css`;\n\nfunction injectHljsTheme(dark: boolean, themeUrl?: string | { light: string; dark: string }): void {\n const id = 'oe-hljs-theme';\n const existing = document.getElementById(id);\n\n let href: string;\n if (typeof themeUrl === 'string') {\n href = themeUrl;\n } else if (typeof themeUrl === 'object') {\n href = dark ? themeUrl.dark : themeUrl.light;\n } else {\n href = dark ? DEFAULT_DARK : DEFAULT_LIGHT;\n }\n\n if (existing) {\n (existing as HTMLLinkElement).href = href;\n return;\n }\n\n const link = document.createElement('link');\n link.id = id;\n link.rel = 'stylesheet';\n link.href = href;\n link.crossOrigin = 'anonymous';\n document.head.appendChild(link);\n}\n\nexport interface HighlightPluginOptions {\n /** Force 'light' or 'dark' theme for code highlighting. Default: follows editor theme. */\n theme?: 'light' | 'dark';\n /**\n * Custom highlight.js CSS theme URL.\n * Pass a string to use the same URL for light and dark,\n * or an object `{ light, dark }` to use different URLs per mode.\n * Available themes: https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/\n */\n themeUrl?: string | { light: string; dark: string };\n}\n\nexport function createHighlightPlugin(opts: HighlightPluginOptions = {}): EditorPlugin {\n let hl: Window['hljs'] | undefined;\n let highlightTimer: ReturnType<typeof setTimeout> | null = null;\n\n function applyHighlighting(editorEl: HTMLElement): void {\n if (!hl) return;\n\n const blocks = editorEl.querySelectorAll<HTMLElement>('pre code');\n const activeNode = document.getSelection()?.anchorNode ?? null;\n\n blocks.forEach((block) => {\n // Skip blocks the user is currently editing\n const pre = block.closest('pre');\n if (pre && activeNode && pre.contains(activeNode)) return;\n\n // Remove stale highlight marker so hljs reprocesses\n delete block.dataset.highlighted;\n block.removeAttribute('data-highlighted');\n hl!.highlightElement(block);\n });\n }\n\n function scheduleHighlight(editorEl: HTMLElement): void {\n if (highlightTimer) clearTimeout(highlightTimer);\n highlightTimer = setTimeout(() => applyHighlighting(editorEl), 400);\n }\n\n return {\n name: 'highlight',\n\n onInit(editor: EditorInterface) {\n const editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n const containerEl = (editor as unknown as { containerEl: HTMLElement }).containerEl;\n\n // Detect theme\n const dark =\n opts.theme === 'dark' ||\n (!opts.theme && containerEl?.dataset.oeTheme === 'dark');\n\n injectHljsTheme(dark, opts.themeUrl);\n\n loadHljs().then((hljs) => {\n hl = hljs;\n hl?.configure({ ignoreUnescapedHTML: true });\n applyHighlighting(editorEl);\n\n editor.on('change', () => scheduleHighlight(editorEl));\n }).catch(() => {\n console.warn('[OpenEdit] highlight.js failed to load from CDN');\n });\n },\n\n onDestroy() {\n if (highlightTimer) clearTimeout(highlightTimer);\n },\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit Plugin — Emoji Picker (K4)\n// Adds a toolbar button that opens an emoji panel. No external dependencies.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface, EditorPlugin } from '../core/types.js';\nimport type { EditorLocale } from '../locales/types.js';\nimport { en } from '../locales/en.js';\n\n// ── Emoji data (locale-independent) ──────────────────────────────────────────\n\ntype EmojiCategoryKey = keyof EditorLocale['plugins']['emoji']['categories'];\n\nconst EMOJI_SETS: Record<EmojiCategoryKey, string[]> = {\n faces: ['😀','😄','😅','😂','🤣','😊','😇','🙂','🙃','😉','😍','🥰','😘','😎','🤓','🤩','🥳','😏','😔','😢','😭','😤','😠','🥺','🤔','🤗','😴','🤯','🥸','😶'],\n hearts: ['❤️','🧡','💛','💚','💙','💜','🖤','🤍','🤎','💕','💞','💓','💗','💖','💝','💘','❣️','💔','❤️🔥','💟'],\n gestures: ['👋','🤚','🖐','✋','🖖','👌','🤌','✌️','🤞','🤟','🤘','👈','👉','👆','👇','☝️','👍','👎','✊','👏','🙌','🫶','🤝','🙏'],\n nature: ['🌸','🌺','🌻','🌹','🌷','🍀','🌿','🌱','🌲','🌳','🌴','🍁','🍂','🍃','🌊','🌈','☀️','🌙','⭐','🌟','💫','⚡','🔥','💧','🌍','🦋','🐾'],\n food: ['🍎','🍊','🍋','🍇','🍓','🍒','🍑','🥝','🍕','🍔','🌮','🌯','🍜','🍣','🍰','🎂','☕','🍵','🧃','🥤','🍺','🥂','🍾'],\n objects: ['💡','🔑','🎵','🎮','📱','💻','📷','🎯','🏆','💎','🔮','📚','✏️','📝','📌','🔔','💬','📧','🚀','🎁','🧩','⚙️','🔧','🎨'],\n symbols: ['✅','❌','⚠️','ℹ️','💯','🔴','🟡','🟢','🔵','⚫','⚪','🔶','🔷','🔸','🔹','▶️','◀️','🔝','🔛','🆕','🆒','🆓','🆙','🏳️','🏴'],\n};\n\nfunction buildEmojiData(locale: EditorLocale) {\n const cats = locale.plugins.emoji.categories;\n return (Object.keys(EMOJI_SETS) as EmojiCategoryKey[]).map((key) => ({\n label: cats[key],\n emojis: EMOJI_SETS[key],\n }));\n}\n\nconst EMOJI_ICON = `<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M8 14s1.5 2 4 2 4-2 4-2\"/><line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\"/><line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\"/></svg>`;\n\n// ── Plugin ────────────────────────────────────────────────────────────────────\n\nexport function createEmojiPlugin(): EditorPlugin {\n let popup: HTMLDivElement | null = null;\n let emojiBtn: HTMLElement | null = null;\n let editorRef: EditorInterface | null = null;\n let savedRange: Range | null = null;\n let locale: EditorLocale = en;\n\n function closePopup(): void {\n if (popup) {\n popup.remove();\n popup = null;\n }\n document.removeEventListener('click', onDocumentClick, true);\n }\n\n function onDocumentClick(e: MouseEvent): void {\n const target = e.target as Node;\n if (popup && !popup.contains(target) && !emojiBtn?.contains(target)) {\n closePopup();\n }\n }\n\n function insertEmoji(emoji: string): void {\n const sel = window.getSelection();\n if (savedRange && sel) {\n sel.removeAllRanges();\n sel.addRange(savedRange);\n } else if (editorRef) {\n editorRef.focus();\n }\n document.execCommand('insertText', false, emoji);\n closePopup();\n editorRef?.focus();\n }\n\n function buildPopup(anchorEl: HTMLElement): void {\n closePopup();\n\n popup = document.createElement('div');\n popup.className = 'oe-emoji-popup';\n\n Object.assign(popup.style, {\n position: 'fixed',\n zIndex: '99999',\n background: 'var(--oe-bg, #ffffff)',\n border: '1px solid var(--oe-border, #e7e5e4)',\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0,0,0,0.14)',\n padding: '10px 12px',\n width: '300px',\n maxHeight: '260px',\n overflowY: 'auto',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n });\n\n for (const category of buildEmojiData(locale)) {\n const label = document.createElement('div');\n Object.assign(label.style, {\n fontSize: '10px',\n fontWeight: '600',\n color: 'var(--oe-muted, #78716c)',\n textTransform: 'uppercase',\n letterSpacing: '0.06em',\n margin: '6px 0 4px',\n });\n label.textContent = category.label;\n popup.appendChild(label);\n\n const grid = document.createElement('div');\n Object.assign(grid.style, {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '1px',\n });\n\n for (const emoji of category.emojis) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = emoji;\n btn.title = emoji;\n Object.assign(btn.style, {\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '4px',\n borderRadius: '6px',\n fontSize: '18px',\n lineHeight: '1',\n transition: 'background 0.1s',\n });\n btn.addEventListener('mouseenter', () => {\n btn.style.background = 'var(--oe-btn-hover-bg, #f5f5f4)';\n });\n btn.addEventListener('mouseleave', () => {\n btn.style.background = 'none';\n });\n // mousedown prevents editor blur; actual insert on mousedown\n btn.addEventListener('mousedown', (e) => {\n e.preventDefault();\n insertEmoji(emoji);\n });\n grid.appendChild(btn);\n }\n\n popup.appendChild(grid);\n }\n\n document.body.appendChild(popup);\n\n // Position below anchor\n const rect = anchorEl.getBoundingClientRect();\n let top = rect.bottom + 4;\n let left = rect.left;\n\n // Clamp to viewport\n if (left + 300 > window.innerWidth - 8) left = window.innerWidth - 308;\n if (left < 8) left = 8;\n\n const estHeight = 260;\n if (top + estHeight > window.innerHeight - 8) top = rect.top - estHeight - 4;\n\n popup.style.top = `${top}px`;\n popup.style.left = `${left}px`;\n\n // Close on outside click (capture phase, next tick)\n setTimeout(() => {\n document.addEventListener('click', onDocumentClick, true);\n }, 0);\n }\n\n return {\n name: 'emoji',\n\n onInit(editor: EditorInterface) {\n editorRef = editor;\n locale = (editor as unknown as { locale: EditorLocale }).locale ?? en;\n\n const editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n const containerEl = (editor as unknown as { containerEl: HTMLElement }).containerEl;\n const toolbarEl = containerEl?.querySelector<HTMLElement>('.oe-toolbar');\n if (!toolbarEl) return;\n\n // Save selection whenever cursor moves in editor\n document.addEventListener('selectionchange', () => {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0 && editorEl.contains(sel.anchorNode)) {\n savedRange = sel.getRangeAt(0).cloneRange();\n }\n });\n\n // Inject button at end of toolbar\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'oe-toolbar-btn';\n btn.title = locale.plugins.emoji.buttonTitle;\n btn.innerHTML = EMOJI_ICON;\n emojiBtn = btn;\n\n btn.addEventListener('mousedown', (e) => {\n e.preventDefault(); // keep editor focused\n // Capture selection before focus potentially changes\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0 && editorEl.contains(sel.anchorNode)) {\n savedRange = sel.getRangeAt(0).cloneRange();\n }\n if (popup) {\n closePopup();\n } else {\n buildPopup(btn);\n }\n });\n\n toolbarEl.appendChild(btn);\n },\n\n onDestroy() {\n closePopup();\n editorRef = null;\n savedRange = null;\n },\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit Plugin — Template Tags (K13)\n// Visually decorates {{variableName}} patterns as styled pills.\n// The tags are stored as plain text in the model — only the view is decorated.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface, EditorPlugin } from '../core/types.js';\n\nconst TAG_RE = /\\{\\{([^{}]+)\\}\\}/g;\n\nfunction decorateTemplateTags(editorEl: HTMLElement): void {\n // Collect all text nodes not already inside a template tag span\n const walker = document.createTreeWalker(\n editorEl,\n NodeFilter.SHOW_TEXT,\n {\n acceptNode(node) {\n const parent = node.parentElement;\n if (!parent) return NodeFilter.FILTER_REJECT;\n if (parent.classList.contains('oe-template-tag')) return NodeFilter.FILTER_REJECT;\n if (parent.tagName === 'TEXTAREA' || parent.tagName === 'SCRIPT') return NodeFilter.FILTER_REJECT;\n return NodeFilter.FILTER_ACCEPT;\n },\n },\n );\n\n const textNodes: Text[] = [];\n let node: Node | null;\n while ((node = walker.nextNode())) {\n const text = node.textContent ?? '';\n if (text.includes('{{')) {\n textNodes.push(node as Text);\n }\n }\n\n for (const textNode of textNodes) {\n const text = textNode.textContent ?? '';\n TAG_RE.lastIndex = 0;\n\n // Check if there's actually a match\n if (!TAG_RE.test(text)) continue;\n\n // Split into parts\n TAG_RE.lastIndex = 0;\n const parts: Array<{ type: 'text' | 'tag'; value: string }> = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = TAG_RE.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push({ type: 'text', value: text.slice(lastIndex, match.index) });\n }\n parts.push({ type: 'tag', value: match[0] });\n lastIndex = TAG_RE.lastIndex;\n }\n if (lastIndex < text.length) {\n parts.push({ type: 'text', value: text.slice(lastIndex) });\n }\n\n // Build replacement fragment\n const fragment = document.createDocumentFragment();\n for (const part of parts) {\n if (part.type === 'tag') {\n const span = document.createElement('span');\n span.className = 'oe-template-tag';\n span.contentEditable = 'false';\n span.textContent = part.value;\n fragment.appendChild(span);\n } else if (part.value) {\n fragment.appendChild(document.createTextNode(part.value));\n }\n }\n\n textNode.parentNode?.replaceChild(fragment, textNode);\n }\n}\n\nexport function createTemplateTagPlugin(): EditorPlugin {\n let decorateTimer: ReturnType<typeof setTimeout> | null = null;\n\n function schedule(editorEl: HTMLElement): void {\n if (decorateTimer) clearTimeout(decorateTimer);\n decorateTimer = setTimeout(() => decorateTemplateTags(editorEl), 200);\n }\n\n return {\n name: 'template-tags',\n\n onInit(editor: EditorInterface) {\n const editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n\n editor.on('change', () => schedule(editorEl));\n\n // Apply on init after first render\n setTimeout(() => decorateTemplateTags(editorEl), 100);\n },\n\n onDestroy() {\n if (decorateTimer) clearTimeout(decorateTimer);\n },\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit Plugin — AI Writing Assistant (N1)\n// Optional plugin for AI-powered text improvement via the Claude API.\n// The /ai slash command or toolbar button opens the prompt panel.\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorInterface, EditorPlugin } from '../core/types.js';\nimport type { EditorLocale } from '../locales/types.js';\nimport { en } from '../locales/en.js';\nimport { deserializeHTML } from '../io/deserializer.js';\nimport { serializeToHTML } from '../io/serializer.js';\n\nconst AI_ICON = `<svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"/></svg>`;\n\n// ── Config ────────────────────────────────────────────────────────────────────\n\nexport interface AIPluginOptions {\n /** Anthropic API key. Keep this server-side in production — use `endpoint` for a proxy. */\n apiKey?: string;\n /**\n * Custom endpoint URL (proxy server). The plugin POSTs the same payload as the\n * Anthropic Messages API. Use this to avoid exposing your API key in the browser.\n * Example: '/api/ai' → your server calls Anthropic and returns the response.\n */\n endpoint?: string;\n /** Model ID. Default: 'claude-sonnet-4-6' */\n model?: string;\n /** Max output tokens. Default: 2048 */\n maxTokens?: number;\n}\n\n// AI prompts sent to Claude stay in English for quality — only labels are localized\nconst AI_PROMPTS = {\n improve: 'Improve the following text stylistically and grammatically. Return only the improved text, no explanations.',\n shorten: 'Shorten the following text to approximately 50% of its length, keeping all important information. Return only the shortened text.',\n expand: 'Expand the following text with more details, examples, and context. Return only the expanded text.',\n summarize: 'Summarize the following text in 2-3 concise sentences. Return only the summary.',\n toGerman: 'Translate the following text to German. Return only the translation.',\n toEnglish: 'Translate the following text to English. Return only the translation, no explanations.',\n};\n\nfunction buildQuickActions(locale: EditorLocale) {\n const a = locale.plugins.ai.actions;\n return [\n { label: a.improve, prompt: AI_PROMPTS.improve },\n { label: a.shorten, prompt: AI_PROMPTS.shorten },\n { label: a.expand, prompt: AI_PROMPTS.expand },\n { label: a.summarize, prompt: AI_PROMPTS.summarize },\n { label: a.toGerman, prompt: AI_PROMPTS.toGerman },\n { label: a.toEnglish, prompt: AI_PROMPTS.toEnglish },\n ];\n}\n\n// ── API call ──────────────────────────────────────────────────────────────────\n\nasync function callClaude(\n userMessage: string,\n opts: AIPluginOptions,\n): Promise<string> {\n const url = opts.endpoint ?? 'https://api.anthropic.com/v1/messages';\n const model = opts.model ?? 'claude-sonnet-4-6';\n\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n };\n\n if (opts.apiKey && !opts.endpoint) {\n headers['x-api-key'] = opts.apiKey;\n headers['anthropic-version'] = '2023-06-01';\n // Required for direct browser access\n headers['anthropic-dangerous-direct-browser-access'] = 'true';\n }\n\n const body = JSON.stringify({\n model,\n max_tokens: opts.maxTokens ?? 2048,\n system: 'You are a professional writing assistant. Follow the user\\'s instruction exactly and return only the desired result — no introduction, no explanation, no commentary.',\n messages: [{ role: 'user', content: userMessage }],\n });\n\n const res = await fetch(url, { method: 'POST', headers, body });\n\n if (!res.ok) {\n const err = await res.text().catch(() => res.statusText);\n throw new Error(`API error ${res.status}: ${err}`);\n }\n\n const data = await res.json();\n const text = data?.content?.[0]?.text as string | undefined;\n if (!text) throw new Error('Unexpected response format');\n return text;\n}\n\n// ── UI helpers ────────────────────────────────────────────────────────────────\n\nfunction css(el: HTMLElement, styles: Partial<CSSStyleDeclaration>): void {\n Object.assign(el.style, styles);\n}\n\n// ── Plugin ────────────────────────────────────────────────────────────────────\n\nexport function createAIPlugin(opts: AIPluginOptions = {}): EditorPlugin {\n if (!opts.apiKey && !opts.endpoint) {\n console.warn('[OpenEdit AI] No apiKey or endpoint provided — plugin inactive.');\n }\n\n let panel: HTMLDivElement | null = null;\n let aiBtn: HTMLElement | null = null;\n let editorRef: EditorInterface | null = null;\n let savedRange: Range | null = null;\n let selectedText = '';\n let locale: EditorLocale = en;\n\n function closePanel(): void {\n if (panel) { panel.remove(); panel = null; }\n document.removeEventListener('click', onDocClick, true);\n }\n\n function onDocClick(e: MouseEvent): void {\n const t = e.target as Node;\n if (panel && !panel.contains(t) && !aiBtn?.contains(t)) closePanel();\n }\n\n function captureSelection(editorEl: HTMLElement): void {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0 && editorEl.contains(sel.anchorNode)) {\n savedRange = sel.getRangeAt(0).cloneRange();\n selectedText = sel.toString().trim();\n } else {\n savedRange = null;\n selectedText = editorRef?.getHTML() ?? '';\n }\n }\n\n function replaceWithResult(html: string): void {\n if (!editorRef) return;\n\n const sel = window.getSelection();\n if (savedRange && sel && selectedText) {\n // Replace selection\n sel.removeAllRanges();\n sel.addRange(savedRange);\n const doc = deserializeHTML(html);\n const cleanHTML = serializeToHTML(doc);\n document.execCommand('insertHTML', false, cleanHTML);\n } else {\n // Replace entire document\n editorRef.setHTML(html);\n }\n editorRef.focus();\n }\n\n function buildPanel(anchorEl: HTMLElement): void {\n closePanel();\n\n panel = document.createElement('div');\n css(panel, {\n position: 'fixed',\n zIndex: '99999',\n background: 'var(--oe-bg, #fff)',\n border: '1px solid var(--oe-border, #e7e5e4)',\n borderRadius: '14px',\n boxShadow: '0 12px 40px rgba(0,0,0,0.16)',\n padding: '16px',\n width: '360px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n });\n\n const ai = locale.plugins.ai;\n\n // Header\n const header = document.createElement('div');\n css(header, { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' });\n header.innerHTML = `${AI_ICON}<span style=\"font-size:13px;font-weight:600;color:var(--oe-text,#1c1917)\">${ai.panelTitle}</span>`;\n panel.appendChild(header);\n\n // Context preview\n const preview = document.createElement('div');\n const previewText = selectedText || ai.noSelection;\n css(preview, {\n fontSize: '12px',\n color: 'var(--oe-text-muted, #78716c)',\n background: 'var(--oe-btn-hover-bg, #f5f5f4)',\n borderRadius: '8px',\n padding: '8px 10px',\n marginBottom: '12px',\n maxHeight: '64px',\n overflow: 'hidden',\n lineHeight: '1.5',\n });\n preview.textContent = previewText.length > 140 ? previewText.slice(0, 140) + '…' : previewText;\n panel.appendChild(preview);\n\n // Quick action buttons\n const quickGrid = document.createElement('div');\n css(quickGrid, { display: 'flex', flexWrap: 'wrap', gap: '6px', marginBottom: '12px' });\n\n for (const action of buildQuickActions(locale)) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = action.label;\n css(btn, {\n padding: '5px 10px',\n fontSize: '12px',\n fontWeight: '500',\n border: '1px solid var(--oe-border, #e7e5e4)',\n borderRadius: '6px',\n background: 'var(--oe-bg, #fff)',\n color: 'var(--oe-text, #1c1917)',\n cursor: 'pointer',\n fontFamily: 'inherit',\n transition: 'background 0.1s',\n });\n btn.addEventListener('mouseenter', () => { btn.style.background = 'var(--oe-btn-hover-bg, #f5f5f4)'; });\n btn.addEventListener('mouseleave', () => { btn.style.background = 'var(--oe-bg, #fff)'; });\n btn.addEventListener('click', () => {\n const context = selectedText || editorRef?.getHTML() || '';\n runPrompt(`${action.prompt}\\n\\n${context}`, resultArea, applyBtn);\n });\n quickGrid.appendChild(btn);\n }\n panel.appendChild(quickGrid);\n\n // Custom prompt\n const promptRow = document.createElement('div');\n css(promptRow, { display: 'flex', gap: '6px', marginBottom: '12px' });\n\n const promptInput = document.createElement('textarea');\n promptInput.placeholder = ai.customPromptPlaceholder;\n promptInput.rows = 2;\n css(promptInput, {\n flex: '1',\n padding: '8px 10px',\n fontSize: '12px',\n border: '1px solid var(--oe-border, #e7e5e4)',\n borderRadius: '8px',\n background: 'var(--oe-bg, #fff)',\n color: 'var(--oe-text, #1c1917)',\n fontFamily: 'inherit',\n resize: 'none',\n outline: 'none',\n });\n\n const runBtn = document.createElement('button');\n runBtn.type = 'button';\n runBtn.textContent = ai.runButton;\n css(runBtn, {\n padding: '0 14px',\n fontSize: '12px',\n fontWeight: '600',\n border: 'none',\n borderRadius: '8px',\n background: '#1c1917',\n color: '#fff',\n cursor: 'pointer',\n fontFamily: 'inherit',\n alignSelf: 'stretch',\n });\n runBtn.addEventListener('click', () => {\n const context = selectedText || editorRef?.getHTML() || '';\n const instruction = promptInput.value.trim();\n if (!instruction) return;\n runPrompt(`${instruction}\\n\\n${context}`, resultArea, applyBtn);\n });\n\n promptRow.appendChild(promptInput);\n promptRow.appendChild(runBtn);\n panel.appendChild(promptRow);\n\n // Result area\n const resultArea = document.createElement('div');\n css(resultArea, {\n display: 'none',\n fontSize: '12px',\n color: 'var(--oe-text, #1c1917)',\n background: 'var(--oe-btn-hover-bg, #f5f5f4)',\n borderRadius: '8px',\n padding: '10px',\n marginBottom: '10px',\n lineHeight: '1.6',\n maxHeight: '120px',\n overflowY: 'auto',\n });\n panel.appendChild(resultArea);\n\n // Apply button (hidden initially)\n const applyBtn = document.createElement('button');\n applyBtn.type = 'button';\n applyBtn.textContent = ai.applyButton;\n applyBtn.style.display = 'none';\n css(applyBtn, {\n width: '100%',\n padding: '9px',\n fontSize: '13px',\n fontWeight: '600',\n border: 'none',\n borderRadius: '8px',\n background: '#1c1917',\n color: '#fff',\n cursor: 'pointer',\n fontFamily: 'inherit',\n });\n applyBtn.addEventListener('click', () => {\n const result = resultArea.dataset.result ?? '';\n if (result) {\n replaceWithResult(result);\n closePanel();\n }\n });\n panel.appendChild(applyBtn);\n\n document.body.appendChild(panel);\n\n // Position\n const rect = anchorEl.getBoundingClientRect();\n let top = rect.bottom + 4;\n let left = rect.left;\n if (left + 360 > window.innerWidth - 8) left = window.innerWidth - 368;\n if (left < 8) left = 8;\n if (top + 400 > window.innerHeight - 8) top = rect.top - 400 - 4;\n panel.style.top = `${top}px`;\n panel.style.left = `${left}px`;\n\n setTimeout(() => {\n document.addEventListener('click', onDocClick, true);\n }, 0);\n }\n\n async function runPrompt(\n fullPrompt: string,\n resultArea: HTMLElement,\n applyBtn: HTMLElement,\n ): Promise<void> {\n const ai = locale.plugins.ai;\n if (!opts.apiKey && !opts.endpoint) {\n resultArea.style.display = 'block';\n resultArea.textContent = ai.noApiKey;\n return;\n }\n\n resultArea.style.display = 'block';\n resultArea.textContent = ai.generating;\n applyBtn.style.display = 'none';\n\n try {\n const result = await callClaude(fullPrompt, opts);\n resultArea.textContent = result.length > 400 ? result.slice(0, 400) + '…' : result;\n resultArea.dataset.result = result;\n applyBtn.style.display = 'block';\n } catch (err) {\n resultArea.textContent = `${ai.errorPrefix}${err instanceof Error ? err.message : String(err)}`;\n }\n }\n\n return {\n name: 'ai-assistant',\n\n onInit(editor: EditorInterface) {\n editorRef = editor;\n locale = (editor as unknown as { locale: EditorLocale }).locale ?? en;\n\n const editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n const containerEl = (editor as unknown as { containerEl: HTMLElement }).containerEl;\n const toolbarEl = containerEl?.querySelector<HTMLElement>('.oe-toolbar');\n if (!toolbarEl) return;\n\n // Track selection\n document.addEventListener('selectionchange', () => {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0 && editorEl.contains(sel.anchorNode)) {\n savedRange = sel.getRangeAt(0).cloneRange();\n selectedText = sel.isCollapsed ? '' : sel.toString().trim();\n }\n });\n\n // Toolbar button\n const sep = document.createElement('div');\n sep.className = 'oe-toolbar-sep';\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'oe-toolbar-btn';\n btn.title = locale.plugins.ai.panelTitle;\n btn.innerHTML = AI_ICON;\n // Accent color for AI button\n css(btn, { color: 'rgb(99,102,241)' });\n aiBtn = btn;\n\n btn.addEventListener('mousedown', (e) => {\n e.preventDefault();\n captureSelection(editorEl);\n if (panel) { closePanel(); } else { buildPanel(btn); }\n });\n\n toolbarEl.appendChild(sep);\n toolbarEl.appendChild(btn);\n\n // Slash command: /ai\n editorEl.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const lineText = range.startContainer.textContent ?? '';\n if (lineText.trim() === '/ai') {\n e.preventDefault();\n // Remove /ai text\n const r = document.createRange();\n r.setStart(range.startContainer, 0);\n r.setEnd(range.startContainer, lineText.length);\n r.deleteContents();\n captureSelection(editorEl);\n buildPanel(btn);\n }\n }\n });\n },\n\n onDestroy() {\n closePanel();\n editorRef = null;\n },\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Callout Plugin\n//\n// Adds slash-command support for inserting callout blocks:\n// /callout → Info callout\n// /callout-info → Info callout\n// /callout-success → Success callout\n// /callout-warning → Warning callout\n// /callout-danger → Danger callout\n//\n// Usage:\n// import { createCalloutPlugin } from 'openedit/plugins/callout';\n// editor.use(createCalloutPlugin());\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorPlugin, EditorInterface } from '../core/types.js';\nimport type { CalloutVariant } from '../core/types.js';\n\nconst CALLOUT_VARIANTS: CalloutVariant[] = ['info', 'success', 'warning', 'danger'];\n\n// Maps slash-command tokens → callout variants\nconst SLASH_MAP: Record<string, CalloutVariant> = {\n '/callout': 'info',\n '/callout-info': 'info',\n '/callout-success': 'success',\n '/callout-warning': 'warning',\n '/callout-danger': 'danger',\n};\n\nexport function createCalloutPlugin(): EditorPlugin {\n let editorEl: HTMLElement | null = null;\n let editorRef: EditorInterface | null = null;\n\n const onKeydown = (e: KeyboardEvent): void => {\n if (e.key !== 'Enter' || e.shiftKey || !editorEl) return;\n\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n\n const range = sel.getRangeAt(0);\n if (!range.collapsed) return;\n\n // Get the text content of the current block element\n let blockEl: Node | null = range.startContainer;\n while (blockEl && blockEl.parentNode !== editorEl) {\n blockEl = blockEl.parentNode;\n }\n if (!blockEl || blockEl.nodeType !== Node.ELEMENT_NODE) return;\n\n const lineText = (blockEl as Element).textContent?.trim() ?? '';\n const matchedVariant = SLASH_MAP[lineText.toLowerCase()];\n if (!matchedVariant) return;\n\n e.preventDefault();\n\n // Clear the slash-command text from the block\n (blockEl as Element).innerHTML = '';\n\n // Insert callout via chain API\n editorRef?.chain().setBlock('callout', { variant: matchedVariant }).run();\n };\n\n return {\n name: 'callout',\n\n onInit(editor: EditorInterface) {\n editorRef = editor;\n // Access the contentEditable element via the internal structure\n editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n if (editorEl) {\n editorEl.addEventListener('keydown', onKeydown);\n }\n },\n\n onDestroy(_editor: EditorInterface) {\n if (editorEl) {\n editorEl.removeEventListener('keydown', onKeydown);\n }\n editorEl = null;\n editorRef = null;\n },\n\n // Keymaps: Ctrl+Shift+I → insert Info callout quickly\n keymaps: {\n 'ctrl+shift+i': (editor: EditorInterface) => {\n editor.chain().setBlock('callout', { variant: 'info' }).run();\n return true;\n },\n },\n };\n}\n\nexport { CALLOUT_VARIANTS };\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Slash Commands Plugin\n//\n// Zeigt ein kontextuelles Dropdown-Menü wenn der Nutzer \"/\" am Anfang\n// eines Blocks tippt. Bekannt aus Notion, Telegram, Slack.\n//\n// Unterstützte Befehle (Standard):\n// /paragraph, /p → Paragraph\n// /h1, /heading1 → Heading 1\n// /h2, /heading2 → Heading 2\n// /h3, /heading3 → Heading 3\n// /h4 – /h6 → Headings 4–6\n// /quote, /blockquote, /bq → Blockquote\n// /code, /codeblock → Code Block\n// /ul, /bullet, /bulletlist → Bullet List\n// /ol, /numbered, /orderedlist → Numbered List\n// /hr, /divider, /--- → Horizontal Rule\n// /callout, /info → Callout Info\n// /success → Callout Success\n// /warning → Callout Warning\n// /danger, /error → Callout Danger\n//\n// Usage:\n// import { createSlashCommandsPlugin } from 'openedit/plugins/slash-commands';\n// editor.use(createSlashCommandsPlugin());\n//\n// // Eigene Befehle ergänzen / ersetzen:\n// editor.use(createSlashCommandsPlugin({ extraCommands: [...] }));\n// editor.use(createSlashCommandsPlugin({ commands: myCommands })); // komplett ersetzen\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { EditorPlugin, EditorInterface } from '../core/types.js';\n\n// ── Typen ────────────────────────────────────────────────────────────────────\n\nexport interface SlashCommand {\n /** Eindeutiger Bezeichner, wird für CSS data-Attribute verwendet */\n id: string;\n /** Angezeigter Titel im Menü */\n title: string;\n /** Optionale kurze Beschreibung unter dem Titel */\n description?: string;\n /** Icon: SVG-String oder HTML-Text (z.B. \"H1\", \"¶\") */\n icon: string;\n /** Suchbegriffe für die Filterung (lowercase) */\n keywords: string[];\n /** Wird ausgeführt wenn der Nutzer den Befehl auswählt */\n execute: (editor: EditorInterface) => void;\n}\n\nexport interface SlashCommandsOptions {\n /** Komplett eigene Befehlsliste (ersetzt die Standardbefehle) */\n commands?: SlashCommand[];\n /** Zusätzliche Befehle die zu den Standardbefehlen hinzugefügt werden */\n extraCommands?: SlashCommand[];\n}\n\n// ── Icons (SVG inline, 16×16, stroke-basiert) ────────────────────────────────\n\nconst SVG_PARAGRAPH = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 4v16\"/><path d=\"M17 4H9.5a4.5 4.5 0 0 0 0 9H13\"/></svg>`;\nconst SVG_QUOTE = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z\"/><path d=\"M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z\"/></svg>`;\nconst SVG_CODE = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/></svg>`;\nconst SVG_UL = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"9\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"9\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"9\" y1=\"18\" x2=\"20\" y2=\"18\"/><circle cx=\"4\" cy=\"6\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"4\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"4\" cy=\"18\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\"/></svg>`;\nconst SVG_OL = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\"/><text x=\"2\" y=\"9\" font-size=\"7\" fill=\"currentColor\" stroke=\"none\" font-family=\"sans-serif\" font-weight=\"700\">1.</text><text x=\"2\" y=\"15\" font-size=\"7\" fill=\"currentColor\" stroke=\"none\" font-family=\"sans-serif\" font-weight=\"700\">2.</text><text x=\"2\" y=\"21\" font-size=\"7\" fill=\"currentColor\" stroke=\"none\" font-family=\"sans-serif\" font-weight=\"700\">3.</text></svg>`;\nconst SVG_HR = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/></svg>`;\nconst SVG_CALLOUT_INFO = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>`;\nconst SVG_CALLOUT_SUCCESS = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/></svg>`;\nconst SVG_CALLOUT_WARN = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>`;\nconst SVG_CALLOUT_DANGER = `<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/></svg>`;\n\nfunction headingIcon(level: number): string {\n return `<span style=\"font-size:11px;font-weight:700;letter-spacing:-0.5px;line-height:1\">H${level}</span>`;\n}\n\n// ── Standard-Befehle ─────────────────────────────────────────────────────────\n\nfunction buildDefaultCommands(): SlashCommand[] {\n return [\n {\n id: 'paragraph',\n title: 'Paragraph',\n description: 'Plain text block',\n icon: SVG_PARAGRAPH,\n keywords: ['paragraph', 'text', 'p', 'normal'],\n execute: (e) => e.chain().setBlock('paragraph').run(),\n },\n {\n id: 'h1',\n title: 'Heading 1',\n description: 'Large section title',\n icon: headingIcon(1),\n keywords: ['heading', 'h1', 'heading1', 'title', '1'],\n execute: (e) => e.chain().setBlock('heading', { level: 1 }).run(),\n },\n {\n id: 'h2',\n title: 'Heading 2',\n description: 'Medium section title',\n icon: headingIcon(2),\n keywords: ['heading', 'h2', 'heading2', '2'],\n execute: (e) => e.chain().setBlock('heading', { level: 2 }).run(),\n },\n {\n id: 'h3',\n title: 'Heading 3',\n description: 'Small section title',\n icon: headingIcon(3),\n keywords: ['heading', 'h3', 'heading3', '3'],\n execute: (e) => e.chain().setBlock('heading', { level: 3 }).run(),\n },\n {\n id: 'h4',\n title: 'Heading 4',\n icon: headingIcon(4),\n keywords: ['heading', 'h4', 'heading4', '4'],\n execute: (e) => e.chain().setBlock('heading', { level: 4 }).run(),\n },\n {\n id: 'h5',\n title: 'Heading 5',\n icon: headingIcon(5),\n keywords: ['heading', 'h5', 'heading5', '5'],\n execute: (e) => e.chain().setBlock('heading', { level: 5 }).run(),\n },\n {\n id: 'h6',\n title: 'Heading 6',\n icon: headingIcon(6),\n keywords: ['heading', 'h6', 'heading6', '6'],\n execute: (e) => e.chain().setBlock('heading', { level: 6 }).run(),\n },\n {\n id: 'quote',\n title: 'Blockquote',\n description: 'Indented citation block',\n icon: SVG_QUOTE,\n keywords: ['quote', 'blockquote', 'bq', 'citation'],\n execute: (e) => e.chain().setBlock('blockquote').run(),\n },\n {\n id: 'code',\n title: 'Code Block',\n description: 'Monospace preformatted code',\n icon: SVG_CODE,\n keywords: ['code', 'codeblock', 'pre', 'monospace', 'snippet'],\n execute: (e) => e.chain().setBlock('code_block').run(),\n },\n {\n id: 'bullet',\n title: 'Bullet List',\n description: 'Unordered list with dots',\n icon: SVG_UL,\n keywords: ['ul', 'bullet', 'bulletlist', 'unordered', 'list', '-'],\n execute: (e) => e.chain().toggleList('bullet_list').run(),\n },\n {\n id: 'numbered',\n title: 'Numbered List',\n description: 'Ordered numbered list',\n icon: SVG_OL,\n keywords: ['ol', 'numbered', 'orderedlist', 'ordered', 'list', '1'],\n execute: (e) => e.chain().toggleList('ordered_list').run(),\n },\n {\n id: 'hr',\n title: 'Divider',\n description: 'Horizontal rule',\n icon: SVG_HR,\n keywords: ['hr', 'divider', 'separator', 'rule', '---'],\n execute: (e) => e.chain().insertHr().run(),\n },\n {\n id: 'callout-info',\n title: 'Callout: Info',\n description: 'Blue informational callout',\n icon: SVG_CALLOUT_INFO,\n keywords: ['callout', 'info', 'note', 'callout-info'],\n execute: (e) => e.chain().setBlock('callout', { variant: 'info' }).run(),\n },\n {\n id: 'callout-success',\n title: 'Callout: Success',\n description: 'Green success callout',\n icon: SVG_CALLOUT_SUCCESS,\n keywords: ['callout', 'success', 'done', 'callout-success'],\n execute: (e) => e.chain().setBlock('callout', { variant: 'success' }).run(),\n },\n {\n id: 'callout-warning',\n title: 'Callout: Warning',\n description: 'Yellow warning callout',\n icon: SVG_CALLOUT_WARN,\n keywords: ['callout', 'warning', 'warn', 'caution', 'callout-warning'],\n execute: (e) => e.chain().setBlock('callout', { variant: 'warning' }).run(),\n },\n {\n id: 'callout-danger',\n title: 'Callout: Danger',\n description: 'Red danger or error callout',\n icon: SVG_CALLOUT_DANGER,\n keywords: ['callout', 'danger', 'error', 'callout-danger'],\n execute: (e) => e.chain().setBlock('callout', { variant: 'danger' }).run(),\n },\n ];\n}\n\n// ── CSS ───────────────────────────────────────────────────────────────────────\n\nconst STYLE_ID = 'oe-slash-menu-styles';\n\nfunction injectMenuStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n.oe-slash-menu {\n position: fixed;\n z-index: 10000;\n background: var(--oe-bg, #ffffff);\n border: 1px solid var(--oe-border, #e7e5e4);\n border-radius: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.13), 0 2px 8px rgba(0,0,0,0.07);\n padding: 5px;\n min-width: 230px;\n max-height: 300px;\n overflow-y: auto;\n font-family: var(--oe-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-size: 13px;\n scrollbar-width: thin;\n scrollbar-color: var(--oe-border, #e7e5e4) transparent;\n}\n.oe-slash-menu-empty {\n padding: 10px 12px;\n color: var(--oe-text-muted, #78716c);\n font-size: 13px;\n}\n.oe-slash-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n user-select: none;\n transition: background 80ms;\n}\n.oe-slash-menu-item:hover {\n background: var(--oe-btn-hover-bg, #f5f5f4);\n}\n.oe-slash-menu-item.oe-slash-active {\n background: var(--oe-btn-active-bg, #1c1917);\n color: var(--oe-btn-active-fg, #ffffff);\n}\n.oe-slash-menu-item.oe-slash-active .oe-slash-menu-desc {\n color: rgba(255,255,255,0.6);\n}\n.oe-slash-menu-icon {\n width: 30px;\n height: 30px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--oe-btn-hover-bg, #f5f5f4);\n border: 1px solid var(--oe-border, #e7e5e4);\n border-radius: 6px;\n flex-shrink: 0;\n color: var(--oe-text, #1c1917);\n}\n.oe-slash-menu-item.oe-slash-active .oe-slash-menu-icon {\n background: rgba(255,255,255,0.12);\n border-color: rgba(255,255,255,0.2);\n color: #ffffff;\n}\n.oe-slash-menu-text { flex: 1; min-width: 0; }\n.oe-slash-menu-title {\n font-weight: 500;\n color: var(--oe-text, #1c1917);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.oe-slash-menu-item.oe-slash-active .oe-slash-menu-title {\n color: var(--oe-btn-active-fg, #ffffff);\n}\n.oe-slash-menu-desc {\n font-size: 11px;\n color: var(--oe-text-muted, #78716c);\n margin-top: 1px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n`;\n document.head.appendChild(style);\n}\n\n// ── Plugin ────────────────────────────────────────────────────────────────────\n\nexport function createSlashCommandsPlugin(options: SlashCommandsOptions = {}): EditorPlugin {\n const commands: SlashCommand[] = options.commands\n ? options.commands\n : [...buildDefaultCommands(), ...(options.extraCommands ?? [])];\n\n let editorEl: HTMLElement | null = null;\n let editorRef: EditorInterface | null = null;\n let menuEl: HTMLElement | null = null;\n let isOpen = false;\n let activeIndex = 0;\n let filteredCommands: SlashCommand[] = [];\n\n // ── Hilfsfunktionen ────────────────────────────────────────────────────────\n\n /** Gibt den aktuellen Block-DOM-Node zurück, wenn sein Text mit \"/\" beginnt */\n function getSlashBlock(): { blockEl: Element; query: string } | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n const range = sel.getRangeAt(0);\n if (!range.collapsed) return null;\n\n let node: Node | null = range.startContainer;\n while (node && node.parentNode !== editorEl) {\n node = node.parentNode;\n }\n if (!node || node.nodeType !== Node.ELEMENT_NODE) return null;\n const blockEl = node as Element;\n\n const text = blockEl.textContent ?? '';\n if (!text.startsWith('/')) return null;\n\n return { blockEl, query: text.slice(1).toLowerCase() };\n }\n\n function filterCommands(query: string): SlashCommand[] {\n if (!query) return commands;\n return commands.filter((cmd) => {\n const q = query.toLowerCase();\n return (\n cmd.id.includes(q) ||\n cmd.title.toLowerCase().includes(q) ||\n cmd.keywords.some((kw) => kw.includes(q))\n );\n });\n }\n\n function getMenuPosition(): { top: number; left: number } {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const rect = sel.getRangeAt(0).getBoundingClientRect();\n if (rect.width > 0 || rect.height > 0) {\n return { top: rect.bottom + 6, left: rect.left };\n }\n }\n // Fallback: position near the editor element\n if (editorEl) {\n const rect = editorEl.getBoundingClientRect();\n return { top: rect.top + 40, left: rect.left + 16 };\n }\n return { top: 100, left: 100 };\n }\n\n function clampPosition(el: HTMLElement, top: number, left: number): { top: number; left: number } {\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n const w = el.offsetWidth || 240;\n const h = el.offsetHeight || 300;\n\n if (left + w > vw - 8) left = vw - w - 8;\n if (left < 8) left = 8;\n if (top + h > vh - 8) top = top - h - 28; // flip above cursor\n if (top < 8) top = 8;\n\n return { top, left };\n }\n\n // ── Menü-Rendering ─────────────────────────────────────────────────────────\n\n function renderMenu(): void {\n if (!menuEl) return;\n\n if (filteredCommands.length === 0) {\n menuEl.innerHTML = `<div class=\"oe-slash-menu-empty\">No matching blocks</div>`;\n return;\n }\n\n menuEl.innerHTML = filteredCommands\n .map((cmd, i) => {\n const isActive = i === activeIndex;\n return `\n <div class=\"oe-slash-menu-item${isActive ? ' oe-slash-active' : ''}\" data-index=\"${i}\">\n <div class=\"oe-slash-menu-icon\">${cmd.icon}</div>\n <div class=\"oe-slash-menu-text\">\n <div class=\"oe-slash-menu-title\">${cmd.title}</div>\n ${cmd.description ? `<div class=\"oe-slash-menu-desc\">${cmd.description}</div>` : ''}\n </div>\n </div>`;\n })\n .join('');\n\n // Scroll active item into view\n const activeEl = menuEl.querySelector<HTMLElement>('.oe-slash-active');\n activeEl?.scrollIntoView({ block: 'nearest' });\n }\n\n function showMenu(): void {\n if (!menuEl) return;\n isOpen = true;\n menuEl.style.display = 'block';\n\n const { top, left } = getMenuPosition();\n menuEl.style.top = `${top}px`;\n menuEl.style.left = `${left}px`;\n\n // Clamp after render (needs offsetWidth/Height)\n requestAnimationFrame(() => {\n if (!menuEl) return;\n const clamped = clampPosition(menuEl, top, left);\n menuEl.style.top = `${clamped.top}px`;\n menuEl.style.left = `${clamped.left}px`;\n });\n\n renderMenu();\n }\n\n function hideMenu(): void {\n if (!menuEl) return;\n isOpen = false;\n menuEl.style.display = 'none';\n menuEl.innerHTML = '';\n activeIndex = 0;\n filteredCommands = [];\n }\n\n function executeCommand(cmd: SlashCommand): void {\n if (!editorRef || !editorEl) return;\n\n const result = getSlashBlock();\n let blockIndex = -1;\n\n if (result) {\n const blockEl = result.blockEl as HTMLElement;\n\n // Remember which block index we're in so we can restore cursor after rerender\n blockIndex = Array.from(editorEl.children).indexOf(blockEl);\n\n // Clear the \"/command\" text\n blockEl.innerHTML = '';\n\n // Explicitly restore the selection inside this block — otherwise the browser\n // resets it to position 0 and getActiveBlockIndex() targets the wrong block.\n const sel = window.getSelection();\n if (sel) {\n const range = document.createRange();\n range.setStart(blockEl, 0);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n }\n }\n\n hideMenu();\n cmd.execute(editorRef);\n\n // After the rerender move the cursor into the transformed block (not doc start).\n // requestAnimationFrame ensures the DOM patch from rerender has settled.\n if (blockIndex >= 0) {\n requestAnimationFrame(() => {\n if (!editorEl) return;\n const block = editorEl.children[blockIndex] as HTMLElement | undefined;\n if (!block) return;\n\n // Find the deepest last text node to place cursor at end of content\n const lastText = lastTextNode(block);\n const sel = window.getSelection();\n if (!sel) return;\n\n const range = document.createRange();\n if (lastText) {\n // Non-empty block: cursor at end of text\n range.setStart(lastText, lastText.length);\n } else if (block instanceof HTMLElement && block.tagName === 'HR') {\n // Void element (<hr>) — put cursor in the block after it\n const next = editorEl.children[blockIndex + 1] as HTMLElement | undefined;\n const target = next ?? block;\n range.setStart(target, 0);\n } else {\n // Empty editable block (e.g. freshly inserted heading) — cursor inside it\n range.setStart(block, 0);\n }\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n });\n }\n }\n\n /** Depth-first search for the deepest last text node inside an element. */\n function lastTextNode(node: Node): Text | null {\n for (let i = node.childNodes.length - 1; i >= 0; i--) {\n const found = lastTextNode(node.childNodes[i]);\n if (found) return found;\n }\n return node.nodeType === Node.TEXT_NODE ? (node as Text) : null;\n }\n\n // ── Event-Handler ──────────────────────────────────────────────────────────\n\n const onInput = (): void => {\n const result = getSlashBlock();\n\n if (!result) {\n if (isOpen) hideMenu();\n return;\n }\n\n const { query } = result;\n filteredCommands = filterCommands(query);\n\n // Prevent active index overflow after re-filter\n if (activeIndex >= filteredCommands.length) activeIndex = 0;\n\n if (filteredCommands.length > 0) {\n showMenu();\n } else {\n // No matches → keep showing \"No matching blocks\" hint briefly\n showMenu();\n }\n };\n\n const onKeydown = (e: KeyboardEvent): void => {\n if (!isOpen) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n e.stopImmediatePropagation();\n activeIndex = (activeIndex + 1) % Math.max(filteredCommands.length, 1);\n renderMenu();\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopImmediatePropagation();\n activeIndex = (activeIndex - 1 + Math.max(filteredCommands.length, 1)) % Math.max(filteredCommands.length, 1);\n renderMenu();\n break;\n\n case 'Enter':\n if (filteredCommands.length > 0 && filteredCommands[activeIndex]) {\n e.preventDefault();\n e.stopImmediatePropagation();\n executeCommand(filteredCommands[activeIndex]);\n }\n break;\n\n case 'Escape':\n e.preventDefault();\n e.stopImmediatePropagation();\n hideMenu();\n break;\n\n case 'Tab':\n // Tab selects like Enter\n if (filteredCommands.length > 0 && filteredCommands[activeIndex]) {\n e.preventDefault();\n e.stopImmediatePropagation();\n executeCommand(filteredCommands[activeIndex]);\n }\n break;\n }\n };\n\n /** Schließt Menü wenn außerhalb geklickt wird */\n const onDocumentClick = (e: MouseEvent): void => {\n if (!isOpen || !menuEl) return;\n if (!menuEl.contains(e.target as Node)) {\n hideMenu();\n }\n };\n\n /** Klick auf ein Menü-Item */\n const onMenuClick = (e: MouseEvent): void => {\n const item = (e.target as Element).closest<HTMLElement>('.oe-slash-menu-item');\n if (!item) return;\n const idx = parseInt(item.dataset.index ?? '0', 10);\n const cmd = filteredCommands[idx];\n if (cmd) executeCommand(cmd);\n };\n\n /** Scrollt das Fenster → Menü neu positionieren */\n const onScroll = (): void => {\n if (isOpen) hideMenu();\n };\n\n // ── Plugin-Objekt ──────────────────────────────────────────────────────────\n\n return {\n name: 'slash-commands',\n\n onInit(editor: EditorInterface) {\n editorRef = editor;\n editorEl = (editor as unknown as { editorEl: HTMLElement }).editorEl;\n if (!editorEl) return;\n\n injectMenuStyles();\n\n // Menü-Element erstellen und an body anhängen\n menuEl = document.createElement('div');\n menuEl.className = 'oe-slash-menu';\n menuEl.style.display = 'none';\n document.body.appendChild(menuEl);\n\n // Events registrieren (keydown muss VOR dem Editor-keydown feuern → capture: true)\n editorEl.addEventListener('keydown', onKeydown, true);\n editorEl.addEventListener('input', onInput);\n menuEl.addEventListener('click', onMenuClick);\n document.addEventListener('click', onDocumentClick, true);\n window.addEventListener('scroll', onScroll, { passive: true });\n },\n\n onDestroy(_editor: EditorInterface) {\n if (editorEl) {\n editorEl.removeEventListener('keydown', onKeydown, true);\n editorEl.removeEventListener('input', onInput);\n }\n if (menuEl) {\n menuEl.removeEventListener('click', onMenuClick);\n menuEl.remove();\n menuEl = null;\n }\n document.removeEventListener('click', onDocumentClick, true);\n window.removeEventListener('scroll', onScroll);\n editorEl = null;\n editorRef = null;\n isOpen = false;\n },\n };\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// OpenEdit — Public API & UMD Entry Point\n//\n// Usage (script tag):\n// const editor = OpenEdit.create('#editor', { placeholder: 'Start typing...' });\n//\n// Usage (ESM/npm):\n// import { OpenEdit } from 'openedit';\n// const editor = OpenEdit.create('#editor');\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport { Editor } from './editor.js';\nimport { en } from './locales/en.js';\nimport { de } from './locales/de.js';\nimport { serializeToMarkdown, deserializeMarkdown } from './io/markdown.js';\nimport { createHighlightPlugin } from './plugins/highlight.js';\nimport { createEmojiPlugin } from './plugins/emoji.js';\nimport { createTemplateTagPlugin } from './plugins/template-tags.js';\nimport { createAIPlugin } from './plugins/ai.js';\nimport { createCalloutPlugin } from './plugins/callout.js';\nimport { createSlashCommandsPlugin } from './plugins/slash-commands.js';\nimport type {\n EditorOptions,\n EditorInterface,\n EditorPlugin,\n EditorDocument,\n EditorEventMap,\n ToolbarItemConfig,\n StatusBarOptions,\n Mark,\n MarkType,\n BlockNode,\n InlineNode,\n TextNode,\n ModelSelection,\n CalloutVariant,\n CalloutNode,\n} from './core/types.js';\n\n// ── Main factory ──────────────────────────────────────────────────────────────\n\n/**\n * Create a new OpenEdit editor instance.\n *\n * @example\n * // Script tag usage\n * const editor = OpenEdit.create('#my-editor', {\n * content: '<p>Hello world</p>',\n * placeholder: 'Start typing...',\n * theme: 'auto',\n * onChange: (html) => console.log(html),\n * });\n *\n * @example\n * // With image upload hook\n * const editor = OpenEdit.create('#editor', {\n * onImageUpload: async (file) => {\n * const formData = new FormData();\n * formData.append('file', file);\n * const res = await fetch('/upload', { method: 'POST', body: formData });\n * const { url } = await res.json();\n * return url;\n * }\n * });\n */\nfunction create(\n element: string | HTMLElement,\n options?: Omit<EditorOptions, 'element'>,\n): EditorInterface {\n return new Editor({ element, ...options });\n}\n\n// ── Namespace export ──────────────────────────────────────────────────────────\n\nexport const OpenEdit = {\n create,\n version: '0.1.0',\n locales: { en, de },\n plugins: {\n highlight: createHighlightPlugin,\n emoji: createEmojiPlugin,\n templateTags: createTemplateTagPlugin,\n ai: createAIPlugin,\n callout: createCalloutPlugin,\n slashCommands: createSlashCommandsPlugin,\n },\n markdown: {\n serialize: serializeToMarkdown,\n deserialize: deserializeMarkdown,\n },\n};\n\n// ── Named exports (for ESM/TypeScript consumers) ──────────────────────────────\n\nexport { Editor, serializeToMarkdown, deserializeMarkdown, createHighlightPlugin, createEmojiPlugin, createTemplateTagPlugin, createAIPlugin, createCalloutPlugin, createSlashCommandsPlugin, en, de };\nexport type {\n EditorOptions,\n EditorInterface,\n EditorPlugin,\n EditorDocument,\n EditorEventMap,\n ToolbarItemConfig,\n StatusBarOptions,\n Mark,\n MarkType,\n BlockNode,\n InlineNode,\n TextNode,\n ModelSelection,\n CalloutVariant,\n CalloutNode,\n};\nexport type { SlashCommand, SlashCommandsOptions } from './plugins/slash-commands.js';\nexport type { EditorLocale } from './locales/types.js';\n\n// ── Default export ────────────────────────────────────────────────────────────\n\nexport default OpenEdit;\n"],"mappings":"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"}
|