@lofcz/edix 0.3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/src/utils.ts","../src/src/doc/position.ts","../src/src/doc/utils.ts","../src/src/doc/edit.ts","../src/src/commands.ts","../src/src/history.ts","../src/src/dom/parser.ts","../src/src/dom/default.ts","../src/src/dom/index.ts","../src/src/extensions/copy/plain.ts","../src/src/extensions/copy/html.ts","../src/src/extensions/utils.ts","../src/src/extensions/copy/internal.ts","../src/src/extensions/paste/file.ts","../src/src/extensions/paste/plain.ts","../src/src/extensions/paste/html.ts","../src/src/extensions/paste/internal.ts","../src/src/hotkey.ts","../src/src/editor.ts","../src/src/mutation.ts","../src/src/plugins/singleline.ts","../src/src/presets/plain.ts"],"sourcesContent":["/**\n * @internal\n */\nexport const min = Math.min;\n\n/**\n * @internal\n */\nexport const { keys, is } = Object;\n\n/**\n * @internal\n */\nexport const isString = (n: unknown) => typeof n === \"string\";\n\n/**\n * @internal\n */\nexport const isFunction = (n: unknown) => typeof n === \"function\";\n\n/**\n * @internal\n */\nexport const microtask: (fn: () => void) => void = isFunction(queueMicrotask)\n ? queueMicrotask\n : (fn) => {\n Promise.resolve().then(fn);\n };\n","import { min } from \"../utils.js\";\nimport type { Path, Position, PositionRange } from \"./types.js\";\n\n/**\n * @internal\n * 0 : same\n * -1: A is before B (forward)\n * 1: A is after B (backward)\n */\nexport const comparePath = (pathA: Path, pathB: Path): 0 | 1 | -1 => {\n const length = min(pathA.length, pathB.length);\n for (let i = 0; i < length; i++) {\n const a = pathA[i]!;\n const b = pathB[i]!;\n if (a < b) return -1;\n if (a > b) return 1;\n }\n return 0;\n};\n\n/**\n * @internal\n * 0 : same\n * -1: A is before B (forward)\n * 1 : A is after B (backward)\n */\nexport const comparePosition = (\n [pathA, offsetA]: Position,\n [pathB, offsetB]: Position,\n): 0 | 1 | -1 => {\n const comp = comparePath(pathA, pathB);\n if (comp === 0) {\n return offsetA === offsetB ? 0 : offsetA < offsetB ? -1 : 1;\n } else {\n return comp;\n }\n};\n\n/**\n * @internal\n */\nexport const toRange = ([a, b]: readonly [\n Position,\n Position,\n]): PositionRange => {\n return comparePosition(a, b) === 1 ? [b, a] : [a, b];\n};\n","import { isTextNode } from \"./edit.js\";\nimport { type DocNode, type InlineNode, type TextNode } from \"./types.js\";\n\n/**\n * @internal\n */\nexport const docToString = (\n doc: DocNode,\n serializer: (node: InlineNode) => string = (n) =>\n isTextNode(n) ? n.text : \"\",\n): string => {\n return doc.children.reduce((acc, r, i) => {\n if (i !== 0) {\n acc += \"\\n\";\n }\n return acc + r.reduce((acc, n) => acc + serializer(n), \"\");\n }, \"\");\n};\n\n/**\n * @internal\n */\nexport const stringToFragment = <T extends TextNode>(\n text: string,\n node?: T,\n): T[][] => {\n return text.split(\"\\n\").map((l) => [{ ...node, text: l } as T]);\n};\n","import { is, isString, keys } from \"../utils.js\";\nimport { comparePath, comparePosition } from \"./position.js\";\nimport type {\n DocNode,\n Fragment,\n InlineNode,\n Position,\n SelectionSnapshot,\n TextNode,\n Path,\n} from \"./types.js\";\nimport { stringToFragment } from \"./utils.js\";\n\nconst TYPE_DELETE = \"delete\";\ntype DeleteOperation = Readonly<{\n type: typeof TYPE_DELETE;\n start: Position;\n end: Position;\n}>;\n\nconst TYPE_INSERT_TEXT = \"insert_text\";\ntype InsertOperation = Readonly<{\n type: typeof TYPE_INSERT_TEXT;\n at: Position;\n text: string;\n}>;\n\nconst TYPE_INSERT_NODE = \"insert_node\";\ntype InsertNodeOperation = Readonly<{\n type: typeof TYPE_INSERT_NODE;\n at: Position;\n fragment: Fragment;\n}>;\n\nconst TYPE_SET_ATTR = \"set_attr\";\ntype SetAttrOperation = Readonly<{\n type: typeof TYPE_SET_ATTR;\n start: Position;\n end: Position;\n key: string;\n value: unknown;\n}>;\n\nexport type Operation =\n | DeleteOperation\n | InsertOperation\n | InsertNodeOperation\n | SetAttrOperation;\n\n/**\n * @internal\n */\nexport const isUnsafeOperation = ({ type }: Operation): boolean =>\n type === TYPE_INSERT_NODE || type === TYPE_SET_ATTR;\n\nexport class Transaction {\n private readonly _ops: Operation[];\n selection?: SelectionSnapshot;\n\n constructor(ops?: readonly Operation[]) {\n this._ops = ops ? ops.slice() : [];\n }\n\n get ops(): readonly Operation[] {\n return this._ops;\n }\n\n insertText(start: Position, text: string): this {\n this._ops.push({\n type: TYPE_INSERT_TEXT,\n at: start,\n text: text,\n });\n return this;\n }\n\n insertFragment(start: Position, fragment: Fragment): this {\n this._ops.push({\n type: TYPE_INSERT_NODE,\n at: start,\n fragment: fragment,\n });\n return this;\n }\n\n delete(start: Position, end: Position): this {\n this._ops.push({\n type: TYPE_DELETE,\n start: start,\n end: end,\n });\n return this;\n }\n\n attr(start: Position, end: Position, key: string, value: unknown): this {\n this._ops.push({\n type: TYPE_SET_ATTR,\n start: start,\n end: end,\n key: key,\n value: value,\n });\n return this;\n }\n\n transform(position: Position): Position {\n return this._ops.reduce((acc, op) => rebasePosition(acc, op), position);\n }\n}\n\n/**\n * @internal\n */\nexport const isTextNode = (node: InlineNode): node is TextNode =>\n \"text\" in node;\n\nconst isSameNode = (a: InlineNode, b: InlineNode): boolean => {\n const aKeys = keys(a);\n if (aKeys.length !== keys(b).length) {\n return false;\n }\n return aKeys.every((k) => {\n if (!(k in b)) {\n return false;\n }\n return k === \"text\" || is((a as any)[k], (b as any)[k]);\n });\n};\n\nconst getNodeSize = (node: InlineNode): number =>\n isTextNode(node) ? node.text.length : 1;\n\n/**\n * @internal\n */\nexport const getLineSize = (line: readonly InlineNode[]): number =>\n line.reduce((acc: number, n) => acc + getNodeSize(n), 0);\n\nconst normalize = <T extends InlineNode>(\n array: T[],\n start: number = 0,\n end: number = array.length - 1,\n): void => {\n let i = start + 1;\n while (i <= end) {\n const prev = array[i - 1]!;\n const curr = array[i]!;\n // merge text nodes with same attrs\n if (isTextNode(prev) && isTextNode(curr) && isSameNode(prev, curr)) {\n array[i - 1] = { ...prev, text: prev.text + curr.text };\n array.splice(i, 1);\n end--;\n } else {\n i++;\n }\n }\n\n // remove empty text nodes, leaving at least one node per block\n i = start;\n while (i <= end) {\n const node = array[i]!;\n if (isTextNode(node) && !node.text && array.length > 1) {\n array.splice(i, 1);\n end--;\n } else {\n i++;\n }\n }\n};\n\nconst concat = <T extends InlineNode>(a: T[], b: readonly T[]): void => {\n if (b.length) {\n const prevLength = a.length;\n a.push(...b);\n if (prevLength) {\n normalize(a, prevLength - 1, prevLength);\n }\n }\n};\n\n/**\n * @internal\n */\nexport const joinBlocks = <T extends InlineNode>(\n ...blocks: (readonly T[])[]\n): readonly T[] => {\n return blocks.reduce<T[]>((acc, b) => {\n concat(acc, b);\n return acc;\n }, []);\n};\n\nconst splitBlock = <T extends InlineNode>(\n nodes: readonly T[],\n offset: number,\n): [readonly T[], readonly T[]] => {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]!;\n const size = getNodeSize(node);\n if (size > offset) {\n const before = nodes.slice(0, i);\n const after = nodes.slice(i + 1);\n if (isTextNode(node)) {\n const beforeText = node.text.slice(0, offset);\n const afterText = node.text.slice(offset);\n if (beforeText || !before.length) {\n before.push({ ...node, text: beforeText });\n }\n if (afterText || !after.length) {\n after.unshift({ ...node, text: afterText });\n }\n } else {\n // node size must be 1\n after.unshift(node);\n }\n return [before, after];\n }\n offset -= size;\n }\n return [nodes, []];\n};\n\nconst normalizePath = (path: Path): number => {\n // TODO support nested node\n return path.length ? path[0]! : 0;\n};\n\nconst blockAtPath = (doc: DocNode, path: Path): readonly InlineNode[] => {\n return doc.children[normalizePath(path)]!;\n};\n\nconst movePath = (path: Path, int: number): Path => {\n // TODO support nested node\n return [normalizePath(path) + int];\n};\n\nconst replaceRange = <T extends DocNode>(\n doc: T,\n start: Position,\n end: Position,\n inserted: Fragment | string,\n): T => {\n const [startPath, startOffset] = start;\n const [endPath, endOffset] = end;\n\n const [before, maybeAfter] = splitBlock(\n blockAtPath(doc, startPath),\n startOffset,\n );\n const after =\n comparePosition(start, end) === -1\n ? splitBlock(blockAtPath(doc, endPath), endOffset)[1]\n : maybeAfter;\n\n if (isString(inserted)) {\n // inherit style from previous text node\n const beforeLength = before.length;\n let anchorNode: TextNode | undefined;\n if (beforeLength) {\n const maybeAnchor = before[beforeLength - 1]!;\n if (isTextNode(maybeAnchor)) {\n anchorNode = maybeAnchor;\n }\n }\n inserted = stringToFragment(inserted, anchorNode);\n }\n\n let lines: (readonly InlineNode[])[];\n if (inserted.length) {\n lines = inserted.slice();\n lines[lines.length - 1] = joinBlocks(lines[lines.length - 1]!, after);\n } else {\n lines = [after];\n }\n lines[0] = joinBlocks(before, lines[0]!);\n\n const sliced = doc.children.slice();\n sliced.splice(\n normalizePath(startPath),\n normalizePath(endPath) - normalizePath(startPath) + 1,\n ...lines,\n );\n return { ...doc, children: sliced };\n};\n\n/**\n * @internal\n */\nexport const sliceDoc = (\n doc: DocNode,\n start: Position,\n end: Position,\n): Fragment => {\n if (comparePosition(start, end) !== -1) {\n return [];\n }\n\n const sliced = doc.children.slice(\n normalizePath(start[0]),\n normalizePath(end[0]) + 1,\n );\n const lastIndex = sliced.length - 1;\n sliced[lastIndex] = splitBlock(sliced[lastIndex]!, end[1])[0];\n sliced[0] = splitBlock(sliced[0]!, start[1])[1];\n return sliced;\n};\n\nconst isValidPosition = (doc: DocNode, [path, offset]: Position): boolean => {\n // TODO improve\n if (!path.length || (path[0]! >= 0 && path[0]! < doc.children.length)) {\n if (offset >= 0 && offset <= getLineSize(blockAtPath(doc, path))) {\n return true;\n }\n }\n return false;\n};\n\nexport const rebasePosition = (position: Position, op: Operation): Position => {\n switch (op.type) {\n case TYPE_DELETE: {\n const { start, end } = op;\n\n if (comparePosition(position, start) !== -1) {\n // start <= position\n return comparePosition(end, position) === -1\n ? // start <= end < position\n [\n movePath(\n position[0],\n normalizePath(start[0]) - normalizePath(end[0]),\n ),\n position[1] +\n (comparePath(end[0], position[0]) === 0\n ? start[1] - end[1]\n : 0),\n ]\n : // start <= position <= end\n start;\n }\n break;\n }\n case TYPE_INSERT_TEXT:\n case TYPE_INSERT_NODE: {\n const at = op.at;\n const lines =\n op.type === TYPE_INSERT_TEXT ? stringToFragment(op.text) : op.fragment;\n\n const lineLength = lines.length;\n const lineDiff = lineLength - 1;\n\n if (comparePosition(position, at) !== -1) {\n // pos <= position\n return [\n movePath(position[0], lineDiff),\n position[1] +\n (comparePath(position[0], at[0]) === 0\n ? getLineSize(lines[lineLength - 1]!) -\n (lineDiff === 0 ? 0 : at[1])\n : 0),\n ];\n }\n break;\n }\n case TYPE_SET_ATTR: {\n break;\n }\n default: {\n op satisfies never;\n }\n }\n return position;\n};\n\nconst rebaseSelection = (\n selection: SelectionSnapshot,\n op: Operation,\n): SelectionSnapshot => {\n return [rebasePosition(selection[0], op), rebasePosition(selection[1], op)];\n};\n\n/**\n * @internal\n */\nexport const isValidSelection = (\n doc: DocNode,\n [anchor, focus]: SelectionSnapshot,\n): boolean => {\n return isValidPosition(doc, anchor) && isValidPosition(doc, focus);\n};\n\n/**\n * @internal\n */\nexport const applyOperation = <T extends DocNode>(\n doc: T,\n selection: SelectionSnapshot,\n op: Operation,\n): [T, SelectionSnapshot] => {\n switch (op.type) {\n case TYPE_DELETE: {\n const { start, end } = op;\n if (\n isValidPosition(doc, start) &&\n isValidPosition(doc, end) &&\n comparePosition(start, end) === -1\n ) {\n doc = replaceRange(doc, start, end, []);\n selection = rebaseSelection(selection, op);\n }\n break;\n }\n case TYPE_INSERT_TEXT: {\n const { at, text } = op;\n if (isValidPosition(doc, at) && text) {\n doc = replaceRange(doc, at, at, text);\n selection = rebaseSelection(selection, op);\n }\n break;\n }\n case TYPE_INSERT_NODE: {\n const { at, fragment } = op;\n if (isValidPosition(doc, at) && fragment.length) {\n doc = replaceRange(doc, at, at, fragment);\n selection = rebaseSelection(selection, op);\n }\n break;\n }\n case TYPE_SET_ATTR: {\n const { start, end, key, value } = op;\n if (\n isValidPosition(doc, start) &&\n isValidPosition(doc, end) &&\n comparePosition(start, end) === -1\n ) {\n doc = replaceRange(\n doc,\n start,\n end,\n sliceDoc(doc, start, end).map((line) =>\n line.map((node) =>\n isTextNode(node) ? { ...node, [key]: value } : node,\n ),\n ),\n );\n }\n break;\n }\n default: {\n op satisfies never;\n }\n }\n\n return [doc, selection];\n};\n","import { toRange } from \"./doc/position.js\";\nimport { getLineSize, isTextNode, sliceDoc, Transaction } from \"./doc/edit.js\";\nimport type { Editor } from \"./editor.js\";\nimport type {\n DocNode,\n InferNode,\n Position,\n PositionRange,\n TextNode,\n} from \"./doc/types.js\";\n\nexport type EditorCommand<A extends unknown[], T extends DocNode> = (\n this: Editor<T>,\n ...args: A\n) => void;\n\n/**\n * Delete content in the selection or specified range.\n */\nexport function Delete(\n this: Editor,\n range: PositionRange = toRange(this.selection),\n) {\n this.apply(new Transaction().delete(...range));\n}\n\n/**\n * Insert text at the caret or specified position.\n */\nexport function InsertText(\n this: Editor,\n text: string,\n position: Position = this.selection[0],\n) {\n this.apply(new Transaction().insertText(position, text));\n}\n\n/**\n * Insert node at the caret or specified position.\n */\nexport function InsertNode<T extends DocNode>(\n this: Editor<T>,\n node: Exclude<InferNode<T>, TextNode>,\n position: Position = this.selection[0],\n) {\n this.apply(new Transaction().insertFragment(position, [[node]]));\n}\n\n/**\n * Insert multiple inline nodes as a single line fragment in one transaction.\n * When `moveCaret` is true (default), the caret moves to the end of the\n * inserted content.\n */\nexport function InsertNodes<T extends DocNode>(\n this: Editor<T>,\n nodes: InferNode<T>[],\n position: Position = this.selection[0],\n moveCaret: boolean = true,\n) {\n const tr = new Transaction().insertFragment(position, [nodes]);\n if (moveCaret) {\n const end = tr.transform(position);\n tr.selection = [end, end];\n }\n this.apply(tr);\n}\n\n/**\n * Replace text in the selection or specified range.\n */\nexport function ReplaceText(this: Editor, text: string) {\n const [start, end] = toRange(this.selection);\n this.apply(new Transaction().delete(start, end).insertText(start, text));\n}\n\n/**\n * Replace all content in the editor.\n */\nexport function ReplaceAll(this: Editor, text: string) {\n const doc = this.doc;\n this.apply(\n new Transaction()\n // TODO improve\n .delete(\n [[], 0],\n [\n [doc.children.length - 1],\n getLineSize(doc.children[doc.children.length - 1]!),\n ],\n )\n .insertText([[], 0], text),\n );\n}\n\ntype ToggleableKey<T> = {\n [K in keyof T]-?: T[K] extends boolean | undefined ? K : never;\n}[keyof T];\n\n/**\n * Format content in the selection or specified range.\n */\nexport function Format<\n T extends DocNode,\n N extends Omit<InferNode<T>, \"text\">,\n K extends Extract<keyof N, string>,\n>(\n this: Editor<T>,\n key: K,\n value: N[K],\n range: PositionRange = toRange(this.selection),\n) {\n this.apply(new Transaction().attr(...range, key, value));\n}\n\n/**\n * Toggle formatting in the selection or specified range.\n */\nexport function ToggleFormat<T extends DocNode>(\n this: Editor<T>,\n key: Extract<ToggleableKey<Omit<InferNode<T>, \"text\">>, string>,\n range: PositionRange = toRange(this.selection),\n) {\n const texts = sliceDoc(this.doc, ...range).flatMap((n) =>\n n.filter(isTextNode),\n );\n if (texts.length) {\n this.apply(\n new Transaction().attr(\n ...range,\n key,\n texts.some((n) => !n[key as keyof typeof n]) ? true : false,\n ),\n );\n }\n}\n","import { rebasePosition, type Operation } from \"./doc/edit.js\";\nimport type { SelectionSnapshot } from \"./doc/types.js\";\n\nconst MAX_HISTORY_LENGTH = 500;\nconst BATCH_HISTORY_TIME = 500;\n\nconst getOperationSelection = (op: Operation): SelectionSnapshot => {\n return \"at\" in op ? [op.at, op.at] : [op.start, op.end];\n};\n\n/**\n * @internal\n */\nexport const createHistory = <T>(initialDoc: T) => {\n let index = 0;\n let prevTime = 0;\n const now = Date.now;\n const histories: [T, Operation[]][] = [[initialDoc, []]];\n\n const get = () => histories[index]!;\n\n const isUndoable = (): boolean => {\n return index > 0;\n };\n\n const isRedoable = (): boolean => {\n return index < histories.length - 1;\n };\n\n return {\n change: (doc: T, ops: Operation[]) => {\n const time = now();\n if (index === 0 || time - prevTime >= BATCH_HISTORY_TIME) {\n index++;\n if (index >= histories.length) {\n histories.push([doc, []]);\n } else {\n histories[index]![1].splice(0);\n }\n }\n prevTime = time;\n histories[index]![0] = doc;\n histories[index]![1].push(...ops);\n histories.splice(index + 1);\n if (index > MAX_HISTORY_LENGTH) {\n index--;\n histories.shift();\n }\n },\n undo: (): [T, SelectionSnapshot] | undefined => {\n if (isUndoable()) {\n const ops = get()[1];\n index--;\n const doc = get()[0];\n return [doc, getOperationSelection(ops[0]!)];\n } else {\n return;\n }\n },\n redo: (): [T, SelectionSnapshot] | undefined => {\n if (isRedoable()) {\n index++;\n const [doc, ops] = get();\n const last = ops[ops.length - 1]!;\n const sel = getOperationSelection(last);\n return [\n doc,\n [rebasePosition(sel[0], last), rebasePosition(sel[1], last)],\n ];\n } else {\n return;\n }\n },\n };\n};\n","let walker: TreeWalker | null = null;\nlet node: Node | null = null;\nlet _token: TokenType | null = null;\nlet config: ParserConfig | null = null;\n\nexport interface ParserConfig {\n /**\n * @internal\n */\n _document: Document;\n /**\n * @internal\n */\n _isBlock: (node: Element) => boolean;\n /**\n * @internal\n */\n _isVoid: (node: Element) => boolean;\n}\n\nconst SHOW_ELEMENT = 0x1;\nconst SHOW_TEXT = 0x4;\n\nconst TOKEN_NULL = 0;\n/** @internal */\nexport const TOKEN_TEXT = 1;\n/** @internal */\nexport const TOKEN_VOID = 2;\n/** @internal */\nexport const TOKEN_SOFT_BREAK = 3;\n/** @internal */\nexport const TOKEN_BLOCK = 4;\nconst TOKEN_EMPTY_BLOCK_ANCHOR = 5;\nconst TOKEN_INVALID_SOFT_BREAK = 6;\n\n/**\n * @internal\n */\nexport type TokenType =\n | typeof TOKEN_NULL\n | typeof TOKEN_TEXT\n | typeof TOKEN_VOID\n | typeof TOKEN_SOFT_BREAK\n | typeof TOKEN_BLOCK\n | typeof TOKEN_EMPTY_BLOCK_ANCHOR\n | typeof TOKEN_INVALID_SOFT_BREAK;\n\nconst ELEMENT_NODE = 1;\nconst TEXT_NODE = 3;\nconst COMMENT_NODE = 8;\n\n/**\n * @internal\n */\nexport const isTextNode = (node: Node): node is Text => {\n return node.nodeType === TEXT_NODE;\n};\n\n/**\n * @internal\n */\nexport const isElementNode = (node: Node): node is Element => {\n return node.nodeType === ELEMENT_NODE;\n};\n\n/**\n * @internal\n */\nexport const isCommentNode = (node: Node): node is Comment => {\n return node.nodeType === COMMENT_NODE;\n};\n\n/**\n * @internal\n */\nexport const getDomNode = <\n T extends TokenType | void,\n>(): T extends typeof TOKEN_TEXT\n ? Text\n : T extends TokenType\n ? Element\n : Text | Element => {\n return node as any;\n};\n\n/**\n * @internal\n */\nexport const getNodeSize = (): number => {\n const token = readToken();\n return token === TOKEN_TEXT\n ? (node as Text).data.length\n : token === TOKEN_VOID\n ? 1\n : 0;\n};\n\n/**\n * @internal\n */\nexport const readToken = (): TokenType => {\n if (_token != null) {\n return _token;\n }\n\n if (node) {\n if (isTextNode(node)) {\n const text = node.data;\n // Ignore empty text nodes some frameworks may generate\n if (text) {\n return (_token =\n // Especially Shift+Enter in Chrome\n text === \"\\n\"\n ? isValidSoftBreak()\n ? TOKEN_SOFT_BREAK\n : TOKEN_INVALID_SOFT_BREAK\n : TOKEN_TEXT);\n }\n } else if (isElementNode(node)) {\n if (node.tagName === \"BR\") {\n return (_token = isValidSoftBreak()\n ? // Especially Shift+Enter in Firefox\n TOKEN_SOFT_BREAK\n : // Returning <div><br/></div> is necessary to anchor selection\n TOKEN_EMPTY_BLOCK_ANCHOR);\n } else if (config!._isVoid(node)) {\n return (_token = TOKEN_VOID);\n } else if (config!._isBlock(node)) {\n return (_token = TOKEN_BLOCK);\n }\n }\n }\n return (_token = TOKEN_NULL);\n};\n\nconst nextNode = (): Node | null => {\n _token = null;\n return (node = walker!.nextNode());\n};\n\n/**\n * @internal\n */\nexport const nextBlock = () => {\n while ((_token = null) || (node = walker!.nextSibling())) {\n if (readToken() === TOKEN_BLOCK) {\n return;\n }\n }\n};\n\n/**\n * @internal\n */\nexport const parentBlock = () => {\n while ((_token = null) || (node = walker!.parentNode())) {\n if (readToken() === TOKEN_BLOCK) {\n return;\n }\n }\n};\n\nconst isValidSoftBreak = (): boolean => {\n // This function will return false if there are no nodes after soft break.\n //\n // In contenteditable, Shift+Enter will insert soft break. \\n in Chrome, <br/> in Firefox. Safari doesn't insert soft break.\n // And \\n or <br/> has a special role that represents empty block in contenteditable.\n // We have to distinguish real soft breaks from empty blocks.\n //\n // There are many possible markups for soft break ([] means text node):\n // <div>[\\n][abc]</div> Shift+Enter at start of line in Chrome\n // <div><br/>[abc]</div> Shift+Enter at start of line in Firefox\n // <div>[ab][\\n][c]</div> Shift+Enter at mid of line in Chrome\n // <div>[ab]<br/>[c]</div> Shift+Enter at mid of line in Firefox\n // <div>[abc][\\n][\\n]</div> Shift+Enter at end of line in Chrome\n // <div>[abc]<br/><br/></div> Shift+Enter at end of line in Firefox\n // <div>[\\n]<br/></div> Shift+Enter at empty line in Chrome\n // <div><br/><br/></div> Shift+Enter at empty line in Firefox\n //\n // And these do not include soft breaks:\n // <div><br/></div> empty line\n // <div>[a]<br/></div> type on empty line in Firefox\n const parent = node!.parentNode!;\n return parse(() => {\n while (nextNode()) {\n if (readToken()) {\n return true;\n }\n if (!parent.contains(node)) {\n break;\n }\n }\n return false;\n });\n};\n\n/**\n * @internal\n */\nexport const readNext = (): Exclude<TokenType, typeof TOKEN_NULL> | void => {\n while (true) {\n if (readToken() === TOKEN_VOID) {\n const current = node!;\n // don't use TreeWalker.nextSibling() to support case like <body><p><a><img /></a></p><p>hello</p></body>\n while (nextNode()) {\n if (!current.contains(node)) {\n break;\n }\n }\n } else {\n nextNode();\n }\n\n if (!node) {\n break;\n }\n\n const t = readToken();\n if (t) {\n return t;\n }\n }\n};\n\n/**\n * @internal\n */\nexport const parse = <T>(\n scopeFn: () => T,\n root?: Node,\n newConfig?: ParserConfig,\n startNode?: Node,\n): T => {\n const prevConfig = config;\n const prevWalker = walker;\n const prevNode = node;\n const prevToken = _token;\n try {\n if (newConfig) {\n config = newConfig;\n walker = config._document.createTreeWalker(\n root!,\n SHOW_TEXT | SHOW_ELEMENT,\n );\n }\n if (startNode) {\n walker!.currentNode = node = startNode;\n }\n return scopeFn();\n } finally {\n config = prevConfig;\n walker = prevWalker;\n node = prevNode;\n _token = prevToken;\n if (walker && prevNode) {\n walker.currentNode = prevNode;\n }\n }\n};\n","const SINGLE_LINE_CONTAINER_NAMES = new Set([\n // https://w3c.github.io/editing/docs/execCommand/#single-line-container\n // non-list single-line container\n \"DIV\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"P\",\n \"PRE\",\n // list single-line container\n \"LI\",\n \"DT\",\n \"DD\",\n\n // other elements for HTML paste\n \"TR\",\n]);\n\n/**\n * @internal\n */\nexport const defaultIsBlockNode = (node: Element): boolean => {\n return SINGLE_LINE_CONTAINER_NAMES.has(node.tagName);\n};\n\n// https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories\n// https://html.spec.whatwg.org/multipage/dom.html#embedded-content-category\nconst EMBEDDED_CONTENT_TAG_NAMES = new Set([\n \"EMBED\",\n \"IMG\",\n \"PICTURE\",\n \"AUDIO\",\n \"VIDEO\",\n \"SVG\",\n \"CANVAS\",\n \"MATH\",\n \"IFRAME\",\n \"OBJECT\",\n]);\n\n/**\n * @internal\n */\nexport const defaultIsVoidNode = (node: Element): boolean => {\n return (\n (node as HTMLElement).contentEditable === \"false\" ||\n EMBEDDED_CONTENT_TAG_NAMES.has(node.tagName)\n );\n};\n","import {\n type TokenType,\n parse,\n getNodeSize,\n getDomNode,\n isElementNode,\n TOKEN_TEXT,\n TOKEN_VOID,\n TOKEN_SOFT_BREAK,\n TOKEN_BLOCK,\n type ParserConfig,\n nextBlock,\n readNext as next,\n parentBlock,\n readToken,\n} from \"./parser.js\";\nimport { comparePosition } from \"../doc/position.js\";\nimport type {\n Position,\n InlineNode,\n SelectionSnapshot,\n PositionRange,\n Fragment,\n TextNode,\n Path,\n} from \"../doc/types.js\";\nimport { min } from \"../utils.js\";\n\nexport { defaultIsBlockNode, defaultIsVoidNode } from \"./default.js\";\n\n// const DOCUMENT_POSITION_DISCONNECTED = 0x01;\nconst DOCUMENT_POSITION_PRECEDING = 0x02;\nconst DOCUMENT_POSITION_FOLLOWING = 0x04;\n// const DOCUMENT_POSITION_CONTAINS = 0x08;\nconst DOCUMENT_POSITION_CONTAINED_BY = 0x10;\n// const DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;\n\nconst compareDomPosition = (a: Node, b: Node) => a.compareDocumentPosition(b);\n\n/**\n * @internal\n */\nexport const getCurrentDocument = (node: Element): Document =>\n node.ownerDocument;\n\n/**\n * @internal\n */\nexport const getDOMSelection = (element: Element): Selection => {\n // TODO support ShadowRoot\n return getCurrentDocument(element).getSelection()!;\n};\n\n/**\n * @internal\n */\nexport const getSelectionRangeInEditor = (\n selection: Selection,\n root: Element,\n): Range | void => {\n if (selection.rangeCount) {\n const range = selection.getRangeAt(0);\n if (root.contains(range.commonAncestorContainer)) {\n return range;\n }\n }\n};\n\nconst setRangeToSelection = (\n root: Element,\n range: Range,\n force: boolean | undefined,\n backward?: boolean,\n): boolean => {\n const selection = getDOMSelection(root);\n if (!force && !getSelectionRangeInEditor(selection, root)) {\n return false;\n }\n selection.removeAllRanges();\n selection.addRange(range);\n if (backward) {\n selection.collapseToEnd();\n selection.extend(range.startContainer, range.startOffset);\n }\n return true;\n};\n\n/**\n * @internal\n */\nexport const setSelectionToDOM = (\n document: Document,\n root: Element,\n [anchor, focus]: SelectionSnapshot,\n config: ParserConfig,\n force?: boolean,\n): boolean => {\n const posDiff = comparePosition(anchor, focus);\n const isCollapsed = posDiff === 0;\n const backward = posDiff === 1;\n const start = backward ? focus : anchor;\n const end = backward ? anchor : focus;\n // special path for empty content with empty selection, necessary for placeholder\n if (\n start[0].length === 0 &&\n start[1] === 0 &&\n isCollapsed &&\n !root.hasChildNodes()\n ) {\n const range = document.createRange();\n range.setStart(root, 0);\n range.setEnd(root, 0);\n\n return setRangeToSelection(root, range, force);\n }\n\n const domStart = findPosition(root, start, config);\n if (!domStart) {\n return false;\n }\n\n const domEnd = isCollapsed ? domStart : findPosition(root, end, config);\n if (!domEnd) {\n return false;\n }\n\n // https://w3c.github.io/contentEditable/#dfn-legal-caret-positions\n const range = document.createRange();\n\n const [startNode, startOffset] = domStart;\n const [endNode, endOffset] = domEnd;\n\n // embed or br\n if (isElementNode(startNode)) {\n if (startOffset < 1) {\n range.setStartBefore(startNode);\n } else {\n range.setStartAfter(startNode);\n }\n } else {\n range.setStart(startNode, startOffset);\n }\n\n // embed or br\n if (isElementNode(endNode)) {\n if (endOffset < 1) {\n range.setEndBefore(endNode);\n } else {\n range.setEndAfter(endNode);\n }\n } else {\n range.setEnd(endNode, endOffset);\n }\n\n return setRangeToSelection(root, range, force, backward);\n};\n\ntype DOMPosition = [node: Text | Element, offsetAtNode: number];\n\nconst findPosition = (\n root: Element,\n [path, offset]: Position,\n config: ParserConfig,\n): DOMPosition | undefined => {\n return parse(\n (): DOMPosition | undefined => {\n let pathIndex = 0;\n let type: TokenType | void;\n while ((type = next())) {\n if (type === TOKEN_BLOCK) {\n if (pathIndex < path.length) {\n for (\n let blockIndex = path[pathIndex++]!;\n blockIndex > 0;\n blockIndex--\n ) {\n nextBlock();\n }\n }\n } else {\n const size = getNodeSize();\n if (offset <= size) {\n return [getDomNode<typeof type>(), offset];\n }\n offset -= size;\n }\n }\n return;\n },\n root,\n config,\n );\n};\n\nconst serializePosition = (\n root: Element,\n node: Node,\n offsetAtNode: number,\n config: ParserConfig,\n): Position => {\n let excludeEnd = true;\n if (root === node && !node.hasChildNodes()) {\n // for placeholder\n return [[], 0];\n }\n\n if (isElementNode(node) && !config._isVoid(node) && node.hasChildNodes()) {\n // If start/end of Range is not selectable node, it will have offset relative to its parent\n // 0 1 2 3\n // <div>aaaa<img /><span>bbbb</span></div>\n //\n // And there are other possible cases:\n // - Selection with Ctrl+A in Firefox\n // - getTargetRanges() when deleting contenteditable:false in Firefox\n // - Selection.setBaseAndExtent(element, 0, element, 0)\n const index = min(offsetAtNode, node.childNodes.length - 1);\n node = node.childNodes[index]!;\n excludeEnd = index === offsetAtNode;\n offsetAtNode = 0;\n }\n\n return parse(\n () => {\n if (readToken() !== TOKEN_BLOCK) {\n parentBlock();\n }\n\n const path = parse((): Path => {\n const blocks: Element[] = [];\n // TODO improve type\n let block: Element | null;\n while ((block = getDomNode<typeof TOKEN_BLOCK>()) && block !== root) {\n blocks.unshift(block);\n parentBlock();\n }\n\n if (!blocks.length) {\n return [];\n }\n\n let i = 0;\n let sib: Element = blocks[blocks.length - 1]!;\n while ((sib = sib.previousElementSibling!)) {\n i++;\n }\n return [i];\n });\n\n let offset = 0;\n while (next()) {\n const comp = compareDomPosition(node, getDomNode());\n if (\n comp === 0 || // same object\n comp & DOCUMENT_POSITION_CONTAINED_BY\n ) {\n if (excludeEnd) {\n break;\n }\n } else if (comp & DOCUMENT_POSITION_FOLLOWING) {\n break;\n }\n offset += getNodeSize();\n }\n return [path, offset + offsetAtNode];\n },\n root,\n config,\n node,\n );\n};\n\n/**\n * @internal\n */\nexport const serializeRange = (\n root: Element,\n config: ParserConfig,\n { startOffset, startContainer, endOffset, endContainer }: AbstractRange,\n): PositionRange => {\n const start = serializePosition(root, startContainer, startOffset, config);\n return [\n start,\n startContainer === endContainer && startOffset === endOffset\n ? start\n : serializePosition(root, endContainer, endOffset, config),\n ];\n};\n\n/**\n * @internal\n */\nexport const getEmptySelectionSnapshot = (): SelectionSnapshot => {\n return [\n [[], 0],\n [[], 0],\n ];\n};\n\n/**\n * @internal\n */\nexport const takeSelectionSnapshot = (\n root: Element,\n config: ParserConfig,\n): SelectionSnapshot => {\n const selection = getDOMSelection(root);\n const domRange = getSelectionRangeInEditor(selection, root);\n if (!domRange) {\n return getEmptySelectionSnapshot();\n }\n\n const range = serializeRange(root, config, domRange);\n const comp = compareDomPosition(selection.anchorNode!, selection.focusNode!);\n\n // https://stackoverflow.com/questions/9180405/detect-direction-of-user-selection-with-javascript\n return (\n comp === 0 // same object\n ? selection.anchorOffset > selection.focusOffset\n : comp & DOCUMENT_POSITION_PRECEDING\n )\n ? [range[1], range[0]]\n : range;\n};\n\n/**\n * @internal\n */\nexport const domToFragment = (\n root: Node,\n config: ParserConfig,\n serializeText: (text: string) => TextNode,\n serializeVoid: (node: Element) => InlineNode | void,\n): Fragment => {\n return parse(\n () => {\n let type: TokenType | void;\n let row: InlineNode[] | null = null;\n let text = \"\";\n let hasContent = false;\n\n const rows: InlineNode[][] = [];\n\n const completeText = () => {\n if (text) {\n if (!row) {\n row = [];\n }\n row.push(serializeText(text));\n text = \"\";\n }\n };\n const completeRow = () => {\n completeText();\n if (!row && hasContent) {\n row = [];\n }\n if (row) {\n rows.push(row);\n }\n row = null;\n hasContent = false;\n };\n\n while ((type = next())) {\n if (type === TOKEN_BLOCK) {\n completeRow();\n } else {\n hasContent = true;\n\n if (type === TOKEN_TEXT) {\n text += getDomNode<typeof type>().data;\n } else if (type === TOKEN_VOID) {\n completeText();\n const docNode = serializeVoid(getDomNode<typeof type>());\n if (docNode) {\n row!.push(docNode);\n }\n } else if (type === TOKEN_SOFT_BREAK) {\n completeRow();\n }\n }\n }\n completeRow();\n\n if (!rows.length) {\n rows.push([]);\n }\n\n return rows;\n },\n root,\n config,\n );\n};\n\n/**\n * @internal\n */\nexport const getPointedCaretPosition = (\n document: Document,\n root: Element,\n { clientX, clientY }: MouseEvent,\n config: ParserConfig,\n): Position | void => {\n // https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint\n // https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint\n // caretPositionFromPoint caretRangeFromPoint\n // Chrome: 128 4\n // Firefox: 20 -\n // Safari: 26.2 5\n if (document.caretPositionFromPoint) {\n const position = document.caretPositionFromPoint(clientX, clientY);\n if (position) {\n return serializePosition(\n root,\n position.offsetNode,\n position.offset,\n config,\n );\n }\n } else if (document.caretRangeFromPoint) {\n const range = document.caretRangeFromPoint(clientX, clientY);\n if (range) {\n return serializePosition(\n root,\n range.startContainer,\n range.startOffset,\n config,\n );\n }\n }\n};\n","import type { DocNode, InlineNode, InferNode } from \"../../doc/types.js\";\nimport { docToString } from \"../../doc/utils.js\";\nimport type { CopyExtension } from \"./types.js\";\n\n/**\n * An extension to handle copying to plain text.\n */\nexport const plainCopy = <T extends DocNode>(\n serializer?: (node: InferNode<T>) => string,\n): CopyExtension => {\n return (dataTransfer, data) => {\n dataTransfer.setData(\n \"text/plain\",\n docToString(\n { children: data },\n serializer as ((node: InlineNode) => string) | undefined,\n ),\n );\n };\n};\n","import { getDOMSelection, getSelectionRangeInEditor } from \"../../dom/index.js\";\nimport type { CopyExtension } from \"./types.js\";\n\n/**\n * An extension to handle copying to HTML.\n */\nexport const htmlCopy = (): CopyExtension => {\n return (dataTransfer, _, element) => {\n const wrapper = document.createElement(\"div\");\n wrapper.appendChild(\n // DOM range must exist here\n getSelectionRangeInEditor(\n getDOMSelection(element),\n element,\n )!.cloneContents(),\n );\n dataTransfer.setData(\"text/html\", wrapper.innerHTML);\n };\n};\n","/**\n * @internal\n */\nexport const INTERNAL_COPY_KEY = \"application/x-edix-editor\";\n","import { INTERNAL_COPY_KEY } from \"../utils.js\";\nimport type { CopyExtension } from \"./types.js\";\n\n/**\n * An extension to handle copying to edix editor instance.\n */\nexport const internalCopy = ({\n key = INTERNAL_COPY_KEY,\n}: {\n key?: string;\n} = {}): CopyExtension => {\n return (dataTransfer, data) => {\n dataTransfer.setData(key, JSON.stringify(data));\n };\n};\n","import type { InlineNode } from \"../../doc/types.js\";\nimport type { PasteExtension } from \"./types.js\";\n\n/**\n * An extension to handle pasting / dropping from File.\n */\nexport const filePaste = (\n handlerByMime: Record<string, (file: File) => InlineNode>,\n): PasteExtension => {\n return (dataTransfer) => {\n for (const item of dataTransfer.items) {\n if (item.kind === \"file\") {\n const mapper = handlerByMime[item.type];\n if (mapper) {\n const file = item.getAsFile();\n if (file) {\n return [[mapper(file)]];\n }\n }\n }\n }\n return null;\n };\n};\n","import type { PasteExtension } from \"./types.js\";\n\n/**\n * An extension to handle pasting / dropping from plain text.\n */\nexport const plainPaste = (): PasteExtension => {\n return (dataTransfer) => {\n return dataTransfer.getData(\"text/plain\");\n };\n};\n","import type { DocNode, InferNode, TextNode } from \"../../doc/types.js\";\nimport { domToFragment } from \"../../dom/index.js\";\nimport { isCommentNode } from \"../../dom/parser.js\";\nimport type { PasteExtension } from \"./types.js\";\n\n/**\n * An extension to handle pasting / dropping from HTML.\n */\nexport const htmlPaste = <T extends DocNode>(\n serializeText: (t: string) => Extract<InferNode<T>, TextNode>,\n serializers: ((\n node: HTMLElement,\n ) => Exclude<InferNode<T>, TextNode> | void)[] = [],\n): PasteExtension => {\n return (dataTransfer, config) => {\n const html = dataTransfer.getData(\"text/html\");\n if (html) {\n let dom: Node = new DOMParser().parseFromString(html, \"text/html\").body;\n let isWindowsCopy = false;\n // https://github.com/w3c/clipboard-apis/issues/193\n for (const n of [...dom.childNodes]) {\n if (isCommentNode(n)) {\n if (n.data === \"StartFragment\") {\n isWindowsCopy = true;\n dom = new DocumentFragment();\n } else if (n.data === \"EndFragment\") {\n isWindowsCopy = false;\n }\n } else if (isWindowsCopy) {\n dom.appendChild(n);\n }\n }\n\n // TODO customizable dom to standard schema and validate\n return domToFragment(dom, config, serializeText, (n) => {\n for (const s of serializers) {\n const node = s(n as HTMLElement);\n if (node) {\n return node;\n }\n }\n return;\n });\n }\n return null;\n };\n};\n","import { INTERNAL_COPY_KEY } from \"../utils.js\";\nimport type { PasteExtension } from \"./types.js\";\n\n/**\n * An extension to handle pasting / dropping from edix editor instance.\n */\nexport const internalPaste = ({\n key = INTERNAL_COPY_KEY,\n}: { key?: string } = {}): PasteExtension => {\n return (dataTransfer) => {\n try {\n return JSON.parse(dataTransfer.getData(key));\n } catch (e) {\n return null;\n }\n };\n};\n","export type KeyboardHandler = (keyboard: KeyboardEvent) => boolean | void;\n\n/**\n * TODO\n */\nexport const hotkey = (\n key: string,\n cb: (e: KeyboardEvent) => void,\n {\n mod,\n shift = false,\n alt = false,\n }: {\n mod?: boolean;\n // ctrl?: boolean;\n // meta?: boolean;\n shift?: boolean;\n alt?: boolean;\n // phase?: 'down' | 'up';\n } = {},\n): KeyboardHandler => {\n key = key.toLowerCase();\n\n return (e): boolean | void => {\n // TODO should we handle it e.code?\n if (e.key.toLowerCase() === key) {\n if (\n // TODO detect OS\n (!mod || e.ctrlKey || e.metaKey) &&\n shift === e.shiftKey &&\n alt === e.altKey\n ) {\n cb(e);\n return true;\n }\n }\n };\n};\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { createHistory } from \"./history.js\";\nimport {\n getCurrentDocument,\n takeSelectionSnapshot,\n setSelectionToDOM,\n getEmptySelectionSnapshot,\n getPointedCaretPosition,\n defaultIsBlockNode,\n defaultIsVoidNode,\n serializeRange,\n} from \"./dom/index.js\";\nimport { createMutationObserver } from \"./mutation.js\";\nimport type { DocNode, Fragment, SelectionSnapshot } from \"./doc/types.js\";\nimport { is, isFunction, isString, microtask } from \"./utils.js\";\nimport type { EditorCommand } from \"./commands.js\";\nimport {\n applyOperation,\n Transaction,\n sliceDoc,\n isTextNode,\n type Operation,\n isUnsafeOperation,\n isValidSelection,\n} from \"./doc/edit.js\";\nimport type { ParserConfig } from \"./dom/parser.js\";\nimport { comparePosition, toRange } from \"./doc/position.js\";\nimport type { EditorPlugin } from \"./plugins/types.js\";\nimport {\n type CopyExtension,\n type PasteExtension,\n plainCopy,\n plainPaste,\n} from \"./extensions/index.js\";\nimport { hotkey, type KeyboardHandler } from \"./hotkey.js\";\n\nconst noop = () => {};\n\n/**\n * https://www.w3.org/TR/input-events-1/#interface-InputEvent-Attributes\n */\ntype InputType =\n | \"insertText\" // insert typed plain text\n | \"insertReplacementText\" // replace existing text by means of a spell checker, auto-correct or similar\n | \"insertLineBreak\" // insert a line break\n | \"insertParagraph\" // insert a paragraph break\n | \"insertOrderedList\" // insert a numbered list\n | \"insertUnorderedList\" // insert a bulleted list\n | \"insertHorizontalRule\" // insert a horizontal rule\n | \"insertFromYank\" // replace the current selection with content stored in a kill buffer\n | \"insertFromDrop\" // insert content into the DOM by means of drop\n | \"insertFromPaste\" // paste\n | \"insertFromPasteAsQuotation\" // paste content as a quotation\n | \"insertTranspose\" // transpose the last two characters that were entered\n | \"insertCompositionText\" // replace the current composition string\n | \"insertLink\" // insert a link\n | \"deleteWordBackward\" // delete a word directly before the caret position\n | \"deleteWordForward\" // delete a word directly after the caret position\n | \"deleteSoftLineBackward\" // delete from the caret to the nearest visual line break before the caret position\n | \"deleteSoftLineForward\" // delete from the caret to the nearest visual line break after the caret position\n | \"deleteEntireSoftLine\" // delete from to the nearest visual line break before the caret position to the nearest visual line break after the caret position\n | \"deleteHardLineBackward\" // delete from the caret to the nearest beginning of a block element or br element before the caret position\n | \"deleteHardLineForward\" // delete from the caret to the nearest end of a block element or br element after the caret position\n | \"deleteByDrag\" // remove content from the DOM by means of drag\n | \"deleteByCut\" // remove the current selection as part of a cut\n | \"deleteContent\" // delete the selection without specifying the direction of the deletion and this intention is not covered by another inputType\n | \"deleteContentBackward\" // delete the content directly before the caret position and this intention is not covered by another inputType or delete the selection with the selection collapsing to its start after the deletion\n | \"deleteContentForward\" // delete the content directly after the caret position and this intention is not covered by another inputType or delete the selection with the selection collapsing to its end after the deletion\n | \"historyUndo\" // undo the last editing action\n | \"historyRedo\" // to redo the last undone editing action\n | \"formatBold\" // initiate bold text\n | \"formatItalic\" // initiate italic text\n | \"formatUnderline\" // initiate underline text\n | \"formatStrikeThrough\" // initiate stricken through text\n | \"formatSuperscript\" // initiate superscript text\n | \"formatSubscript\" // initiate subscript text\n | \"formatJustifyFull\" // make the current selection fully justified\n | \"formatJustifyCenter\" // center align the current selection\n | \"formatJustifyRight\" // right align the current selection\n | \"formatJustifyLeft\" // left align the current selection\n | \"formatIndent\" // indent the current selection\n | \"formatOutdent\" // outdent the current selection\n | \"formatRemove\" // remove all formatting from the current selection\n | \"formatSetBlockTextDirection\" // set the text block direction\n | \"formatSetInlineTextDirection\" // set the text inline direction\n | \"formatBackColor\" // change the background color\n | \"formatFontColor\" // change the font color\n | \"formatFontName\" // change the font-family\n // Legacy events older Chrome/Safari may dispatch\n // https://github.com/w3c/input-events/pull/122\n | \"deleteCompositionText\"\n | \"deleteByComposition\"\n | \"insertFromComposition\";\n\n/**\n * Options of {@link createEditor}.\n */\nexport interface EditorOptions<\n T extends DocNode,\n S extends StandardSchemaV1<T, T> | void = void,\n> {\n /**\n * Optional [Standard Schema](https://github.com/standard-schema/standard-schema) to validate unsafe edits.\n */\n schema?: S;\n /**\n * Initial document.\n */\n doc: T;\n /**\n * The state editable or not.\n */\n readonly?: boolean;\n /**\n * TODO\n */\n plugins?: EditorPlugin[];\n /**\n * Functions to handle keyboard events.\n *\n * Return `true` if you want to stop propagation.\n */\n keyboard?: KeyboardHandler[];\n /**\n * Functions to handle copy events\n * @default [plainCopy()]\n */\n copy?: [CopyExtension, ...rest: CopyExtension[]];\n /**\n * Functions to handle paste / drop events\n * @default [plainPaste()]\n */\n paste?: [PasteExtension, ...rest: PasteExtension[]];\n /**\n * TODO\n */\n isBlock?: (node: HTMLElement) => boolean;\n /**\n * Automatically scroll the mounted element to keep the caret visible\n * after document changes. Scroll is coalesced via rAF for zero\n * synchronous layout cost during input handling.\n */\n autoScroll?: boolean;\n /**\n * Callback invoked when document changes.\n */\n onChange: (doc: T) => void;\n /**\n * Callback invoked when errors happen.\n *\n * @default console.error\n */\n onError?: (message: string) => void;\n}\n\n/**\n * The editor instance.\n */\nexport interface Editor<T extends DocNode = DocNode> {\n readonly doc: T;\n /**\n * Whether the document is empty (no text content, no void nodes).\n * Recomputed once per commit — O(1) read.\n */\n readonly isEmpty: boolean;\n selection: SelectionSnapshot;\n /**\n * The getter/setter for the editor's read-only state.\n * `true` to read-only. `false` to editable.\n */\n readonly: boolean;\n /**\n * Enable/disable auto-scroll after document changes.\n */\n autoScroll: boolean;\n /**\n * Dispatches editing operations.\n * @param tr {@link Transaction} or {@link EditorCommand}\n * @param args arguments of {@link EditorCommand}\n * @param immediate If true, flushes queued operations immediately.\n */\n apply(tr: Transaction, immediate?: boolean): this;\n apply<A extends unknown[]>(fn: EditorCommand<A, T>, ...args: A): this;\n /**\n * A function to make DOM editable.\n * @returns A function to stop subscribing DOM changes and restores previous DOM state.\n */\n input: (element: HTMLElement) => () => void;\n}\n\n/**\n * A function to initialize {@link Editor}.\n */\nexport const createEditor = <\n T extends DocNode,\n S extends StandardSchemaV1<T, T> | void = void,\n>({\n doc,\n readonly = false,\n schema,\n plugins,\n keyboard,\n copy: copyExtensions = [plainCopy()],\n paste: pasteExtensions = [plainPaste()],\n isBlock = defaultIsBlockNode,\n autoScroll: _autoScroll = false,\n onChange,\n onError = console.error,\n}: EditorOptions<T, S>): Editor<T> => {\n let selection: SelectionSnapshot = getEmptySelectionSnapshot();\n let setContentEditable: () => void = noop;\n\n // Auto-scroll state — coalesced via rAF, zero sync layout cost\n let _mountedEl: HTMLElement | null = null;\n let _scrollRAF = 0;\n const scheduleScroll = () => {\n if (_mountedEl && !_scrollRAF) {\n const el = _mountedEl;\n _scrollRAF = requestAnimationFrame(() => {\n _scrollRAF = 0;\n el.scrollTop = el.scrollHeight;\n });\n }\n };\n\n const computeEmpty = (d: DocNode): boolean => {\n for (const line of d.children) {\n for (const node of line) {\n if (!isTextNode(node) || node.text.length > 0) return false;\n }\n }\n return true;\n };\n let _empty = computeEmpty(doc);\n\n const validate = (value: T, onError: (m: string) => void): boolean => {\n if (!schema) {\n onError(\n \"An unsafe operation was detected. We recommend using schema option.\",\n );\n return true;\n }\n const result = schema[\"~standard\"].validate(value);\n if (result instanceof Promise) {\n onError(\"async validate is not supported.\");\n } else if (result.issues) {\n onError(result.issues.map((i) => i.message).join(\"\\n\"));\n } else {\n return true;\n }\n return false;\n };\n\n let initialError: string | undefined;\n if (\n !validate(doc, (m) => {\n initialError = m;\n }) &&\n initialError\n ) {\n throw new Error(initialError);\n }\n\n const keydownHandlers: KeyboardHandler[] = [\n hotkey(\n \"z\",\n () => {\n if (!readonly) {\n const nextHistory = history.undo();\n if (nextHistory) {\n doc = nextHistory[0];\n _empty = computeEmpty(doc);\n updateSelection(nextHistory[1]);\n onChange(doc);\n if (_autoScroll) scheduleScroll();\n }\n }\n },\n { mod: true },\n ),\n hotkey(\n \"z\",\n () => {\n if (!readonly) {\n const nextHistory = history.redo();\n if (nextHistory) {\n doc = nextHistory[0];\n _empty = computeEmpty(doc);\n updateSelection(nextHistory[1]);\n onChange(doc);\n if (_autoScroll) scheduleScroll();\n }\n }\n },\n { mod: true, shift: true },\n ),\n ];\n if (keyboard) {\n keydownHandlers.push(...keyboard);\n }\n\n const applyHooks: Exclude<EditorPlugin[\"apply\"], undefined>[] = [];\n const mountHooks: Exclude<EditorPlugin[\"mount\"], undefined>[] = [];\n if (plugins) {\n plugins.forEach(({ apply, mount }) => {\n if (apply) {\n applyHooks.push(apply);\n }\n if (mount) {\n mountHooks.push(mount);\n }\n });\n plugins = undefined;\n }\n\n const transactions: Transaction[] = [];\n const apply = (tr: Transaction, immediate?: boolean) => {\n if (!readonly) {\n const shouldFlush = !transactions.length;\n transactions.unshift(tr);\n if (immediate) {\n commit();\n } else if (shouldFlush) {\n microtask(commit);\n }\n }\n };\n\n const commit = () => {\n if (transactions.length) {\n const currentDoc = doc;\n const ops: Operation[] = [];\n const length = applyHooks.length;\n\n let tr: Transaction | undefined;\n while ((tr = transactions.pop())) {\n for (let op of tr.ops) {\n let index = 0;\n\n const dispatch = () => {\n if (index < length) {\n const i = index;\n applyHooks[index]!(op, next);\n if (i === index) {\n next();\n }\n } else if (index === length) {\n index++;\n\n try {\n const [nextDoc, nextSelection] = applyOperation(\n doc,\n selection,\n op,\n );\n if (!isUnsafeOperation(op) || validate(nextDoc, onError)) {\n doc = nextDoc;\n selection = nextSelection;\n ops.push(op);\n }\n } catch (e) {\n // rollback\n onError(\"rollback operation: \" + e);\n }\n }\n };\n\n const next = (o?: Operation): void => {\n if (o) {\n op = o;\n }\n index++;\n dispatch();\n };\n\n dispatch();\n }\n if (tr.selection) {\n updateSelection(tr.selection);\n }\n }\n\n if (!is(currentDoc, doc)) {\n _empty = computeEmpty(doc);\n history.change(doc, ops);\n onChange(doc);\n if (_autoScroll) scheduleScroll();\n }\n }\n };\n\n const updateSelection = (s: SelectionSnapshot) => {\n if (isValidSelection(doc, s)) {\n selection = s;\n }\n };\n\n const editor: Editor<T> = {\n get doc() {\n return doc;\n },\n get isEmpty() {\n return _empty;\n },\n get selection() {\n return selection;\n },\n set selection(value) {\n updateSelection(value);\n },\n get readonly() {\n return readonly;\n },\n set readonly(value) {\n readonly = value;\n setContentEditable();\n },\n get autoScroll() {\n return _autoScroll;\n },\n set autoScroll(value) {\n _autoScroll = value;\n },\n apply: (tr: Transaction | EditorCommand<any, T>, ...args: unknown[]) => {\n if (isFunction(tr)) {\n tr.call(editor, ...args);\n } else {\n apply(tr, args[0] as boolean | undefined);\n }\n return editor;\n },\n input: (element) => {\n _mountedEl = element;\n\n if (\n !(window.InputEvent && isFunction(InputEvent.prototype.getTargetRanges))\n ) {\n onError(\"beforeinput event is not supported.\");\n return noop;\n }\n\n // https://w3c.github.io/contentEditable/\n // https://w3c.github.io/editing/docs/execCommand/\n // https://w3c.github.io/selection-api/\n const {\n contentEditable: prevContentEditable,\n role: prevRole,\n ariaMultiLine: prevAriaMultiLine,\n ariaReadOnly: prevAriaReadOnly,\n } = element;\n const prevWhiteSpace = element.style.whiteSpace;\n\n element.role = \"textbox\";\n // https://html.spec.whatwg.org/multipage/interaction.html#best-practices-for-in-page-editors\n element.style.whiteSpace = \"pre-wrap\";\n element.ariaMultiLine = \"true\";\n\n let disposed = false;\n let selectionReverted = false;\n let inputTransaction: [Transaction, SelectionSnapshot] | null = null;\n let isComposing = false;\n let hasFocus = false;\n let isDragging = false;\n\n const document = getCurrentDocument(element);\n\n const parserConfig: ParserConfig = {\n _document: document,\n _isBlock: isBlock as ParserConfig[\"_isBlock\"],\n _isVoid: defaultIsVoidNode,\n };\n\n setContentEditable = () => {\n element.contentEditable = readonly ? \"false\" : \"true\";\n element.ariaReadOnly = readonly ? \"true\" : null;\n };\n\n setContentEditable();\n\n const copy = (dataTransfer: DataTransfer, fragment: Fragment) => {\n for (const ex of copyExtensions) {\n ex(dataTransfer, fragment, element);\n }\n };\n const paste = (dataTransfer: DataTransfer): string | Fragment | void => {\n for (const ex of pasteExtensions) {\n const pasted = ex(dataTransfer, parserConfig);\n if (pasted) {\n return pasted;\n }\n }\n onError(\"failed to serialize pasted data\");\n };\n\n const observer = createMutationObserver(element, () => {\n // TODO optimize\n // Mutation to selected DOM may change selection, so restore it.\n setSelectionToDOM(document, element, selection, parserConfig);\n });\n\n const syncSelection = () => {\n updateSelection(takeSelectionSnapshot(element, parserConfig));\n };\n\n const flushInput = () => {\n const queue = observer._flush();\n\n observer._record(false);\n\n if (queue.length) {\n // Revert DOM\n let m: MutationRecord | undefined;\n while ((m = queue.pop())) {\n if (m.type === \"childList\") {\n const { target, removedNodes, addedNodes, nextSibling } = m;\n for (let i = removedNodes.length - 1; i >= 0; i--) {\n target.insertBefore(removedNodes[i]!, nextSibling);\n }\n for (let i = addedNodes.length - 1; i >= 0; i--) {\n target.removeChild(addedNodes[i]!);\n }\n } else {\n (m.target as CharacterData).nodeValue = m.oldValue!;\n }\n }\n observer._flush();\n\n // Restore previous selection\n // Updating selection may schedule the next selectionchange event\n // It should be ignored especially in firefox not to confuse editor state\n selectionReverted = setSelectionToDOM(\n document,\n element,\n selection,\n parserConfig,\n true,\n );\n }\n\n if (inputTransaction) {\n updateSelection(inputTransaction[1]);\n apply(inputTransaction[0]);\n inputTransaction = null;\n }\n isComposing = false;\n };\n\n // spec compliant: keydown -> beforeinput -> input (-> keyup)\n // Safari (IME) : beforeinput -> input -> keydown (-> keyup)\n // https://w3c.github.io/uievents/#events-keyboard-event-order\n // https://bugs.webkit.org/show_bug.cgi?id=165004\n const onKeyDown = (e: KeyboardEvent) => {\n if (isComposing) return;\n\n for (const handler of keydownHandlers) {\n if (handler(e)) {\n e.preventDefault();\n observer._record(false);\n return;\n }\n }\n };\n\n const onInput = () => {\n if (!isComposing) {\n flushInput();\n }\n };\n const onBeforeInput = (e: InputEvent) => {\n e.preventDefault();\n\n const inputType = e.inputType as InputType;\n\n if (inputType.startsWith(\"format\")) {\n // Ignore format inputs from document.execCommand() or shortcuts like mod+b.\n return;\n }\n if (inputType === \"historyUndo\" || inputType === \"historyRedo\") {\n // Cancel for now.\n return;\n }\n\n if (isComposing) {\n // Unfortunately, input events related to composition are not cancellable.\n // So we record mutations to DOM and revert them after composition ended.\n observer._record(true);\n } else {\n syncSelection();\n }\n\n const domRange = e.getTargetRanges()[0];\n if (domRange) {\n // Read input\n const range = serializeRange(element, parserConfig, domRange);\n let data =\n inputType === \"insertParagraph\" || inputType === \"insertLineBreak\"\n ? \"\\n\"\n : e.data;\n if (data == null) {\n const dataTransfer = e.dataTransfer;\n if (dataTransfer) {\n // In some cases (e.g. insertReplacementText), dataTransfer contains text.\n data = dataTransfer.getData(\"text/plain\");\n }\n }\n\n let tr: Transaction;\n if (!inputTransaction) {\n inputTransaction = [new Transaction(), selection];\n }\n tr = inputTransaction[0];\n if (comparePosition(...range) !== 0) {\n // replace or delete\n tr.delete(...range);\n }\n if (data) {\n // replace or insert\n tr.insertText(range[0], data);\n }\n }\n\n if (!isComposing) {\n flushInput();\n }\n };\n const onCompositionStart = () => {\n if (!isComposing) {\n syncSelection();\n }\n isComposing = true;\n };\n const onCompositionEnd = () => {\n flushInput();\n };\n\n const onFocus = () => {\n hasFocus = true;\n syncSelection();\n };\n const onBlur = () => {\n hasFocus = false;\n };\n\n const onSelectionChange = () => {\n if (selectionReverted) {\n selectionReverted = false;\n return;\n }\n // Safari may dispatch selectionchange event after dragstart\n if (hasFocus && !isComposing && !isDragging) {\n syncSelection();\n }\n };\n\n const copySelected = (dataTransfer: DataTransfer) => {\n syncSelection();\n if (comparePosition(...selection) !== 0) {\n copy(dataTransfer, sliceDoc(doc, ...toRange(selection)));\n }\n };\n\n const onCopy = (e: ClipboardEvent) => {\n e.preventDefault();\n copySelected(e.clipboardData!);\n };\n const onCut = (e: ClipboardEvent) => {\n e.preventDefault();\n if (!readonly) {\n copySelected(e.clipboardData!);\n apply(new Transaction().delete(...toRange(selection)));\n }\n };\n const onPaste = (e: ClipboardEvent) => {\n e.preventDefault();\n const pasted = paste(e.clipboardData!);\n if (pasted) {\n const [start, end] = toRange(selection);\n const tr = new Transaction().delete(start, end);\n if (isString(pasted)) {\n tr.insertText(start, pasted);\n } else {\n tr.insertFragment(start, pasted);\n }\n apply(tr);\n }\n };\n\n const onDrop = (e: DragEvent) => {\n e.preventDefault();\n\n const dataTransfer = e.dataTransfer;\n const droppedPosition = getPointedCaretPosition(\n document,\n element,\n e,\n parserConfig,\n );\n if (dataTransfer && droppedPosition) {\n const tr = new Transaction();\n if (isDragging) {\n tr.delete(...toRange(selection));\n }\n const pasted = paste(dataTransfer);\n if (pasted) {\n const pos = tr.transform(droppedPosition);\n if (isString(pasted)) {\n tr.insertText(pos, pasted);\n } else {\n tr.insertFragment(pos, pasted);\n }\n tr.selection = [pos, tr.transform(droppedPosition)];\n }\n apply(tr);\n element.focus({ preventScroll: true });\n }\n };\n const onDragStart = (e: DragEvent) => {\n isDragging = true;\n copySelected(e.dataTransfer!);\n };\n const onDragEnd = () => {\n isDragging = false;\n };\n\n document.addEventListener(\"selectionchange\", onSelectionChange);\n element.addEventListener(\"keydown\", onKeyDown);\n element.addEventListener(\"input\", onInput);\n element.addEventListener(\"beforeinput\", onBeforeInput);\n element.addEventListener(\"compositionstart\", onCompositionStart);\n element.addEventListener(\"compositionend\", onCompositionEnd);\n element.addEventListener(\"focus\", onFocus);\n element.addEventListener(\"blur\", onBlur);\n element.addEventListener(\"copy\", onCopy);\n element.addEventListener(\"cut\", onCut);\n element.addEventListener(\"paste\", onPaste);\n element.addEventListener(\"drop\", onDrop);\n element.addEventListener(\"dragstart\", onDragStart);\n element.addEventListener(\"dragend\", onDragEnd);\n\n const unmountHooks: (() => void)[] = [];\n mountHooks.forEach((mount) => {\n const cb = mount(element);\n if (cb) {\n unmountHooks.push(cb);\n }\n });\n\n return () => {\n if (disposed) return;\n disposed = true;\n\n if (_scrollRAF) {\n cancelAnimationFrame(_scrollRAF);\n _scrollRAF = 0;\n }\n _mountedEl = null;\n\n // TODO improve\n setContentEditable = noop;\n\n element.contentEditable = prevContentEditable;\n element.role = prevRole;\n element.ariaMultiLine = prevAriaMultiLine;\n element.ariaReadOnly = prevAriaReadOnly;\n element.style.whiteSpace = prevWhiteSpace;\n\n observer._dispose();\n\n document.removeEventListener(\"selectionchange\", onSelectionChange);\n element.removeEventListener(\"keydown\", onKeyDown);\n element.removeEventListener(\"input\", onInput);\n element.removeEventListener(\"beforeinput\", onBeforeInput);\n element.removeEventListener(\"compositionstart\", onCompositionStart);\n element.removeEventListener(\"compositionend\", onCompositionEnd);\n element.removeEventListener(\"focus\", onFocus);\n element.removeEventListener(\"blur\", onBlur);\n element.removeEventListener(\"copy\", onCopy);\n element.removeEventListener(\"cut\", onCut);\n element.removeEventListener(\"paste\", onPaste);\n element.removeEventListener(\"drop\", onDrop);\n element.removeEventListener(\"dragstart\", onDragStart);\n element.removeEventListener(\"dragend\", onDragEnd);\n\n unmountHooks.forEach((cb) => {\n cb();\n });\n };\n },\n };\n\n const history = createHistory<T>(doc);\n\n return editor;\n};\n","/**\n * @internal\n */\nexport const createMutationObserver = (\n element: Element,\n onMutationIgnored: () => void,\n) => {\n let isInputing = false;\n\n const queue: MutationRecord[] = [];\n const process = (records: MutationRecord[]) => {\n if (isInputing) {\n queue.push(...records);\n }\n };\n // https://dom.spec.whatwg.org/#interface-mutationobserver\n const mo = new MutationObserver((records) => {\n process(records);\n if (!isInputing) {\n onMutationIgnored();\n }\n });\n\n const sync = () => {\n process(mo.takeRecords());\n };\n\n mo.observe(element, {\n characterData: true,\n characterDataOldValue: true,\n childList: true,\n subtree: true,\n });\n\n return {\n _record(enable: boolean) {\n if (!isInputing && enable) {\n sync();\n }\n isInputing = enable;\n },\n _flush: (): MutationRecord[] => {\n sync();\n return queue.splice(0);\n },\n _dispose() {\n queue.splice(0);\n mo.disconnect();\n },\n };\n};\n","import { joinBlocks } from \"../doc/edit.js\";\nimport type { EditorPlugin } from \"./types.js\";\n\nexport const singlelinePlugin = (): EditorPlugin => {\n return {\n mount: (element) => {\n element.ariaMultiLine = null;\n },\n apply: (op, next) => {\n if (op.type === \"insert_text\") {\n op = {\n ...op,\n text: op.text.replaceAll(\"\\n\", \"\"),\n };\n } else if (op.type === \"insert_node\") {\n op = {\n ...op,\n fragment: [joinBlocks(...op.fragment)],\n };\n }\n next(op);\n },\n };\n};\n","import { docToString, stringToFragment } from \"../doc/utils.js\";\nimport { isTextNode } from \"../doc/edit.js\";\nimport { createEditor, type Editor, type EditorOptions } from \"../editor.js\";\nimport { singlelinePlugin } from \"../plugins/index.js\";\nimport type { EditorPlugin } from \"../plugins/types.js\";\nimport type { InlineNode } from \"../doc/types.js\";\n\ntype PlainDoc = { children: { text: string }[][] };\n\n/**\n * Describes which lines changed between two document snapshots.\n * Starting at line `start`, `oldCount` lines were replaced with `newCount` lines.\n * `lines` contains the text content of the new lines in the dirty window.\n */\nexport interface DirtyRange {\n start: number;\n oldCount: number;\n newCount: number;\n lines: string[];\n}\n\nexport interface PlainEditorOptions extends Omit<\n EditorOptions<PlainDoc>,\n \"doc\" | \"schema\" | \"onChange\"\n> {\n /**\n * Initial document text.\n */\n text: string;\n /**\n * TODO\n */\n singleline?: boolean;\n /**\n * Callback invoked when document changes.\n */\n onChange: (text: string, dirtyRange: DirtyRange) => void;\n}\n\nconst lineText = (nodes: readonly InlineNode[]): string => {\n let s = \"\";\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]!;\n if (isTextNode(n)) s += n.text;\n }\n return s;\n};\n\n/**\n * Compute the dirty range between two children arrays using reference equality.\n */\nconst computeDirtyRange = (\n prev: readonly (readonly InlineNode[])[],\n next: readonly (readonly InlineNode[])[],\n): DirtyRange => {\n const oldLen = prev.length;\n const newLen = next.length;\n const scanEnd = Math.min(oldLen, newLen);\n\n let front = 0;\n while (front < scanEnd && prev[front] === next[front]) front++;\n\n let oldBack = oldLen;\n let newBack = newLen;\n while (\n oldBack > front &&\n newBack > front &&\n prev[oldBack - 1] === next[newBack - 1]\n ) {\n oldBack--;\n newBack--;\n }\n\n const count = newBack - front;\n const lines: string[] = new Array(count);\n for (let i = 0; i < count; i++) {\n lines[i] = lineText(next[front + i]!);\n }\n\n return { start: front, oldCount: oldBack - front, newCount: count, lines };\n};\n\n/**\n * A function to initialize editor with plaintext.\n */\nexport const createPlainEditor = ({\n text,\n singleline,\n plugins: optsPlugins = [],\n onChange,\n ...opts\n}: PlainEditorOptions): Editor<PlainDoc> => {\n const plugins: EditorPlugin[] = [...optsPlugins];\n if (singleline) {\n plugins.unshift(singlelinePlugin());\n }\n const initialChildren = stringToFragment(text);\n let prevChildren: readonly (readonly InlineNode[])[] = initialChildren;\n return createEditor({\n ...opts,\n doc: { children: initialChildren },\n plugins,\n onChange: (doc) => {\n const dirty = computeDirtyRange(prevChildren, doc.children);\n prevChildren = doc.children;\n onChange(docToString(doc), dirty);\n },\n });\n};\n"],"names":["min","Math","keys","is","Object","isString","n","isFunction","microtask","queueMicrotask","fn","Promise","resolve","then","comparePath","pathA","pathB","length","i","a","b","comparePosition","offsetA","offsetB","comp","toRange","docToString","doc","serializer","isTextNode","text","children","reduce","acc","r","stringToFragment","node","split","map","l","TYPE_DELETE","TYPE_INSERT_TEXT","TYPE_INSERT_NODE","TYPE_SET_ATTR","isUnsafeOperation","type","Transaction","_ops","selection","constructor","ops","this","slice","insertText","start","push","at","insertFragment","fragment","end","attr","key","value","transform","position","op","rebasePosition","isSameNode","aKeys","every","k","getNodeSize","getLineSize","line","joinBlocks","blocks","prevLength","array","prev","curr","splice","normalize","concat","splitBlock","nodes","offset","size","before","after","beforeText","afterText","unshift","normalizePath","path","blockAtPath","movePath","int","replaceRange","inserted","startPath","startOffset","endPath","endOffset","maybeAfter","beforeLength","anchorNode","maybeAnchor","lines","sliced","sliceDoc","lastIndex","isValidPosition","lineLength","lineDiff","rebaseSelection","applyOperation","Delete","range","apply","delete","InsertText","InsertNode","InsertNodes","moveCaret","tr","ReplaceText","ReplaceAll","Format","ToggleFormat","texts","flatMap","filter","some","getOperationSelection","walker","_token","config","isElementNode","nodeType","isCommentNode","getDomNode","token","readToken","data","isValidSoftBreak","tagName","_isVoid","_isBlock","nextNode","nextBlock","nextSibling","parentBlock","parentNode","parent","parse","contains","readNext","current","t","scopeFn","root","newConfig","startNode","prevConfig","prevWalker","prevNode","prevToken","_document","createTreeWalker","SHOW_TEXT","currentNode","SINGLE_LINE_CONTAINER_NAMES","Set","defaultIsBlockNode","has","EMBEDDED_CONTENT_TAG_NAMES","defaultIsVoidNode","contentEditable","compareDomPosition","compareDocumentPosition","getCurrentDocument","ownerDocument","getDOMSelection","element","getSelection","getSelectionRangeInEditor","rangeCount","getRangeAt","commonAncestorContainer","setRangeToSelection","force","backward","removeAllRanges","addRange","collapseToEnd","extend","startContainer","setSelectionToDOM","document","anchor","focus","posDiff","isCollapsed","hasChildNodes","createRange","setStart","setEnd","domStart","findPosition","domEnd","endNode","setStartBefore","setStartAfter","setEndBefore","setEndAfter","pathIndex","next","blockIndex","serializePosition","offsetAtNode","excludeEnd","index","childNodes","block","sib","previousElementSibling","serializeRange","endContainer","plainCopy","dataTransfer","setData","htmlCopy","_","wrapper","createElement","appendChild","cloneContents","innerHTML","INTERNAL_COPY_KEY","internalCopy","JSON","stringify","filePaste","handlerByMime","item","items","kind","mapper","file","getAsFile","plainPaste","getData","htmlPaste","serializeText","serializers","html","dom","DOMParser","parseFromString","body","isWindowsCopy","DocumentFragment","serializeVoid","row","hasContent","rows","completeText","completeRow","docNode","domToFragment","s","internalPaste","e","hotkey","cb","mod","shift","alt","toLowerCase","ctrlKey","metaKey","shiftKey","altKey","noop","createEditor","readonly","schema","plugins","keyboard","copy","copyExtensions","paste","pasteExtensions","isBlock","autoScroll","_autoScroll","onChange","onError","console","error","setContentEditable","_mountedEl","_scrollRAF","scheduleScroll","el","requestAnimationFrame","scrollTop","scrollHeight","computeEmpty","d","_empty","validate","result","issues","message","join","initialError","m","Error","keydownHandlers","nextHistory","history","undo","updateSelection","redo","applyHooks","mountHooks","forEach","mount","undefined","transactions","immediate","shouldFlush","commit","currentDoc","pop","dispatch","nextDoc","nextSelection","o","change","isValidSelection","editor","isEmpty","args","call","input","window","InputEvent","prototype","getTargetRanges","prevContentEditable","role","prevRole","ariaMultiLine","prevAriaMultiLine","ariaReadOnly","prevAriaReadOnly","prevWhiteSpace","style","whiteSpace","disposed","selectionReverted","inputTransaction","isComposing","hasFocus","isDragging","parserConfig","ex","pasted","observer","onMutationIgnored","isInputing","queue","process","records","mo","MutationObserver","sync","takeRecords","observe","characterData","characterDataOldValue","childList","subtree","_record","enable","_flush","_dispose","disconnect","createMutationObserver","syncSelection","domRange","focusNode","anchorOffset","focusOffset","takeSelectionSnapshot","flushInput","target","removedNodes","addedNodes","insertBefore","removeChild","nodeValue","oldValue","onKeyDown","handler","preventDefault","onInput","onBeforeInput","inputType","startsWith","onCompositionStart","onCompositionEnd","onFocus","onBlur","onSelectionChange","copySelected","onCopy","clipboardData","onCut","onPaste","onDrop","droppedPosition","clientX","clientY","caretPositionFromPoint","offsetNode","caretRangeFromPoint","getPointedCaretPosition","pos","preventScroll","onDragStart","onDragEnd","addEventListener","unmountHooks","cancelAnimationFrame","removeEventListener","initialDoc","prevTime","now","Date","histories","get","time","last","sel","createHistory","singlelinePlugin","replaceAll","lineText","createPlainEditor","singleline","optsPlugins","opts","initialChildren","prevChildren","dirty","oldLen","newLen","scanEnd","front","oldBack","newBack","count","Array","oldCount","newCount","computeDirtyRange"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { singlelinePlugin } from "./singleline.js";
2
+ export type { EditorPlugin } from "./types.js";
@@ -0,0 +1,2 @@
1
+ import type { EditorPlugin } from "./types.js";
2
+ export declare const singlelinePlugin: () => EditorPlugin;
@@ -0,0 +1,5 @@
1
+ import { type Operation } from "../doc/edit.js";
2
+ export interface EditorPlugin {
3
+ apply?: (op: Operation, next: (op?: Operation) => void) => void;
4
+ mount?: (element: HTMLElement) => void | (() => void);
5
+ }
@@ -0,0 +1,2 @@
1
+ export { createPlainEditor } from "./plain.js";
2
+ export type { PlainEditorOptions, DirtyRange } from "./plain.js";
@@ -0,0 +1,36 @@
1
+ import { type Editor, type EditorOptions } from "../editor.js";
2
+ type PlainDoc = {
3
+ children: {
4
+ text: string;
5
+ }[][];
6
+ };
7
+ /**
8
+ * Describes which lines changed between two document snapshots.
9
+ * Starting at line `start`, `oldCount` lines were replaced with `newCount` lines.
10
+ * `lines` contains the text content of the new lines in the dirty window.
11
+ */
12
+ export interface DirtyRange {
13
+ start: number;
14
+ oldCount: number;
15
+ newCount: number;
16
+ lines: string[];
17
+ }
18
+ export interface PlainEditorOptions extends Omit<EditorOptions<PlainDoc>, "doc" | "schema" | "onChange"> {
19
+ /**
20
+ * Initial document text.
21
+ */
22
+ text: string;
23
+ /**
24
+ * TODO
25
+ */
26
+ singleline?: boolean;
27
+ /**
28
+ * Callback invoked when document changes.
29
+ */
30
+ onChange: (text: string, dirtyRange: DirtyRange) => void;
31
+ }
32
+ /**
33
+ * A function to initialize editor with plaintext.
34
+ */
35
+ export declare const createPlainEditor: ({ text, singleline, plugins: optsPlugins, onChange, ...opts }: PlainEditorOptions) => Editor<PlainDoc>;
36
+ export {};
package/lib/utils.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "@lofcz/edix",
3
+ "version": "0.3.0",
4
+ "description": "An experimental, framework agnostic, small (4kB+) contenteditable state manager.",
5
+ "main": "lib/index.cjs",
6
+ "module": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "type": "module",
9
+ "exports": {
10
+ "./package.json": "./package.json",
11
+ ".": {
12
+ "types": "./lib/index.d.ts",
13
+ "require": "./lib/index.cjs",
14
+ "default": "./lib/index.js"
15
+ }
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "files": [
21
+ "lib"
22
+ ],
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "build": "rollup -c",
26
+ "tsc": "tsc -p . --noEmit",
27
+ "test": "vitest --run --project=unit",
28
+ "test:browser": "vitest --run --project=browser",
29
+ "format": "prettier '**/*.{js,ts,jsx,tsx}' --write --cache",
30
+ "format:ci": "prettier '**/*.{js,ts,jsx,tsx}' -l",
31
+ "storybook": "storybook dev -p 6006",
32
+ "storybook:build": "storybook build",
33
+ "e2e": "npx playwright test",
34
+ "typedoc": "typedoc",
35
+ "size": "size-limit",
36
+ "prepare": "patch-package",
37
+ "prepublishOnly": "npm run typedoc && rimraf lib && npm run build",
38
+ "changeset": "changeset",
39
+ "changeset:version": "changeset version",
40
+ "changeset:publish": "npm run build && changeset publish"
41
+ },
42
+ "dependencies": {
43
+ "@standard-schema/spec": "^1.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@changesets/cli": "^2.30.0",
47
+ "@playwright/test": "1.58.2",
48
+ "@rollup/plugin-terser": "0.4.4",
49
+ "@rollup/plugin-typescript": "12.3.0",
50
+ "@size-limit/preset-small-lib": "^12.0.0",
51
+ "@storybook/addon-docs": "^10.3.1",
52
+ "@storybook/react-vite": "^10.3.1",
53
+ "@textlint/kernel": "^15.0.0",
54
+ "@textlint/textlint-plugin-text": "^15.0.0",
55
+ "@types/react": "^19.2.0",
56
+ "@types/react-dom": "^19.2.0",
57
+ "@vitest/browser-playwright": "^4.0.18",
58
+ "diff": "^8.0.0",
59
+ "esbuild": "^0.27.0",
60
+ "kuromojin": "^3.0.1",
61
+ "linkifyjs": "^4.3.1",
62
+ "patch-package": "^8.0.0",
63
+ "path-browserify": "^1.0.1",
64
+ "prettier": "^3.8.1",
65
+ "prism-react-renderer": "^2.4.1",
66
+ "react": "^19.2.3",
67
+ "react-dom": "^19.2.3",
68
+ "react-is": "^19.2.3",
69
+ "remark-gfm": "^4.0.1",
70
+ "remark-parse": "^11.0.0",
71
+ "rimraf": "6.1.3",
72
+ "rollup": "^4.54.0",
73
+ "size-limit": "^12.0.0",
74
+ "storybook": "^10.3.1",
75
+ "textlint-rule-preset-japanese": "^10.0.4",
76
+ "typedoc": "^0.28.0",
77
+ "typedoc-plugin-markdown": "^4.6.3",
78
+ "typescript": "^5.9.3",
79
+ "unified": "^11.0.5",
80
+ "valibot": "^1.2.0",
81
+ "vite": "^8.0.6",
82
+ "vitest": "^4.1.0"
83
+ },
84
+ "repository": {
85
+ "type": "git",
86
+ "url": "git+https://github.com/lofcz/edix.git"
87
+ },
88
+ "keywords": [
89
+ "contenteditable",
90
+ "ui",
91
+ "headless",
92
+ "textarea",
93
+ "input",
94
+ "form",
95
+ "highlight",
96
+ "autocomplete",
97
+ "tagging",
98
+ "combobox",
99
+ "richtext",
100
+ "editor",
101
+ "wysiwyg",
102
+ "react",
103
+ "vue",
104
+ "svelte",
105
+ "solid",
106
+ "angular",
107
+ "preact"
108
+ ],
109
+ "author": "inokawa <stratoooo-taster@yahoo.co.jp> (https://github.com/inokawa/)",
110
+ "license": "MIT",
111
+ "bugs": {
112
+ "url": "https://github.com/lofcz/edix/issues"
113
+ },
114
+ "homepage": "https://github.com/lofcz/edix#readme"
115
+ }