@bikdotai/bik-component-library 0.0.809 → 0.0.810
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/dist/cjs/editor/BikEditor.js +1 -1
- package/dist/cjs/editor/BikEditor.js.map +1 -1
- package/dist/cjs/editor/BikEditor.styles.js +10 -5
- package/dist/cjs/editor/BikEditor.styles.js.map +1 -1
- package/dist/cjs/editor/BikEditor.types.js.map +1 -1
- package/dist/cjs/editor/BikEditor.utils.js +1 -1
- package/dist/cjs/editor/BikEditor.utils.js.map +1 -1
- package/dist/cjs/editor/extensions/buildExtensions.js +1 -1
- package/dist/cjs/editor/extensions/buildExtensions.js.map +1 -1
- package/dist/cjs/editor/extensions/plainClipboard/PasteNormalizationExtension.js +2 -0
- package/dist/cjs/editor/extensions/plainClipboard/PasteNormalizationExtension.js.map +1 -0
- package/dist/cjs/editor/extensions/plainClipboard/pasteUtils.js +1 -1
- package/dist/cjs/editor/extensions/plainClipboard/pasteUtils.js.map +1 -1
- package/dist/cjs/node_modules/@tiptap/extension-paragraph/dist/index.js +2 -0
- package/dist/cjs/node_modules/@tiptap/extension-paragraph/dist/index.js.map +1 -0
- package/dist/cjs/src/editor/BikEditor.styles.d.ts +1 -0
- package/dist/cjs/src/editor/BikEditor.types.d.ts +2 -0
- package/dist/cjs/src/editor/BikEditor.utils.d.ts +8 -4
- package/dist/cjs/src/editor/extensions/buildExtensions.d.ts +2 -1
- package/dist/cjs/src/editor/extensions/plainClipboard/PasteNormalizationExtension.d.ts +13 -0
- package/dist/cjs/src/editor/extensions/plainClipboard/pasteUtils.d.ts +40 -8
- package/dist/esm/editor/BikEditor.js +1 -1
- package/dist/esm/editor/BikEditor.js.map +1 -1
- package/dist/esm/editor/BikEditor.styles.js +14 -9
- package/dist/esm/editor/BikEditor.styles.js.map +1 -1
- package/dist/esm/editor/BikEditor.types.js.map +1 -1
- package/dist/esm/editor/BikEditor.utils.js +1 -1
- package/dist/esm/editor/BikEditor.utils.js.map +1 -1
- package/dist/esm/editor/extensions/buildExtensions.js +1 -1
- package/dist/esm/editor/extensions/buildExtensions.js.map +1 -1
- package/dist/esm/editor/extensions/plainClipboard/PasteNormalizationExtension.js +2 -0
- package/dist/esm/editor/extensions/plainClipboard/PasteNormalizationExtension.js.map +1 -0
- package/dist/esm/editor/extensions/plainClipboard/pasteUtils.js +1 -1
- package/dist/esm/editor/extensions/plainClipboard/pasteUtils.js.map +1 -1
- package/dist/esm/node_modules/@tiptap/extension-paragraph/dist/index.js +2 -0
- package/dist/esm/node_modules/@tiptap/extension-paragraph/dist/index.js.map +1 -0
- package/dist/esm/src/editor/BikEditor.styles.d.ts +1 -0
- package/dist/esm/src/editor/BikEditor.types.d.ts +2 -0
- package/dist/esm/src/editor/BikEditor.utils.d.ts +8 -4
- package/dist/esm/src/editor/extensions/buildExtensions.d.ts +2 -1
- package/dist/esm/src/editor/extensions/plainClipboard/PasteNormalizationExtension.d.ts +13 -0
- package/dist/esm/src/editor/extensions/plainClipboard/pasteUtils.d.ts +40 -8
- package/package.json +1 -1
- package/dist/cjs/editor/extensions/plainClipboard/ClipboardNormalizationExtension.js +0 -2
- package/dist/cjs/editor/extensions/plainClipboard/ClipboardNormalizationExtension.js.map +0 -1
- package/dist/cjs/editor/extensions/plainClipboard/PlainClipboardExtension.js +0 -2
- package/dist/cjs/editor/extensions/plainClipboard/PlainClipboardExtension.js.map +0 -1
- package/dist/cjs/src/editor/extensions/plainClipboard/ClipboardNormalizationExtension.d.ts +0 -7
- package/dist/cjs/src/editor/extensions/plainClipboard/PlainClipboardExtension.d.ts +0 -2
- package/dist/esm/editor/extensions/plainClipboard/ClipboardNormalizationExtension.js +0 -2
- package/dist/esm/editor/extensions/plainClipboard/ClipboardNormalizationExtension.js.map +0 -1
- package/dist/esm/editor/extensions/plainClipboard/PlainClipboardExtension.js +0 -2
- package/dist/esm/editor/extensions/plainClipboard/PlainClipboardExtension.js.map +0 -1
- package/dist/esm/src/editor/extensions/plainClipboard/ClipboardNormalizationExtension.d.ts +0 -7
- package/dist/esm/src/editor/extensions/plainClipboard/PlainClipboardExtension.d.ts +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BikEditor.utils.js","sources":["../../../src/editor/BikEditor.utils.ts"],"sourcesContent":["import { Editor } from '@tiptap/core';\nimport { DOMParser, DOMSerializer } from 'prosemirror-model';\nimport { EditorSnapshot, FormatState } from './BikEditor.types';\n\n// ---------------------------------------------------------------------------\n// HTML normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Normalise HTML before passing it to TipTap.\n *\n * 1. Strip all `<span>` tags first (keeps inner content). This exposes\n * bare `<br>` inside otherwise-empty paragraphs like Quill's\n * `<p><span class=\"ql-cursor\"><br></span></p>`.\n * 2. Then convert `<p><br></p>` → `<p></p>`. ProseMirror parses `<p></p>`\n * as an empty paragraph node and adds its own DOM `<br>` for cursor\n * positioning — one blank line, correct height. Leaving the `<br>`\n * would create a `hard_break` child AND the cursor `<br>`, producing\n * double-height blank lines.\n */\nexport function normalizeHtml(html: string): string {\n\treturn html\n\t\t.replace(/<\\/?span[^>]*>/gi, '')\n\t\t.replace(/(<p[^>]*>)\\s*<br\\s*\\/?>\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * Convert ProseMirror's `<p></p>` empty paragraphs to the universally\n * rendered `<p><br></p>` format before the HTML leaves the editor.\n * Email clients, chat bubbles, and every renderer understand `<p><br></p>`\n * as a visible blank line, whereas `<p></p>` collapses to zero height.\n */\nexport function toPortableHtml(html: string): string {\n\treturn html.replace(/<p><\\/p>/g, '<p><br></p>');\n}\n\n// ---------------------------------------------------------------------------\n// Inline content insertion\n// ---------------------------------------------------------------------------\n\n/**\n * Insert HTML at the current cursor position, merging the first paragraph\n * inline while preserving the block structure of subsequent paragraphs/lists.\n *\n * Uses `parseSlice` which returns an open-ended Slice — the first block merges\n * at the cursor, middle blocks stay intact, and the last block merges with any\n * text that follows the cursor.\n */\nexport function insertInlineHtml(editor: Editor, html: string): void {\n\tconst normalised = normalizeHtml(html);\n\tconst dom = document.createElement('div');\n\tdom.innerHTML = normalised;\n\tconst slice = DOMParser.fromSchema(editor.schema).parseSlice(dom);\n\n\tif (slice.size === 0) return;\n\n\tconst { from, to } = editor.state.selection;\n\tconst tr = editor.state.tr.replaceRange(from, to, slice);\n\teditor.view.dispatch(tr);\n}\n\n// ---------------------------------------------------------------------------\n// Section divider HTML\n// ---------------------------------------------------------------------------\n\nexport const SECTION_DIVIDER_HTML = (id: string) =>\n\t`<div data-section-divider=\"${id}\" style=\"display:none;height:0;line-height:0;overflow:hidden;\"></div>`;\n\n/**\n * Build initial HTML for an editor with one or more named sections below the body.\n * Each section is separated by an invisible divider node.\n *\n * @example\n * buildSectionedContent('<p>Body</p>', [\n * { id: 'signature', content: '<p>Alice</p>' },\n * { id: 'forwarded', content: '<p>--- fwd ---</p>' },\n * ])\n */\nexport function buildSectionedContent(\n\tbody: string,\n\tsections: Array<{ id: string; content: string }>,\n): string {\n\tlet html = body;\n\tfor (const s of sections) {\n\t\thtml += SECTION_DIVIDER_HTML(s.id) + s.content;\n\t}\n\treturn html;\n}\n\n// ---------------------------------------------------------------------------\n// Section extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the editor HTML into a Map of sectionId → raw HTML string.\n * The special key `'body'` always holds the content before the first divider.\n * Map iteration order matches document order.\n */\nexport function extractAllSections(editor: Editor): Map<string, string> {\n\treturn extractAllSectionsFromHtml(editor.getHTML());\n}\n\n/**\n * Extract just the body portion (everything before the first section divider)\n * from an HTML string produced by BikEditor. Safe to call on plain HTML too —\n * returns the full string when no dividers are present.\n */\nexport function getBodyHtml(html: string): string {\n\tif (!html) return '';\n\tconst sections = extractAllSectionsFromHtml(html);\n\treturn sections.get('body') ?? html;\n}\n\n/**\n * Extract everything from the first section divider onward (dividers + their\n * content). Returns an empty string when there are no sections.\n */\nexport function getSectionsHtml(html: string): string {\n\tif (!html) return '';\n\tconst body = getBodyHtml(html);\n\treturn html.substring(body.length);\n}\n\nexport function extractAllSectionsFromHtml(html: string): Map<string, string> {\n\tconst sections = new Map<string, string>();\n\tconst dividerRegex =\n\t\t/<div[^>]*data-section-divider=\"([^\"]*)[^>]*>\\s*<\\/div>/g;\n\tconst dividers: Array<{ id: string; index: number; endIndex: number }> = [];\n\n\tlet match: RegExpExecArray | null;\n\twhile ((match = dividerRegex.exec(html)) !== null) {\n\t\tdividers.push({\n\t\t\tid: match[1],\n\t\t\tindex: match.index,\n\t\t\tendIndex: match.index + match[0].length,\n\t\t});\n\t}\n\n\tif (dividers.length === 0) {\n\t\tsections.set('body', html);\n\t\treturn sections;\n\t}\n\n\tsections.set('body', html.slice(0, dividers[0].index));\n\tfor (let i = 0; i < dividers.length; i++) {\n\t\tconst start = dividers[i].endIndex;\n\t\tconst end = i + 1 < dividers.length ? dividers[i + 1].index : html.length;\n\t\tsections.set(dividers[i].id, html.slice(start, end));\n\t}\n\n\treturn sections;\n}\n\n/** Returns BikEditorContent for a single named section (or 'body'). */\nexport function extractSectionContent(\n\teditor: Editor,\n\tid: string,\n): EditorSnapshot {\n\tconst startPos = findSectionStartPos(editor, id);\n\tif (startPos === -1)\n\t\treturn { html: '', text: '', isEmpty: true, characterCount: 0 };\n\tconst endPos = findSectionEndPos(editor, id);\n\tconst slice = editor.state.doc.slice(startPos, endPos);\n\tconst container = document.createElement('div');\n\tconst serializer = DOMSerializer.fromSchema(editor.schema);\n\tcontainer.appendChild(serializer.serializeFragment(slice.content));\n\tconst html = toPortableHtml(container.innerHTML);\n\tconst text = container.textContent ?? '';\n\treturn { html, text, isEmpty: !text.trim(), characterCount: text.length };\n}\n\n/**\n * Parse an HTML string into ProseMirror nodes using the editor's schema.\n */\nfunction parseHtmlToNodes(editor: Editor, html: string) {\n\tconst container = document.createElement('div');\n\tcontainer.innerHTML = html;\n\treturn DOMParser.fromSchema(editor.schema).parse(container).content;\n}\n\n/**\n * Replace the content of a single named section without touching others.\n * Uses a ProseMirror transaction to replace only the target range,\n * preserving undo history and other sections' state.\n * If the section doesn't exist yet, it is appended at the end of the document.\n */\nexport function setSectionContentInEditor(\n\teditor: Editor,\n\tid: string,\n\thtml: string,\n): void {\n\tconst startPos = findSectionStartPos(editor, id);\n\tif (startPos === -1) {\n\t\tconst endPos = editor.state.doc.content.size;\n\t\tconst dividerHtml = SECTION_DIVIDER_HTML(id) + html;\n\t\teditor.commands.insertContentAt(endPos, dividerHtml, {\n\t\t\tupdateSelection: false,\n\t\t});\n\t\treturn;\n\t}\n\tconst endPos = findSectionEndPos(editor, id);\n\tconst fragment = parseHtmlToNodes(editor, html);\n\tconst { tr } = editor.state;\n\t// For body, replace from position 0 (before the first paragraph's opening\n\t// boundary) so ProseMirror swaps entire block nodes cleanly. Position 1\n\t// (inside the paragraph) would force ProseMirror to close the open paragraph\n\t// first, creating a ghost empty <p></p>.\n\tconst replaceFrom = id === 'body' ? 0 : startPos;\n\ttr.replaceWith(replaceFrom, endPos, fragment);\n\ttr.setMeta('addToHistory', false);\n\teditor.view.dispatch(tr);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience shorthands (body = first section)\n// ---------------------------------------------------------------------------\n\n/** Extract BikEditorContent for just the body (before the first divider). */\nexport function extractBodyContent(editor: Editor): EditorSnapshot {\n\treturn extractSectionContent(editor, 'body');\n}\n\n// ---------------------------------------------------------------------------\n// Active formats\n// ---------------------------------------------------------------------------\n\nexport function extractActiveFormats(editor: Editor): FormatState {\n\treturn {\n\t\tbold: editor.isActive('bold'),\n\t\titalic: editor.isActive('italic'),\n\t\tunderline: editor.isActive('underline'),\n\t\tstrike: editor.isActive('strike'),\n\t\tbulletList: editor.isActive('bulletList'),\n\t\torderedList: editor.isActive('orderedList'),\n\t\tblockquote: editor.isActive('blockquote'),\n\t\tcodeBlock: editor.isActive('codeBlock'),\n\t\tlink: (() => {\n\t\t\tif (editor.isActive('link')) {\n\t\t\t\treturn { href: editor.getAttributes('link')['href'] ?? '' };\n\t\t\t}\n\t\t\tconst { $from } = editor.state.selection;\n\t\t\tif ($from.pos > 0) {\n\t\t\t\tconst before = $from.doc.resolve($from.pos);\n\t\t\t\tconst linkMark = before.marks().find((m) => m.type.name === 'link');\n\t\t\t\tif (!linkMark) {\n\t\t\t\t\tconst nodeBefore = $from.nodeBefore;\n\t\t\t\t\tconst markOnPrev = nodeBefore?.marks.find(\n\t\t\t\t\t\t(m) => m.type.name === 'link',\n\t\t\t\t\t);\n\t\t\t\t\tif (markOnPrev) {\n\t\t\t\t\t\treturn { href: markOnPrev.attrs['href'] ?? '' };\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn { href: linkMark.attrs['href'] ?? '' };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t})(),\n\t\ttextAlign: editor.isActive({ textAlign: 'center' })\n\t\t\t? 'center'\n\t\t\t: editor.isActive({ textAlign: 'right' })\n\t\t\t? 'right'\n\t\t\t: editor.isActive({ textAlign: 'justify' })\n\t\t\t? 'justify'\n\t\t\t: editor.isActive({ textAlign: 'left' })\n\t\t\t? 'left'\n\t\t\t: null,\n\t\tfontFamily: editor.getAttributes('textStyle')['fontFamily'] ?? null,\n\t\tfontSize: editor.getAttributes('textStyle')['fontSize'] ?? null,\n\t\tcolor: editor.getAttributes('textStyle')['color'] ?? null,\n\t\thighlight: editor.isActive('highlight')\n\t\t\t? editor.getAttributes('highlight')['color'] ?? 'default'\n\t\t\t: null,\n\t\tsuperscript: editor.isActive('superscript'),\n\t\tsubscript: editor.isActive('subscript'),\n\t};\n}\n\nexport function extractContent(editor: Editor): EditorSnapshot {\n\treturn {\n\t\thtml: toPortableHtml(editor.getHTML()),\n\t\ttext: editor.getText(),\n\t\tisEmpty: editor.isEmpty,\n\t\tcharacterCount:\n\t\t\teditor.storage.characterCount?.characters?.() ?? editor.getText().length,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Section position helpers (used by insertAtSectionStart/End and appendBodyContent)\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the ProseMirror position immediately after a section's opening divider\n * (i.e. the start of the section's own content).\n * For `'body'`, returns 1 (beginning of the document).\n * Returns -1 if the named section's divider is not found.\n */\nexport function findSectionStartPos(editor: Editor, sectionId: string): number {\n\tif (sectionId === 'body') return 1;\n\tlet startPos = -1;\n\teditor.state.doc.descendants((node, pos) => {\n\t\tif (startPos !== -1) return false;\n\t\tif (\n\t\t\tnode.type.name === 'sectionDivider' &&\n\t\t\tnode.attrs['sectionId'] === sectionId\n\t\t) {\n\t\t\tstartPos = pos + node.nodeSize; // position right after the divider\n\t\t}\n\t});\n\treturn startPos;\n}\n\n/**\n * Returns the ProseMirror position at the end of a named section's content:\n * - For `'body'`: position of the first section divider, or end of document.\n * - For a named section: position of the next divider, or end of document.\n */\nexport function findSectionEndPos(editor: Editor, sectionId: string): number {\n\tconst doc = editor.state.doc;\n\tconst docSize = doc.content.size;\n\n\tif (sectionId === 'body') {\n\t\tlet firstDividerPos = -1;\n\t\tdoc.descendants((node, pos) => {\n\t\t\tif (firstDividerPos !== -1) return false;\n\t\t\tif (node.type.name === 'sectionDivider') {\n\t\t\t\tfirstDividerPos = pos;\n\t\t\t}\n\t\t});\n\t\treturn firstDividerPos !== -1 ? firstDividerPos : docSize;\n\t}\n\n\tlet passedSection = false;\n\tlet nextDividerPos = -1;\n\tdoc.descendants((node, pos) => {\n\t\tif (nextDividerPos !== -1) return false;\n\t\tif (!passedSection) {\n\t\t\tif (\n\t\t\t\tnode.type.name === 'sectionDivider' &&\n\t\t\t\tnode.attrs['sectionId'] === sectionId\n\t\t\t) {\n\t\t\t\tpassedSection = true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (node.type.name === 'sectionDivider') {\n\t\t\tnextDividerPos = pos;\n\t\t}\n\t});\n\treturn nextDividerPos !== -1 ? nextDividerPos : docSize;\n}\n"],"names":["normalizeHtml","html","replace","toPortableHtml","SECTION_DIVIDER_HTML","id","getBodyHtml","_a","extractAllSectionsFromHtml","get","sections","Map","dividerRegex","dividers","match","exec","push","index","endIndex","length","set","slice","i","start","end","extractSectionContent","editor","startPos","findSectionStartPos","text","isEmpty","characterCount","endPos","findSectionEndPos","state","doc","container","document","createElement","serializer","DOMSerializer","fromSchema","schema","appendChild","serializeFragment","content","innerHTML","textContent","trim","sectionId","descendants","node","pos","type","name","attrs","nodeSize","docSize","size","firstDividerPos","passedSection","nextDividerPos","body","s","bold","isActive","italic","underline","strike","bulletList","orderedList","blockquote","codeBlock","link","href","getAttributes","$from","selection","linkMark","resolve","marks","find","m","_c","nodeBefore","markOnPrev","_b","textAlign","fontFamily","fontSize","color","highlight","_d","superscript","subscript","getHTML","getText","storage","characters","substring","normalised","dom","DOMParser","parseSlice","from","to","tr","replaceRange","view","dispatch","dividerHtml","commands","insertContentAt","updateSelection","fragment","parse","parseHtmlToNodes","replaceFrom","replaceWith","setMeta"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BikEditor.utils.js","sources":["../../../src/editor/BikEditor.utils.ts"],"sourcesContent":["import { Editor } from '@tiptap/core';\nimport { DOMParser, DOMSerializer } from 'prosemirror-model';\nimport { EditorSnapshot, FormatState } from './BikEditor.types';\nimport { normalizeHardBreaks } from './extensions/plainClipboard/pasteUtils';\n\n// ---------------------------------------------------------------------------\n// HTML normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Normalise HTML before passing it to TipTap.\n *\n * 1. Strip all `<span>` tags first (keeps inner content). This exposes\n * bare `<br>` inside otherwise-empty paragraphs like Quill's\n * `<p><span class=\"ql-cursor\"><br></span></p>`.\n * 2. Then convert `<p><br></p>` → `<p></p>`. ProseMirror parses `<p></p>`\n * as an empty paragraph node and adds its own DOM `<br>` for cursor\n * positioning — one blank line, correct height. Leaving the `<br>`\n * would create a `hard_break` child AND the cursor `<br>`, producing\n * double-height blank lines.\n */\nexport function normalizeHtml(html: string): string {\n\treturn html\n\t\t.replace(/<\\/?span[^>]*>/gi, '')\n\t\t.replace(/(<p[^>]*>)\\s*<br\\s*\\/?>\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * Make editor HTML portable across email clients and chat renderers.\n *\n * 1. Double a trailing `<br>` in content paragraphs so the blank line is\n * visible. A single trailing `<br>` before `</p>` is swallowed by most\n * email clients; `<br><br>` renders as a visible blank line (matches\n * Gmail's output).\n * 2. Convert empty `<p></p>` to `<p><br></p>` — email clients collapse\n * zero-height paragraphs without a `<br>`.\n */\nexport function toPortableHtml(html: string): string {\n\tconst div = document.createElement('div');\n\tdiv.innerHTML = html;\n\tconst blocks = div.querySelectorAll('p');\n\tfor (const p of Array.from(blocks)) {\n\t\tif (p.childNodes.length === 0) {\n\t\t\tp.appendChild(document.createElement('br'));\n\t\t\tcontinue;\n\t\t}\n\t\tconst last = p.lastChild;\n\t\tif (last instanceof HTMLBRElement && p.childNodes.length > 1) {\n\t\t\tp.appendChild(document.createElement('br'));\n\t\t}\n\t}\n\treturn div.innerHTML;\n}\n\n// ---------------------------------------------------------------------------\n// Inline content insertion\n// ---------------------------------------------------------------------------\n\n/**\n * Insert HTML at the current cursor position, merging the first paragraph\n * inline while preserving the block structure of subsequent paragraphs/lists.\n *\n * Uses `parseSlice` which returns an open-ended Slice — the first block merges\n * at the cursor, middle blocks stay intact, and the last block merges with any\n * text that follows the cursor.\n */\nexport function insertInlineHtml(editor: Editor, html: string): void {\n\tconst normalised = normalizeHtml(html);\n\tconst dom = document.createElement('div');\n\tdom.innerHTML = normalised;\n\tconst slice = DOMParser.fromSchema(editor.schema).parseSlice(dom);\n\n\tif (slice.size === 0) return;\n\n\tconst { from, to } = editor.state.selection;\n\tconst tr = editor.state.tr.replaceRange(from, to, slice);\n\teditor.view.dispatch(tr);\n}\n\n// ---------------------------------------------------------------------------\n// Section divider HTML\n// ---------------------------------------------------------------------------\n\nexport const SECTION_DIVIDER_HTML = (id: string) =>\n\t`<div data-section-divider=\"${id}\" style=\"display:none;height:0;line-height:0;overflow:hidden;\"></div>`;\n\n/**\n * Build initial HTML for an editor with one or more named sections below the body.\n * Each section is separated by an invisible divider node.\n *\n * @example\n * buildSectionedContent('<p>Body</p>', [\n * { id: 'signature', content: '<p>Alice</p>' },\n * { id: 'forwarded', content: '<p>--- fwd ---</p>' },\n * ])\n */\nexport function buildSectionedContent(\n\tbody: string,\n\tsections: Array<{ id: string; content: string }>,\n): string {\n\tlet html = body;\n\tfor (const s of sections) {\n\t\thtml += SECTION_DIVIDER_HTML(s.id) + s.content;\n\t}\n\treturn html;\n}\n\n// ---------------------------------------------------------------------------\n// Section extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the editor HTML into a Map of sectionId → raw HTML string.\n * The special key `'body'` always holds the content before the first divider.\n * Map iteration order matches document order.\n */\nexport function extractAllSections(editor: Editor): Map<string, string> {\n\treturn extractAllSectionsFromHtml(editor.getHTML());\n}\n\n/**\n * Extract just the body portion (everything before the first section divider)\n * from an HTML string produced by BikEditor. Safe to call on plain HTML too —\n * returns the full string when no dividers are present.\n */\nexport function getBodyHtml(html: string): string {\n\tif (!html) return '';\n\tconst sections = extractAllSectionsFromHtml(html);\n\treturn sections.get('body') ?? html;\n}\n\n/**\n * Extract everything from the first section divider onward (dividers + their\n * content). Returns an empty string when there are no sections.\n */\nexport function getSectionsHtml(html: string): string {\n\tif (!html) return '';\n\tconst body = getBodyHtml(html);\n\treturn html.substring(body.length);\n}\n\nexport function extractAllSectionsFromHtml(html: string): Map<string, string> {\n\tconst sections = new Map<string, string>();\n\tconst dividerRegex =\n\t\t/<div[^>]*data-section-divider=\"([^\"]*)[^>]*>\\s*<\\/div>/g;\n\tconst dividers: Array<{ id: string; index: number; endIndex: number }> = [];\n\n\tlet match: RegExpExecArray | null;\n\twhile ((match = dividerRegex.exec(html)) !== null) {\n\t\tdividers.push({\n\t\t\tid: match[1],\n\t\t\tindex: match.index,\n\t\t\tendIndex: match.index + match[0].length,\n\t\t});\n\t}\n\n\tif (dividers.length === 0) {\n\t\tsections.set('body', html);\n\t\treturn sections;\n\t}\n\n\tsections.set('body', html.slice(0, dividers[0].index));\n\tfor (let i = 0; i < dividers.length; i++) {\n\t\tconst start = dividers[i].endIndex;\n\t\tconst end = i + 1 < dividers.length ? dividers[i + 1].index : html.length;\n\t\tsections.set(dividers[i].id, html.slice(start, end));\n\t}\n\n\treturn sections;\n}\n\n/** Returns BikEditorContent for a single named section (or 'body'). */\nexport function extractSectionContent(\n\teditor: Editor,\n\tid: string,\n): EditorSnapshot {\n\tconst startPos = findSectionStartPos(editor, id);\n\tif (startPos === -1)\n\t\treturn { html: '', text: '', isEmpty: true, characterCount: 0 };\n\tconst endPos = findSectionEndPos(editor, id);\n\tconst slice = editor.state.doc.slice(startPos, endPos);\n\tconst container = document.createElement('div');\n\tconst serializer = DOMSerializer.fromSchema(editor.schema);\n\tcontainer.appendChild(serializer.serializeFragment(slice.content));\n\tconst html = toPortableHtml(container.innerHTML);\n\tconst text = container.textContent ?? '';\n\treturn { html, text, isEmpty: !text.trim(), characterCount: text.length };\n}\n\n/**\n * Parse an HTML string into ProseMirror nodes using the editor's schema.\n */\nfunction parseHtmlToNodes(editor: Editor, html: string) {\n\tconst container = document.createElement('div');\n\tcontainer.innerHTML = html;\n\treturn DOMParser.fromSchema(editor.schema).parse(container).content;\n}\n\n/**\n * Replace the content of a single named section without touching others.\n * Uses a ProseMirror transaction to replace only the target range,\n * preserving undo history and other sections' state.\n * If the section doesn't exist yet, it is appended at the end of the document.\n */\nexport function setSectionContentInEditor(\n\teditor: Editor,\n\tid: string,\n\thtml: string,\n): void {\n\tconst startPos = findSectionStartPos(editor, id);\n\tif (startPos === -1) {\n\t\tconst endPos = editor.state.doc.content.size;\n\t\tconst dividerHtml = SECTION_DIVIDER_HTML(id) + html;\n\t\teditor.commands.insertContentAt(endPos, dividerHtml, {\n\t\t\tupdateSelection: false,\n\t\t});\n\t\treturn;\n\t}\n\tconst endPos = findSectionEndPos(editor, id);\n\tconst fragment = parseHtmlToNodes(editor, html);\n\tconst { tr } = editor.state;\n\t// For body, replace from position 0 (before the first paragraph's opening\n\t// boundary) so ProseMirror swaps entire block nodes cleanly. Position 1\n\t// (inside the paragraph) would force ProseMirror to close the open paragraph\n\t// first, creating a ghost empty <p></p>.\n\tconst replaceFrom = id === 'body' ? 0 : startPos;\n\ttr.replaceWith(replaceFrom, endPos, fragment);\n\ttr.setMeta('addToHistory', false);\n\teditor.view.dispatch(tr);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience shorthands (body = first section)\n// ---------------------------------------------------------------------------\n\n/** Extract BikEditorContent for just the body (before the first divider). */\nexport function extractBodyContent(editor: Editor): EditorSnapshot {\n\treturn extractSectionContent(editor, 'body');\n}\n\n// ---------------------------------------------------------------------------\n// Active formats\n// ---------------------------------------------------------------------------\n\nexport function extractActiveFormats(editor: Editor): FormatState {\n\treturn {\n\t\tbold: editor.isActive('bold'),\n\t\titalic: editor.isActive('italic'),\n\t\tunderline: editor.isActive('underline'),\n\t\tstrike: editor.isActive('strike'),\n\t\tbulletList: editor.isActive('bulletList'),\n\t\torderedList: editor.isActive('orderedList'),\n\t\tblockquote: editor.isActive('blockquote'),\n\t\tcodeBlock: editor.isActive('codeBlock'),\n\t\tlink: (() => {\n\t\t\tif (editor.isActive('link')) {\n\t\t\t\treturn { href: editor.getAttributes('link')['href'] ?? '' };\n\t\t\t}\n\t\t\tconst { $from } = editor.state.selection;\n\t\t\tif ($from.pos > 0) {\n\t\t\t\tconst before = $from.doc.resolve($from.pos);\n\t\t\t\tconst linkMark = before.marks().find((m) => m.type.name === 'link');\n\t\t\t\tif (!linkMark) {\n\t\t\t\t\tconst nodeBefore = $from.nodeBefore;\n\t\t\t\t\tconst markOnPrev = nodeBefore?.marks.find(\n\t\t\t\t\t\t(m) => m.type.name === 'link',\n\t\t\t\t\t);\n\t\t\t\t\tif (markOnPrev) {\n\t\t\t\t\t\treturn { href: markOnPrev.attrs['href'] ?? '' };\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn { href: linkMark.attrs['href'] ?? '' };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t})(),\n\t\ttextAlign: editor.isActive({ textAlign: 'center' })\n\t\t\t? 'center'\n\t\t\t: editor.isActive({ textAlign: 'right' })\n\t\t\t? 'right'\n\t\t\t: editor.isActive({ textAlign: 'justify' })\n\t\t\t? 'justify'\n\t\t\t: editor.isActive({ textAlign: 'left' })\n\t\t\t? 'left'\n\t\t\t: null,\n\t\tfontFamily: editor.getAttributes('textStyle')['fontFamily'] ?? null,\n\t\tfontSize: editor.getAttributes('textStyle')['fontSize'] ?? null,\n\t\tcolor: editor.getAttributes('textStyle')['color'] ?? null,\n\t\thighlight: editor.isActive('highlight')\n\t\t\t? editor.getAttributes('highlight')['color'] ?? 'default'\n\t\t\t: null,\n\t\tsuperscript: editor.isActive('superscript'),\n\t\tsubscript: editor.isActive('subscript'),\n\t};\n}\n\nexport function extractContent(editor: Editor): EditorSnapshot {\n\tconst html = toPortableHtml(editor.getHTML());\n\tconst normalized = normalizeHardBreaks(editor.state.doc.content);\n\tconst tempDoc = editor.schema.node('doc', null, normalized);\n\tconst text = tempDoc.textBetween(0, tempDoc.content.size, '\\n', '\\n');\n\treturn {\n\t\thtml,\n\t\ttext,\n\t\tisEmpty: editor.isEmpty,\n\t\tcharacterCount: text.length,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Section position helpers (used by insertAtSectionStart/End and appendBodyContent)\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the ProseMirror position immediately after a section's opening divider\n * (i.e. the start of the section's own content).\n * For `'body'`, returns 1 (beginning of the document).\n * Returns -1 if the named section's divider is not found.\n */\nexport function findSectionStartPos(editor: Editor, sectionId: string): number {\n\tif (sectionId === 'body') return 1;\n\tlet startPos = -1;\n\teditor.state.doc.descendants((node, pos) => {\n\t\tif (startPos !== -1) return false;\n\t\tif (\n\t\t\tnode.type.name === 'sectionDivider' &&\n\t\t\tnode.attrs['sectionId'] === sectionId\n\t\t) {\n\t\t\tstartPos = pos + node.nodeSize; // position right after the divider\n\t\t}\n\t});\n\treturn startPos;\n}\n\n/**\n * Returns the ProseMirror position at the end of a named section's content:\n * - For `'body'`: position of the first section divider, or end of document.\n * - For a named section: position of the next divider, or end of document.\n */\nexport function findSectionEndPos(editor: Editor, sectionId: string): number {\n\tconst doc = editor.state.doc;\n\tconst docSize = doc.content.size;\n\n\tif (sectionId === 'body') {\n\t\tlet firstDividerPos = -1;\n\t\tdoc.descendants((node, pos) => {\n\t\t\tif (firstDividerPos !== -1) return false;\n\t\t\tif (node.type.name === 'sectionDivider') {\n\t\t\t\tfirstDividerPos = pos;\n\t\t\t}\n\t\t});\n\t\treturn firstDividerPos !== -1 ? firstDividerPos : docSize;\n\t}\n\n\tlet passedSection = false;\n\tlet nextDividerPos = -1;\n\tdoc.descendants((node, pos) => {\n\t\tif (nextDividerPos !== -1) return false;\n\t\tif (!passedSection) {\n\t\t\tif (\n\t\t\t\tnode.type.name === 'sectionDivider' &&\n\t\t\t\tnode.attrs['sectionId'] === sectionId\n\t\t\t) {\n\t\t\t\tpassedSection = true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (node.type.name === 'sectionDivider') {\n\t\t\tnextDividerPos = pos;\n\t\t}\n\t});\n\treturn nextDividerPos !== -1 ? nextDividerPos : docSize;\n}\n"],"names":["normalizeHtml","html","replace","toPortableHtml","div","document","createElement","innerHTML","blocks","querySelectorAll","p","Array","from","childNodes","length","appendChild","lastChild","HTMLBRElement","SECTION_DIVIDER_HTML","id","getBodyHtml","_a","extractAllSectionsFromHtml","get","sections","Map","dividerRegex","dividers","match","exec","push","index","endIndex","set","slice","i","start","end","extractSectionContent","editor","startPos","findSectionStartPos","text","isEmpty","characterCount","endPos","findSectionEndPos","state","doc","container","serializer","DOMSerializer","fromSchema","schema","serializeFragment","content","textContent","trim","sectionId","descendants","node","pos","type","name","attrs","nodeSize","docSize","size","firstDividerPos","passedSection","nextDividerPos","body","s","bold","isActive","italic","underline","strike","bulletList","orderedList","blockquote","codeBlock","link","href","getAttributes","$from","selection","linkMark","resolve","marks","find","m","_c","nodeBefore","markOnPrev","_b","textAlign","fontFamily","fontSize","color","highlight","_d","superscript","subscript","getHTML","normalized","normalizeHardBreaks","tempDoc","textBetween","substring","normalised","dom","DOMParser","parseSlice","to","tr","replaceRange","view","dispatch","dividerHtml","commands","insertContentAt","updateSelection","fragment","parse","parseHtmlToNodes","replaceFrom","replaceWith","setMeta"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@tiptap/extension-character-count"),n=require("@tiptap/extension-color"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../node_modules/@tiptap/core/dist/index.js"),t=require("@tiptap/extension-character-count"),n=require("@tiptap/extension-color"),i=require("@tiptap/extension-font-family"),r=require("@tiptap/extension-highlight"),o=require("@tiptap/extension-image"),a=require("@tiptap/extension-link"),s=require("../../node_modules/@tiptap/extension-paragraph/dist/index.js"),d=require("@tiptap/extension-placeholder"),l=require("@tiptap/extension-subscript"),u=require("@tiptap/extension-superscript"),p=require("@tiptap/extension-text-align"),c=require("@tiptap/extension-text-style"),x=require("@tiptap/extension-underline"),m=require("@tiptap/starter-kit"),f=require("./FontSizeExtension.js"),h=require("./mention/MentionExtension.js"),g=require("./paste/PasteExtension.js"),S=require("./plainClipboard/PasteNormalizationExtension.js"),q=require("./sectionDivider/SectionDividerNode.js"),v=require("./sendShortcut/SendShortcutExtension.js"),E=require("./slashCommand/SlashCommandExtension.js"),b=require("./variable/VariableDecorationExtension.js");function M(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var k=M(t),j=M(n),C=M(i),P=M(r),y=M(o),D=M(a),T=M(d),w=M(l),z=M(u),A=M(p),I=M(x),N=M(m);exports.buildExtensions=function(t){var n,i,r,o,a,d,l,u,p;const x=null!==(i=null===(n=t.features)||void 0===n?void 0:n.richPaste)&&void 0!==i&&i,m=null!==(o=null===(r=t.features)||void 0===r?void 0:r.richTypography)&&void 0!==o&&o,M=null===(a=t.features)||void 0===a?void 0:a.allowedMarks,_=e=>!M||M.includes(e),H=s.default.extend({renderHTML(t){let{HTMLAttributes:n}=t;return["p",e.mergeAttributes(n,{style:"margin: 0px; padding: 0px;"}),0]}});return[...[N.default.configure({link:!1,underline:!1,paragraph:!x&&{},hardBreak:!1,bold:!!_("bold")&&{},italic:!!_("italic")&&{},strike:!!_("strike")&&{},code:!!_("code")&&{}}),e.Extension.create({name:"shiftEnterParagraph",addKeyboardShortcuts:()=>({"Shift-Enter":e=>{let{editor:t}=e;return t.commands.splitBlock()}})}),...x?[H]:[],..._("underline")?[I.default]:[],D.default.extend({addPasteRules:()=>[],addInputRules:()=>[]}).configure({openOnClick:!1,autolink:!1,linkOnPaste:!1,HTMLAttributes:{rel:"noopener noreferrer",class:"bik-link"}}),c.TextStyle,T.default.configure({placeholder:null!==(d=t.placeholder)&&void 0!==d?d:"Type a message..."}),...t.onPaste?[g.PasteExtension.configure({onPaste:t.onPaste})]:[],S.PasteNormalizationExtension.configure({preserveMarks:x}),v.SendShortcutExtension.configure({onSend:t.onSend,sendShortcut:t.sendShortcut,extraShortcuts:null!==(l=t.shortcuts)&&void 0!==l?l:[]}),b.VariableDecorationExtension,...t.maxCharacters?[k.default.configure({limit:t.maxCharacters})]:[],q.SectionDividerNode],...[...(null===(u=t.mentions)||void 0===u?void 0:u.agents)?[h.buildAgentMentionExtension(t.mentions.agents,t.onMentionSelected,t.renderMentionItem,t.renderMentionDropdown)]:[],...(null===(p=t.mentions)||void 0===p?void 0:p.teams)?[h.buildTeamMentionExtension(t.mentions.teams,t.onMentionSelected,t.renderMentionItem,t.renderMentionDropdown)]:[],...t.slashCommands?[E.buildSlashCommandExtension(t.slashCommands,t.onSlashCommandSelected,t.renderSlashCommandItem,t.renderSlashCommandDropdown)]:[]],...m?[j.default,P.default.configure({multicolor:!0}),C.default,f.FontSizeExtension,A.default.configure({types:["heading","paragraph"]}),w.default,z.default,y.default]:[j.default]]};
|
|
2
2
|
//# sourceMappingURL=buildExtensions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { ClipboardNormalizationExtension } from './plainClipboard/ClipboardNormalizationExtension';\nimport { PlainClipboardExtension } from './plainClipboard/PlainClipboardExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\t// Non-rich editors: strip rich marks + normalize blanks on paste.\n\t\t// Rich editors (email): only normalize blanks (fix Google Docs spacing).\n\t\t...(!hasRichPaste\n\t\t\t? [PlainClipboardExtension]\n\t\t\t: [ClipboardNormalizationExtension]),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["config","hasRichPaste","_b","_a","features","richPaste","hasRichTypography","_d","_c","richTypography","allowedMarks","_e","isMark","m","includes","StarterKit","configure","link","underline","bold","italic","strike","code","Underline","Link","extend","addPasteRules","addInputRules","openOnClick","autolink","linkOnPaste","HTMLAttributes","rel","class","TextStyle","Placeholder","placeholder","_f","onPaste","PasteExtension","ClipboardNormalizationExtension","PlainClipboardExtension","SendShortcutExtension","onSend","sendShortcut","extraShortcuts","_g","shortcuts","VariableDecorationExtension","maxCharacters","CharacterCount","limit","SectionDividerNode","_h","mentions","agents","buildAgentMentionExtension","onMentionSelected","renderMentionItem","renderMentionDropdown","_j","teams","buildTeamMentionExtension","slashCommands","buildSlashCommandExtension","onSlashCommandSelected","renderSlashCommandItem","renderSlashCommandDropdown","Color","Highlight","multicolor","FontFamily","FontSizeExtension","TextAlign","types","Subscript","Superscript","Image"],"mappings":"mtCAqEM,SAA0BA,yBAC/B,MAAMC,EAA6C,QAA9BC,EAAiB,QAAjBC,EAAAH,EAAOI,gBAAU,IAAAD,OAAA,EAAAA,EAAAE,iBAAa,IAAAH,GAAAA,EAC7CI,EAAuD,QAAnCC,EAAiB,QAAjBC,EAAAR,EAAOI,gBAAU,IAAAI,OAAA,EAAAA,EAAAC,sBAAkB,IAAAF,GAAAA,EAKvDG,EAA8B,QAAfC,EAAAX,EAAOI,gBAAQ,IAAAO,OAAA,EAAAA,EAAED,aAChCE,EAAUC,IACdH,GAAgBA,EAAaI,SAASD,GA0GxC,MAAO,IAxGM,CAIZE,EAAAA,QAAWC,UAAU,CACpBC,MAAM,EACNC,WAAW,EACXC,OAAMP,EAAO,SAAU,CAAE,EACzBQ,SAAQR,EAAO,WAAY,CAAE,EAC7BS,SAAQT,EAAO,WAAY,CAAE,EAC7BU,OAAMV,EAAO,SAAU,CAAE,OAEtBA,EAAO,aAAe,CAACW,EAAAA,SAAa,GAGxCC,EAAAA,QAAKC,OAAO,CACXC,cAAeA,IAAM,GACrBC,cAAeA,IAAM,KACnBX,UAAU,CACZY,aAAa,EACbC,UAAU,EACVC,aAAa,EACbC,eAAgB,CACfC,IAAK,sBACLC,MAAO,cAGTC,EAAAA,UACAC,EAAW,QAACnB,UAAU,CACrBoB,oBAAaC,EAAArC,EAAOoC,2BAAe,yBAKhCpC,EAAOsC,QACR,CAACC,EAAAA,eAAevB,UAAU,CAAEsB,QAAStC,EAAOsC,WAC5C,MAGErC,EAEF,CAACuC,EAAAA,iCADD,CAACC,2BAEJC,EAAqBA,sBAAC1B,UAAU,CAC/B2B,OAAQ3C,EAAO2C,OACfC,aAAc5C,EAAO4C,aACrBC,uBAAgBC,EAAA9C,EAAO+C,yBAAa,KAErCC,EAA2BA,+BACvBhD,EAAOiD,cACR,CAACC,EAAc,QAAClC,UAAU,CAAEmC,MAAOnD,EAAOiD,iBAC1C,GAKHG,EAAAA,uBAGyB,aACrBC,EAAArD,EAAOsD,+BAAUC,QAClB,CACAC,6BACCxD,EAAOsD,SAASC,OAChBvD,EAAOyD,kBACPzD,EAAO0D,kBACP1D,EAAO2D,wBAGR,eACCC,EAAA5D,EAAOsD,+BAAUO,OAClB,CACAC,EAAyBA,0BACxB9D,EAAOsD,SAASO,MAChB7D,EAAOyD,kBACPzD,EAAO0D,kBACP1D,EAAO2D,wBAGR,MACC3D,EAAO+D,cACR,CACAC,EAAAA,2BACChE,EAAO+D,cACP/D,EAAOiE,uBACPjE,EAAOkE,uBACPlE,EAAOmE,6BAGR,OAGmB7D,EACpB,CACA8D,EAAAA,QACAC,EAAAA,QAAUrD,UAAU,CAAEsD,YAAY,IAClCC,EAAU,QACVC,oBACAC,EAAAA,QAAUzD,UAAU,CAAE0D,MAAO,CAAC,UAAW,eACzCC,EAAS,QACTC,EAAW,QACXC,EAAK,SAEL,CAACT,EAAK,SAGV"}
|
|
1
|
+
{"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import { Extension, mergeAttributes } from '@tiptap/core';\nimport CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { PasteNormalizationExtension } from './plainClipboard/PasteNormalizationExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\tconst emailParagraph = Paragraph.extend({\n\t\trenderHTML({ HTMLAttributes }) {\n\t\t\treturn [\n\t\t\t\t'p',\n\t\t\t\tmergeAttributes(HTMLAttributes, {\n\t\t\t\t\tstyle: 'margin: 0px; padding: 0px;',\n\t\t\t\t}),\n\t\t\t\t0,\n\t\t\t];\n\t\t},\n\t});\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\tparagraph: hasRichPaste ? false : {},\n\t\t\thardBreak: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\tExtension.create({\n\t\t\tname: 'shiftEnterParagraph',\n\t\t\taddKeyboardShortcuts() {\n\t\t\t\treturn {\n\t\t\t\t\t'Shift-Enter': ({ editor }) => editor.commands.splitBlock(),\n\t\t\t\t};\n\t\t\t},\n\t\t}),\n\t\t...(hasRichPaste ? [emailParagraph] : []),\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\tPasteNormalizationExtension.configure({\n\t\t\tpreserveMarks: hasRichPaste,\n\t\t}),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["config","hasRichPaste","_b","_a","features","richPaste","hasRichTypography","_d","_c","richTypography","allowedMarks","_e","isMark","m","includes","emailParagraph","Paragraph","extend","renderHTML","_ref","HTMLAttributes","mergeAttributes","style","StarterKit","configure","link","underline","paragraph","hardBreak","bold","italic","strike","code","Extension","create","name","addKeyboardShortcuts","_ref2","editor","commands","splitBlock","Underline","Link","addPasteRules","addInputRules","openOnClick","autolink","linkOnPaste","rel","class","TextStyle","Placeholder","placeholder","_f","onPaste","PasteExtension","PasteNormalizationExtension","preserveMarks","SendShortcutExtension","onSend","sendShortcut","extraShortcuts","_g","shortcuts","VariableDecorationExtension","maxCharacters","CharacterCount","limit","SectionDividerNode","_h","mentions","agents","buildAgentMentionExtension","onMentionSelected","renderMentionItem","renderMentionDropdown","_j","teams","buildTeamMentionExtension","slashCommands","buildSlashCommandExtension","onSlashCommandSelected","renderSlashCommandItem","renderSlashCommandDropdown","Color","Highlight","multicolor","FontFamily","FontSizeExtension","TextAlign","types","Subscript","Superscript","Image"],"mappings":"2xCAsEM,SAA0BA,yBAC/B,MAAMC,EAA6C,QAA9BC,EAAiB,QAAjBC,EAAAH,EAAOI,gBAAU,IAAAD,OAAA,EAAAA,EAAAE,iBAAa,IAAAH,GAAAA,EAC7CI,EAAuD,QAAnCC,EAAiB,QAAjBC,EAAAR,EAAOI,gBAAU,IAAAI,OAAA,EAAAA,EAAAC,sBAAkB,IAAAF,GAAAA,EAKvDG,EAA8B,QAAfC,EAAAX,EAAOI,gBAAQ,IAAAO,OAAA,EAAAA,EAAED,aAChCE,EAAUC,IACdH,GAAgBA,EAAaI,SAASD,GAElCE,EAAiBC,EAAS,QAACC,OAAO,CACvCC,WAAUC,GAAmB,IAAlBC,eAAEA,GAAgBD,EAC5B,MAAO,CACN,IACAE,EAAeA,gBAACD,EAAgB,CAC/BE,MAAO,+BAER,EAEF,IAoHD,MAAO,IAjHM,CAIZC,EAAAA,QAAWC,UAAU,CACpBC,MAAM,EACNC,WAAW,EACXC,WAAW1B,GAAuB,CAAE,EACpC2B,WAAW,EACXC,OAAMjB,EAAO,SAAU,CAAE,EACzBkB,SAAQlB,EAAO,WAAY,CAAE,EAC7BmB,SAAQnB,EAAO,WAAY,CAAE,EAC7BoB,OAAMpB,EAAO,SAAU,CAAE,IAE1BqB,EAASA,UAACC,OAAO,CAChBC,KAAM,sBACNC,qBAAoBA,KACZ,CACN,cAAeC,IAAA,IAACC,OAAEA,GAAQD,EAAA,OAAKC,EAAOC,SAASC,YAAY,SAI1DvC,EAAe,CAACc,GAAkB,MAClCH,EAAO,aAAe,CAAC6B,EAAS,SAAI,GAGxCC,EAAAA,QAAKzB,OAAO,CACX0B,cAAeA,IAAM,GACrBC,cAAeA,IAAM,KACnBpB,UAAU,CACZqB,aAAa,EACbC,UAAU,EACVC,aAAa,EACb3B,eAAgB,CACf4B,IAAK,sBACLC,MAAO,cAGTC,EAAAA,UACAC,EAAW,QAAC3B,UAAU,CACrB4B,oBAAaC,EAAArD,EAAOoD,2BAAe,yBAKhCpD,EAAOsD,QACR,CAACC,EAAAA,eAAe/B,UAAU,CAAE8B,QAAStD,EAAOsD,WAC5C,GACHE,EAAAA,4BAA4BhC,UAAU,CACrCiC,cAAexD,IAEhByD,EAAqBA,sBAAClC,UAAU,CAC/BmC,OAAQ3D,EAAO2D,OACfC,aAAc5D,EAAO4D,aACrBC,uBAAgBC,EAAA9D,EAAO+D,yBAAa,KAErCC,EAA2BA,+BACvBhE,EAAOiE,cACR,CAACC,EAAc,QAAC1C,UAAU,CAAE2C,MAAOnE,EAAOiE,iBAC1C,GAKHG,EAAAA,uBAGyB,aACrBC,EAAArE,EAAOsE,+BAAUC,QAClB,CACAC,6BACCxE,EAAOsE,SAASC,OAChBvE,EAAOyE,kBACPzE,EAAO0E,kBACP1E,EAAO2E,wBAGR,eACCC,EAAA5E,EAAOsE,+BAAUO,OAClB,CACAC,EAAyBA,0BACxB9E,EAAOsE,SAASO,MAChB7E,EAAOyE,kBACPzE,EAAO0E,kBACP1E,EAAO2E,wBAGR,MACC3E,EAAO+E,cACR,CACAC,EAAAA,2BACChF,EAAO+E,cACP/E,EAAOiF,uBACPjF,EAAOkF,uBACPlF,EAAOmF,6BAGR,OAGmB7E,EACpB,CACA8E,EAAAA,QACAC,EAAAA,QAAU7D,UAAU,CAAE8D,YAAY,IAClCC,EAAU,QACVC,oBACAC,EAAAA,QAAUjE,UAAU,CAAEkE,MAAO,CAAC,UAAW,eACzCC,EAAS,QACTC,EAAW,QACXC,EAAK,SAEL,CAACT,EAAK,SAGV"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../../node_modules/@tiptap/core/dist/index.js"),t=require("@tiptap/pm/model"),r=require("@tiptap/pm/state"),a=require("@tiptap/pm/view"),n=require("./pasteUtils.js");const i=e.Extension.create({name:"pasteNormalization",addOptions:()=>({preserveMarks:!1}),addProseMirrorPlugins(){const e=this.options.preserveMarks;return[new r.Plugin({props:{decorations(e){const t=[];return e.doc.descendants(((e,r)=>{"paragraph"===e.type.name&&0===e.childCount&&t.push(a.Decoration.node(r,r+e.nodeSize,{class:"is-blank"}))})),a.DecorationSet.create(e.doc,t)},handlePaste(e,t){var r,a;const i=null===(r=t.clipboardData)||void 0===r?void 0:r.getData("text/html");if(null==i?void 0:i.includes("data-pm-slice"))return!1;const s=null===(a=t.clipboardData)||void 0===a?void 0:a.getData("text/plain");if(!s)return!1;const o=n.parseClipboardText(s,e.state.schema);return e.dispatch(e.state.tr.replaceSelection(o)),!0},clipboardTextParser:(e,t,r,a)=>n.parseClipboardText(e,a.state.schema),transformPastedHTML(e){if(e.includes("data-pm-slice"))return e;const t=document.createElement("div");return t.innerHTML=e,n.wrapInlineContent(t),n.cleanBlockBrs(t),n.stripTrailingBrs(t),t.innerHTML},transformPasted(r){let a=n.normalizeHardBreaks(r.content);return e||(a=n.stripRichMarks(a)),new t.Slice(a,r.openStart,r.openEnd)}}})]}});exports.PasteNormalizationExtension=i;
|
|
2
|
+
//# sourceMappingURL=PasteNormalizationExtension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PasteNormalizationExtension.js","sources":["../../../../../src/editor/extensions/plainClipboard/PasteNormalizationExtension.ts"],"sourcesContent":["import { Extension } from '@tiptap/core';\nimport { Slice } from '@tiptap/pm/model';\nimport { Plugin } from '@tiptap/pm/state';\nimport { Decoration, DecorationSet } from '@tiptap/pm/view';\nimport {\n\tcleanBlockBrs,\n\tnormalizeHardBreaks,\n\tparseClipboardText,\n\tstripRichMarks,\n\tstripTrailingBrs,\n\twrapInlineContent,\n} from './pasteUtils';\n\n/**\n * Unified paste normalizer for all editor channels.\n *\n * Uses ProseMirror's recommended hooks instead of overriding handlePaste:\n * - clipboardTextParser: plain text → Slice (preserves blank lines)\n * - transformPastedHTML: HTML → HTML (cleans Google Docs / Word artifacts)\n * - transformPasted: Slice → Slice (normalizes hardBreaks, strips marks)\n *\n * Also decorates empty paragraphs with an `is-blank` CSS class so the\n * editor stylesheet can give blank lines precise height without extra margins.\n */\nexport const PasteNormalizationExtension = Extension.create({\n\tname: 'pasteNormalization',\n\taddOptions() {\n\t\treturn {\n\t\t\tpreserveMarks: false,\n\t\t};\n\t},\n\taddProseMirrorPlugins() {\n\t\tconst preserveMarks = this.options.preserveMarks;\n\t\treturn [\n\t\t\tnew Plugin({\n\t\t\t\tprops: {\n\t\t\t\t\tdecorations(state) {\n\t\t\t\t\t\tconst decorations: Decoration[] = [];\n\t\t\t\t\t\tstate.doc.descendants((node, pos) => {\n\t\t\t\t\t\t\tif (node.type.name === 'paragraph' && node.childCount === 0) {\n\t\t\t\t\t\t\t\tdecorations.push(\n\t\t\t\t\t\t\t\t\tDecoration.node(pos, pos + node.nodeSize, {\n\t\t\t\t\t\t\t\t\t\tclass: 'is-blank',\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn DecorationSet.create(state.doc, decorations);\n\t\t\t\t\t},\n\n\t\t\t\t\thandlePaste(view, event) {\n\t\t\t\t\t\tconst html = event.clipboardData?.getData('text/html');\n\t\t\t\t\t\tif (html?.includes('data-pm-slice')) return false;\n\t\t\t\t\t\tconst text = event.clipboardData?.getData('text/plain');\n\t\t\t\t\t\tif (!text) return false;\n\t\t\t\t\t\tconst slice = parseClipboardText(text, view.state.schema);\n\t\t\t\t\t\tview.dispatch(view.state.tr.replaceSelection(slice));\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t},\n\n\t\t\t\t\tclipboardTextParser(text, _$context, _plain, view) {\n\t\t\t\t\t\treturn parseClipboardText(text, view.state.schema);\n\t\t\t\t\t},\n\n\t\t\t\t\ttransformPastedHTML(html) {\n\t\t\t\t\t\tif (html.includes('data-pm-slice')) return html;\n\t\t\t\t\t\tconst container = document.createElement('div');\n\t\t\t\t\t\tcontainer.innerHTML = html;\n\t\t\t\t\t\twrapInlineContent(container);\n\t\t\t\t\t\tcleanBlockBrs(container);\n\t\t\t\t\t\tstripTrailingBrs(container);\n\t\t\t\t\t\treturn container.innerHTML;\n\t\t\t\t\t},\n\n\t\t\t\t\ttransformPasted(slice) {\n\t\t\t\t\t\tlet content = normalizeHardBreaks(slice.content);\n\t\t\t\t\t\tif (!preserveMarks) {\n\t\t\t\t\t\t\tcontent = stripRichMarks(content);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn new Slice(content, slice.openStart, slice.openEnd);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t];\n\t},\n});\n"],"names":["PasteNormalizationExtension","Extension","create","name","addOptions","preserveMarks","addProseMirrorPlugins","this","options","Plugin","props","decorations","state","doc","descendants","node","pos","type","childCount","push","Decoration","nodeSize","class","DecorationSet","handlePaste","view","event","html","_a","clipboardData","getData","includes","text","_b","slice","parseClipboardText","schema","dispatch","tr","replaceSelection","clipboardTextParser","_$context","_plain","transformPastedHTML","container","document","createElement","innerHTML","wrapInlineContent","cleanBlockBrs","stripTrailingBrs","transformPasted","content","normalizeHardBreaks","stripRichMarks","Slice","openStart","openEnd"],"mappings":"kQAwBaA,EAA8BC,EAASA,UAACC,OAAO,CAC3DC,KAAM,qBACNC,WAAUA,KACF,CACNC,eAAe,IAGjBC,wBACC,MAAMD,EAAgBE,KAAKC,QAAQH,cACnC,MAAO,CACN,IAAII,EAAAA,OAAO,CACVC,MAAO,CACNC,YAAYC,GACX,MAAMD,EAA4B,GAUlC,OATAC,EAAMC,IAAIC,aAAY,CAACC,EAAMC,KACL,cAAnBD,EAAKE,KAAKd,MAA4C,IAApBY,EAAKG,YAC1CP,EAAYQ,KACXC,aAAWL,KAAKC,EAAKA,EAAMD,EAAKM,SAAU,CACzCC,MAAO,aAGT,IAEKC,EAAaA,cAACrB,OAAOU,EAAMC,IAAKF,EACvC,EAEDa,YAAYC,EAAMC,WACjB,MAAMC,EAA0B,QAAnBC,EAAAF,EAAMG,qBAAa,IAAAD,OAAA,EAAAA,EAAEE,QAAQ,aAC1C,GAAIH,aAAI,EAAJA,EAAMI,SAAS,iBAAkB,OAAO,EAC5C,MAAMC,EAA0B,QAAnBC,EAAAP,EAAMG,qBAAa,IAAAI,OAAA,EAAAA,EAAEH,QAAQ,cAC1C,IAAKE,EAAM,OAAO,EAClB,MAAME,EAAQC,EAAAA,mBAAmBH,EAAMP,EAAKb,MAAMwB,QAElD,OADAX,EAAKY,SAASZ,EAAKb,MAAM0B,GAAGC,iBAAiBL,KACtC,CACP,EAEDM,oBAAmBA,CAACR,EAAMS,EAAWC,EAAQjB,IACrCU,EAAkBA,mBAACH,EAAMP,EAAKb,MAAMwB,QAG5CO,oBAAoBhB,GACnB,GAAIA,EAAKI,SAAS,iBAAkB,OAAOJ,EAC3C,MAAMiB,EAAYC,SAASC,cAAc,OAKzC,OAJAF,EAAUG,UAAYpB,EACtBqB,EAAiBA,kBAACJ,GAClBK,EAAaA,cAACL,GACdM,EAAgBA,iBAACN,GACVA,EAAUG,SACjB,EAEDI,gBAAgBjB,GACf,IAAIkB,EAAUC,EAAAA,oBAAoBnB,EAAMkB,SAIxC,OAHK/C,IACJ+C,EAAUE,EAAAA,eAAeF,IAEnB,IAAIG,EAAKA,MAACH,EAASlB,EAAMsB,UAAWtB,EAAMuB,QAClD,KAIJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@tiptap/pm/model");const t=new Set(["P","LI","H1","H2","H3","H4","H5","H6","TD","TH","PRE"]),r=new Set(["bold","italic","strike","underline","code"]);exports.BLOCK_SELECTOR=
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@tiptap/pm/model");const t=new Set(["P","LI","H1","H2","H3","H4","H5","H6","TD","TH","PRE"]),r="p,div,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,table,pre",o=new Set(["bold","italic","strike","underline","code"]);exports.BLOCK_SELECTOR=r,exports.TEXTBLOCK_TAGS=t,exports.cleanBlockBrs=function(e){const r=Array.from(e.querySelectorAll("br"));for(const o of r){let r=!1,n=o.parentElement;for(;n&&n!==e;){if(t.has(n.tagName)){r=!0;break}n=n.parentElement}r||o.parentElement.replaceChild(document.createElement("p"),o)}},exports.normalizeHardBreaks=function t(r){const o=[];return r.forEach((r=>{var n,l;if("paragraph"===r.type.name){if(1===r.childCount&&"hardBreak"===(null===(n=r.firstChild)||void 0===n?void 0:n.type.name))return void o.push(r.type.create(r.attrs));if(r.childCount>1&&"hardBreak"===(null===(l=r.lastChild)||void 0===l?void 0:l.type.name)){const t=[];return r.content.forEach(((e,o,n)=>{n<r.childCount-1&&t.push(e)})),void o.push(r.copy(e.Fragment.from(t)))}}r.isBlock&&!r.isLeaf?o.push(r.copy(t(r.content))):o.push(r)})),e.Fragment.from(o)},exports.parseClipboardText=function(t,r){const o=t.split("\n").map((e=>r.node("paragraph",null,e?[r.text(e)]:[])));return new e.Slice(e.Fragment.fromArray(o),1,1)},exports.stripRichMarks=function t(r){const n=[];return r.forEach((e=>{if(e.isText){const t=e.marks.filter((e=>o.has(e.type.name)));n.push(t.length===e.marks.length?e:e.mark(t))}else n.push(e.copy(t(e.content)))})),e.Fragment.from(n)},exports.stripTrailingBrs=function(e){for(const t of["p","div"])for(const r of Array.from(e.querySelectorAll(t))){const e=r.lastElementChild;"BR"===(null==e?void 0:e.tagName)&&r.childNodes.length>1&&e.remove()}},exports.wrapInlineContent=function(e){if(e.querySelector(r))return;const t=Array.from(e.childNodes),o=[[]];for(let e=0;e<t.length;e++){const r=t[e];r instanceof HTMLBRElement&&t[e+1]instanceof HTMLBRElement?(o.push([]),e++):o[o.length-1].push(r)}for(;e.firstChild;)e.firstChild.remove();for(const t of o){const r=document.createElement("p");for(const e of t)r.appendChild(e);e.appendChild(r)}};
|
|
2
2
|
//# sourceMappingURL=pasteUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pasteUtils.js","sources":["../../../../../src/editor/extensions/plainClipboard/pasteUtils.ts"],"sourcesContent":["import { Fragment, Node as PMNode } from '@tiptap/pm/model';\n\nexport const TEXTBLOCK_TAGS = new Set([\n\t'P',\n\t'LI',\n\t'H1',\n\t'H2',\n\t'H3',\n\t'H4',\n\t'H5',\n\t'H6',\n\t'TD',\n\t'TH',\n\t'PRE',\n]);\n\nexport const BLOCK_SELECTOR =\n\t'p,div,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,table,pre';\n\nconst BASIC_MARKS = new Set(['bold', 'italic', 'strike', 'underline', 'code']);\n\n/**\n * Replace `<br>` elements that sit at block level (not inside a textblock\n * like `<p>`, `<li>`, etc.) with empty `<p></p>` elements. Google Docs\n * puts standalone `<br>` tags between paragraphs to represent blank lines.\n
|
|
1
|
+
{"version":3,"file":"pasteUtils.js","sources":["../../../../../src/editor/extensions/plainClipboard/pasteUtils.ts"],"sourcesContent":["import { Fragment, Node as PMNode, Schema, Slice } from '@tiptap/pm/model';\n\nexport const TEXTBLOCK_TAGS = new Set([\n\t'P',\n\t'LI',\n\t'H1',\n\t'H2',\n\t'H3',\n\t'H4',\n\t'H5',\n\t'H6',\n\t'TD',\n\t'TH',\n\t'PRE',\n]);\n\nexport const BLOCK_SELECTOR =\n\t'p,div,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,table,pre';\n\nconst BASIC_MARKS = new Set(['bold', 'italic', 'strike', 'underline', 'code']);\n\n/**\n * When pasted HTML is entirely inline (no block elements), split at\n * `<br><br>` boundaries into `<p>` elements. Single `<br>` stays as a\n * line break inside the paragraph.\n */\nexport function wrapInlineContent(container: HTMLElement): void {\n\tif (container.querySelector(BLOCK_SELECTOR)) return;\n\n\tconst children = Array.from(container.childNodes);\n\tconst groups: Node[][] = [[]];\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst node = children[i];\n\t\tif (\n\t\t\tnode instanceof HTMLBRElement &&\n\t\t\tchildren[i + 1] instanceof HTMLBRElement\n\t\t) {\n\t\t\tgroups.push([]);\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tgroups[groups.length - 1].push(node);\n\t}\n\n\twhile (container.firstChild) container.firstChild.remove();\n\n\tfor (const group of groups) {\n\t\tconst p = document.createElement('p');\n\t\tfor (const node of group) p.appendChild(node);\n\t\tcontainer.appendChild(p);\n\t}\n}\n\n/**\n * Replace `<br>` elements that sit at block level (not inside a textblock\n * like `<p>`, `<li>`, etc.) with empty `<p></p>` elements. Google Docs\n * puts standalone `<br>` tags between paragraphs to represent blank lines.\n */\nexport function cleanBlockBrs(container: HTMLElement): void {\n\tconst brs = Array.from(container.querySelectorAll('br'));\n\tfor (const br of brs) {\n\t\tlet insideTextblock = false;\n\t\tlet el = br.parentElement;\n\t\twhile (el && el !== container) {\n\t\t\tif (TEXTBLOCK_TAGS.has(el.tagName)) {\n\t\t\t\tinsideTextblock = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tel = el.parentElement;\n\t\t}\n\t\tif (!insideTextblock) {\n\t\t\tbr.parentElement!.replaceChild(document.createElement('p'), br);\n\t\t}\n\t}\n}\n\n/**\n * Strip trailing `<br>` from content paragraphs in the DOM.\n * Google Docs adds a `<br>` at the end of non-empty paragraphs as a cursor\n * placeholder. Without this, ProseMirror creates a trailing hardBreak that\n * adds a visual blank line at the bottom of the paragraph.\n */\nexport function stripTrailingBrs(container: HTMLElement): void {\n\tfor (const tag of ['p', 'div']) {\n\t\tfor (const el of Array.from(container.querySelectorAll(tag))) {\n\t\t\tconst last = el.lastElementChild;\n\t\t\tif (last?.tagName === 'BR' && el.childNodes.length > 1) {\n\t\t\t\tlast.remove();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Convert paragraphs containing only a hardBreak into truly empty paragraphs.\n * Google Docs represents blank lines as `<p><br></p>` — ProseMirror parses\n * the `<br>` as a hardBreak node, which renders double-height. Converting to\n * an empty paragraph (childCount 0) gives a single-height blank line.\n *\n * Does NOT collapse consecutive empties — that would destroy user intent.\n */\nexport function normalizeHardBreaks(fragment: Fragment): Fragment {\n\tconst nodes: PMNode[] = [];\n\tfragment.forEach((node) => {\n\t\tif (node.type.name === 'paragraph') {\n\t\t\tif (node.childCount === 1 && node.firstChild?.type.name === 'hardBreak') {\n\t\t\t\tnodes.push(node.type.create(node.attrs));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (node.childCount > 1 && node.lastChild?.type.name === 'hardBreak') {\n\t\t\t\tconst children: PMNode[] = [];\n\t\t\t\tnode.content.forEach((child, _offset, index) => {\n\t\t\t\t\tif (index < node.childCount - 1) children.push(child);\n\t\t\t\t});\n\t\t\t\tnodes.push(node.copy(Fragment.from(children)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (node.isBlock && !node.isLeaf) {\n\t\t\tnodes.push(node.copy(normalizeHardBreaks(node.content)));\n\t\t} else {\n\t\t\tnodes.push(node);\n\t\t}\n\t});\n\treturn Fragment.from(nodes);\n}\n\n/**\n * Strip \"rich\" marks (link, textStyle, highlight, etc.) while keeping\n * basic marks (bold, italic, strike, underline, code) that messaging\n * channels can represent.\n */\nexport function stripRichMarks(fragment: Fragment): Fragment {\n\tconst nodes: PMNode[] = [];\n\tfragment.forEach((node) => {\n\t\tif (node.isText) {\n\t\t\tconst kept = node.marks.filter((m) => BASIC_MARKS.has(m.type.name));\n\t\t\tnodes.push(kept.length === node.marks.length ? node : node.mark(kept));\n\t\t} else {\n\t\t\tnodes.push(node.copy(stripRichMarks(node.content)));\n\t\t}\n\t});\n\treturn Fragment.from(nodes);\n}\n\n/**\n * Parse plain clipboard text into a ProseMirror Slice, preserving blank\n * lines as empty paragraphs. This replaces ProseMirror's default text\n * parser which uses `\\n+` and loses all blank lines.\n *\n * Best for channels with compact paragraph gaps (e.g. WhatsApp) where\n * blank lines need structural representation as empty paragraphs.\n */\nexport function parseClipboardText(text: string, schema: Schema): Slice {\n\tconst paragraphs = text\n\t\t.split('\\n')\n\t\t.map((line) =>\n\t\t\tschema.node('paragraph', null, line ? [schema.text(line)] : []),\n\t\t);\n\treturn new Slice(Fragment.fromArray(paragraphs), 1, 1);\n}\n\n/**\n * Parse plain clipboard text into a single paragraph with hardBreaks for\n * every `\\n`. Produces the same structure as Gmail's Cmd+Shift+V output:\n * one block element with `<br>` for line breaks and `<br><br>` for blank\n * lines. This gives identical html and text output to Gmail.\n */\nexport function parseClipboardTextAsBreaks(\n\ttext: string,\n\tschema: Schema,\n): Slice {\n\tconst trimmed = text.replace(/\\n+$/, '');\n\tif (!trimmed) {\n\t\treturn new Slice(Fragment.from(schema.node('paragraph', null, [])), 1, 1);\n\t}\n\tconst hardBreak = schema.nodes['hardBreak'];\n\tconst lines = trimmed.split('\\n');\n\tconst children: PMNode[] = [];\n\tlines.forEach((line, i) => {\n\t\tif (line) children.push(schema.text(line));\n\t\tif (i < lines.length - 1 && hardBreak) {\n\t\t\tchildren.push(hardBreak.create());\n\t\t}\n\t});\n\treturn new Slice(\n\t\tFragment.from(\n\t\t\tschema.node('paragraph', null, children.length ? children : []),\n\t\t),\n\t\t1,\n\t\t1,\n\t);\n}\n"],"names":["TEXTBLOCK_TAGS","Set","BLOCK_SELECTOR","BASIC_MARKS","container","brs","Array","from","querySelectorAll","br","insideTextblock","el","parentElement","has","tagName","replaceChild","document","createElement","normalizeHardBreaks","fragment","nodes","forEach","node","type","name","childCount","_a","firstChild","push","create","attrs","_b","lastChild","children","content","child","_offset","index","copy","Fragment","isBlock","isLeaf","text","schema","paragraphs","split","map","line","Slice","fromArray","stripRichMarks","isText","kept","marks","filter","m","length","mark","tag","last","lastElementChild","childNodes","remove","querySelector","groups","i","HTMLBRElement","group","p","appendChild"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../core/dist/index.js"),r=" ",t=e.Node.create({name:"paragraph",priority:1e3,addOptions:()=>({HTMLAttributes:{}}),group:"block",content:"inline*",parseHTML:()=>[{tag:"p"}],renderHTML({HTMLAttributes:r}){return["p",e.mergeAttributes(this.options.HTMLAttributes,r),0]},parseMarkdown:(e,t)=>{const n=e.tokens||[];if(1===n.length&&"image"===n[0].type)return t.parseChildren([n[0]]);const a=t.parseInline(n);return!(1===n.length&&"text"===n[0].type&&(n[0].raw===r||n[0].text===r||" "===n[0].raw||" "===n[0].text))||1!==a.length||"text"!==a[0].type||a[0].text!==r&&" "!==a[0].text?t.createNode("paragraph",void 0,a):t.createNode("paragraph",void 0,[])},renderMarkdown:(e,t,n)=>{var a,o;if(!e)return"";const s=Array.isArray(e.content)?e.content:[];if(0===s.length){const e=Array.isArray(null==(a=null==n?void 0:n.previousNode)?void 0:a.content)?n.previousNode.content:[];return"paragraph"===(null==(o=null==n?void 0:n.previousNode)?void 0:o.type)&&0===e.length?r:""}return t.renderChildren(s)},addCommands(){return{setParagraph:()=>({commands:e})=>e.setNode(this.name)}},addKeyboardShortcuts(){return{"Mod-Alt-0":()=>this.editor.commands.setParagraph()}}}),n=t;exports.Paragraph=t,exports.default=n;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../node_modules/@tiptap/extension-paragraph/dist/index.js"],"sourcesContent":["// src/paragraph.ts\nimport { mergeAttributes, Node } from \"@tiptap/core\";\nvar EMPTY_PARAGRAPH_MARKDOWN = \" \";\nvar NBSP_CHAR = \"\\xA0\";\nvar Paragraph = Node.create({\n name: \"paragraph\",\n priority: 1e3,\n addOptions() {\n return {\n HTMLAttributes: {}\n };\n },\n group: \"block\",\n content: \"inline*\",\n parseHTML() {\n return [{ tag: \"p\" }];\n },\n renderHTML({ HTMLAttributes }) {\n return [\"p\", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];\n },\n parseMarkdown: (token, helpers) => {\n const tokens = token.tokens || [];\n if (tokens.length === 1 && tokens[0].type === \"image\") {\n return helpers.parseChildren([tokens[0]]);\n }\n const content = helpers.parseInline(tokens);\n const hasExplicitEmptyParagraphMarker = tokens.length === 1 && tokens[0].type === \"text\" && (tokens[0].raw === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].text === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].raw === NBSP_CHAR || tokens[0].text === NBSP_CHAR);\n if (hasExplicitEmptyParagraphMarker && content.length === 1 && content[0].type === \"text\" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) {\n return helpers.createNode(\"paragraph\", void 0, []);\n }\n return helpers.createNode(\"paragraph\", void 0, content);\n },\n renderMarkdown: (node, h, ctx) => {\n var _a, _b;\n if (!node) {\n return \"\";\n }\n const content = Array.isArray(node.content) ? node.content : [];\n if (content.length === 0) {\n const previousContent = Array.isArray((_a = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _a.content) ? ctx.previousNode.content : [];\n const previousNodeIsEmptyParagraph = ((_b = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _b.type) === \"paragraph\" && previousContent.length === 0;\n return previousNodeIsEmptyParagraph ? EMPTY_PARAGRAPH_MARKDOWN : \"\";\n }\n return h.renderChildren(content);\n },\n addCommands() {\n return {\n setParagraph: () => ({ commands }) => {\n return commands.setNode(this.name);\n }\n };\n },\n addKeyboardShortcuts() {\n return {\n \"Mod-Alt-0\": () => this.editor.commands.setParagraph()\n };\n }\n});\n\n// src/index.ts\nvar index_default = Paragraph;\nexport {\n Paragraph,\n index_default as default\n};\n//# sourceMappingURL=index.js.map"],"names":["EMPTY_PARAGRAPH_MARKDOWN","Paragraph","Node","create","name","priority","addOptions","HTMLAttributes","group","content","parseHTML","tag","renderHTML","mergeAttributes","this","options","parseMarkdown","token","helpers","tokens","length","type","parseChildren","parseInline","raw","text","createNode","renderMarkdown","node","h","ctx","_a","_b","Array","isArray","previousContent","previousNode","renderChildren","addCommands","setParagraph","commands","setNode","addKeyboardShortcuts","editor","index_default"],"mappings":"8GAEIA,EAA2B,SAE3BC,EAAYC,EAAIA,KAACC,OAAO,CAC1BC,KAAM,YACNC,SAAU,IACVC,WAAU,KACD,CACLC,eAAgB,CAAE,IAGtBC,MAAO,QACPC,QAAS,UACTC,UAAS,IACA,CAAC,CAAEC,IAAK,MAEjBC,YAAWL,eAAEA,IACX,MAAO,CAAC,IAAKM,EAAAA,gBAAgBC,KAAKC,QAAQR,eAAgBA,GAAiB,EAC5E,EACDS,cAAe,CAACC,EAAOC,KACrB,MAAMC,EAASF,EAAME,QAAU,GAC/B,GAAsB,IAAlBA,EAAOC,QAAmC,UAAnBD,EAAO,GAAGE,KACnC,OAAOH,EAAQI,cAAc,CAACH,EAAO,KAEvC,MAAMV,EAAUS,EAAQK,YAAYJ,GAEpC,QAD0D,IAAlBA,EAAOC,QAAmC,SAAnBD,EAAO,GAAGE,OAAoBF,EAAO,GAAGK,MAAQxB,GAA4BmB,EAAO,GAAGM,OAASzB,GAvBlJ,MAuB8KmB,EAAO,GAAGK,KAvBxL,MAuB6ML,EAAO,GAAGM,QACzK,IAAnBhB,EAAQW,QAAoC,SAApBX,EAAQ,GAAGY,MAAoBZ,EAAQ,GAAGgB,OAASzB,GAxBtG,MAwBkIS,EAAQ,GAAGgB,KAGlJP,EAAQQ,WAAW,iBAAa,EAAQjB,GAFtCS,EAAQQ,WAAW,iBAAa,EAAQ,GAEM,EAEzDC,eAAgB,CAACC,EAAMC,EAAGC,KACxB,IAAIC,EAAIC,EACR,IAAKJ,EACH,MAAO,GAET,MAAMnB,EAAUwB,MAAMC,QAAQN,EAAKnB,SAAWmB,EAAKnB,QAAU,GAC7D,GAAuB,IAAnBA,EAAQW,OAAc,CACxB,MAAMe,EAAkBF,MAAMC,QAA0D,OAAjDH,EAAY,MAAPD,OAAc,EAASA,EAAIM,mBAAwB,EAASL,EAAGtB,SAAWqB,EAAIM,aAAa3B,QAAU,GAEjJ,MADqH,eAA7B,OAAjDuB,EAAY,MAAPF,OAAc,EAASA,EAAIM,mBAAwB,EAASJ,EAAGX,OAAoD,IAA3Bc,EAAgBf,OAC9GpB,EAA2B,EAClE,CACD,OAAO6B,EAAEQ,eAAe5B,EAAQ,EAElC6B,cACE,MAAO,CACLC,aAAc,IAAM,EAAGC,cACdA,EAASC,QAAQ3B,KAAKV,MAGlC,EACDsC,uBACE,MAAO,CACL,YAAa,IAAM5B,KAAK6B,OAAOH,SAASD,eAE3C,IAICK,EAAgB3C"}
|
|
@@ -728,4 +728,6 @@ export interface BikEditorProps {
|
|
|
728
728
|
minHeight?: string;
|
|
729
729
|
/** Maximum height. The editor scrolls internally when exceeded. CSS value, e.g. `'400px'`. */
|
|
730
730
|
maxHeight?: string;
|
|
731
|
+
/** Gap between consecutive paragraphs. CSS value, defaults to `'4px'`. */
|
|
732
|
+
paragraphGap?: string;
|
|
731
733
|
}
|
|
@@ -14,10 +14,14 @@ import { EditorSnapshot, FormatState } from './BikEditor.types';
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function normalizeHtml(html: string): string;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Make editor HTML portable across email clients and chat renderers.
|
|
18
|
+
*
|
|
19
|
+
* 1. Double a trailing `<br>` in content paragraphs so the blank line is
|
|
20
|
+
* visible. A single trailing `<br>` before `</p>` is swallowed by most
|
|
21
|
+
* email clients; `<br><br>` renders as a visible blank line (matches
|
|
22
|
+
* Gmail's output).
|
|
23
|
+
* 2. Convert empty `<p></p>` to `<p><br></p>` — email clients collapse
|
|
24
|
+
* zero-height paragraphs without a `<br>`.
|
|
21
25
|
*/
|
|
22
26
|
export declare function toPortableHtml(html: string): string;
|
|
23
27
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
1
2
|
import type { ReactNode } from 'react';
|
|
2
3
|
import type { EditorFeatures, EditorSnapshot, KeyboardShortcut, MentionDropdownRenderProps, MentionItem, PasteData, SlashCommandDropdownRenderProps, SlashCommandItem } from '../BikEditor.types';
|
|
3
4
|
interface ExtensionConfig {
|
|
@@ -33,5 +34,5 @@ interface ExtensionConfig {
|
|
|
33
34
|
renderSlashCommandItem?: (item: SlashCommandItem, isActive: boolean) => ReactNode;
|
|
34
35
|
renderSlashCommandDropdown?: (props: SlashCommandDropdownRenderProps) => ReactNode;
|
|
35
36
|
}
|
|
36
|
-
export declare function buildExtensions(config: ExtensionConfig): (import("@tiptap/core").Extension<import("@tiptap/starter-kit").StarterKitOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-underline").UnderlineOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-link").LinkOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-text-style").TextStyleOptions, any> |
|
|
37
|
+
export declare function buildExtensions(config: ExtensionConfig): (import("@tiptap/core").Node<import("@tiptap/extension-paragraph").ParagraphOptions, any> | Extension<import("@tiptap/starter-kit").StarterKitOptions, any> | Extension<any, any> | import("@tiptap/core").Mark<import("@tiptap/extension-underline").UnderlineOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-link").LinkOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-text-style").TextStyleOptions, any> | Extension<import("@tiptap/extensions").PlaceholderOptions, any> | Extension<import("@tiptap/extensions").CharacterCountOptions, import("@tiptap/extensions").CharacterCountStorage> | import("@tiptap/core").Node<any, any> | import("@tiptap/core").Node<import("@tiptap/extension-mention").MentionOptions<any, import("@tiptap/extension-mention").MentionNodeAttrs>, any> | Extension<import("@tiptap/extension-text-style").ColorOptions, any> | import("@tiptap/core").Mark<import("@tiptap/extension-highlight").HighlightOptions, any> | Extension<import("@tiptap/extension-text-align").TextAlignOptions, any> | import("@tiptap/core").Node<import("@tiptap/extension-image").ImageOptions, any>)[];
|
|
37
38
|
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
/**
|
|
3
|
+
* Unified paste normalizer for all editor channels.
|
|
4
|
+
*
|
|
5
|
+
* Uses ProseMirror's recommended hooks instead of overriding handlePaste:
|
|
6
|
+
* - clipboardTextParser: plain text → Slice (preserves blank lines)
|
|
7
|
+
* - transformPastedHTML: HTML → HTML (cleans Google Docs / Word artifacts)
|
|
8
|
+
* - transformPasted: Slice → Slice (normalizes hardBreaks, strips marks)
|
|
9
|
+
*
|
|
10
|
+
* Also decorates empty paragraphs with an `is-blank` CSS class so the
|
|
11
|
+
* editor stylesheet can give blank lines precise height without extra margins.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PasteNormalizationExtension: Extension<any, any>;
|
|
@@ -1,21 +1,53 @@
|
|
|
1
|
-
import { Fragment } from '@tiptap/pm/model';
|
|
1
|
+
import { Fragment, Schema, Slice } from '@tiptap/pm/model';
|
|
2
2
|
export declare const TEXTBLOCK_TAGS: Set<string>;
|
|
3
3
|
export declare const BLOCK_SELECTOR = "p,div,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,table,pre";
|
|
4
|
+
/**
|
|
5
|
+
* When pasted HTML is entirely inline (no block elements), split at
|
|
6
|
+
* `<br><br>` boundaries into `<p>` elements. Single `<br>` stays as a
|
|
7
|
+
* line break inside the paragraph.
|
|
8
|
+
*/
|
|
9
|
+
export declare function wrapInlineContent(container: HTMLElement): void;
|
|
4
10
|
/**
|
|
5
11
|
* Replace `<br>` elements that sit at block level (not inside a textblock
|
|
6
12
|
* like `<p>`, `<li>`, etc.) with empty `<p></p>` elements. Google Docs
|
|
7
13
|
* puts standalone `<br>` tags between paragraphs to represent blank lines.
|
|
8
|
-
* Without this, ProseMirror parses them as top-level hardBreak nodes.
|
|
9
14
|
*/
|
|
10
15
|
export declare function cleanBlockBrs(container: HTMLElement): void;
|
|
11
16
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* Strip trailing `<br>` from content paragraphs in the DOM.
|
|
18
|
+
* Google Docs adds a `<br>` at the end of non-empty paragraphs as a cursor
|
|
19
|
+
* placeholder. Without this, ProseMirror creates a trailing hardBreak that
|
|
20
|
+
* adds a visual blank line at the bottom of the paragraph.
|
|
21
|
+
*/
|
|
22
|
+
export declare function stripTrailingBrs(container: HTMLElement): void;
|
|
23
|
+
/**
|
|
24
|
+
* Convert paragraphs containing only a hardBreak into truly empty paragraphs.
|
|
25
|
+
* Google Docs represents blank lines as `<p><br></p>` — ProseMirror parses
|
|
26
|
+
* the `<br>` as a hardBreak node, which renders double-height. Converting to
|
|
27
|
+
* an empty paragraph (childCount 0) gives a single-height blank line.
|
|
28
|
+
*
|
|
29
|
+
* Does NOT collapse consecutive empties — that would destroy user intent.
|
|
14
30
|
*/
|
|
15
|
-
export declare function
|
|
31
|
+
export declare function normalizeHardBreaks(fragment: Fragment): Fragment;
|
|
16
32
|
/**
|
|
17
|
-
* Strip
|
|
18
|
-
* basic
|
|
19
|
-
*
|
|
33
|
+
* Strip "rich" marks (link, textStyle, highlight, etc.) while keeping
|
|
34
|
+
* basic marks (bold, italic, strike, underline, code) that messaging
|
|
35
|
+
* channels can represent.
|
|
20
36
|
*/
|
|
21
37
|
export declare function stripRichMarks(fragment: Fragment): Fragment;
|
|
38
|
+
/**
|
|
39
|
+
* Parse plain clipboard text into a ProseMirror Slice, preserving blank
|
|
40
|
+
* lines as empty paragraphs. This replaces ProseMirror's default text
|
|
41
|
+
* parser which uses `\n+` and loses all blank lines.
|
|
42
|
+
*
|
|
43
|
+
* Best for channels with compact paragraph gaps (e.g. WhatsApp) where
|
|
44
|
+
* blank lines need structural representation as empty paragraphs.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseClipboardText(text: string, schema: Schema): Slice;
|
|
47
|
+
/**
|
|
48
|
+
* Parse plain clipboard text into a single paragraph with hardBreaks for
|
|
49
|
+
* every `\n`. Produces the same structure as Gmail's Cmd+Shift+V output:
|
|
50
|
+
* one block element with `<br>` for line breaks and `<br><br>` for blank
|
|
51
|
+
* lines. This gives identical html and text output to Gmail.
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseClipboardTextAsBreaks(text: string, schema: Schema): Slice;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsxs as t,jsx as n}from"react/jsx-runtime";import{getMarkRange as e}from"../node_modules/@tiptap/core/dist/index.js";import{useEditor as o,EditorContent as
|
|
1
|
+
import{jsxs as t,jsx as n}from"react/jsx-runtime";import{getMarkRange as e}from"../node_modules/@tiptap/core/dist/index.js";import{useEditor as o,EditorContent as r}from"@tiptap/react";import{forwardRef as i,useRef as l,useEffect as s,useImperativeHandle as c}from"react";import{BikEditorShell as a}from"./BikEditor.styles.js";import{DEFAULT_FORMAT_STATE as u}from"./BikEditor.types.js";import{buildSectionedContent as d,normalizeHtml as m,extractContent as g,extractActiveFormats as v,extractSectionContent as h,extractBodyContent as f,insertInlineHtml as p,setSectionContentInEditor as C,findSectionEndPos as S,findSectionStartPos as k}from"./BikEditor.utils.js";import{buildExtensions as y}from"./extensions/buildExtensions.js";import{LinkBubbleMenu as x}from"./floating/LinkBubbleMenu.js";function B(t){return Object.entries(t).map((t=>{let[n,e]=t;return`${n.replace(/([A-Z])/g,(t=>`-${t.toLowerCase()}`))}:${e}`})).join(";")}const b=i(((i,b)=>{var A,I,j,L,E,O,T;const{initialContent:w,sections:F,features:H,disabled:M,maxCharacters:z,shortcuts:R,sendShortcut:D,mentions:N,slashCommands:U,link:P,onPaste:$,onReady:q,onChange:G,onSend:J,onFocus:V,onBlur:W,onSelectionChange:Z,minHeight:_,maxHeight:K,paragraphGap:Q,style:X,className:Y,editorClassName:tt,editorStyle:nt}=i,et=(null==F?void 0:F.length)?d(m(null!=w?w:""),F.map((t=>Object.assign(Object.assign({},t),{content:m(t.content)})))):m(null!=w?w:""),ot=l([]),rt=l(!1),it=l(q);it.current=q;const lt=l(G);lt.current=G;const st=l(Z);st.current=Z;const ct=l(V);ct.current=V;const at=l(W);at.current=W;const ut=l(J);ut.current=J;const dt=l(null!==(A=null==N?void 0:N.agents)&&void 0!==A?A:[]);dt.current=null!==(I=null==N?void 0:N.agents)&&void 0!==I?I:[];const mt=l(null!==(j=null==N?void 0:N.teams)&&void 0!==j?j:[]);mt.current=null!==(L=null==N?void 0:N.teams)&&void 0!==L?L:[];const gt=l(null!==(E=null==U?void 0:U.items)&&void 0!==E?E:[]);gt.current=null!==(O=null==U?void 0:U.items)&&void 0!==O?O:[];const vt=o({extensions:y({features:H,placeholder:i.placeholder,maxCharacters:z,hasSections:(null!==(T=null==F?void 0:F.length)&&void 0!==T?T:0)>0,mentions:{agents:N?dt:void 0,teams:N?mt:void 0},slashCommands:U?gt:void 0,onPaste:$,onSend:J?t=>{var n;return null===(n=ut.current)||void 0===n?void 0:n.call(ut,t)}:void 0,sendShortcut:D,shortcuts:R,onMentionSelected:null==N?void 0:N.onSelect,onSlashCommandSelected:null==U?void 0:U.onSelect,renderMentionItem:null==N?void 0:N.renderItem,renderMentionDropdown:null==N?void 0:N.renderDropdown,renderSlashCommandItem:null==U?void 0:U.renderItem,renderSlashCommandDropdown:null==U?void 0:U.renderDropdown}),content:et,editable:!M,immediatelyRender:!1,editorProps:{attributes:Object.assign(Object.assign({},tt?{class:tt}:{}),nt?{style:B(nt)}:{})},onUpdate:t=>{let{editor:n}=t;var e;null===(e=lt.current)||void 0===e||e.call(lt,g(n))},onTransaction:t=>{let{editor:n}=t;var e;null===(e=st.current)||void 0===e||e.call(st,v(n))},onFocus:()=>{var t;return null===(t=ct.current)||void 0===t?void 0:t.call(ct)},onBlur:()=>{var t;return null===(t=at.current)||void 0===t?void 0:t.call(at)}});function ht(t){vt?t(vt):ot.current.push(t)}return s((()=>{var t;if(vt){if(ot.current.length>0){ot.current.splice(0).forEach((t=>t(vt)))}rt.current||(rt.current=!0,null===(t=it.current)||void 0===t||t.call(it))}}),[vt]),c(b,(()=>({focus:t=>ht((n=>n.commands.focus(t))),blur:()=>ht((t=>t.commands.blur())),clearContent:()=>ht((t=>t.commands.clearContent(!0))),setContent:t=>{const n=m(t);ht((t=>t.commands.setContent(n,{emitUpdate:!0})))},insertContent:t=>{const n=m(t);ht((t=>t.commands.insertContent(n)))},insertInlineContent:t=>{ht((n=>p(n,t)))},insertAtStart:t=>{const n=m(t);ht((t=>t.commands.insertContentAt(1,n)))},insertBlock:t=>{const n=m(t);ht((t=>t.commands.insertContentAt(t.state.doc.content.size,n)))},appendInline:t=>{const n=m(t);ht((t=>t.commands.insertContentAt(t.state.doc.content.size-1,n)))},insertAtEnd(t){this.insertBlock(t)},appendContent(t){this.appendInline(t)},getContent:()=>vt?g(vt):{html:"",text:"",isEmpty:!0,characterCount:0},getJSON:()=>{var t;return null!==(t=null==vt?void 0:vt.getJSON())&&void 0!==t?t:null},getMentions:()=>{const t=new Set,n=new Set;return vt?(vt.state.doc.descendants((e=>{"mentionAgent"===e.type.name&&null!=e.attrs.id?t.add(String(e.attrs.id)):"mentionTeam"===e.type.name&&null!=e.attrs.id&&n.add(String(e.attrs.id))})),{agentIds:Array.from(t),teamIds:Array.from(n)}):{agentIds:[],teamIds:[]}},getCursorPosition:()=>{if(!vt)return{from:0,to:0};const{from:t,to:n}=vt.state.selection;return{from:t,to:n}},insertAtPosition:(t,n)=>{const e=m(n);ht((n=>n.commands.insertContentAt(t,e)))},getSelectedText:()=>{if(!vt)return"";const{state:t}=vt,{from:n,to:o,empty:r}=t.selection;if(!r)return t.doc.textBetween(n,o," ");const i=t.schema.marks.link;if(i){const o=e(t.doc.resolve(n),i);if(o)return t.doc.textBetween(o.from,o.to," ")}return""},getCharacterCount:()=>{var t,n,e,o;return{count:null!==(o=null!==(e=null===(n=null===(t=null==vt?void 0:vt.storage.characterCount)||void 0===t?void 0:t.characters)||void 0===n?void 0:n.call(t))&&void 0!==e?e:null==vt?void 0:vt.getText().length)&&void 0!==o?o:0,limit:null!=z?z:null}},getSectionContent:t=>vt?h(vt,t):{html:"",text:"",isEmpty:!0,characterCount:0},setSectionContent:(t,n)=>{const e=m(n);ht((n=>C(n,t,e)))},focusSection:t=>{ht((n=>{if("body"===t){let t=n.state.doc.content.size-1;return n.state.doc.descendants(((e,o)=>("sectionDivider"===e.type.name&&t===n.state.doc.content.size-1&&(t=o-1),t===n.state.doc.content.size-1))),t<1&&(t=1),void n.chain().focus().setTextSelection(t).run()}let e=-1;n.state.doc.descendants(((n,o)=>{if(-1!==e)return!1;"sectionDivider"===n.type.name&&n.attrs.sectionId===t&&(e=o+n.nodeSize+1)})),-1!==e&&n.chain().focus().setTextSelection(e).run()}))},clearSection:t=>{ht((n=>C(n,t,"<p></p>")))},getBodyContent:()=>vt?f(vt):{html:"",text:"",isEmpty:!0,characterCount:0},setBodyContent:t=>{const n=m(t);ht((t=>C(t,"body",n)))},setBodyAndSections:(t,n)=>{const e=d(m(t),n.map((t=>({id:t.id,content:m(t.content)}))));ht((t=>t.commands.setContent(e,{emitUpdate:!0})))},appendBodyContent:t=>{const n=m(t);ht((t=>{const e=S(t,"body");t.commands.insertContentAt(e-1,n)}))},insertAtSectionStart:(t,n)=>{const e=m(n);ht((n=>{const o=k(n,t);-1!==o&&n.commands.insertContentAt(o,e)}))},insertAtSectionEnd:(t,n)=>{const e=m(n);ht((n=>{const o=S(n,t);n.commands.insertContentAt(o,e)}))},getActiveFormats:()=>vt?v(vt):u,actions:{toggleBold:()=>null==vt?void 0:vt.chain().focus().toggleBold().run(),toggleItalic:()=>null==vt?void 0:vt.chain().focus().toggleItalic().run(),toggleUnderline:()=>null==vt?void 0:vt.chain().focus().toggleUnderline().run(),toggleStrike:()=>null==vt?void 0:vt.chain().focus().toggleStrike().run(),toggleBulletList:()=>null==vt?void 0:vt.chain().focus().toggleBulletList().run(),toggleOrderedList:()=>null==vt?void 0:vt.chain().focus().toggleOrderedList().run(),toggleBlockquote:()=>null==vt?void 0:vt.chain().focus().toggleBlockquote().run(),toggleCodeBlock:()=>null==vt?void 0:vt.chain().focus().toggleCodeBlock().run(),setTextAlign:t=>null==vt?void 0:vt.chain().focus().setTextAlign(t).run(),setFontFamily:t=>null==vt?void 0:vt.chain().focus().setFontFamily(t).run(),setFontSize:t=>null==vt?void 0:vt.chain().focus().setFontSize(t).run(),setColor:t=>null==vt?void 0:vt.chain().focus().setColor(t).run(),setHighlight:t=>null==vt?void 0:vt.chain().focus().toggleHighlight({color:t}).run(),unsetColor:()=>null==vt?void 0:vt.chain().focus().unsetColor().run(),unsetHighlight:()=>null==vt?void 0:vt.chain().focus().unsetHighlight().run(),setLink:(t,n)=>{vt&&(void 0!==n?vt.chain().focus().extendMarkRange("link").command((e=>{let{tr:o,state:r}=e;const{from:i,to:l}=o.selection,s=r.schema.marks.link;if(!s)return!1;const c=r.schema.text(n,[s.create({href:t})]),a=r.schema.text(" ");return o.replaceWith(i,l,[c,a]),!0})).run():vt.chain().focus().extendMarkRange("link").setLink({href:t}).insertContent(" ").run())},updateLink:t=>null==vt?void 0:vt.chain().focus().extendMarkRange("link").setLink({href:t}).run(),removeLink:()=>null==vt?void 0:vt.chain().focus().extendMarkRange("link").unsetLink().run(),insertEmoji:t=>null==vt?void 0:vt.chain().focus().insertContent(t).run(),insertVariable:t=>null==vt?void 0:vt.chain().focus().insertContent(`{{${t}}}`).run(),insertHtml:t=>null==vt?void 0:vt.commands.insertContent(m(t)),undo:()=>null==vt?void 0:vt.chain().focus().undo().run(),redo:()=>null==vt?void 0:vt.chain().focus().redo().run(),canUndo:()=>{var t;return null!==(t=null==vt?void 0:vt.can().undo())&&void 0!==t&&t},canRedo:()=>{var t;return null!==(t=null==vt?void 0:vt.can().redo())&&void 0!==t&&t},toggleSuperscript:()=>null==vt?void 0:vt.chain().focus().toggleSuperscript().run(),toggleSubscript:()=>null==vt?void 0:vt.chain().focus().toggleSubscript().run(),insertImage:t=>null==vt?void 0:vt.chain().focus().setImage({src:t}).run()}})),[vt]),vt?t(a,Object.assign({minHeight:_,maxHeight:K,paragraphGap:Q,style:X,className:Y},{children:[n(r,{editor:vt}),n(x,{editor:vt,renderLinkTooltip:null==P?void 0:P.renderTooltip})]})):null}));b.displayName="BikEditor";export{b as BikEditor};
|
|
2
2
|
//# sourceMappingURL=BikEditor.js.map
|