@bavuchoko/js-editor 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 +234 -0
- package/dist/Editor.d.ts +39 -0
- package/dist/Editor.d.ts.map +1 -0
- package/dist/TableSelection.d.ts +8 -0
- package/dist/TableSelection.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +32 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/document.d.ts +110 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/history.d.ts +26 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2343 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +2 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/sanitize.d.ts +3 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/selection.d.ts +32 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/style.css +2 -0
- package/dist/theme.d.ts +9 -0
- package/dist/theme.d.ts.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/editor/selection.ts","../src/editor/commands.ts","../src/editor/history.ts","../src/editor/markdown.ts","../src/editor/document.ts","../src/editor/sanitize.ts","../src/editor/TableSelection.tsx","../src/editor/Toolbar.tsx","../src/editor/Editor.tsx"],"sourcesContent":["export const BLOCK_TAGS = new Set([\n 'P',\n 'H1',\n 'H2',\n 'H3',\n 'BLOCKQUOTE',\n 'PRE',\n 'UL',\n 'OL',\n 'LI',\n 'DIV',\n 'TD',\n 'TH',\n 'TABLE',\n])\n\nexport type AlignValue = 'left' | 'center' | 'right'\n\nexport type ActiveState = {\n bold: boolean\n italic: boolean\n underline: boolean\n block: string\n align: AlignValue\n inList: 'ul' | 'ol' | 'checklist' | null\n textColor: string | null\n highlightColor: string | null\n inTable: boolean\n}\n\n/** 툴바 클릭 등으로 네이티브 selection이 비어도 복구할 수 있게 보관 */\nlet lastEditorRange: Range | null = null\n\nexport function getEditorSelection(editor: HTMLElement): Selection | null {\n const selection = window.getSelection()\n if (!selection || selection.rangeCount === 0) return null\n if (!editor.contains(selection.anchorNode)) return null\n return selection\n}\n\nexport function captureSelection(editor: HTMLElement) {\n const selection = getEditorSelection(editor)\n if (!selection || selection.rangeCount === 0) return\n try {\n lastEditorRange = selection.getRangeAt(0).cloneRange()\n } catch {\n /* ignore */\n }\n}\n\nexport function restoreSavedSelection(editor: HTMLElement): boolean {\n if (!lastEditorRange) return false\n if (!editor.contains(lastEditorRange.commonAncestorContainer)) return false\n const selection = window.getSelection()\n if (!selection) return false\n try {\n selection.removeAllRanges()\n selection.addRange(lastEditorRange)\n return true\n } catch {\n return false\n }\n}\n\nexport function getRange(editor: HTMLElement): Range | null {\n const selection = getEditorSelection(editor)\n if (selection && selection.rangeCount > 0) {\n const range = selection.getRangeAt(0)\n try {\n lastEditorRange = range.cloneRange()\n } catch {\n /* ignore */\n }\n return range\n }\n\n // 셀 드래그 선택 등으로 selection이 비었을 때 마지막 위치 사용\n if (\n lastEditorRange &&\n editor.contains(lastEditorRange.commonAncestorContainer)\n ) {\n return lastEditorRange.cloneRange()\n }\n return null\n}\n\nexport function isElement(node: Node | null): node is HTMLElement {\n return node !== null && node.nodeType === Node.ELEMENT_NODE\n}\n\nexport function closest(\n node: Node | null,\n predicate: (el: HTMLElement) => boolean,\n root?: HTMLElement,\n): HTMLElement | null {\n let current: Node | null = node\n while (current && current !== root) {\n if (isElement(current) && predicate(current)) return current\n current = current.parentNode\n }\n return null\n}\n\nexport function closestTag(\n node: Node | null,\n tagNames: string | string[],\n root?: HTMLElement,\n): HTMLElement | null {\n const tags = (Array.isArray(tagNames) ? tagNames : [tagNames]).map((t) =>\n t.toUpperCase(),\n )\n return closest(node, (el) => tags.includes(el.tagName), root)\n}\n\nexport function getBlockElement(\n node: Node | null,\n root: HTMLElement,\n): HTMLElement | null {\n const block = closest(\n node,\n (el) => BLOCK_TAGS.has(el.tagName) && el !== root,\n root,\n )\n if (!block) return null\n if (block.tagName === 'LI') return block\n if (block.tagName === 'UL' || block.tagName === 'OL') {\n return closestTag(node, 'LI', root) ?? block\n }\n return block\n}\n\nexport function setSelectionToContents(el: Node, collapseToEnd = false) {\n const selection = window.getSelection()\n if (!selection) return\n const range = document.createRange()\n range.selectNodeContents(el)\n if (collapseToEnd) range.collapse(false)\n selection.removeAllRanges()\n selection.addRange(range)\n}\n\nexport function setCaretAfter(node: Node) {\n const selection = window.getSelection()\n if (!selection) return\n const range = document.createRange()\n range.setStartAfter(node)\n range.collapse(true)\n selection.removeAllRanges()\n selection.addRange(range)\n}\n\nexport function setCaretInside(el: HTMLElement, atStart = false) {\n const selection = window.getSelection()\n if (!selection) return\n const range = document.createRange()\n range.selectNodeContents(el)\n range.collapse(atStart)\n selection.removeAllRanges()\n selection.addRange(range)\n}\n\nexport function placeCaretInEmptyBlock(block: HTMLElement) {\n const emptyText = !block.textContent?.replace(/\\u200B/g, '').trim()\n // <br>만 있는 셀은 textContent가 빈 문자열이라 중복 br이 생기지 않도록 가드\n if (emptyText && !block.querySelector('br, img')) {\n block.appendChild(document.createElement('br'))\n }\n setCaretInside(block, true)\n}\n\nfunction computeAlign(block: HTMLElement | null): AlignValue {\n if (!block) return 'left'\n const align = (block.style.textAlign || '').toLowerCase()\n if (align === 'center' || align === 'right' || align === 'left') {\n return align\n }\n return 'left'\n}\n\nfunction isChecklistListEl(el: HTMLElement) {\n return (\n el.tagName === 'UL' &&\n (el.classList.contains('editor-checklist') ||\n el.getAttribute('data-list') === 'checklist')\n )\n}\n\n/** 체크리스트를 일반 ul보다 먼저 판별 */\nfunction getListKind(\n node: Node | null,\n root: HTMLElement,\n): ActiveState['inList'] {\n if (!node) return null\n\n const checklist = closest(node, (el) => isChecklistListEl(el), root)\n if (checklist) return 'checklist'\n\n const ol = closestTag(node, 'OL', root)\n if (ol) return 'ol'\n\n const ul = closest(node, (el) => el.tagName === 'UL' && !isChecklistListEl(el), root)\n if (ul) return 'ul'\n\n return null\n}\n\nexport function getActiveState(editor: HTMLElement): ActiveState {\n const range = getRange(editor)\n const node = range?.startContainer ?? null\n const endNode = range?.endContainer ?? null\n const block = getBlockElement(node, editor)\n\n // 드래그 선택 시 시작/끝 중 체크리스트면 체크리스트로 인식\n const startKind = getListKind(node, editor)\n const endKind = getListKind(endNode, editor)\n const inList =\n startKind === 'checklist' || endKind === 'checklist'\n ? 'checklist'\n : startKind ?? endKind\n\n let blockName = 'p'\n if (block) {\n if (block.tagName === 'LI') {\n blockName =\n inList === 'checklist' ? 'checklist' : inList === 'ol' ? 'ol' : 'ul'\n } else if (block.tagName === 'PRE') {\n blockName = 'pre'\n } else if (block.tagName === 'TD' || block.tagName === 'TH') {\n blockName = 'table'\n } else if (isChecklistListEl(block)) {\n blockName = 'checklist'\n } else if (block.tagName !== 'TABLE') {\n blockName = block.tagName.toLowerCase()\n }\n }\n\n const colorSpan = closest(\n node,\n (el) => el.tagName === 'SPAN' && Boolean(el.style.color),\n editor,\n )\n const highlightSpan = closest(\n node,\n (el) => el.tagName === 'SPAN' && Boolean(el.style.backgroundColor),\n editor,\n )\n const tableCell = closestTag(node, ['TD', 'TH'], editor)\n\n return {\n bold: Boolean(closestTag(node, ['STRONG', 'B'], editor)),\n italic: Boolean(closestTag(node, ['EM', 'I'], editor)),\n underline: Boolean(closestTag(node, 'U', editor)),\n block: blockName,\n align: computeAlign(block),\n inList,\n textColor: colorSpan?.style.color || tableCell?.style.color || null,\n highlightColor:\n highlightSpan?.style.backgroundColor ||\n tableCell?.style.backgroundColor ||\n null,\n inTable: Boolean(closestTag(node, 'TABLE', editor)),\n }\n}\n\nexport function getTextBeforeCaretInBlock(\n editor: HTMLElement,\n): { block: HTMLElement; text: string; range: Range } | null {\n const range = getRange(editor)\n if (!range || !range.collapsed) return null\n const block = getBlockElement(range.startContainer, editor)\n if (!block) return null\n\n const prefixRange = range.cloneRange()\n prefixRange.selectNodeContents(block)\n prefixRange.setEnd(range.startContainer, range.startOffset)\n return { block, text: prefixRange.toString(), range }\n}\n","import {\n closest,\n closestTag,\n getBlockElement,\n getRange,\n isElement,\n placeCaretInEmptyBlock,\n restoreSavedSelection,\n setCaretAfter,\n setCaretInside,\n setSelectionToContents,\n type AlignValue,\n} from './selection'\n\nconst INLINE_TAG_MAP: Record<string, string> = {\n bold: 'STRONG',\n italic: 'EM',\n underline: 'U',\n}\n\nfunction unwrapElement(el: HTMLElement) {\n const parent = el.parentNode\n if (!parent) return\n while (el.firstChild) {\n parent.insertBefore(el.firstChild, el)\n }\n parent.removeChild(el)\n parent.normalize()\n}\n\nfunction wrapRangeWithTag(range: Range, tagName: string): HTMLElement {\n const el = document.createElement(tagName)\n if (range.collapsed) {\n el.appendChild(document.createTextNode('\\u200B'))\n range.insertNode(el)\n setSelectionToContents(el.firstChild!, false)\n const selection = window.getSelection()\n if (selection) {\n const r = document.createRange()\n r.setStart(el.firstChild!, 1)\n r.collapse(true)\n selection.removeAllRanges()\n selection.addRange(r)\n }\n return el\n }\n\n try {\n range.surroundContents(el)\n } catch {\n const contents = range.extractContents()\n el.appendChild(contents)\n range.insertNode(el)\n }\n\n setSelectionToContents(el)\n return el\n}\n\nfunction findInlineWrapper(\n node: Node | null,\n tagName: string,\n root: HTMLElement,\n): HTMLElement | null {\n const aliases =\n tagName === 'STRONG'\n ? ['STRONG', 'B']\n : tagName === 'EM'\n ? ['EM', 'I']\n : [tagName]\n return closestTag(node, aliases, root)\n}\n\nexport function toggleInline(editor: HTMLElement, mark: keyof typeof INLINE_TAG_MAP) {\n const range = getRange(editor)\n if (!range) return false\n\n const tagName = INLINE_TAG_MAP[mark]\n const wrapper = findInlineWrapper(range.commonAncestorContainer, tagName, editor)\n\n if (wrapper) {\n unwrapElement(wrapper)\n return true\n }\n\n wrapRangeWithTag(range, tagName.toLowerCase())\n return true\n}\n\nfunction createParagraph(): HTMLElement {\n const p = document.createElement('p')\n p.appendChild(document.createElement('br'))\n return p\n}\n\nfunction ensureDefaultContent(editor: HTMLElement) {\n if (!editor.firstChild) {\n editor.appendChild(createParagraph())\n }\n}\n\nfunction replaceBlock(block: HTMLElement, tagName: string): HTMLElement {\n if (block.tagName === tagName.toUpperCase()) return block\n\n const next = document.createElement(tagName)\n while (block.firstChild) {\n next.appendChild(block.firstChild)\n }\n if (block.style.textAlign) {\n next.style.textAlign = block.style.textAlign\n }\n block.replaceWith(next)\n if (!next.textContent) {\n next.appendChild(document.createElement('br'))\n }\n return next\n}\n\nfunction isChecklistList(el: HTMLElement | null | undefined): boolean {\n return Boolean(\n el &&\n el.tagName === 'UL' &&\n (el.classList.contains('editor-checklist') || el.dataset.list === 'checklist'),\n )\n}\n\nfunction getListItemContentRoot(block: HTMLElement): HTMLElement {\n if (block.tagName === 'LI') {\n const content = block.querySelector(':scope > .checklist-content')\n if (content instanceof HTMLElement) return content\n }\n return block\n}\n\nfunction unwrapListItem(li: HTMLElement, editor: HTMLElement): HTMLElement {\n const list = li.parentElement\n // createParagraph()는 빈 br를 미리 넣어서, 여기선 빈 p만 만든 뒤 내용을 옮긴다\n const p = document.createElement('p')\n const contentRoot = getListItemContentRoot(li)\n if (contentRoot !== li) {\n while (contentRoot.firstChild) p.appendChild(contentRoot.firstChild)\n } else {\n while (li.firstChild) {\n const child = li.firstChild\n if (isElement(child) && child.classList.contains('checklist-box')) {\n child.remove()\n continue\n }\n p.appendChild(child)\n }\n }\n if (!p.textContent?.replace(/\\u200B/g, '').trim() && !p.querySelector('img')) {\n // li에 br만 있던 경우 중복 br 방지\n if (!p.querySelector('br')) {\n p.appendChild(document.createElement('br'))\n }\n }\n\n if (list && (list.tagName === 'UL' || list.tagName === 'OL')) {\n const parent = list.parentNode ?? editor\n if (li.nextSibling) {\n const after = document.createElement(list.tagName)\n while (li.nextSibling) {\n after.appendChild(li.nextSibling)\n }\n parent.insertBefore(after, list.nextSibling)\n parent.insertBefore(p, after)\n } else {\n parent.insertBefore(p, list.nextSibling)\n }\n li.remove()\n if (!list.children.length) list.remove()\n } else {\n li.replaceWith(p)\n }\n return p\n}\n\nfunction rangeIntersectsNode(range: Range, node: Node): boolean {\n try {\n const nodeRange = document.createRange()\n nodeRange.selectNode(node)\n return (\n range.compareBoundaryPoints(Range.END_TO_START, nodeRange) < 0 &&\n range.compareBoundaryPoints(Range.START_TO_END, nodeRange) > 0\n )\n } catch {\n return false\n }\n}\n\nfunction isLineEmpty(container: HTMLElement) {\n return (\n !container.textContent?.replace(/\\u200B/g, '').trim() &&\n !container.querySelector('img')\n )\n}\n\n/** 블록 내용을 <br> / \\\\n 기준으로 줄 단위로 분리 */\nfunction extractLinesByBreaks(block: HTMLElement): HTMLElement[] {\n if (block.tagName === 'PRE') {\n const text = block.querySelector('code')?.textContent ?? block.textContent ?? ''\n return text.split('\\n').map((line) => {\n const holder = document.createElement('div')\n if (line) holder.textContent = line\n return holder\n })\n }\n\n const lines: HTMLElement[] = []\n let current = document.createElement('div')\n\n const flush = () => {\n lines.push(current)\n current = document.createElement('div')\n }\n\n for (const child of Array.from(block.childNodes)) {\n if (isElement(child) && child.tagName === 'BR') {\n child.remove()\n flush()\n continue\n }\n\n if (child.nodeType === Node.TEXT_NODE && (child.textContent ?? '').includes('\\n')) {\n const text = child.textContent ?? ''\n child.remove()\n const parts = text.split('\\n')\n parts.forEach((part, index) => {\n if (part) current.appendChild(document.createTextNode(part))\n if (index < parts.length - 1) flush()\n })\n continue\n }\n\n current.appendChild(child)\n }\n flush()\n\n while (lines.length > 1 && isLineEmpty(lines[lines.length - 1])) {\n lines.pop()\n }\n return lines.length ? lines : [document.createElement('div')]\n}\n\n/** 선택 영역과 겹치는 블록들 (여러 문단 / 리스트 항목) */\nfunction getSelectedBlocks(editor: HTMLElement, range: Range): HTMLElement[] {\n const result: HTMLElement[] = []\n\n for (const child of Array.from(editor.children)) {\n if (!isElement(child)) continue\n\n if (child.tagName === 'UL' || child.tagName === 'OL') {\n for (const li of Array.from(child.children)) {\n if (isElement(li) && li.tagName === 'LI' && rangeIntersectsNode(range, li)) {\n result.push(li)\n }\n }\n continue\n }\n\n if (\n ['P', 'H1', 'H2', 'H3', 'BLOCKQUOTE', 'PRE', 'DIV'].includes(child.tagName) &&\n rangeIntersectsNode(range, child)\n ) {\n result.push(child)\n }\n }\n\n if (result.length === 0) {\n const block = getBlockElement(range.startContainer, editor)\n if (block && block.tagName !== 'TABLE' && block.tagName !== 'TD' && block.tagName !== 'TH') {\n result.push(block)\n }\n }\n\n return result\n}\n\nfunction lineToListItem(line: HTMLElement): HTMLLIElement {\n const li = document.createElement('li')\n while (line.firstChild) li.appendChild(line.firstChild)\n if (isLineEmpty(li)) li.appendChild(document.createElement('br'))\n return li\n}\n\nfunction lineToChecklistItem(line: HTMLElement, checked = false): HTMLLIElement {\n const li = document.createElement('li')\n li.dataset.checked = checked ? 'true' : 'false'\n\n const box = document.createElement('span')\n box.className = 'checklist-box'\n box.contentEditable = 'false'\n box.setAttribute('role', 'checkbox')\n box.setAttribute('aria-checked', checked ? 'true' : 'false')\n box.tabIndex = -1\n\n const content = document.createElement('span')\n content.className = 'checklist-content'\n while (line.firstChild) content.appendChild(line.firstChild)\n if (isLineEmpty(content)) content.appendChild(document.createElement('br'))\n\n li.appendChild(box)\n li.appendChild(content)\n return li\n}\n\nfunction createChecklistList(): HTMLUListElement {\n const list = document.createElement('ul')\n list.className = 'editor-checklist'\n list.dataset.list = 'checklist'\n return list\n}\n\n/** from~to 노드를 다시 드래그 선택 상태로 만든다 */\nfunction selectNodeSpan(from: Node, to: Node) {\n const selection = window.getSelection()\n if (!selection) return\n const next = document.createRange()\n try {\n next.setStartBefore(from)\n next.setEndAfter(to)\n } catch {\n next.selectNodeContents(from)\n }\n selection.removeAllRanges()\n selection.addRange(next)\n}\n\nfunction isSameListKind(block: HTMLElement, kind: 'ul' | 'ol' | 'checklist') {\n if (block.tagName !== 'LI' || !block.parentElement) return false\n const parent = block.parentElement\n if (kind === 'checklist') return isChecklistList(parent)\n if (kind === 'ol') return parent.tagName === 'OL'\n return parent.tagName === 'UL' && !isChecklistList(parent)\n}\n\n/** 선택/현재 블록을 줄바꿈 기준으로 목록 항목으로 변환 */\nfunction convertSelectionToList(\n editor: HTMLElement,\n listKind: 'ul' | 'ol' | 'checklist',\n): boolean {\n const range = getRange(editor)\n if (!range) return false\n\n const keepSelection = !range.collapsed\n const blocks = getSelectedBlocks(editor, range)\n if (blocks.length === 0) return false\n\n const allSameList = blocks.every((block) => isSameListKind(block, listKind))\n\n if (allSameList) {\n const paragraphs: HTMLElement[] = []\n for (const li of [...blocks].reverse()) {\n paragraphs.unshift(unwrapListItem(li, editor))\n }\n if (paragraphs.length === 0) return true\n if (keepSelection) {\n selectNodeSpan(paragraphs[0], paragraphs[paragraphs.length - 1])\n } else {\n setCaretInside(paragraphs[0])\n }\n return true\n }\n\n const list =\n listKind === 'checklist'\n ? createChecklistList()\n : document.createElement(listKind)\n\n for (const block of blocks) {\n const checked =\n block.tagName === 'LI' && block.dataset.checked === 'true'\n const source = getListItemContentRoot(block)\n for (const line of extractLinesByBreaks(source)) {\n if (listKind === 'checklist') {\n list.appendChild(lineToChecklistItem(line, checked))\n } else {\n list.appendChild(lineToListItem(line))\n }\n }\n }\n\n if (!list.children.length) {\n list.appendChild(\n listKind === 'checklist'\n ? lineToChecklistItem(document.createElement('div'))\n : lineToListItem(document.createElement('div')),\n )\n }\n\n const first = blocks[0]\n first.before(list)\n\n for (const block of blocks) {\n const parent = block.parentElement\n block.remove()\n if (\n parent &&\n (parent.tagName === 'UL' || parent.tagName === 'OL') &&\n !parent.children.length\n ) {\n parent.remove()\n }\n }\n\n const items = Array.from(list.children) as HTMLElement[]\n const firstLi = items[0]\n const lastLi = items[items.length - 1]\n if (!firstLi) return true\n\n if (keepSelection) {\n selectNodeSpan(firstLi, lastLi)\n } else {\n const caretTarget =\n (firstLi.querySelector('.checklist-content') as HTMLElement | null) ??\n firstLi\n setCaretInside(caretTarget)\n }\n return true\n}\n\n/** 체크리스트 체크박스 클릭 토글 */\nexport function handleChecklistClick(\n editor: HTMLElement,\n event: MouseEvent,\n): boolean {\n const target = event.target\n if (!(target instanceof Element)) return false\n const box = target.closest('.checklist-box')\n if (!box || !editor.contains(box)) return false\n const li = box.closest('li')\n if (!li || !isChecklistList(li.parentElement)) return false\n\n event.preventDefault()\n const next = li.dataset.checked === 'true' ? 'false' : 'true'\n li.dataset.checked = next\n box.setAttribute('aria-checked', next)\n return true\n}\n\nexport function setBlockFormat(\n editor: HTMLElement,\n format: 'p' | 'h1' | 'h2' | 'h3' | 'blockquote' | 'pre' | 'ul' | 'ol' | 'checklist',\n) {\n ensureDefaultContent(editor)\n const range = getRange(editor)\n if (!range) return false\n\n let block = getBlockElement(range.startContainer, editor)\n if (!block) {\n editor.appendChild(createParagraph())\n block = editor.firstElementChild as HTMLElement\n }\n\n if (format === 'ul' || format === 'ol' || format === 'checklist') {\n return convertSelectionToList(editor, format)\n }\n\n if (block.tagName === 'LI') {\n block = unwrapListItem(block, editor)\n }\n\n if (format === 'pre') {\n const text = block.textContent ?? ''\n const pre = document.createElement('pre')\n const code = document.createElement('code')\n code.textContent = text === '\\n' ? '' : text\n if (!code.textContent) code.appendChild(document.createTextNode(''))\n pre.appendChild(code)\n block.replaceWith(pre)\n setCaretInside(code)\n return true\n }\n\n if (block.tagName === 'PRE') {\n const code = block.querySelector('code')\n const text = code?.textContent ?? block.textContent ?? ''\n const next = document.createElement(format)\n next.textContent = text\n if (!next.textContent) next.appendChild(document.createElement('br'))\n block.replaceWith(next)\n setCaretInside(next)\n return true\n }\n\n const next = replaceBlock(block, format)\n setCaretInside(next)\n return true\n}\n\nexport function setAlign(editor: HTMLElement, align: AlignValue) {\n const range = getRange(editor)\n if (!range) return false\n const block = getBlockElement(range.startContainer, editor)\n if (!block) return false\n const target =\n block.tagName === 'LI' ? (block.parentElement as HTMLElement) : block\n target.style.textAlign = align === 'left' ? '' : align\n return true\n}\n\nfunction findStyleSpan(\n node: Node | null,\n prop: 'color' | 'backgroundColor',\n root: HTMLElement,\n): HTMLElement | null {\n return closest(\n node,\n (el) => el.tagName === 'SPAN' && Boolean(el.style[prop]),\n root,\n )\n}\n\nfunction cleanupEmptySpan(el: HTMLElement) {\n if (!el.getAttribute('style')?.trim() && !el.className) {\n unwrapElement(el)\n }\n}\n\nfunction getSelectedTableCells(editor: HTMLElement): HTMLTableCellElement[] {\n return Array.from(\n editor.querySelectorAll('td.is-cell-selected, th.is-cell-selected'),\n ) as HTMLTableCellElement[]\n}\n\nfunction applyStyleToTableCells(\n cells: HTMLTableCellElement[],\n prop: 'color' | 'backgroundColor',\n value: string | null,\n) {\n for (const cell of cells) {\n cell.style[prop] = value ?? ''\n }\n return true\n}\n\nfunction applyInlineStyle(\n editor: HTMLElement,\n prop: 'color' | 'backgroundColor',\n value: string | null,\n) {\n // 드래그로 고른 셀들 → 셀 전체에 적용 (테이블에서 바로 보이도록)\n const selectedCells = getSelectedTableCells(editor)\n if (selectedCells.length > 0) {\n return applyStyleToTableCells(selectedCells, prop, value)\n }\n\n const range = getRange(editor)\n if (!range) return false\n\n const tableCell = closestTag(\n range.startContainer,\n ['TD', 'TH'],\n editor,\n ) as HTMLTableCellElement | null\n\n // 셀 안 캐럿만 있을 때: span(투명 문자) 대신 셀 스타일 — 빈 셀에서도 보이게\n if (tableCell && range.collapsed) {\n tableCell.style[prop] = value ?? ''\n return true\n }\n\n // 셀 안 일부 텍스트 선택: 인라인 span\n if (tableCell && !range.collapsed) {\n if (!value) {\n const span = wrapRangeWithTag(range, 'span')\n if (prop === 'color') span.style.color = 'inherit'\n else span.style.backgroundColor = 'transparent'\n return true\n }\n const span = wrapRangeWithTag(range, 'span')\n span.style[prop] = value\n return true\n }\n\n const existing = findStyleSpan(range.commonAncestorContainer, prop, editor)\n\n if (!value) {\n if (!existing) return true\n // Only clear when the selection is collapsed inside the span,\n // or the selection fully covers that span — never clear a larger region implicitly.\n if (range.collapsed && existing.contains(range.startContainer)) {\n existing.style[prop] = ''\n cleanupEmptySpan(existing)\n return true\n }\n if (!range.collapsed) {\n // Wrap selection in a neutral span by stripping via nested override\n const span = wrapRangeWithTag(range, 'span')\n if (prop === 'color') {\n span.style.color = 'inherit'\n } else {\n span.style.backgroundColor = 'transparent'\n }\n return true\n }\n return true\n }\n\n // Always wrap only the current caret/selection.\n // Do NOT recolor an ancestor span in place — that was recoloring whole code blocks.\n const span = wrapRangeWithTag(range, 'span')\n span.style[prop] = value\n return true\n}\n\nexport function setTextColor(editor: HTMLElement, color: string | null) {\n return applyInlineStyle(editor, 'color', color)\n}\n\nexport function setHighlightColor(editor: HTMLElement, color: string | null) {\n return applyInlineStyle(editor, 'backgroundColor', color)\n}\n\nexport function insertHTML(editor: HTMLElement, html: string) {\n const range = getRange(editor)\n if (!range) return false\n range.deleteContents()\n const template = document.createElement('template')\n template.innerHTML = html\n const frag = template.content\n const last = frag.lastChild\n range.insertNode(frag)\n if (last) setCaretAfter(last)\n return true\n}\n\nfunction createTableCell(tag: 'td' | 'th' = 'td'): HTMLTableCellElement {\n const cell = document.createElement(tag)\n cell.appendChild(document.createElement('br'))\n return cell\n}\n\nexport function insertTable(\n editor: HTMLElement,\n rows = 3,\n cols = 3,\n): boolean {\n const range = getRange(editor)\n if (!range) return false\n\n const rowCount = Math.min(Math.max(rows, 1), 20)\n const colCount = Math.min(Math.max(cols, 1), 20)\n\n const table = document.createElement('table')\n table.className = 'editor-table'\n const tbody = document.createElement('tbody')\n\n for (let r = 0; r < rowCount; r++) {\n const tr = document.createElement('tr')\n for (let c = 0; c < colCount; c++) {\n tr.appendChild(createTableCell('td'))\n }\n tbody.appendChild(tr)\n }\n table.appendChild(tbody)\n\n const block = getBlockElement(range.startContainer, editor)\n if (block && block.tagName !== 'TD' && block.tagName !== 'TH') {\n const empty =\n !block.textContent?.replace(/\\u200B/g, '').trim() &&\n !block.querySelector('img, table')\n if (empty) {\n block.replaceWith(table)\n } else {\n block.after(table)\n }\n } else {\n range.collapse(true)\n range.insertNode(table)\n }\n\n const after = createParagraph()\n table.after(after)\n\n const firstCell = table.querySelector('th, td') as HTMLElement | null\n if (firstCell) placeCaretInEmptyBlock(firstCell)\n return true\n}\n\nfunction getTableCells(table: HTMLTableElement): HTMLTableCellElement[] {\n return Array.from(table.querySelectorAll('th, td'))\n}\n\nfunction getCurrentTableCell(\n editor: HTMLElement,\n): HTMLTableCellElement | null {\n const range = getRange(editor)\n if (!range) return null\n return closestTag(range.startContainer, ['TD', 'TH'], editor) as HTMLTableCellElement | null\n}\n\nfunction cellHasVisibleContent(cell: HTMLElement): boolean {\n const text = cell.textContent?.replace(/\\u200B/g, '').trim() ?? ''\n if (text) return true\n return Boolean(cell.querySelector('img'))\n}\n\nfunction isCellEffectivelyEmpty(cell: HTMLElement): boolean {\n return !cellHasVisibleContent(cell)\n}\n\nfunction getTableRows(table: HTMLTableElement): HTMLTableRowElement[] {\n return Array.from(table.querySelectorAll('tr'))\n}\n\nfunction getRowCells(row: HTMLTableRowElement): HTMLTableCellElement[] {\n return Array.from(row.children).filter(\n (el): el is HTMLTableCellElement =>\n el.tagName === 'TD' || el.tagName === 'TH',\n )\n}\n\nfunction getCellCoord(\n cell: HTMLTableCellElement,\n): { table: HTMLTableElement; row: number; col: number } | null {\n const tr = cell.parentElement as HTMLTableRowElement | null\n const table = cell.closest('table') as HTMLTableElement | null\n if (!tr || !table) return null\n const rows = getTableRows(table)\n const row = rows.indexOf(tr)\n const col = getRowCells(tr).indexOf(cell)\n if (row < 0 || col < 0) return null\n return { table, row, col }\n}\n\nfunction appendCellContent(\n target: HTMLTableCellElement,\n source: HTMLTableCellElement,\n) {\n if (!cellHasVisibleContent(source)) return\n if (isCellEffectivelyEmpty(target)) {\n target.innerHTML = ''\n } else {\n target.appendChild(document.createElement('br'))\n }\n while (source.firstChild) {\n target.appendChild(source.firstChild)\n }\n}\n\n/** 사각 선택 영역의 셀들을 병합 (가로 colspan + 세로 rowspan) */\nexport function mergeCells(cells: HTMLTableCellElement[]): boolean {\n if (cells.length < 2) return false\n\n const coords = cells.map((cell) => getCellCoord(cell))\n if (coords.some((c) => !c)) return false\n const table = coords[0]!.table\n if (!coords.every((c) => c!.table === table)) return false\n\n const rows = coords.map((c) => c!.row)\n const cols = coords.map((c) => c!.col)\n const r0 = Math.min(...rows)\n const r1 = Math.max(...rows)\n const c0 = Math.min(...cols)\n const c1 = Math.max(...cols)\n\n if (\n cells.some(\n (cell) => (cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1,\n )\n ) {\n return false\n }\n\n const tableRows = getTableRows(table)\n const expected: HTMLTableCellElement[] = []\n for (let r = r0; r <= r1; r++) {\n const rowCells = getRowCells(tableRows[r])\n for (let c = c0; c <= c1; c++) {\n const cell = rowCells[c]\n if (!cell) return false\n expected.push(cell)\n }\n }\n\n const selectedSet = new Set(cells)\n if (\n expected.length !== cells.length ||\n !expected.every((cell) => selectedSet.has(cell))\n ) {\n return false\n }\n\n const first = getRowCells(tableRows[r0])[c0]\n if (!first) return false\n\n first.colSpan = c1 - c0 + 1\n first.rowSpan = r1 - r0 + 1\n\n for (const cell of expected) {\n if (cell === first) continue\n appendCellContent(first, cell)\n cell.remove()\n }\n\n setCaretInside(first)\n return true\n}\n\n/** @deprecated use mergeCells */\nexport function mergeColumnCells(cells: HTMLTableCellElement[]): boolean {\n return mergeCells(cells)\n}\n\n/** 현재 셀을 오른쪽 옆 셀과 열 병합 (colspan) */\nexport function mergeColumns(editor: HTMLElement): boolean {\n const cell = getCurrentTableCell(editor)\n if (!cell) return false\n\n const next = cell.nextElementSibling as HTMLTableCellElement | null\n if (!next || (next.tagName !== 'TD' && next.tagName !== 'TH')) return false\n if (next.tagName !== cell.tagName) return false\n\n return mergeCells([cell, next])\n}\n\nexport function handleTableTab(\n editor: HTMLElement,\n event: KeyboardEvent,\n): boolean {\n if (event.key !== 'Tab') return false\n const range = getRange(editor)\n if (!range) return false\n\n const cell = closestTag(range.startContainer, ['TD', 'TH'], editor)\n if (!cell) return false\n const table = closestTag(cell, 'TABLE', editor) as HTMLTableElement | null\n if (!table) return false\n\n event.preventDefault()\n const cells = getTableCells(table)\n const index = cells.indexOf(cell as HTMLTableCellElement)\n if (index < 0) return true\n\n const nextIndex = event.shiftKey ? index - 1 : index + 1\n if (nextIndex < 0 || nextIndex >= cells.length) {\n if (!event.shiftKey) {\n // add a new row at the end\n const lastRow = table.querySelector('tr:last-child')\n const colCount = lastRow?.children.length ?? 1\n const tr = document.createElement('tr')\n for (let i = 0; i < colCount; i++) tr.appendChild(createTableCell('td'))\n const body = table.tBodies[0] ?? table\n body.appendChild(tr)\n placeCaretInEmptyBlock(tr.cells[0])\n }\n return true\n }\n\n placeCaretInEmptyBlock(cells[nextIndex])\n return true\n}\n\nexport function handleEnterKey(editor: HTMLElement, event: KeyboardEvent): boolean {\n const range = getRange(editor)\n if (!range || !range.collapsed) return false\n\n const cell = closestTag(range.startContainer, ['TD', 'TH'], editor)\n if (cell) {\n event.preventDefault()\n const br = document.createElement('br')\n range.insertNode(br)\n setCaretAfter(br)\n return true\n }\n\n const pre = closestTag(range.startContainer, 'PRE', editor)\n if (pre) {\n event.preventDefault()\n const textNode = document.createTextNode('\\n')\n range.insertNode(textNode)\n setCaretAfter(textNode)\n return true\n }\n\n const li = closestTag(range.startContainer, 'LI', editor)\n if (li) {\n const text = li.textContent?.replace(/\\u200B/g, '').trim() ?? ''\n if (text === '') {\n event.preventDefault()\n const p = unwrapListItem(li, editor)\n placeCaretInEmptyBlock(p)\n return true\n }\n\n event.preventDefault()\n const inChecklist = isChecklistList(li.parentElement)\n const contentRoot = getListItemContentRoot(li)\n\n const after = range.cloneRange()\n after.selectNodeContents(contentRoot)\n after.setStart(range.startContainer, range.startOffset)\n const trailing = after.extractContents()\n\n let newLi: HTMLLIElement\n if (inChecklist) {\n const holder = document.createElement('div')\n if (trailing.textContent || trailing.querySelector?.('img, br')) {\n holder.appendChild(trailing)\n }\n newLi = lineToChecklistItem(holder, false)\n } else {\n newLi = document.createElement('li')\n if (trailing.textContent || trailing.querySelector?.('img, br')) {\n newLi.appendChild(trailing)\n } else {\n newLi.appendChild(document.createElement('br'))\n }\n }\n\n li.after(newLi)\n const caretTarget =\n (newLi.querySelector('.checklist-content') as HTMLElement | null) ?? newLi\n setCaretInside(caretTarget, true)\n return true\n }\n\n const quote = closestTag(range.startContainer, 'BLOCKQUOTE', editor)\n if (quote) {\n const text = quote.textContent?.replace(/\\u200B/g, '').trim() ?? ''\n if (text === '') {\n event.preventDefault()\n const p = createParagraph()\n quote.replaceWith(p)\n placeCaretInEmptyBlock(p)\n return true\n }\n }\n\n return false\n}\n\nexport function focusEditor(editor: HTMLElement) {\n editor.focus()\n const selection = window.getSelection()\n if (!selection) return\n if (\n selection.rangeCount > 0 &&\n selection.anchorNode &&\n editor.contains(selection.anchorNode)\n ) {\n return\n }\n if (restoreSavedSelection(editor)) return\n\n const last = editor.lastElementChild as HTMLElement | null\n if (last) setCaretInside(last)\n else {\n ensureDefaultContent(editor)\n setCaretInside(editor.firstElementChild as HTMLElement)\n }\n}\n","export type HistorySnapshot = {\n html: string\n selection?: {\n startPath: number[]\n startOffset: number\n endPath: number[]\n endOffset: number\n }\n}\n\nfunction nodePath(root: HTMLElement, node: Node): number[] {\n const path: number[] = []\n let current: Node | null = node\n while (current && current !== root) {\n const parent: Node | null = current.parentNode\n if (!parent) break\n path.unshift(Array.prototype.indexOf.call(parent.childNodes, current))\n current = parent\n }\n return path\n}\n\nfunction nodeFromPath(root: HTMLElement, path: number[]): Node | null {\n let current: Node = root\n for (const index of path) {\n if (!current.childNodes[index]) return null\n current = current.childNodes[index]\n }\n return current\n}\n\nexport function captureSelection(root: HTMLElement): HistorySnapshot['selection'] {\n const selection = window.getSelection()\n if (!selection || selection.rangeCount === 0) return undefined\n if (!root.contains(selection.anchorNode) || !selection.focusNode) return undefined\n try {\n const range = selection.getRangeAt(0)\n return {\n startPath: nodePath(root, range.startContainer),\n startOffset: range.startOffset,\n endPath: nodePath(root, range.endContainer),\n endOffset: range.endOffset,\n }\n } catch {\n return undefined\n }\n}\n\nfunction maxOffset(node: Node): number {\n return node.nodeType === Node.TEXT_NODE\n ? (node as Text).data.length\n : node.childNodes.length\n}\n\nexport function restoreSelection(\n root: HTMLElement,\n snapshot?: HistorySnapshot['selection'],\n) {\n if (!snapshot) return\n const startNode = nodeFromPath(root, snapshot.startPath)\n const endNode = nodeFromPath(root, snapshot.endPath)\n if (!startNode || !endNode) return\n try {\n const range = document.createRange()\n range.setStart(startNode, Math.min(snapshot.startOffset, maxOffset(startNode)))\n range.setEnd(endNode, Math.min(snapshot.endOffset, maxOffset(endNode)))\n const selection = window.getSelection()\n selection?.removeAllRanges()\n selection?.addRange(range)\n } catch {\n // ignore invalid restored ranges\n }\n}\n\nexport class EditorHistory {\n private undoStack: HistorySnapshot[] = []\n private redoStack: HistorySnapshot[] = []\n private lastPush = 0\n private debounceMs: number\n private maxSize: number\n\n constructor(debounceMs = 300, maxSize = 100) {\n this.debounceMs = debounceMs\n this.maxSize = maxSize\n }\n\n reset(html: string) {\n this.undoStack = [{ html }]\n this.redoStack = []\n this.lastPush = Date.now()\n }\n\n push(editor: HTMLElement, force = false) {\n const html = editor.innerHTML\n const now = Date.now()\n const top = this.undoStack[this.undoStack.length - 1]\n if (top && top.html === html) return\n\n if (!force && top && now - this.lastPush < this.debounceMs) {\n this.undoStack[this.undoStack.length - 1] = {\n html,\n selection: captureSelection(editor),\n }\n return\n }\n\n this.undoStack.push({\n html,\n selection: captureSelection(editor),\n })\n if (this.undoStack.length > this.maxSize) {\n this.undoStack.shift()\n }\n this.redoStack = []\n this.lastPush = now\n }\n\n undo(editor: HTMLElement): boolean {\n if (this.undoStack.length <= 1) return false\n const current = this.undoStack.pop()!\n this.redoStack.push(current)\n const prev = this.undoStack[this.undoStack.length - 1]\n editor.innerHTML = prev.html\n restoreSelection(editor, prev.selection)\n return true\n }\n\n redo(editor: HTMLElement): boolean {\n const next = this.redoStack.pop()\n if (!next) return false\n this.undoStack.push(next)\n editor.innerHTML = next.html\n restoreSelection(editor, next.selection)\n return true\n }\n\n canUndo() {\n return this.undoStack.length > 1\n }\n\n canRedo() {\n return this.redoStack.length > 0\n }\n}\n","import { setBlockFormat } from './commands'\nimport { getTextBeforeCaretInBlock } from './selection'\n\ntype MarkdownHandler = (editor: HTMLElement) => boolean\n\nfunction deletePrefix(block: HTMLElement, prefixLength: number) {\n const walker = document.createTreeWalker(block, NodeFilter.SHOW_TEXT)\n let remaining = prefixLength\n let node = walker.nextNode() as Text | null\n while (node && remaining > 0) {\n const value = node.data\n if (value.length <= remaining) {\n remaining -= value.length\n const next = walker.nextNode() as Text | null\n node.remove()\n node = next\n } else {\n node.data = value.slice(remaining)\n remaining = 0\n }\n }\n if (!block.textContent) {\n block.appendChild(document.createElement('br'))\n }\n}\n\nfunction applyBlockShortcut(\n editor: HTMLElement,\n pattern: RegExp,\n format: 'h1' | 'h2' | 'h3' | 'blockquote' | 'ul' | 'ol' | 'pre',\n): boolean {\n const info = getTextBeforeCaretInBlock(editor)\n if (!info) return false\n const match = pattern.exec(info.text)\n if (!match) return false\n\n deletePrefix(info.block, match[0].length)\n setBlockFormat(editor, format)\n return true\n}\n\nfunction selectOffsetsInBlock(\n block: HTMLElement,\n startOffset: number,\n endOffset: number,\n): Range | null {\n const range = document.createRange()\n const walker = document.createTreeWalker(block, NodeFilter.SHOW_TEXT)\n let pos = 0\n let startNode: Text | null = null\n let startNodeOffset = 0\n let endNode: Text | null = null\n let endNodeOffset = 0\n let node = walker.nextNode() as Text | null\n\n while (node) {\n const nextPos = pos + node.data.length\n if (!startNode && startOffset >= pos && startOffset <= nextPos) {\n startNode = node\n startNodeOffset = startOffset - pos\n }\n if (endOffset >= pos && endOffset <= nextPos) {\n endNode = node\n endNodeOffset = endOffset - pos\n break\n }\n pos = nextPos\n node = walker.nextNode() as Text | null\n }\n\n if (!startNode || !endNode) return null\n range.setStart(startNode, startNodeOffset)\n range.setEnd(endNode, endNodeOffset)\n return range\n}\n\nfunction applyInlineShortcut(\n editor: HTMLElement,\n pattern: RegExp,\n mark: 'bold' | 'italic',\n): boolean {\n const info = getTextBeforeCaretInBlock(editor)\n if (!info) return false\n const match = pattern.exec(info.text)\n if (!match) return false\n\n const full = match[0]\n const inner = match[1]\n const startOffset = info.text.length - full.length\n const range = selectOffsetsInBlock(info.block, startOffset, info.text.length)\n if (!range) return false\n\n range.deleteContents()\n\n const tag = mark === 'bold' ? 'strong' : 'em'\n const el = document.createElement(tag)\n el.textContent = inner\n range.insertNode(el)\n\n const space = document.createTextNode(' ')\n el.after(space)\n const selection = window.getSelection()\n if (selection) {\n const after = document.createRange()\n after.setStart(space, 1)\n after.collapse(true)\n selection.removeAllRanges()\n selection.addRange(after)\n }\n return true\n}\n\nconst handlers: MarkdownHandler[] = [\n (editor) => applyBlockShortcut(editor, /^### $/, 'h3'),\n (editor) => applyBlockShortcut(editor, /^## $/, 'h2'),\n (editor) => applyBlockShortcut(editor, /^# $/, 'h1'),\n (editor) => applyBlockShortcut(editor, /^> $/, 'blockquote'),\n (editor) => applyBlockShortcut(editor, /^(-|\\*) $/, 'ul'),\n (editor) => applyBlockShortcut(editor, /^\\d+\\. $/, 'ol'),\n (editor) => applyBlockShortcut(editor, /^``` $/, 'pre'),\n (editor) => applyInlineShortcut(editor, /\\*\\*(.+?)\\*\\* $/, 'bold'),\n (editor) => applyInlineShortcut(editor, /(?<!\\*)\\*([^*\\n]+)\\* $/, 'italic'),\n]\n\nexport function tryMarkdownShortcut(editor: HTMLElement): boolean {\n for (const handler of handlers) {\n if (handler(editor)) return true\n }\n return false\n}\n","/** 에디터 문서 JSON 스키마 */\n\nexport type TextAlign = 'left' | 'center' | 'right'\n\nexport type Mark =\n | { type: 'bold' }\n | { type: 'italic' }\n | { type: 'underline' }\n | { type: 'code' }\n | { type: 'textStyle'; attrs?: { color?: string } }\n | { type: 'highlight'; attrs?: { color?: string } }\n\nexport type TextNode = {\n type: 'text'\n text: string\n marks?: Mark[]\n}\n\nexport type HardBreakNode = { type: 'hardBreak' }\n\nexport type InlineNode = TextNode | HardBreakNode\n\nexport type ParagraphNode = {\n type: 'paragraph'\n attrs?: { textAlign?: TextAlign }\n content?: InlineNode[]\n}\n\nexport type HeadingNode = {\n type: 'heading'\n attrs: { level: 1 | 2 | 3; textAlign?: TextAlign }\n content?: InlineNode[]\n}\n\nexport type BlockquoteNode = {\n type: 'blockquote'\n attrs?: { textAlign?: TextAlign }\n content?: InlineNode[]\n}\n\nexport type CodeBlockNode = {\n type: 'codeBlock'\n content?: TextNode[]\n}\n\nexport type ListItemNode = {\n type: 'listItem'\n content?: Array<ParagraphNode | BulletListNode | OrderedListNode>\n}\n\nexport type BulletListNode = {\n type: 'bulletList'\n content?: ListItemNode[]\n}\n\nexport type OrderedListNode = {\n type: 'orderedList'\n content?: ListItemNode[]\n}\n\nexport type TaskItemNode = {\n type: 'taskItem'\n attrs?: { checked?: boolean }\n content?: InlineNode[]\n}\n\nexport type TaskListNode = {\n type: 'taskList'\n content?: TaskItemNode[]\n}\n\nexport type TableCellNode = {\n type: 'tableCell'\n attrs?: {\n colspan?: number\n rowspan?: number\n color?: string\n backgroundColor?: string\n }\n content?: InlineNode[]\n}\n\nexport type TableRowNode = {\n type: 'tableRow'\n content?: TableCellNode[]\n}\n\nexport type TableNode = {\n type: 'table'\n content?: TableRowNode[]\n}\n\nexport type BlockNode =\n | ParagraphNode\n | HeadingNode\n | BlockquoteNode\n | CodeBlockNode\n | BulletListNode\n | OrderedListNode\n | TaskListNode\n | TableNode\n\nexport type EditorDoc = {\n type: 'doc'\n content: BlockNode[]\n}\n\nfunction isElement(node: Node): node is HTMLElement {\n return node.nodeType === Node.ELEMENT_NODE\n}\n\nfunction parseAlign(style: string | null | undefined): TextAlign | undefined {\n const align = (style || '').match(/text-align:\\s*(left|center|right)/i)?.[1]\n if (align === 'left' || align === 'center' || align === 'right') return align\n return undefined\n}\n\nfunction inlineFromNode(node: Node, inherited: Mark[] = []): InlineNode[] {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent?.replace(/\\u200B/g, '') ?? ''\n if (!text) return []\n return [\n inherited.length\n ? { type: 'text', text, marks: inherited }\n : { type: 'text', text },\n ]\n }\n\n if (!isElement(node)) return []\n const el = node\n\n if (el.tagName === 'BR') return [{ type: 'hardBreak' }]\n\n const localMarks: Mark[] = []\n const tag = el.tagName\n if (tag === 'STRONG' || tag === 'B') localMarks.push({ type: 'bold' })\n else if (tag === 'EM' || tag === 'I') localMarks.push({ type: 'italic' })\n else if (tag === 'U') localMarks.push({ type: 'underline' })\n else if (tag === 'CODE') localMarks.push({ type: 'code' })\n else if (tag === 'SPAN') {\n if (el.style.color) {\n localMarks.push({ type: 'textStyle', attrs: { color: el.style.color } })\n }\n if (el.style.backgroundColor) {\n localMarks.push({\n type: 'highlight',\n attrs: { color: el.style.backgroundColor },\n })\n }\n }\n\n const nextMarks = [...inherited, ...localMarks]\n const out: InlineNode[] = []\n el.childNodes.forEach((child) => {\n out.push(...inlineFromNode(child, nextMarks))\n })\n return out\n}\n\nfunction inlineFromChildren(el: HTMLElement): InlineNode[] {\n const out: InlineNode[] = []\n el.childNodes.forEach((child) => {\n out.push(...inlineFromNode(child))\n })\n return out\n}\n\nfunction listItemFromElement(li: HTMLElement): ListItemNode {\n const content: ListItemNode['content'] = []\n let inlineBuf: InlineNode[] = []\n\n const flushInline = () => {\n if (inlineBuf.length) {\n content.push({ type: 'paragraph', content: inlineBuf })\n inlineBuf = []\n }\n }\n\n li.childNodes.forEach((child) => {\n if (isElement(child) && (child.tagName === 'UL' || child.tagName === 'OL')) {\n flushInline()\n const list = blockFromElement(child)\n if (list) content.push(list as BulletListNode | OrderedListNode)\n return\n }\n if (isElement(child) && child.tagName === 'P') {\n flushInline()\n content.push({\n type: 'paragraph',\n attrs: alignAttrs(child),\n content: inlineFromChildren(child),\n })\n return\n }\n inlineBuf.push(...inlineFromNode(child))\n })\n flushInline()\n\n return { type: 'listItem', content: content.length ? content : [{ type: 'paragraph' }] }\n}\n\nfunction alignAttrs(el: HTMLElement): { textAlign?: TextAlign } | undefined {\n const textAlign = parseAlign(el.getAttribute('style') || el.style.textAlign)\n return textAlign ? { textAlign } : undefined\n}\n\nfunction cellAttrs(el: HTMLTableCellElement): TableCellNode['attrs'] {\n const attrs: NonNullable<TableCellNode['attrs']> = {}\n const colspan = el.colSpan || 1\n const rowspan = el.rowSpan || 1\n if (colspan > 1) attrs.colspan = colspan\n if (rowspan > 1) attrs.rowspan = rowspan\n if (el.style.color) attrs.color = el.style.color\n if (el.style.backgroundColor) attrs.backgroundColor = el.style.backgroundColor\n return Object.keys(attrs).length ? attrs : undefined\n}\n\nfunction blockFromElement(el: HTMLElement): BlockNode | null {\n const tag = el.tagName\n\n if (tag === 'P') {\n return {\n type: 'paragraph',\n attrs: alignAttrs(el),\n content: inlineFromChildren(el),\n }\n }\n\n if (tag === 'H1' || tag === 'H2' || tag === 'H3') {\n const level = Number(tag[1]) as 1 | 2 | 3\n return {\n type: 'heading',\n attrs: { level, ...alignAttrs(el) },\n content: inlineFromChildren(el),\n }\n }\n\n if (tag === 'BLOCKQUOTE') {\n return {\n type: 'blockquote',\n attrs: alignAttrs(el),\n content: inlineFromChildren(el),\n }\n }\n\n if (tag === 'PRE') {\n const text = el.textContent ?? ''\n return {\n type: 'codeBlock',\n content: text ? [{ type: 'text', text }] : [],\n }\n }\n\n if (tag === 'UL') {\n const isChecklist =\n el.classList.contains('editor-checklist') ||\n el.getAttribute('data-list') === 'checklist'\n\n if (isChecklist) {\n return {\n type: 'taskList',\n content: Array.from(el.children)\n .filter((c): c is HTMLElement => c.tagName === 'LI')\n .map((li) => {\n const contentEl =\n (li.querySelector(\n ':scope > .checklist-content',\n ) as HTMLElement | null) ?? li\n return {\n type: 'taskItem' as const,\n attrs: { checked: li.dataset.checked === 'true' },\n content: inlineFromChildren(contentEl),\n }\n }),\n }\n }\n\n return {\n type: 'bulletList',\n content: Array.from(el.children)\n .filter((c): c is HTMLElement => c.tagName === 'LI')\n .map(listItemFromElement),\n }\n }\n\n if (tag === 'OL') {\n return {\n type: 'orderedList',\n content: Array.from(el.children)\n .filter((c): c is HTMLElement => c.tagName === 'LI')\n .map(listItemFromElement),\n }\n }\n\n if (tag === 'TABLE') {\n const rows = Array.from(el.querySelectorAll(':scope > tbody > tr, :scope > tr'))\n return {\n type: 'table',\n content: rows.map((tr) => ({\n type: 'tableRow' as const,\n content: Array.from(tr.children)\n .filter(\n (c): c is HTMLTableCellElement =>\n c.tagName === 'TD' || c.tagName === 'TH',\n )\n .map((cell) => ({\n type: 'tableCell' as const,\n attrs: cellAttrs(cell),\n content: inlineFromChildren(cell),\n })),\n })),\n }\n }\n\n if (tag === 'DIV') {\n return {\n type: 'paragraph',\n content: inlineFromChildren(el),\n }\n }\n\n return null\n}\n\n/** contentEditable 루트 → JSON */\nexport function domToJson(root: HTMLElement): EditorDoc {\n const content: BlockNode[] = []\n root.childNodes.forEach((child) => {\n if (!isElement(child)) {\n const text = child.textContent?.replace(/\\u200B/g, '').trim()\n if (text) content.push({ type: 'paragraph', content: [{ type: 'text', text }] })\n return\n }\n const block = blockFromElement(child)\n if (block) content.push(block)\n })\n return { type: 'doc', content: content.length ? content : [{ type: 'paragraph' }] }\n}\n\nexport function htmlToJson(html: string): EditorDoc {\n const template = document.createElement('template')\n template.innerHTML = html\n const root = document.createElement('div')\n root.append(...Array.from(template.content.childNodes))\n return domToJson(root)\n}\n\nfunction escapeText(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n}\n\nfunction renderMarks(text: string, marks?: Mark[]): string {\n let html = escapeText(text)\n if (!marks?.length) return html\n\n for (const mark of marks) {\n switch (mark.type) {\n case 'bold':\n html = `<strong>${html}</strong>`\n break\n case 'italic':\n html = `<em>${html}</em>`\n break\n case 'underline':\n html = `<u>${html}</u>`\n break\n case 'code':\n html = `<code>${html}</code>`\n break\n case 'textStyle':\n if (mark.attrs?.color) {\n html = `<span style=\"color: ${mark.attrs.color}\">${html}</span>`\n }\n break\n case 'highlight':\n if (mark.attrs?.color) {\n html = `<span style=\"background-color: ${mark.attrs.color}\">${html}</span>`\n }\n break\n }\n }\n return html\n}\n\nfunction renderInline(nodes?: InlineNode[]): string {\n if (!nodes?.length) return '<br>'\n const html = nodes\n .map((node) => {\n if (node.type === 'hardBreak') return '<br>'\n return renderMarks(node.text, node.marks)\n })\n .join('')\n return html || '<br>'\n}\n\nfunction styleAttr(textAlign?: TextAlign, extra?: string): string {\n const parts: string[] = []\n if (textAlign && textAlign !== 'left') parts.push(`text-align: ${textAlign}`)\n if (extra) parts.push(extra)\n return parts.length ? ` style=\"${parts.join('; ')}\"` : ''\n}\n\nfunction renderBlock(node: BlockNode): string {\n switch (node.type) {\n case 'paragraph':\n return `<p${styleAttr(node.attrs?.textAlign)}>${renderInline(node.content)}</p>`\n case 'heading': {\n const tag = `h${node.attrs.level}`\n return `<${tag}${styleAttr(node.attrs.textAlign)}>${renderInline(node.content)}</${tag}>`\n }\n case 'blockquote':\n return `<blockquote${styleAttr(node.attrs?.textAlign)}>${renderInline(node.content)}</blockquote>`\n case 'codeBlock': {\n const text = node.content?.map((n) => n.text).join('') ?? ''\n return `<pre><code>${escapeText(text)}</code></pre>`\n }\n case 'bulletList':\n return `<ul>${(node.content ?? []).map(renderListItem).join('')}</ul>`\n case 'orderedList':\n return `<ol>${(node.content ?? []).map(renderListItem).join('')}</ol>`\n case 'taskList':\n return `<ul class=\"editor-checklist\" data-list=\"checklist\">${(\n node.content ?? []\n )\n .map((item) => {\n const checked = item.attrs?.checked ? 'true' : 'false'\n return `<li data-checked=\"${checked}\"><span class=\"checklist-box\" contenteditable=\"false\" role=\"checkbox\" aria-checked=\"${checked}\" tabindex=\"-1\"></span><span class=\"checklist-content\">${renderInline(item.content)}</span></li>`\n })\n .join('')}</ul>`\n case 'table':\n return `<table class=\"editor-table\"><tbody>${(node.content ?? [])\n .map(\n (row) =>\n `<tr>${(row.content ?? [])\n .map((cell) => {\n const attrs: string[] = []\n if (cell.attrs?.colspan && cell.attrs.colspan > 1) {\n attrs.push(`colspan=\"${cell.attrs.colspan}\"`)\n }\n if (cell.attrs?.rowspan && cell.attrs.rowspan > 1) {\n attrs.push(`rowspan=\"${cell.attrs.rowspan}\"`)\n }\n const styles: string[] = []\n if (cell.attrs?.color) styles.push(`color: ${cell.attrs.color}`)\n if (cell.attrs?.backgroundColor) {\n styles.push(`background-color: ${cell.attrs.backgroundColor}`)\n }\n const style = styles.length ? ` style=\"${styles.join('; ')}\"` : ''\n return `<td${attrs.length ? ` ${attrs.join(' ')}` : ''}${style}>${renderInline(cell.content)}</td>`\n })\n .join('')}</tr>`,\n )\n .join('')}</tbody></table>`\n default:\n return ''\n }\n}\n\nfunction renderListItem(item: ListItemNode): string {\n const inner = (item.content ?? [])\n .map((child) => {\n if (child.type === 'paragraph') {\n return renderInline(child.content)\n }\n return renderBlock(child)\n })\n .join('')\n return `<li>${inner || '<br>'}</li>`\n}\n\n/** JSON → HTML 문자열 */\nexport function jsonToHtml(doc: EditorDoc): string {\n if (!doc || doc.type !== 'doc' || !Array.isArray(doc.content)) {\n return '<p><br></p>'\n }\n const html = doc.content.map(renderBlock).join('')\n return html || '<p><br></p>'\n}\n\n/** 느슨한 입력도 EditorDoc으로 정규화 */\nexport function normalizeDoc(input: unknown): EditorDoc {\n if (\n input &&\n typeof input === 'object' &&\n (input as EditorDoc).type === 'doc' &&\n Array.isArray((input as EditorDoc).content)\n ) {\n return input as EditorDoc\n }\n return { type: 'doc', content: [{ type: 'paragraph' }] }\n}\n","const ALLOWED_TAGS = new Set([\n 'P',\n 'BR',\n 'STRONG',\n 'B',\n 'EM',\n 'I',\n 'U',\n 'CODE',\n 'PRE',\n 'H1',\n 'H2',\n 'H3',\n 'UL',\n 'OL',\n 'LI',\n 'BLOCKQUOTE',\n 'SPAN',\n 'DIV',\n 'TABLE',\n 'THEAD',\n 'TBODY',\n 'TR',\n 'TH',\n 'TD',\n])\n\nconst ALLOWED_ATTRS: Record<string, Set<string>> = {\n SPAN: new Set([\n 'style',\n 'class',\n 'contenteditable',\n 'role',\n 'aria-checked',\n 'tabindex',\n ]),\n DIV: new Set([]),\n P: new Set(['style']),\n H1: new Set(['style']),\n H2: new Set(['style']),\n H3: new Set(['style']),\n BLOCKQUOTE: new Set(['style']),\n UL: new Set(['style', 'class', 'data-list']),\n OL: new Set(['style']),\n LI: new Set(['style', 'class', 'data-checked']),\n TABLE: new Set(['class']),\n TH: new Set(['style', 'colspan', 'rowspan']),\n TD: new Set(['style', 'colspan', 'rowspan']),\n}\n\nconst ALLOWED_CLASSES = new Set([\n 'editor-table',\n 'editor-checklist',\n 'checklist-box',\n 'checklist-content',\n])\n\nconst HEX_COLOR = /#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})\\b/i\nconst RGB_COLOR = /rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}(?:\\s*,\\s*(?:0|1|0?\\.\\d+))?\\s*\\)/i\n\nfunction sanitizeStyle(style: string, allowColor = false): string {\n const parts: string[] = []\n const align = /text-align\\s*:\\s*(left|center|right)/i.exec(style)\n if (align) parts.push(`text-align: ${align[1].toLowerCase()}`)\n\n if (allowColor) {\n const color = /(?:^|;)\\s*color\\s*:\\s*([^;]+)/i.exec(style)\n if (color) {\n const value = color[1].trim()\n if (HEX_COLOR.test(value) || RGB_COLOR.test(value)) {\n parts.push(`color: ${value}`)\n }\n }\n const bg = /(?:^|;)\\s*background-color\\s*:\\s*([^;]+)/i.exec(style)\n if (bg) {\n const value = bg[1].trim()\n if (\n HEX_COLOR.test(value) ||\n RGB_COLOR.test(value) ||\n value.toLowerCase() === 'transparent'\n ) {\n parts.push(`background-color: ${value}`)\n }\n }\n }\n\n return parts.join('; ')\n}\n\nfunction cleanNode(node: Node, out: ParentNode) {\n if (node.nodeType === Node.TEXT_NODE) {\n out.appendChild(document.createTextNode(node.textContent ?? ''))\n return\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) return\n const el = node as HTMLElement\n const tag = el.tagName\n\n // Strip links/images but keep their text/children\n if (tag === 'A' || tag === 'IMG' || tag === 'FIGURE') {\n el.childNodes.forEach((child) => cleanNode(child, out))\n return\n }\n\n if (!ALLOWED_TAGS.has(tag)) {\n el.childNodes.forEach((child) => cleanNode(child, out))\n return\n }\n\n if (tag === 'DIV') {\n const p = document.createElement('p')\n el.childNodes.forEach((child) => cleanNode(child, p))\n if (p.childNodes.length) out.appendChild(p)\n return\n }\n\n const next = document.createElement(tag.toLowerCase())\n const allowed = ALLOWED_ATTRS[tag] ?? new Set<string>()\n\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase()\n if (!allowed.has(name) && !allowed.has(attr.name)) continue\n\n if (name === 'style') {\n const cleaned = sanitizeStyle(\n attr.value,\n tag === 'SPAN' || tag === 'TD' || tag === 'TH',\n )\n if (cleaned) next.setAttribute('style', cleaned)\n continue\n }\n\n if (name === 'colspan' || name === 'rowspan') {\n const n = Number.parseInt(attr.value, 10)\n if (Number.isFinite(n) && n >= 1 && n <= 100) {\n next.setAttribute(name, String(n))\n }\n continue\n }\n\n if (name === 'class') {\n const kept = attr.value\n .split(/\\s+/)\n .filter((cls) => ALLOWED_CLASSES.has(cls))\n if (kept.length) next.setAttribute('class', kept.join(' '))\n continue\n }\n\n if (name === 'data-list') {\n if (attr.value === 'checklist') next.setAttribute(name, attr.value)\n continue\n }\n\n if (name === 'data-checked') {\n if (attr.value === 'true' || attr.value === 'false') {\n next.setAttribute(name, attr.value)\n }\n continue\n }\n\n if (name === 'contenteditable') {\n if (attr.value === 'false') next.setAttribute(name, 'false')\n continue\n }\n\n if (name === 'role') {\n if (attr.value === 'checkbox') next.setAttribute(name, attr.value)\n continue\n }\n\n if (name === 'aria-checked') {\n if (attr.value === 'true' || attr.value === 'false') {\n next.setAttribute(name, attr.value)\n }\n continue\n }\n\n if (name === 'tabindex') {\n if (attr.value === '-1') next.setAttribute(name, attr.value)\n continue\n }\n\n next.setAttribute(name, attr.value)\n }\n\n el.childNodes.forEach((child) => cleanNode(child, next))\n\n if (tag === 'PRE') {\n const code = next.querySelector('code')\n if (!code) {\n const codeEl = document.createElement('code')\n while (next.firstChild) codeEl.appendChild(next.firstChild)\n next.appendChild(codeEl)\n }\n }\n\n out.appendChild(next)\n}\n\nexport function sanitizeHTML(html: string): string {\n const template = document.createElement('template')\n template.innerHTML = html\n const container = document.createElement('div')\n template.content.childNodes.forEach((child) => cleanNode(child, container))\n\n if (!container.innerHTML.trim()) {\n return ''\n }\n\n const hasBlock = Array.from(container.childNodes).some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE &&\n ['P', 'H1', 'H2', 'H3', 'UL', 'OL', 'PRE', 'BLOCKQUOTE', 'TABLE'].includes(\n (n as HTMLElement).tagName,\n ),\n )\n\n if (!hasBlock) {\n const p = document.createElement('p')\n while (container.firstChild) p.appendChild(container.firstChild)\n container.appendChild(p)\n }\n\n return container.innerHTML\n}\n\nexport function sanitizePlainText(text: string): string {\n return text\n .split(/\\n{2,}/)\n .map((para) => {\n const lines = para\n .split('\\n')\n .map((line) =>\n line.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'),\n )\n .join('<br>')\n return `<p>${lines || '<br>'}</p>`\n })\n .join('')\n}\n","import { useEffect, useRef, useState, type RefObject } from 'react'\n\ntype TableSelectionProps = {\n editorRef: RefObject<HTMLDivElement | null>\n onMerge: (cells: HTMLTableCellElement[]) => void\n}\n\nfunction cellFromEventTarget(target: EventTarget | null) {\n if (!(target instanceof Element)) return null\n return target.closest('td, th') as HTMLTableCellElement | null\n}\n\nfunction getTableRows(table: HTMLTableElement) {\n return Array.from(table.rows)\n}\n\nfunction getRowCells(row: HTMLTableRowElement) {\n return Array.from(row.children).filter(\n (el): el is HTMLTableCellElement =>\n el.tagName === 'TD' || el.tagName === 'TH',\n )\n}\n\nfunction getCellCoord(cell: HTMLTableCellElement) {\n const tr = cell.parentElement as HTMLTableRowElement | null\n const table = cell.closest('table') as HTMLTableElement | null\n if (!tr || !table) return null\n const rows = getTableRows(table)\n const row = rows.indexOf(tr)\n const col = getRowCells(tr).indexOf(cell)\n if (row < 0 || col < 0) return null\n return { table, row, col }\n}\n\n/**\n * elementFromPoint는 contentEditable 세로 드래그 중 tbody/table만 가리키는 경우가 있어\n * 셀 getBoundingClientRect로 직접 히트 테스트한다.\n */\nfunction cellAtPoint(\n x: number,\n y: number,\n table: HTMLTableElement,\n): HTMLTableCellElement | null {\n const cells = table.querySelectorAll('td, th')\n let hit: HTMLTableCellElement | null = null\n let hitArea = Infinity\n\n for (const cell of cells) {\n const rect = cell.getBoundingClientRect()\n if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {\n const area = rect.width * rect.height\n // rowspan/colspan 겹침 시 더 작은 셀 우선\n if (area < hitArea) {\n hit = cell as HTMLTableCellElement\n hitArea = area\n }\n }\n }\n if (hit) return hit\n\n // 보더/간격 위: 행·열을 좌표로 추정\n const rows = getTableRows(table)\n if (rows.length === 0) return null\n\n let rowIndex = 0\n for (let i = 0; i < rows.length; i++) {\n const rect = rows[i].getBoundingClientRect()\n const mid = (rect.top + rect.bottom) / 2\n if (y >= rect.top) rowIndex = i\n if (y <= mid) {\n rowIndex = i\n break\n }\n if (y <= rect.bottom) {\n rowIndex = i\n break\n }\n }\n\n const rowCells = getRowCells(rows[rowIndex])\n if (rowCells.length === 0) return null\n\n let colIndex = 0\n for (let i = 0; i < rowCells.length; i++) {\n const rect = rowCells[i].getBoundingClientRect()\n if (x >= rect.left) colIndex = i\n if (x <= rect.right) {\n colIndex = i\n break\n }\n }\n return rowCells[colIndex] ?? null\n}\n\n/** 앵커~현재 셀을 포함하는 직사각형 영역의 셀들 */\nfunction cellsInRect(\n a: HTMLTableCellElement,\n b: HTMLTableCellElement,\n): HTMLTableCellElement[] {\n const ca = getCellCoord(a)\n const cb = getCellCoord(b)\n if (!ca || !cb || ca.table !== cb.table) return [a]\n\n const r0 = Math.min(ca.row, cb.row)\n const r1 = Math.max(ca.row, cb.row)\n const c0 = Math.min(ca.col, cb.col)\n const c1 = Math.max(ca.col, cb.col)\n const rows = getTableRows(ca.table)\n const result: HTMLTableCellElement[] = []\n\n for (let r = r0; r <= r1; r++) {\n const rowCells = getRowCells(rows[r])\n for (let c = c0; c <= c1; c++) {\n if (rowCells[c]) result.push(rowCells[c])\n }\n }\n return result.length ? result : [a]\n}\n\nfunction selectionBounds(cells: HTMLTableCellElement[]) {\n const rects = cells.map((cell) => cell.getBoundingClientRect())\n const top = Math.min(...rects.map((r) => r.top))\n const left = Math.min(...rects.map((r) => r.left))\n const right = Math.max(...rects.map((r) => r.right))\n return {\n top: top - 40,\n left: (left + right) / 2,\n }\n}\n\n/** 브라우저가 td/th 전체를 통째로 선택한 상태인지 */\nfunction isWholeCellSelection(selection: Selection, editor: HTMLElement) {\n if (selection.rangeCount === 0 || selection.isCollapsed) return false\n const range = selection.getRangeAt(0)\n const ancestor =\n range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE\n ? (range.commonAncestorContainer as Element)\n : range.commonAncestorContainer.parentElement\n const cell = ancestor?.closest?.('td, th') as HTMLTableCellElement | null\n if (!cell || !editor.contains(cell)) return false\n\n const cellRange = document.createRange()\n cellRange.selectNodeContents(cell)\n return (\n range.compareBoundaryPoints(Range.START_TO_START, cellRange) <= 0 &&\n range.compareBoundaryPoints(Range.END_TO_END, cellRange) >= 0\n )\n}\n\nexport function TableSelection({\n editorRef,\n onMerge,\n}: TableSelectionProps) {\n const [selected, setSelected] = useState<HTMLTableCellElement[]>([])\n const [floatPos, setFloatPos] = useState<{ top: number; left: number } | null>(\n null,\n )\n const selectedRef = useRef<HTMLTableCellElement[]>([])\n const anchorRef = useRef<HTMLTableCellElement | null>(null)\n const draggingRef = useRef(false)\n const cellModeRef = useRef(false)\n const lastPointerRef = useRef({ x: 0, y: 0 })\n\n const paintSelection = (cells: HTMLTableCellElement[]) => {\n selectedRef.current.forEach((cell) =>\n cell.classList.remove('is-cell-selected'),\n )\n\n // 셀 선택은 2개 이상일 때만 (단일/병합 셀 클릭 하이라이트 방지)\n if (cells.length < 2) {\n selectedRef.current = []\n setSelected([])\n setFloatPos(null)\n return\n }\n\n cells.forEach((cell) => cell.classList.add('is-cell-selected'))\n selectedRef.current = cells\n setSelected(cells)\n setFloatPos(selectionBounds(cells))\n }\n\n const clearSelection = () => {\n selectedRef.current.forEach((cell) =>\n cell.classList.remove('is-cell-selected'),\n )\n selectedRef.current = []\n setSelected([])\n setFloatPos(null)\n anchorRef.current = null\n draggingRef.current = false\n cellModeRef.current = false\n editorRef.current?.classList.remove('is-table-cell-selecting')\n }\n\n useEffect(() => {\n const editor = editorRef.current\n if (!editor) return\n\n const placeCaretAtPoint = (x: number, y: number) => {\n const selection = window.getSelection()\n if (!selection) return\n\n const doc = document as Document & {\n caretRangeFromPoint?: (x: number, y: number) => Range | null\n caretPositionFromPoint?: (\n x: number,\n y: number,\n ) => { offsetNode: Node; offset: number } | null\n }\n\n let range: Range | null = null\n if (typeof doc.caretRangeFromPoint === 'function') {\n range = doc.caretRangeFromPoint(x, y)\n } else if (typeof doc.caretPositionFromPoint === 'function') {\n const pos = doc.caretPositionFromPoint(x, y)\n if (pos) {\n range = document.createRange()\n range.setStart(pos.offsetNode, pos.offset)\n range.collapse(true)\n }\n }\n\n if (!range || !editor.contains(range.commonAncestorContainer)) return\n selection.removeAllRanges()\n selection.addRange(range)\n }\n\n const enterCellMode = (editorEl: HTMLElement) => {\n if (cellModeRef.current) return\n cellModeRef.current = true\n editorEl.classList.add('is-table-cell-selecting')\n window.getSelection()?.removeAllRanges()\n }\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return\n if ((event.target as Element | null)?.closest?.('.table-selection-float')) {\n return\n }\n\n const cell = cellFromEventTarget(event.target)\n if (!cell || !editor.contains(cell)) {\n clearSelection()\n return\n }\n\n // 셀 하이라이트만 비움 — 같은 셀 안 드래그는 글자 선택에 맡김\n selectedRef.current.forEach((c) => c.classList.remove('is-cell-selected'))\n selectedRef.current = []\n setSelected([])\n setFloatPos(null)\n editor.classList.remove('is-table-cell-selecting')\n\n anchorRef.current = cell\n draggingRef.current = true\n cellModeRef.current = false\n lastPointerRef.current = { x: event.clientX, y: event.clientY }\n }\n\n const onPointerMove = (event: PointerEvent) => {\n const anchor = anchorRef.current\n if (!draggingRef.current || !anchor) return\n\n lastPointerRef.current = { x: event.clientX, y: event.clientY }\n\n const table = anchor.closest('table') as HTMLTableElement | null\n if (!table || !editor.contains(table)) return\n\n const over = cellAtPoint(event.clientX, event.clientY, table)\n\n // 다른 셀로 넘어갈 때만 셀 선택 (같은 셀·병합 셀 클릭/드래그는 글자 선택)\n if (!cellModeRef.current) {\n if (over && over !== anchor) {\n enterCellMode(editor)\n } else {\n return\n }\n }\n\n event.preventDefault()\n if (!over) return\n paintSelection(cellsInRect(anchor, over))\n }\n\n const onPointerUp = () => {\n if (!draggingRef.current) return\n const multiCell =\n cellModeRef.current && selectedRef.current.length >= 2\n draggingRef.current = false\n editor.classList.remove('is-table-cell-selecting')\n\n if (!multiCell) {\n selectedRef.current.forEach((c) =>\n c.classList.remove('is-cell-selected'),\n )\n selectedRef.current = []\n setSelected([])\n setFloatPos(null)\n\n const sel = window.getSelection()\n const keepText =\n sel &&\n !sel.isCollapsed &&\n editor.contains(sel.anchorNode) &&\n !isWholeCellSelection(sel, editor)\n\n // 글자 드래그 선택은 유지, 병합 셀 통째 선택만 캐럿으로 교정\n if (!keepText) {\n placeCaretAtPoint(lastPointerRef.current.x, lastPointerRef.current.y)\n }\n }\n\n anchorRef.current = null\n cellModeRef.current = false\n }\n\n const onSelectStart = (event: Event) => {\n if (cellModeRef.current && draggingRef.current) {\n event.preventDefault()\n }\n }\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') clearSelection()\n }\n\n editor.addEventListener('pointerdown', onPointerDown)\n document.addEventListener('pointermove', onPointerMove, { passive: false })\n document.addEventListener('pointerup', onPointerUp)\n document.addEventListener('selectstart', onSelectStart)\n document.addEventListener('keydown', onKeyDown)\n\n return () => {\n editor.removeEventListener('pointerdown', onPointerDown)\n document.removeEventListener('pointermove', onPointerMove)\n document.removeEventListener('pointerup', onPointerUp)\n document.removeEventListener('selectstart', onSelectStart)\n document.removeEventListener('keydown', onKeyDown)\n editor.classList.remove('is-table-cell-selecting')\n selectedRef.current.forEach((cell) =>\n cell.classList.remove('is-cell-selected'),\n )\n }\n }, [editorRef])\n\n useEffect(() => {\n if (selected.length < 2) return\n const update = () => setFloatPos(selectionBounds(selected))\n window.addEventListener('scroll', update, true)\n window.addEventListener('resize', update)\n return () => {\n window.removeEventListener('scroll', update, true)\n window.removeEventListener('resize', update)\n }\n }, [selected])\n\n if (!floatPos || selected.length < 2) return null\n\n return (\n <button\n type=\"button\"\n className=\"table-selection-float\"\n style={{ top: floatPos.top, left: floatPos.left }}\n title=\"셀 병합\"\n aria-label=\"셀 병합\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onMerge(selected)\n clearSelection()\n }}\n >\n <IconMergeCells />\n </button>\n )\n}\n\nfunction IconMergeCells() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M4 6h16\" />\n <path d=\"M4 18h16\" />\n <path d=\"M4 12h4\" />\n <path d=\"M16 12h4\" />\n <path d=\"M10 9l-3 3l3 3\" />\n <path d=\"M14 9l3 3l-3 3\" />\n </svg>\n )\n}\n","import {\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react'\nimport type { ActiveState, AlignValue } from './selection'\n\ntype HeadingFormat = 'p' | 'h1' | 'h2' | 'h3'\n\nfunction IconTextSize() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M3 7v-2h13v2\" />\n <path d=\"M10 5v14\" />\n <path d=\"M12 19h-4\" />\n <path d=\"M15 13v-1h6v1\" />\n <path d=\"M18 12v7\" />\n <path d=\"M17 19h2\" />\n </svg>\n )\n}\n\nfunction IconH1() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M19 18v-8l-2 2\" />\n <path d=\"M4 6v12\" />\n <path d=\"M12 6v12\" />\n <path d=\"M11 18h2\" />\n <path d=\"M3 18h2\" />\n <path d=\"M4 12h8\" />\n <path d=\"M3 6h2\" />\n <path d=\"M11 6h2\" />\n </svg>\n )\n}\n\nfunction IconH2() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M17 12a2 2 0 1 1 4 0c0 .591 -.417 1.318 -.816 1.858l-3.184 4.143l4 0\" />\n <path d=\"M4 6v12\" />\n <path d=\"M12 6v12\" />\n <path d=\"M11 18h2\" />\n <path d=\"M3 18h2\" />\n <path d=\"M4 12h8\" />\n <path d=\"M3 6h2\" />\n <path d=\"M11 6h2\" />\n </svg>\n )\n}\n\nfunction IconH3() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M19 14a2 2 0 1 0 -2 -2\" />\n <path d=\"M17 16a2 2 0 1 0 2 -2\" />\n <path d=\"M4 6v12\" />\n <path d=\"M12 6v12\" />\n <path d=\"M11 18h2\" />\n <path d=\"M3 18h2\" />\n <path d=\"M4 12h8\" />\n <path d=\"M3 6h2\" />\n <path d=\"M11 6h2\" />\n </svg>\n )\n}\n\nconst HEADING_OPTIONS: {\n value: HeadingFormat\n label: string\n icon: ReactNode\n previewClass: string\n}[] = [\n {\n value: 'p',\n label: '본문',\n icon: <IconTextSize />,\n previewClass: 'heading-option-preview--p',\n },\n {\n value: 'h1',\n label: 'Heading 1',\n icon: <IconH1 />,\n previewClass: 'heading-option-preview--h1',\n },\n {\n value: 'h2',\n label: 'Heading 2',\n icon: <IconH2 />,\n previewClass: 'heading-option-preview--h2',\n },\n {\n value: 'h3',\n label: 'Heading 3',\n icon: <IconH3 />,\n previewClass: 'heading-option-preview--h3',\n },\n]\n\nfunction currentHeading(block: string): HeadingFormat {\n if (block === 'h1' || block === 'h2' || block === 'h3' || block === 'p') {\n return block\n }\n return 'p'\n}\n\nexport type ToolbarCommand =\n | { type: 'inline'; mark: 'bold' | 'italic' | 'underline' }\n | {\n type: 'block'\n format: 'p' | 'h1' | 'h2' | 'h3' | 'blockquote' | 'pre' | 'ul' | 'ol' | 'checklist'\n }\n | { type: 'align'; align: AlignValue }\n | { type: 'textColor'; color: string | null }\n | { type: 'highlightColor'; color: string | null }\n | { type: 'insertTable'; rows?: number; cols?: number }\n | { type: 'undo' }\n | { type: 'redo' }\n\nconst TEXT_COLORS = [\n '#000000',\n '#6b7280',\n '#9f1239',\n '#c2410c',\n '#a16207',\n '#15803d',\n '#1d4ed8',\n '#7c3aed',\n '#db2777',\n '#dc2626',\n] as const\n\nconst HIGHLIGHT_COLORS = [\n null,\n '#f3f4f6',\n '#fce7f3',\n '#ffedd5',\n '#fef9c3',\n '#dcfce7',\n '#dbeafe',\n '#ede9fe',\n '#fbcfe8',\n '#fee2e2',\n] as const\n\nfunction colorsMatch(a: string | null | undefined, b: string | null | undefined) {\n if (!a && !b) return true\n if (!a || !b) return false\n const normalize = (value: string) => {\n const el = document.createElement('span')\n el.style.color = value\n return el.style.color\n }\n try {\n return normalize(a) === normalize(b)\n } catch {\n return a.toLowerCase() === b.toLowerCase()\n }\n}\n\ntype ToolbarProps = {\n active: ActiveState\n onCommand: (command: ToolbarCommand) => void\n}\n\nfunction IconBulletList() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M21 6a1 1 0 0 1 -1 1h-10a1 1 0 1 1 0 -2h10a1 1 0 0 1 1 1\" />\n <path d=\"M21 12a1 1 0 0 1 -1 1h-10a1 1 0 0 1 0 -2h10a1 1 0 0 1 1 1\" />\n <path d=\"M21 18a1 1 0 0 1 -1 1h-10a1 1 0 0 1 0 -2h10a1 1 0 0 1 1 1\" />\n <path d=\"M7 5.995v.02c0 1.099 -.895 1.99 -2 1.99s-2 -.891 -2 -1.99v-.02c0 -1.099 .895 -1.99 2 -1.99s2 .891 2 1.99\" />\n <path d=\"M7 11.995v.02c0 1.099 -.895 1.99 -2 1.99s-2 -.891 -2 -1.99v-.02c0 -1.099 .895 -1.99 2 -1.99s2 .891 2 1.99\" />\n <path d=\"M7 17.995v.02c0 1.099 -.895 1.99 -2 1.99s-2 -.891 -2 -1.99v-.02c0 -1.099 .895 -1.99 2 -1.99s2 .891 2 1.99\" />\n </svg>\n )\n}\n\nfunction IconOrderedList() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M11 6h9\" />\n <path d=\"M11 12h9\" />\n <path d=\"M12 18h8\" />\n <path d=\"M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4\" />\n <path d=\"M6 10v-6l-2 2\" />\n </svg>\n )\n}\n\nfunction IconChecklist() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n viewBox=\"0 0 16 16\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.54 2.981h-2a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-.5-.5Zm-2-1a1.5 1.5 0 0 0-1.5 1.5v2a1.5 1.5 0 0 0 1.5 1.5h2a1.5 1.5 0 0 0 1.5-1.5v-2a1.5 1.5 0 0 0-1.5-1.5h-2Zm2 8.015h-2a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-.5-.5Zm-2-1a1.5 1.5 0 0 0-1.5 1.5v2a1.5 1.5 0 0 0 1.5 1.5h2a1.5 1.5 0 0 0 1.5-1.5v-2a1.5 1.5 0 0 0-1.5-1.5h-2Z\"\n clipRule=\"evenodd\"\n />\n <path d=\"M8.007 4.481c0-.345.28-.625.625-.625h5.739a.625.625 0 1 1 0 1.25H8.63a.625.625 0 0 1-.624-.625Zm0 7.015c0-.346.28-.625.625-.625h5.739a.625.625 0 1 1 0 1.25H8.63a.625.625 0 0 1-.624-.625Z\" />\n </svg>\n )\n}\n\nfunction IconBold() {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M6 2.5C5.17157 2.5 4.5 3.17157 4.5 4V20C4.5 20.8284 5.17157 21.5 6 21.5H15C16.4587 21.5 17.8576 20.9205 18.8891 19.8891C19.9205 18.8576 20.5 17.4587 20.5 16C20.5 14.5413 19.9205 13.1424 18.8891 12.1109C18.6781 11.9 18.4518 11.7079 18.2128 11.5359C19.041 10.5492 19.5 9.29829 19.5 8C19.5 6.54131 18.9205 5.14236 17.8891 4.11091C16.8576 3.07946 15.4587 2.5 14 2.5H6ZM14 10.5C14.663 10.5 15.2989 10.2366 15.7678 9.76777C16.2366 9.29893 16.5 8.66304 16.5 8C16.5 7.33696 16.2366 6.70107 15.7678 6.23223C15.2989 5.76339 14.663 5.5 14 5.5H7.5V10.5H14ZM7.5 18.5V13.5H15C15.663 13.5 16.2989 13.7634 16.7678 14.2322C17.2366 14.7011 17.5 15.337 17.5 16C17.5 16.663 17.2366 17.2989 16.7678 17.7678C16.2989 18.2366 15.663 18.5 15 18.5H7.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n )\n}\n\nfunction IconItalic() {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M15 20H7V18H9.92661L12.0425 6H9V4H17V6H14.0734L11.9575 18H15V20Z\"\n fill=\"currentColor\"\n />\n </svg>\n )\n}\n\nfunction IconUnderline() {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M7 4C7 3.44772 6.55228 3 6 3C5.44772 3 5 3.44772 5 4V10C5 11.8565 5.7375 13.637 7.05025 14.9497C8.36301 16.2625 10.1435 17 12 17C13.8565 17 15.637 16.2625 16.9497 14.9497C18.2625 13.637 19 11.8565 19 10V4C19 3.44772 18.5523 3 18 3C17.4477 3 17 3.44772 17 4V10C17 11.3261 16.4732 12.5979 15.5355 13.5355C14.5979 14.4732 13.3261 15 12 15C10.6739 15 9.40215 14.4732 8.46447 13.5355C7.52678 12.5979 7 11.3261 7 10V4ZM4 19C3.44772 19 3 19.4477 3 20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20C21 19.4477 20.5523 19 20 19H4Z\"\n fill=\"currentColor\"\n />\n </svg>\n )\n}\n\nfunction IconCodeBlock() {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M15.4545 4.2983C15.6192 3.77115 15.3254 3.21028 14.7983 3.04554C14.2712 2.88081 13.7103 3.1746 13.5455 3.70175L8.54554 19.7017C8.38081 20.2289 8.6746 20.7898 9.20175 20.9545C9.72889 21.1192 10.2898 20.8254 10.4545 20.2983L15.4545 4.2983Z\"\n fill=\"currentColor\"\n />\n <path\n d=\"M6.70711 7.29289C7.09763 7.68342 7.09763 8.31658 6.70711 8.70711L3.41421 12L6.70711 15.2929C7.09763 15.6834 7.09763 16.3166 6.70711 16.7071C6.31658 17.0976 5.68342 17.0976 5.29289 16.7071L1.29289 12.7071C0.902369 12.3166 0.902369 11.6834 1.29289 11.2929L5.29289 7.29289C5.68342 6.90237 6.31658 6.90237 6.70711 7.29289Z\"\n fill=\"currentColor\"\n />\n <path\n d=\"M17.2929 7.29289C17.6834 6.90237 18.3166 6.90237 18.7071 7.29289L22.7071 11.2929C23.0976 11.6834 23.0976 12.3166 22.7071 12.7071L18.7071 16.7071C18.3166 17.0976 17.6834 17.0976 17.2929 16.7071C16.9024 16.3166 16.9024 15.6834 17.2929 15.2929L20.5858 12L17.2929 8.70711C16.9024 8.31658 16.9024 7.68342 17.2929 7.29289Z\"\n fill=\"currentColor\"\n />\n </svg>\n )\n}\n\nfunction IconTable() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14z\" />\n <path d=\"M3 10h18\" />\n <path d=\"M10 3v18\" />\n </svg>\n )\n}\n\nconst TABLE_PICKER_MAX = 10\n\nfunction TableInsertPicker({\n active,\n onInsert,\n}: {\n active: boolean\n onInsert: (rows: number, cols: number) => void\n}) {\n const [open, setOpen] = useState(false)\n const [rows, setRows] = useState(0)\n const [cols, setCols] = useState(0)\n const [dragging, setDragging] = useState(false)\n const rootRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false)\n setDragging(false)\n setRows(0)\n setCols(0)\n }\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n setDragging(false)\n setRows(0)\n setCols(0)\n }\n }\n const onPointerUp = () => setDragging(false)\n const timer = window.setTimeout(() => {\n document.addEventListener('pointerdown', onPointerDown)\n }, 0)\n document.addEventListener('keydown', onKeyDown)\n document.addEventListener('pointerup', onPointerUp)\n return () => {\n window.clearTimeout(timer)\n document.removeEventListener('pointerdown', onPointerDown)\n document.removeEventListener('keydown', onKeyDown)\n document.removeEventListener('pointerup', onPointerUp)\n }\n }, [open])\n\n const selectSize = (nextRows: number, nextCols: number) => {\n setRows(nextRows)\n setCols(nextCols)\n }\n\n const confirm = (nextRows: number, nextCols: number) => {\n if (nextRows < 1 || nextCols < 1) return\n onInsert(nextRows, nextCols)\n setOpen(false)\n setDragging(false)\n setRows(0)\n setCols(0)\n }\n\n const label =\n rows > 0 && cols > 0 ? `${rows} × ${cols}` : '테이블 크기 선택'\n\n return (\n <div className=\"toolbar-dropdown table-picker\" ref={rootRef}>\n <button\n type=\"button\"\n className={`toolbar-btn${open || active ? ' is-active' : ''}`}\n title=\"테이블 삽입\"\n aria-label=\"테이블 삽입\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setOpen((v) => !v)\n setRows(0)\n setCols(0)\n setDragging(false)\n }}\n >\n <IconTable />\n </button>\n {open && (\n <div\n className=\"table-picker-menu\"\n role=\"dialog\"\n aria-label=\"테이블 크기 선택\"\n onMouseDown={(e) => e.preventDefault()}\n >\n <p className=\"table-picker-label\">{label}</p>\n <div\n className=\"table-picker-grid\"\n onPointerLeave={() => {\n if (!dragging) {\n setRows(0)\n setCols(0)\n }\n }}\n >\n {Array.from({ length: TABLE_PICKER_MAX * TABLE_PICKER_MAX }, (_, index) => {\n const r = Math.floor(index / TABLE_PICKER_MAX) + 1\n const c = (index % TABLE_PICKER_MAX) + 1\n const selected = r <= rows && c <= cols\n return (\n <button\n key={`${r}-${c}`}\n type=\"button\"\n className={`table-picker-cell${selected ? ' is-selected' : ''}`}\n aria-label={`${r}행 ${c}열`}\n onPointerEnter={() => selectSize(r, c)}\n onPointerDown={(e) => {\n e.preventDefault()\n setDragging(true)\n selectSize(r, c)\n }}\n onPointerUp={(e) => {\n e.preventDefault()\n confirm(r, c)\n }}\n />\n )\n })}\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction IconAlignLeft() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M4 6l16 0\" />\n <path d=\"M4 12l10 0\" />\n <path d=\"M4 18l14 0\" />\n </svg>\n )\n}\n\nfunction IconAlignCenter() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M4 6l16 0\" />\n <path d=\"M8 12l8 0\" />\n <path d=\"M6 18l12 0\" />\n </svg>\n )\n}\n\nfunction IconAlignRight() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M4 6l16 0\" />\n <path d=\"M10 12l10 0\" />\n <path d=\"M6 18l14 0\" />\n </svg>\n )\n}\n\nfunction Btn({\n label,\n title,\n active,\n disabled,\n onClick,\n}: {\n label: ReactNode\n title: string\n active?: boolean\n disabled?: boolean\n onClick: () => void\n}) {\n const isText = typeof label === 'string'\n return (\n <button\n type=\"button\"\n className={`toolbar-btn${isText ? ' toolbar-btn--text' : ''}${\n active ? ' is-active' : ''\n }`}\n title={title}\n aria-label={title}\n aria-pressed={active}\n disabled={disabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n >\n {label}\n </button>\n )\n}\n\nfunction Sep() {\n return <span className=\"toolbar-sep\" aria-hidden=\"true\" />\n}\n\nfunction IconTextColor({ color }: { color: string | null }) {\n return (\n <span className=\"color-trigger-icon\">\n <span className=\"color-trigger-letter\">A</span>\n <span\n className=\"color-trigger-bar\"\n style={{ background: color || 'currentColor' }}\n />\n </span>\n )\n}\n\nfunction ColorPicker({\n textColor,\n highlightColor,\n onTextColor,\n onHighlightColor,\n}: {\n textColor: string | null\n highlightColor: string | null\n onTextColor: (color: string | null) => void\n onHighlightColor: (color: string | null) => void\n}) {\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false)\n }\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') setOpen(false)\n }\n const timer = window.setTimeout(() => {\n document.addEventListener('pointerdown', onPointerDown)\n }, 0)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n window.clearTimeout(timer)\n document.removeEventListener('pointerdown', onPointerDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open])\n\n return (\n <div className=\"toolbar-dropdown color-picker\" ref={rootRef}>\n <button\n type=\"button\"\n className={`toolbar-btn toolbar-dropdown-trigger${open ? ' is-active' : ''}`}\n title=\"텍스트 색상\"\n aria-label=\"텍스트 색상\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => setOpen((v) => !v)}\n >\n <IconTextColor color={textColor} />\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6l6 -6\" />\n </svg>\n </button>\n {open && (\n <div className=\"color-picker-menu\" role=\"dialog\" aria-label=\"색상 선택\">\n <div className=\"color-picker-section\">\n <p className=\"color-picker-title\">글자색</p>\n <div className=\"color-picker-grid\">\n {TEXT_COLORS.map((color) => (\n <button\n key={color}\n type=\"button\"\n className={`color-swatch color-swatch--text${\n colorsMatch(textColor, color) ? ' is-active' : ''\n }`}\n title={color}\n aria-label={`글자색 ${color}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onTextColor(color)\n setOpen(false)\n }}\n >\n <span\n className=\"color-swatch-face\"\n style={\n {\n color,\n '--swatch-color': color,\n } as CSSProperties\n }\n >\n A\n </span>\n </button>\n ))}\n </div>\n </div>\n <div className=\"color-picker-section\">\n <p className=\"color-picker-title\">배경색</p>\n <div className=\"color-picker-grid\">\n {HIGHLIGHT_COLORS.map((color, index) => (\n <button\n key={color ?? 'none'}\n type=\"button\"\n className={`color-swatch color-swatch--highlight${\n colorsMatch(highlightColor, color) || (!highlightColor && index === 0)\n ? ' is-active'\n : ''\n }`}\n title={color ?? '없음'}\n aria-label={color ? `배경색 ${color}` : '배경색 없음'}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onHighlightColor(color)\n setOpen(false)\n }}\n >\n <span\n className=\"color-swatch-face\"\n style={\n {\n background: color ?? '#ffffff',\n '--swatch-color': color ?? '#d1d5db',\n } as CSSProperties\n }\n />\n </button>\n ))}\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction HeadingDropdown({\n block,\n onSelect,\n}: {\n block: string\n onSelect: (format: HeadingFormat) => void\n}) {\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement>(null)\n const current = currentHeading(block)\n const currentOption =\n HEADING_OPTIONS.find((option) => option.value === current) ??\n HEADING_OPTIONS[0]\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) {\n setOpen(false)\n }\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') setOpen(false)\n }\n // defer so the opening click doesn't immediately close the menu\n const timer = window.setTimeout(() => {\n document.addEventListener('pointerdown', onPointerDown)\n }, 0)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n window.clearTimeout(timer)\n document.removeEventListener('pointerdown', onPointerDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open])\n\n return (\n <div className=\"toolbar-dropdown\" ref={rootRef}>\n <button\n type=\"button\"\n className={`toolbar-btn toolbar-dropdown-trigger heading-dropdown-trigger${\n open ? ' is-active' : ''\n }`}\n title=\"문단 스타일\"\n aria-label={`문단 스타일: ${currentOption.label}`}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => setOpen((v) => !v)}\n >\n <span className=\"heading-dropdown-trigger-icon\" aria-hidden=\"true\">\n {currentOption.icon}\n </span>\n <span className=\"heading-dropdown-value\">{currentOption.label}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6l6 -6\" />\n </svg>\n </button>\n {open && (\n <ul\n className=\"toolbar-dropdown-menu heading-dropdown-menu\"\n role=\"listbox\"\n aria-label=\"문단 스타일\"\n >\n {HEADING_OPTIONS.map((option) => (\n <li key={option.value} role=\"option\" aria-selected={current === option.value}>\n <button\n type=\"button\"\n className={`toolbar-dropdown-item heading-dropdown-item${\n current === option.value ? ' is-active' : ''\n }`}\n title={option.label}\n aria-label={option.label}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onSelect(option.value)\n setOpen(false)\n }}\n >\n <span className=\"heading-option-icon\" aria-hidden=\"true\">\n {option.icon}\n </span>\n <span className={`heading-option-preview ${option.previewClass}`}>\n {option.label}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport function Toolbar({ active, onCommand }: ToolbarProps) {\n return (\n <div className=\"toolbar\" role=\"toolbar\" aria-label=\"서식 도구\">\n <HeadingDropdown\n block={active.block}\n onSelect={(format) => onCommand({ type: 'block', format })}\n />\n <Sep />\n <Btn\n label={<IconBold />}\n title=\"굵게\"\n active={active.bold}\n onClick={() => onCommand({ type: 'inline', mark: 'bold' })}\n />\n <Btn\n label={<IconItalic />}\n title=\"기울임\"\n active={active.italic}\n onClick={() => onCommand({ type: 'inline', mark: 'italic' })}\n />\n <Btn\n label={<IconUnderline />}\n title=\"밑줄\"\n active={active.underline}\n onClick={() => onCommand({ type: 'inline', mark: 'underline' })}\n />\n <ColorPicker\n textColor={active.textColor}\n highlightColor={active.highlightColor}\n onTextColor={(color) => onCommand({ type: 'textColor', color })}\n onHighlightColor={(color) => onCommand({ type: 'highlightColor', color })}\n />\n <Sep />\n <Btn\n label={<IconBulletList />}\n title=\"글머리 기호 목록\"\n active={active.inList === 'ul'}\n onClick={() => onCommand({ type: 'block', format: 'ul' })}\n />\n <Btn\n label={<IconOrderedList />}\n title=\"번호 매기기 목록\"\n active={active.inList === 'ol'}\n onClick={() => onCommand({ type: 'block', format: 'ol' })}\n />\n <Btn\n label={<IconChecklist />}\n title=\"체크리스트\"\n active={active.inList === 'checklist'}\n onClick={() => onCommand({ type: 'block', format: 'checklist' })}\n />\n <Btn\n label={<IconCodeBlock />}\n title=\"코드 블록\"\n active={active.block === 'pre'}\n onClick={() => onCommand({ type: 'block', format: 'pre' })}\n />\n <TableInsertPicker\n active={active.inTable}\n onInsert={(rows, cols) =>\n onCommand({ type: 'insertTable', rows, cols })\n }\n />\n <Sep />\n <Btn\n label={<IconAlignLeft />}\n title=\"왼쪽 정렬\"\n active={active.align === 'left'}\n onClick={() => onCommand({ type: 'align', align: 'left' })}\n />\n <Btn\n label={<IconAlignCenter />}\n title=\"가운데 정렬\"\n active={active.align === 'center'}\n onClick={() => onCommand({ type: 'align', align: 'center' })}\n />\n <Btn\n label={<IconAlignRight />}\n title=\"오른쪽 정렬\"\n active={active.align === 'right'}\n onClick={() => onCommand({ type: 'align', align: 'right' })}\n />\n </div>\n )\n}\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type ClipboardEvent,\n type CSSProperties,\n type KeyboardEvent,\n type MouseEvent,\n} from 'react'\nimport {\n focusEditor,\n handleChecklistClick,\n handleEnterKey,\n handleTableTab,\n insertHTML,\n insertTable,\n mergeCells,\n setAlign,\n setBlockFormat,\n setHighlightColor,\n setTextColor,\n toggleInline,\n} from './commands'\nimport { EditorHistory } from './history'\nimport { tryMarkdownShortcut } from './markdown'\nimport {\n domToJson,\n jsonToHtml,\n normalizeDoc,\n type EditorDoc,\n} from './document'\nimport { sanitizeHTML, sanitizePlainText } from './sanitize'\nimport {\n captureSelection,\n getActiveState,\n type ActiveState,\n} from './selection'\nimport { TableSelection } from './TableSelection'\nimport { type EditorTheme } from './theme'\nimport { Toolbar, type ToolbarCommand } from './Toolbar'\n\nconst EMPTY_HTML = '<p><br></p>'\n\nfunction isEditorEmpty(editor: HTMLElement) {\n const text = editor.textContent?.replace(/\\u200B/g, '').trim() ?? ''\n if (text) return false\n return !editor.querySelector('table, img, pre, ul, ol, blockquote, h1, h2, h3')\n}\n\nconst defaultActive: ActiveState = {\n bold: false,\n italic: false,\n underline: false,\n block: 'p',\n align: 'left',\n inList: null,\n textColor: null,\n highlightColor: null,\n inTable: false,\n}\n\nexport type EditorHandle = {\n /** 현재 본문 HTML */\n getHtml: () => string\n /** DB 등에서 불러온 HTML 넣기 (sanitize + 히스토리 리셋) */\n setHtml: (html: string) => void\n /** 현재 본문 JSON */\n getJson: () => EditorDoc\n /** DB 등에서 불러온 JSON 넣기 (HTML 변환 + sanitize + 히스토리 리셋) */\n setJson: (doc: EditorDoc | unknown) => void\n /** 본문 텍스트만 */\n getText: () => string\n /** 편집 영역에 포커스 */\n focus: () => void\n}\n\nexport type EditorProps = {\n initialHTML?: string\n /** 에디터 루트 클래스 */\n className?: string\n /** 에디터 루트 인라인 스타일 */\n style?: CSSProperties\n /**\n * 툴바/본문 스타일\n * @example theme={{ toolbar: { background: '#fff' }, content: { padding: 24 } }}\n */\n theme?: EditorTheme\n /** 툴바 영역 클래스 */\n toolbarClassName?: string\n /** 툴바 영역 인라인 스타일 (theme.toolbar와 병합) */\n toolbarStyle?: CSSProperties\n /** 본문(페이퍼) 영역 클래스 */\n contentClassName?: string\n /** 본문 편집 영역 인라인 스타일 (theme.content와 병합) */\n contentStyle?: CSSProperties\n}\n\nexport const Editor = forwardRef<EditorHandle, EditorProps>(function Editor(\n {\n initialHTML = EMPTY_HTML,\n className,\n style,\n theme,\n toolbarClassName,\n toolbarStyle,\n contentClassName,\n contentStyle,\n },\n ref,\n) {\n const editableRef = useRef<HTMLDivElement>(null)\n const historyRef = useRef(new EditorHistory())\n const [active, setActive] = useState<ActiveState>(defaultActive)\n const [empty, setEmpty] = useState(true)\n\n const syncEmpty = () => {\n const editor = editableRef.current\n if (!editor) return\n setEmpty(isEditorEmpty(editor))\n }\n\n const syncToolbar = () => {\n const editor = editableRef.current\n if (!editor) return\n setActive(getActiveState(editor))\n syncEmpty()\n }\n\n const applyHTML = (html: string) => {\n const editor = editableRef.current\n if (!editor) return\n editor.innerHTML = sanitizeHTML(html) || EMPTY_HTML\n historyRef.current.reset(editor.innerHTML)\n syncToolbar()\n }\n\n const commitHistory = (force = false) => {\n const editor = editableRef.current\n if (!editor) return\n historyRef.current.push(editor, force)\n syncToolbar()\n }\n\n useImperativeHandle(ref, () => ({\n getHtml: () => editableRef.current?.innerHTML ?? '',\n setHtml: (html: string) => applyHTML(html),\n getJson: () => {\n const editor = editableRef.current\n if (!editor) return { type: 'doc', content: [{ type: 'paragraph' }] }\n return domToJson(editor)\n },\n setJson: (doc: EditorDoc | unknown) => {\n applyHTML(jsonToHtml(normalizeDoc(doc)))\n },\n getText: () => editableRef.current?.innerText ?? '',\n focus: () => {\n const editor = editableRef.current\n if (editor) focusEditor(editor)\n },\n }))\n\n useEffect(() => {\n applyHTML(initialHTML)\n }, [initialHTML])\n\n useEffect(() => {\n const onSelectionChange = () => {\n const editor = editableRef.current\n if (!editor) return\n captureSelection(editor)\n setActive(getActiveState(editor))\n }\n document.addEventListener('selectionchange', onSelectionChange)\n return () => document.removeEventListener('selectionchange', onSelectionChange)\n }, [])\n\n const runCommand = (command: ToolbarCommand) => {\n const editor = editableRef.current\n if (!editor) return\n focusEditor(editor)\n\n switch (command.type) {\n case 'inline':\n toggleInline(editor, command.mark)\n break\n case 'block':\n setBlockFormat(editor, command.format)\n break\n case 'align':\n setAlign(editor, command.align)\n break\n case 'textColor':\n setTextColor(editor, command.color)\n break\n case 'highlightColor':\n setHighlightColor(editor, command.color)\n break\n case 'insertTable':\n insertTable(editor, command.rows ?? 3, command.cols ?? 3)\n break\n case 'undo':\n historyRef.current.undo(editor)\n syncToolbar()\n return\n case 'redo':\n historyRef.current.redo(editor)\n syncToolbar()\n return\n }\n\n commitHistory(true)\n }\n\n const onInput = () => {\n commitHistory(false)\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const editor = editableRef.current\n if (!editor) return\n\n const mod = event.metaKey || event.ctrlKey\n\n if (mod && event.key.toLowerCase() === 'z') {\n event.preventDefault()\n if (event.shiftKey) historyRef.current.redo(editor)\n else historyRef.current.undo(editor)\n syncToolbar()\n return\n }\n\n if (mod && event.key.toLowerCase() === 'y') {\n event.preventDefault()\n historyRef.current.redo(editor)\n syncToolbar()\n return\n }\n\n if (mod && event.key.toLowerCase() === 'b') {\n event.preventDefault()\n toggleInline(editor, 'bold')\n commitHistory(true)\n return\n }\n\n if (mod && event.key.toLowerCase() === 'i') {\n event.preventDefault()\n toggleInline(editor, 'italic')\n commitHistory(true)\n return\n }\n\n if (mod && event.key.toLowerCase() === 'u') {\n event.preventDefault()\n toggleInline(editor, 'underline')\n commitHistory(true)\n return\n }\n\n if (handleTableTab(editor, event.nativeEvent)) {\n commitHistory(true)\n return\n }\n\n if (event.key === 'Enter' && !event.shiftKey) {\n if (handleEnterKey(editor, event.nativeEvent)) {\n commitHistory(true)\n return\n }\n }\n\n if (event.key === ' ') {\n requestAnimationFrame(() => {\n if (tryMarkdownShortcut(editor)) {\n commitHistory(true)\n }\n })\n }\n }\n\n const onPaste = (event: ClipboardEvent<HTMLDivElement>) => {\n const editor = editableRef.current\n if (!editor) return\n event.preventDefault()\n\n const html = event.clipboardData.getData('text/html')\n const text = event.clipboardData.getData('text/plain')\n const cleaned = html ? sanitizeHTML(html) : sanitizePlainText(text)\n if (!cleaned) return\n insertHTML(editor, cleaned)\n commitHistory(true)\n }\n\n const onMouseDown = (event: MouseEvent<HTMLDivElement>) => {\n const editor = editableRef.current\n if (!editor) return\n if (handleChecklistClick(editor, event.nativeEvent)) {\n commitHistory(true)\n }\n }\n\n const mergedToolbarStyle: CSSProperties = {\n ...theme?.toolbar,\n ...toolbarStyle,\n }\n\n const mergedContentStyle: CSSProperties = {\n ...theme?.content,\n ...contentStyle,\n }\n\n return (\n <div\n className={['wysiwyg', className].filter(Boolean).join(' ')}\n style={style}\n >\n <div\n className={['toolbar-shell', toolbarClassName].filter(Boolean).join(' ')}\n style={mergedToolbarStyle}\n >\n <Toolbar active={active} onCommand={runCommand} />\n </div>\n <div\n className={['editor-paper', contentClassName].filter(Boolean).join(' ')}\n style={mergedContentStyle}\n >\n <div\n ref={editableRef}\n className={['editor-surface', empty ? 'is-empty' : '']\n .filter(Boolean)\n .join(' ')}\n contentEditable\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-label=\"편집 영역\"\n data-placeholder=\"내용을 입력하세요\"\n spellCheck\n suppressContentEditableWarning\n onInput={onInput}\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n onMouseDown={onMouseDown}\n onBlur={syncToolbar}\n />\n <TableSelection\n editorRef={editableRef}\n onMerge={(cells) => {\n if (mergeCells(cells)) commitHistory(true)\n }}\n />\n </div>\n </div>\n )\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/editor/markdown.ts"],"names":[],"mappings":"AA4HA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAKhE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/editor/sanitize.ts"],"names":[],"mappings":"AAwMA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyBjD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAatD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const BLOCK_TAGS: Set<string>;
|
|
2
|
+
export type AlignValue = 'left' | 'center' | 'right';
|
|
3
|
+
export type ActiveState = {
|
|
4
|
+
bold: boolean;
|
|
5
|
+
italic: boolean;
|
|
6
|
+
underline: boolean;
|
|
7
|
+
block: string;
|
|
8
|
+
align: AlignValue;
|
|
9
|
+
inList: 'ul' | 'ol' | 'checklist' | null;
|
|
10
|
+
textColor: string | null;
|
|
11
|
+
highlightColor: string | null;
|
|
12
|
+
inTable: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function getEditorSelection(editor: HTMLElement): Selection | null;
|
|
15
|
+
export declare function captureSelection(editor: HTMLElement): void;
|
|
16
|
+
export declare function restoreSavedSelection(editor: HTMLElement): boolean;
|
|
17
|
+
export declare function getRange(editor: HTMLElement): Range | null;
|
|
18
|
+
export declare function isElement(node: Node | null): node is HTMLElement;
|
|
19
|
+
export declare function closest(node: Node | null, predicate: (el: HTMLElement) => boolean, root?: HTMLElement): HTMLElement | null;
|
|
20
|
+
export declare function closestTag(node: Node | null, tagNames: string | string[], root?: HTMLElement): HTMLElement | null;
|
|
21
|
+
export declare function getBlockElement(node: Node | null, root: HTMLElement): HTMLElement | null;
|
|
22
|
+
export declare function setSelectionToContents(el: Node, collapseToEnd?: boolean): void;
|
|
23
|
+
export declare function setCaretAfter(node: Node): void;
|
|
24
|
+
export declare function setCaretInside(el: HTMLElement, atStart?: boolean): void;
|
|
25
|
+
export declare function placeCaretInEmptyBlock(block: HTMLElement): void;
|
|
26
|
+
export declare function getActiveState(editor: HTMLElement): ActiveState;
|
|
27
|
+
export declare function getTextBeforeCaretInBlock(editor: HTMLElement): {
|
|
28
|
+
block: HTMLElement;
|
|
29
|
+
text: string;
|
|
30
|
+
range: Range;
|
|
31
|
+
} | null;
|
|
32
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../src/editor/selection.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,aAcrB,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEpD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,WAAW,GAAG,IAAI,CAAA;IACxC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAKxE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,QAQnD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAYlE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CAoB1D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,WAAW,CAEhE;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,SAAS,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,EACvC,IAAI,CAAC,EAAE,WAAW,GACjB,WAAW,GAAG,IAAI,CAOpB;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAC3B,IAAI,CAAC,EAAE,WAAW,GACjB,WAAW,GAAG,IAAI,CAKpB;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,IAAI,EAAE,WAAW,GAChB,WAAW,GAAG,IAAI,CAYpB;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,UAAQ,QAQrE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,QAQvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,UAAQ,QAQ9D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,QAOxD;AAsCD,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAwD/D;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,WAAW,GAClB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAU3D"}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.wysiwyg{--editor-width:min(920px, 100%);--editor-toolbar-bg:var(--surface);--editor-toolbar-border:var(--border);--editor-toolbar-padding-y:2px;--editor-toolbar-btn-size:26px;--editor-toolbar-icon-size:16px;--editor-toolbar-btn-radius:6px;--editor-toolbar-btn-hover-bg:var(--btn-hover);--editor-toolbar-btn-active-bg:var(--btn-hover);--editor-toolbar-btn-active-color:var(--accent);--editor-content-bg:var(--surface);--editor-content-color:var(--text-h);--editor-content-font-family:inherit;--editor-content-font-size:inherit;--editor-content-line-height:1.5;--editor-content-padding:8px 14px;--editor-content-min-height:520px;--editor-content-radius:0;--editor-content-shadow:none;--editor-content-caret:auto;width:var(--editor-width);text-align:left;flex-direction:column;align-items:stretch;gap:0;margin:0 auto;display:flex}.toolbar-shell{z-index:20;width:100%;padding:var(--editor-toolbar-padding-y) 8px;background:var(--editor-toolbar-bg);border-bottom:1px solid var(--editor-toolbar-border);justify-content:flex-start;display:flex;position:sticky;top:0;overflow:visible}.toolbar{background:var(--editor-toolbar-bg);box-shadow:none;border:none;border-radius:0;flex-wrap:nowrap;align-items:center;gap:1px;max-width:100%;padding:1px 2px;display:flex;overflow:visible}.toolbar-btn{appearance:none;color:var(--text-h);font:600 11px/1 var(--sans);width:var(--editor-toolbar-btn-size);height:var(--editor-toolbar-btn-size);min-width:var(--editor-toolbar-btn-size);border-radius:var(--editor-toolbar-btn-radius);cursor:pointer;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;transition:background .15s,color .15s;display:inline-flex}.toolbar-btn svg{width:var(--editor-toolbar-icon-size);height:var(--editor-toolbar-icon-size);display:block}.toolbar-btn:hover:not(:disabled){background:var(--editor-toolbar-btn-hover-bg)}.toolbar-btn.is-active{background:var(--editor-toolbar-btn-active-bg);color:var(--editor-toolbar-btn-active-color);font-weight:700}.toolbar-btn.is-active:hover:not(:disabled){background:var(--editor-toolbar-btn-hover-bg)}.toolbar-btn:disabled{opacity:.32;cursor:not-allowed}.toolbar-btn--text{width:auto;min-width:var(--editor-toolbar-btn-size);letter-spacing:.01em;padding:0 6px;font-size:11px}.toolbar-sep{background:#c4c9d2;flex-shrink:0;width:1px;height:18px;margin:0 7px}.toolbar-dropdown{z-index:30;flex-shrink:0;position:relative}.toolbar-dropdown-trigger{border-radius:8px;gap:1px;width:auto;min-width:42px;padding:0 6px}.toolbar-dropdown-trigger svg:last-child{opacity:.5;width:12px;height:12px}.heading-dropdown-trigger{gap:4px;min-width:0;padding:0 6px 0 4px}.heading-dropdown-trigger-icon{flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.heading-dropdown-trigger-icon svg{width:var(--editor-toolbar-icon-size);height:var(--editor-toolbar-icon-size)}.heading-dropdown-value{font:500 12px/1 var(--sans);color:var(--text-h);text-overflow:ellipsis;white-space:nowrap;max-width:88px;overflow:hidden}.toolbar-dropdown-menu{z-index:40;border:1px solid var(--border);background:var(--surface);min-width:40px;box-shadow:var(--shadow-float);border-radius:10px;margin:0;padding:4px;list-style:none;position:absolute;top:calc(100% + 4px);left:50%;transform:translate(-50%)}.toolbar-dropdown-item{appearance:none;width:28px;height:28px;color:var(--text-h);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;padding:0;display:inline-flex}.toolbar-dropdown-item:hover{background:var(--btn-hover)}.toolbar-dropdown-item.is-active{background:var(--btn-hover);color:var(--accent)}.toolbar-dropdown-item svg{width:16px;height:16px;display:block}.heading-dropdown-menu{min-width:180px;padding:6px;left:0;transform:none}.heading-dropdown-item{border-radius:8px;justify-content:flex-start;gap:10px;width:100%;height:auto;min-height:36px;padding:6px 10px}.heading-option-icon{width:18px;height:18px;color:var(--text);opacity:.75;flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.heading-option-icon svg{width:16px;height:16px}.heading-option-preview{text-align:left;color:var(--text-h);white-space:nowrap;line-height:1.2;display:block}.heading-option-preview--p{font-size:14px;font-weight:400}.heading-option-preview--h1{font-size:22px;font-weight:700}.heading-option-preview--h2{font-size:18px;font-weight:700}.heading-option-preview--h3{font-size:15px;font-weight:650}.heading-dropdown-item.is-active .heading-option-preview{color:inherit}.color-trigger-icon{flex-direction:column;align-items:center;gap:1px;line-height:1;display:inline-flex}.color-trigger-letter{font:700 12px/1 var(--sans)}.color-trigger-bar{border-radius:1px;width:12px;height:2px;display:block}.color-picker-menu{z-index:40;border:1px solid var(--border);background:var(--surface);width:max-content;box-shadow:var(--shadow-float);border-radius:12px;padding:10px;position:absolute;top:calc(100% + 6px);left:0}.color-picker-section+.color-picker-section{margin-top:10px}.color-picker-title{font:700 12px/1.2 var(--sans);color:var(--text-h);margin:0 0 6px}.color-picker-grid{grid-template-columns:repeat(5,auto);justify-content:start;gap:5px 2px;display:grid}.color-swatch{appearance:none;cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;width:30px;height:30px;padding:0;display:inline-flex}.color-swatch.is-active{background:#f3f4f6}.color-swatch:hover,.color-swatch.is-active:hover{background:#e8eaed}@media (prefers-color-scheme:dark){.color-swatch.is-active{background:#2a303c}.color-swatch:hover,.color-swatch.is-active:hover{background:#353b48}}.color-swatch-face{box-sizing:border-box;background:#fff;border:1px solid #d1d5db;border-radius:999px;justify-content:center;align-items:center;width:20px;height:20px;transition:border-color .12s;display:inline-flex}.color-swatch--text .color-swatch-face{font:700 10px/1 var(--sans);border-color:var(--swatch-color,#d1d5db)}.color-swatch--highlight .color-swatch-face{border-color:color-mix(in srgb, var(--swatch-color,#e5e7eb) 35%, #d1d5db 65%)}.color-swatch:hover .color-swatch-face{border-color:color-mix(in srgb, var(--swatch-color,#9ca3af) 70%, #000 30%)}@media (prefers-color-scheme:dark){.color-swatch-face{background:var(--surface);border-color:var(--border)}.color-swatch:hover .color-swatch-face{border-color:color-mix(in srgb, var(--swatch-color,#9ca3af) 75%, #fff 25%)}}.editor-paper{background:var(--editor-content-bg);width:100%;box-shadow:none;min-height:var(--editor-content-min-height);padding:var(--editor-content-padding);color:var(--editor-content-color);font-family:var(--editor-content-font-family);font-size:var(--editor-content-font-size);line-height:var(--editor-content-line-height);border:none;border-radius:0;overflow:hidden}.editor-surface{min-height:100%;color:inherit;font:inherit;line-height:inherit;caret-color:var(--editor-content-caret);outline:none;padding:0}.editor-surface>:first-child{margin-top:0}.editor-surface h1,.editor-surface h2,.editor-surface h3{color:var(--text-h);font-family:var(--heading);margin:1.4em 0 .5em;font-weight:650;line-height:1.25}.editor-surface h1{letter-spacing:-.03em;font-size:2rem}.editor-surface h2{letter-spacing:-.02em;font-size:1.5rem}.editor-surface h3{font-size:1.2rem}.editor-surface p{margin:.75em 0}.editor-surface blockquote{border-left:3px solid var(--accent);color:var(--text);margin:1em 0;padding:.25em 0 .25em 1em}.editor-surface ul,.editor-surface ol{margin:.75em 0;padding-left:1.4em}.editor-surface li{margin:.25em 0}.editor-surface ul.editor-checklist{margin:.75em 0;padding-left:0;list-style:none}.editor-surface ul.editor-checklist>li{align-items:flex-start;gap:8px;margin:.25em 0;display:flex}.editor-surface ul.editor-checklist .checklist-box{cursor:pointer;box-sizing:border-box;background:#fff;border:1.5px solid #94a3b8;border-radius:3px;flex-shrink:0;width:16px;height:16px;margin-top:.2em}.editor-surface ul.editor-checklist>li[data-checked=true] .checklist-box{border-color:var(--accent);background-color:var(--accent);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:12px 12px}.editor-surface ul.editor-checklist>li[data-checked=true] .checklist-content{color:var(--text);opacity:.65;text-decoration:line-through}.editor-surface ul.editor-checklist .checklist-content{flex:1;min-width:0}.editor-surface code{font-family:var(--mono);background:var(--code-bg);color:var(--text-h);border-radius:6px;padding:.15em .4em;font-size:.9em}.editor-surface pre{background:var(--code-bg);border:1px solid var(--border);border-radius:12px;margin:1em 0;padding:14px 16px;overflow-x:auto}.editor-surface pre code{white-space:pre;background:0 0;padding:0;font-size:.92em;line-height:1.55}.editor-surface .editor-table{border-collapse:collapse;table-layout:auto;width:max-content;max-width:100%;margin:1em 0}.editor-surface .editor-table th,.editor-surface .editor-table td{vertical-align:top;word-break:break-word;border:1px solid #94a3b8;min-width:48px;padding:4px 6px}.editor-surface .editor-table th{font-weight:inherit;text-align:left}.editor-surface .editor-table th.is-cell-selected,.editor-surface .editor-table td.is-cell-selected{box-shadow:none;background:#7dd3fc59}.editor-surface.is-table-cell-selecting,.editor-surface.is-table-cell-selecting *{caret-color:#0000;-webkit-user-select:none!important;user-select:none!important}.table-selection-float{z-index:50;border:1px solid var(--border);background:var(--surface);width:34px;height:34px;box-shadow:var(--shadow-float);color:var(--text-h);cursor:pointer;border-radius:999px;justify-content:center;align-items:center;padding:0;display:inline-flex;position:fixed;transform:translate(-50%)}.table-selection-float:hover{background:var(--btn-hover);color:var(--accent)}.table-selection-float svg{width:18px;height:18px;display:block}@media (prefers-color-scheme:dark){.editor-surface .editor-table th,.editor-surface .editor-table td{border-color:#64748b}.editor-surface .editor-table th.is-cell-selected,.editor-surface .editor-table td.is-cell-selected{box-shadow:none;background:#38bdf847}}.table-picker-menu{z-index:40;border:1px solid var(--border);background:var(--surface);box-shadow:var(--shadow-float);-webkit-user-select:none;user-select:none;border-radius:12px;padding:10px;position:absolute;top:calc(100% + 6px);left:0}.table-picker-label{font:700 12px/1.2 var(--sans);color:var(--text-h);min-height:1.2em;margin:0 0 8px}.table-picker-grid{grid-template-rows:repeat(10,16px);grid-template-columns:repeat(10,16px);gap:3px;display:grid}.table-picker-cell{appearance:none;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:2px;width:16px;height:16px;padding:0}.table-picker-cell.is-selected{background:color-mix(in srgb, var(--accent) 22%, #fff);border-color:var(--accent)}@media (prefers-color-scheme:dark){.table-picker-cell{background:var(--surface);border-color:var(--border)}.table-picker-cell.is-selected{background:color-mix(in srgb, var(--accent) 30%, var(--surface));border-color:var(--accent)}}.editor-surface.is-empty:before{content:attr(data-placeholder);color:var(--text);opacity:.45;pointer-events:none;float:left;height:0}@media (width<=640px){.editor-surface{min-height:420px;padding:24px 20px 48px}.toolbar{flex-wrap:wrap;justify-content:flex-start}}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
/** 툴바 / 본문 각각에 CSS 객체를 넘기는 테마 */
|
|
3
|
+
export type EditorTheme = {
|
|
4
|
+
toolbar?: CSSProperties;
|
|
5
|
+
content?: CSSProperties;
|
|
6
|
+
};
|
|
7
|
+
/** @deprecated EditorTheme 사용 */
|
|
8
|
+
export type EditorThemeVars = EditorTheme;
|
|
9
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/editor/theme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,iCAAiC;AACjC,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB,CAAA;AAED,iCAAiC;AACjC,MAAM,MAAM,eAAe,GAAG,WAAW,CAAA"}
|