@pixerate/editor 0.26.0 → 0.26.1

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.
@@ -12,7 +12,7 @@ var _chunkHDYMLS3Xcjs = require('../chunk-HDYMLS3X.cjs');
12
12
 
13
13
 
14
14
 
15
- var _chunkNFVBT3UZcjs = require('../chunk-NFVBT3UZ.cjs');
15
+ var _chunk66IMF6ODcjs = require('../chunk-66IMF6OD.cjs');
16
16
  require('../chunk-BVP3TSLH.cjs');
17
17
  require('../chunk-IFHKKVXH.cjs');
18
18
 
@@ -710,7 +710,7 @@ var _state = require('@tiptap/pm/state');
710
710
  var toEditor = (target) => "commands" in target ? target : target.editor;
711
711
  function replaceWithText(editor, from, to, text) {
712
712
  const { state, view } = editor;
713
- const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to, _chunkNFVBT3UZcjs.plainTextToSlice.call(void 0, text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
713
+ const tr = /[\r\n]/.test(text) ? state.tr.replace(from, to, _chunk66IMF6ODcjs.plainTextToSlice.call(void 0, text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
714
714
  view.dispatch(tr.scrollIntoView());
715
715
  }
716
716
  function selectionInfo(editor) {
@@ -743,7 +743,7 @@ function createRichTextToolProvider(target, options) {
743
743
  },
744
744
  handler: ({ format = "text" }) => {
745
745
  const ed = editor();
746
- const content = format === "html" ? ed.getHTML() : format === "markdown" ? _chunkNFVBT3UZcjs.getEditorMarkdown.call(void 0, ed) : _chunkNFVBT3UZcjs.getEditorText.call(void 0, ed);
746
+ const content = format === "html" ? ed.getHTML() : format === "markdown" ? _chunk66IMF6ODcjs.getEditorMarkdown.call(void 0, ed) : _chunk66IMF6ODcjs.getEditorText.call(void 0, ed);
747
747
  return { format, content, selection: selectionInfo(ed), size: ed.state.doc.content.size };
748
748
  }
749
749
  };
@@ -12,7 +12,7 @@ import {
12
12
  getEditorMarkdown,
13
13
  getEditorText,
14
14
  plainTextToSlice
15
- } from "../chunk-JOIREKL7.js";
15
+ } from "../chunk-UKI64KUD.js";
16
16
  import "../chunk-O5RYH2PG.js";
17
17
  import "../chunk-SWE3NPOU.js";
18
18
  import {
@@ -377,6 +377,88 @@ function extractMentionsFromDoc(docOrEditor) {
377
377
 
378
378
  // src/serializers/mentions.ts
379
379
  var MARKDOWN_MENTION_SPLIT_REGEX = /(```[\s\S]*?```|~~~[\s\S]*?~~~|<pre[\s\S]*?<\/pre>|<code[\s\S]*?<\/code>|`[^`\n]+`|<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["']|data-mention=["'][^"']*["'])[^>]*>[\s\S]*?<\/span>|<[^>]+>|\]\([^)]+\)|(?:https?|file|vscode|cursor|mailto):\/\/[^\s<)]+)/gi;
380
+ var MENTION_SPAN_ATTRIBUTE = /\b(?:data-type=["']mention["']|class=["'][^"'>]*pixerate-mention-node[^"'>]*["']|data-mention=["'][^"'>]*["'])/gi;
381
+ var URL_SCHEMES = ["https://", "http://", "file://", "vscode://", "cursor://", "mailto://"];
382
+ var URL_STOP = /[\s<)]/;
383
+ function splitMarkdownMentionParts(text) {
384
+ const lower = asciiLower(text);
385
+ const n = text.length;
386
+ const cache = /* @__PURE__ */ new Map();
387
+ const next = (needle, from) => {
388
+ const cached = cache.get(needle);
389
+ if (cached && cached.from <= from && (cached.at === -1 || cached.at >= from)) return cached.at;
390
+ const at = lower.indexOf(needle, from);
391
+ cache.set(needle, { from, at });
392
+ return at;
393
+ };
394
+ let attrFrom = -1;
395
+ let attrAt = -1;
396
+ const nextAttribute = (from) => {
397
+ if (attrFrom !== -1 && (attrAt === -1 || attrAt >= from) && attrFrom <= from) return attrAt;
398
+ MENTION_SPAN_ATTRIBUTE.lastIndex = from;
399
+ attrAt = _nullishCoalesce(_optionalChain([MENTION_SPAN_ATTRIBUTE, 'access', _13 => _13.exec, 'call', _14 => _14(text), 'optionalAccess', _15 => _15.index]), () => ( -1));
400
+ attrFrom = from;
401
+ return attrAt;
402
+ };
403
+ const matchAt = (i) => {
404
+ const c = text[i];
405
+ if (c === "`" || c === "~") {
406
+ const fence = c.repeat(3);
407
+ if (text.startsWith(fence, i)) {
408
+ const close = next(fence, i + 3);
409
+ if (close !== -1) return close + 3;
410
+ }
411
+ if (c === "`") {
412
+ const tick = next("`", i + 1);
413
+ const newline = next("\n", i + 1);
414
+ if (tick > i + 1 && (newline === -1 || tick < newline)) return tick + 1;
415
+ }
416
+ return -1;
417
+ }
418
+ if (c === "<") {
419
+ for (const tag of ["pre", "code"]) {
420
+ if (lower.startsWith(`<${tag}`, i)) {
421
+ const close = next(`</${tag}>`, i + tag.length + 1);
422
+ if (close !== -1) return close + tag.length + 3;
423
+ }
424
+ }
425
+ const gt = next(">", i + 1);
426
+ if (lower.startsWith("<span", i) && !/\w/.test(_nullishCoalesce(text[i + 5], () => ( ""))) && gt !== -1) {
427
+ const attr = nextAttribute(i + 5);
428
+ if (attr !== -1 && attr < gt) {
429
+ const close = next("</span>", gt + 1);
430
+ if (close !== -1) return close + 7;
431
+ }
432
+ }
433
+ return gt > i + 1 ? gt + 1 : -1;
434
+ }
435
+ if (c === "]") {
436
+ if (text[i + 1] !== "(") return -1;
437
+ const close = next(")", i + 2);
438
+ return close > i + 2 ? close + 1 : -1;
439
+ }
440
+ for (const scheme of URL_SCHEMES) {
441
+ if (!lower.startsWith(scheme, i)) continue;
442
+ let end = i + scheme.length;
443
+ while (end < n && !URL_STOP.test(text[end])) end++;
444
+ return end > i + scheme.length ? end : -1;
445
+ }
446
+ return -1;
447
+ };
448
+ const parts = [];
449
+ let last = 0;
450
+ for (let i = 0; i < n; ) {
451
+ const end = matchAt(i);
452
+ if (end === -1) {
453
+ i++;
454
+ continue;
455
+ }
456
+ parts.push(text.slice(last, i), text.slice(i, end));
457
+ last = i = end;
458
+ }
459
+ parts.push(text.slice(last));
460
+ return parts;
461
+ }
380
462
  function stripMarkdownCode(content) {
381
463
  if (!content || typeof content !== "string") return "";
382
464
  let text = replaceDelimited(content, /```/g, "```");
@@ -427,7 +509,7 @@ function extractMentions(content, options = {}) {
427
509
  if (details) {
428
510
  const suffix = _chunkBVP3TSLHcjs.readRoleSuffix.call(void 0, textOnly, regex.lastIndex, options.roles);
429
511
  if (suffix) regex.lastIndex = suffix.end;
430
- const key = `${handle}\0${_nullishCoalesce(_optionalChain([suffix, 'optionalAccess', _13 => _13.role]), () => ( ""))}`;
512
+ const key = `${handle}\0${_nullishCoalesce(_optionalChain([suffix, 'optionalAccess', _16 => _16.role]), () => ( ""))}`;
431
513
  if (handle && !seen.has(key)) {
432
514
  seen.add(key);
433
515
  detailed.push(suffix ? { handle, role: suffix.role } : { handle });
@@ -487,7 +569,7 @@ function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
487
569
  const allMentionables = options.mentionables || [];
488
570
  const defaultType = options.defaultType || "user";
489
571
  const resolveMention = options.resolveMention;
490
- const parts = content.split(MARKDOWN_MENTION_SPLIT_REGEX);
572
+ const parts = splitMarkdownMentionParts(content);
491
573
  return parts.map((part) => {
492
574
  if (!part) return "";
493
575
  if (part.startsWith("```") || part.startsWith("~~~") || part.startsWith("`") || part.startsWith("](") || /^(?:https?|file|vscode|cursor|mailto):\/\//i.test(part) || /^<pre[\s>]/i.test(part) || /^<code[\s>]/i.test(part) || /^<[^>]+>$/i.test(part) || /^<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["']|data-mention=["'][^"']*["'])/i.test(
@@ -503,25 +585,25 @@ function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
503
585
  if (!rawHandle) return match2;
504
586
  const lowerHandle = rawHandle.toLowerCase();
505
587
  const entity = allMentionables.find((e) => {
506
- if (_optionalChain([e, 'access', _14 => _14.label, 'optionalAccess', _15 => _15.toLowerCase, 'call', _16 => _16()]) === lowerHandle) return true;
507
- if (_optionalChain([e, 'access', _17 => _17.handle, 'optionalAccess', _18 => _18.toLowerCase, 'call', _19 => _19()]) === lowerHandle) return true;
508
- if (_optionalChain([e, 'access', _20 => _20.name, 'optionalAccess', _21 => _21.toLowerCase, 'call', _22 => _22()]) === lowerHandle) return true;
509
- if (_optionalChain([e, 'access', _23 => _23.id, 'optionalAccess', _24 => _24.toLowerCase, 'call', _25 => _25()]) === lowerHandle) return true;
588
+ if (_optionalChain([e, 'access', _17 => _17.label, 'optionalAccess', _18 => _18.toLowerCase, 'call', _19 => _19()]) === lowerHandle) return true;
589
+ if (_optionalChain([e, 'access', _20 => _20.handle, 'optionalAccess', _21 => _21.toLowerCase, 'call', _22 => _22()]) === lowerHandle) return true;
590
+ if (_optionalChain([e, 'access', _23 => _23.name, 'optionalAccess', _24 => _24.toLowerCase, 'call', _25 => _25()]) === lowerHandle) return true;
591
+ if (_optionalChain([e, 'access', _26 => _26.id, 'optionalAccess', _27 => _27.toLowerCase, 'call', _28 => _28()]) === lowerHandle) return true;
510
592
  if (e.type === "agent" && `agent${(e.handle || e.label || "").toLowerCase()}` === lowerHandle) return true;
511
- if (_optionalChain([e, 'access', _26 => _26.aliases, 'optionalAccess', _27 => _27.some, 'call', _28 => _28(
593
+ if (_optionalChain([e, 'access', _29 => _29.aliases, 'optionalAccess', _30 => _30.some, 'call', _31 => _31(
512
594
  (a) => a.toLowerCase() === lowerHandle || e.type === "agent" && `agent${a.toLowerCase()}` === lowerHandle
513
595
  )])) {
514
596
  return true;
515
597
  }
516
598
  return false;
517
599
  });
518
- let id = _optionalChain([entity, 'optionalAccess', _29 => _29.id]);
519
- let label = _optionalChain([entity, 'optionalAccess', _30 => _30.label]) || _optionalChain([entity, 'optionalAccess', _31 => _31.handle]) || rawHandle;
520
- let type = _optionalChain([entity, 'optionalAccess', _32 => _32.type]);
521
- let avatarUrl = _optionalChain([entity, 'optionalAccess', _33 => _33.avatarUrl]);
522
- let color = _optionalChain([entity, 'optionalAccess', _34 => _34.color]) || _optionalChain([entity, 'optionalAccess', _35 => _35.hexColor]);
600
+ let id = _optionalChain([entity, 'optionalAccess', _32 => _32.id]);
601
+ let label = _optionalChain([entity, 'optionalAccess', _33 => _33.label]) || _optionalChain([entity, 'optionalAccess', _34 => _34.handle]) || rawHandle;
602
+ let type = _optionalChain([entity, 'optionalAccess', _35 => _35.type]);
603
+ let avatarUrl = _optionalChain([entity, 'optionalAccess', _36 => _36.avatarUrl]);
604
+ let color = _optionalChain([entity, 'optionalAccess', _37 => _37.color]) || _optionalChain([entity, 'optionalAccess', _38 => _38.hexColor]);
523
605
  if (!entity) {
524
- const customResolved = _optionalChain([resolveMention, 'optionalCall', _36 => _36(rawHandle)]);
606
+ const customResolved = _optionalChain([resolveMention, 'optionalCall', _39 => _39(rawHandle)]);
525
607
  if (customResolved) {
526
608
  id = _nullishCoalesce(customResolved.id, () => ( id));
527
609
  label = _nullishCoalesce(_nullishCoalesce(customResolved.label, () => ( customResolved.handle)), () => ( label));
@@ -564,7 +646,7 @@ function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
564
646
  let match;
565
647
  while ((match = regex.exec(part)) !== null) {
566
648
  const suffix = _chunkBVP3TSLHcjs.readRoleSuffix.call(void 0, part, regex.lastIndex, options.roles);
567
- const rendered = render(match[0], match[1], match[2], match[3], _optionalChain([suffix, 'optionalAccess', _37 => _37.role]));
649
+ const rendered = render(match[0], match[1], match[2], match[3], _optionalChain([suffix, 'optionalAccess', _40 => _40.role]));
568
650
  if (suffix && rendered !== match[0]) regex.lastIndex = suffix.end;
569
651
  out += part.slice(last, match.index) + rendered;
570
652
  last = regex.lastIndex;
@@ -702,8 +784,8 @@ function sanitizeEmbeddedHtml(html, stripDisallowed = false) {
702
784
  }
703
785
  var defaultHtmlInline = md.renderer.rules.html_inline;
704
786
  var defaultHtmlBlock = md.renderer.rules.html_block;
705
- md.renderer.rules.html_inline = (tokens, idx, options, env, self) => _optionalChain([env, 'optionalAccess', _38 => _38.allowUnsafeHtml]) ? defaultHtmlInline(tokens, idx, options, env, self) : sanitizeEmbeddedHtml(tokens[idx].content);
706
- md.renderer.rules.html_block = (tokens, idx, options, env, self) => _optionalChain([env, 'optionalAccess', _39 => _39.allowUnsafeHtml]) ? defaultHtmlBlock(tokens, idx, options, env, self) : sanitizeEmbeddedHtml(tokens[idx].content);
787
+ md.renderer.rules.html_inline = (tokens, idx, options, env, self) => _optionalChain([env, 'optionalAccess', _41 => _41.allowUnsafeHtml]) ? defaultHtmlInline(tokens, idx, options, env, self) : sanitizeEmbeddedHtml(tokens[idx].content);
788
+ md.renderer.rules.html_block = (tokens, idx, options, env, self) => _optionalChain([env, 'optionalAccess', _42 => _42.allowUnsafeHtml]) ? defaultHtmlBlock(tokens, idx, options, env, self) : sanitizeEmbeddedHtml(tokens[idx].content);
707
789
  function preprocessMarkdownFileLinks(content) {
708
790
  if (!content) return "";
709
791
  return content.replace(
@@ -719,23 +801,23 @@ function preprocessMarkdownFileLinks(content) {
719
801
  function markdownToTipTapHtml(markdown, options) {
720
802
  if (!markdown) return "<p></p>";
721
803
  let content = markdown;
722
- if (_optionalChain([options, 'optionalAccess', _40 => _40.preprocessFileLinks])) {
804
+ if (_optionalChain([options, 'optionalAccess', _43 => _43.preprocessFileLinks])) {
723
805
  content = preprocessMarkdownFileLinks(content);
724
806
  }
725
- if (_optionalChain([options, 'optionalAccess', _41 => _41.preprocessMentions]) || _optionalChain([options, 'optionalAccess', _42 => _42.mentionables]) || _optionalChain([options, 'optionalAccess', _43 => _43.preprocessOptions])) {
807
+ if (_optionalChain([options, 'optionalAccess', _44 => _44.preprocessMentions]) || _optionalChain([options, 'optionalAccess', _45 => _45.mentionables]) || _optionalChain([options, 'optionalAccess', _46 => _46.preprocessOptions])) {
726
808
  const mentionOpts = options.preprocessOptions || {
727
809
  mentionables: options.mentionables
728
810
  };
729
811
  content = preprocessMarkdownMentions(content, mentionOpts);
730
812
  }
731
- return md.render(content, { allowUnsafeHtml: _optionalChain([options, 'optionalAccess', _44 => _44.allowUnsafeHtml]) });
813
+ return md.render(content, { allowUnsafeHtml: _optionalChain([options, 'optionalAccess', _47 => _47.allowUnsafeHtml]) });
732
814
  }
733
815
  function getEditorMarkdown(editor, options = {}) {
734
816
  if (!editor) return "";
735
- const markdown = _optionalChain([editor, 'access', _45 => _45.storage, 'optionalAccess', _46 => _46.markdown]);
736
- if (typeof _optionalChain([markdown, 'optionalAccess', _47 => _47.getMarkdown]) === "function") {
737
- const doc = _optionalChain([editor, 'access', _48 => _48.state, 'optionalAccess', _49 => _49.doc]);
738
- if (doc && typeof _optionalChain([markdown, 'access', _50 => _50.serializer, 'optionalAccess', _51 => _51.serialize]) === "function") {
817
+ const markdown = _optionalChain([editor, 'access', _48 => _48.storage, 'optionalAccess', _49 => _49.markdown]);
818
+ if (typeof _optionalChain([markdown, 'optionalAccess', _50 => _50.getMarkdown]) === "function") {
819
+ const doc = _optionalChain([editor, 'access', _51 => _51.state, 'optionalAccess', _52 => _52.doc]);
820
+ if (doc && typeof _optionalChain([markdown, 'access', _53 => _53.serializer, 'optionalAccess', _54 => _54.serialize]) === "function") {
739
821
  const resolved = _chunkIFHKKVXHcjs.resolveTrackedChanges.call(void 0, doc, _nullishCoalesce(options.trackedChanges, () => ( "accepted")));
740
822
  if (resolved !== doc) return markdown.serializer.serialize(resolved);
741
823
  }
@@ -760,5 +842,6 @@ function getEditorMarkdown(editor, options = {}) {
760
842
 
761
843
 
762
844
 
763
- exports.MARKDOWN_MENTION_SPLIT_REGEX = MARKDOWN_MENTION_SPLIT_REGEX; exports.stripMarkdownCode = stripMarkdownCode; exports.extractMentions = extractMentions; exports.parseMentionSegments = parseMentionSegments; exports.preprocessMarkdownMentions = preprocessMarkdownMentions; exports.escapeHtml = escapeHtml; exports.plainTextToTipTapHtml = plainTextToTipTapHtml; exports.plainTextToSlice = plainTextToSlice; exports.htmlToPlainText = htmlToPlainText; exports.getEditorText = getEditorText; exports.extractMentionsFromDoc = extractMentionsFromDoc; exports.sanitizeHtml = sanitizeHtml; exports.preprocessMarkdownFileLinks = preprocessMarkdownFileLinks; exports.markdownToTipTapHtml = markdownToTipTapHtml; exports.getEditorMarkdown = getEditorMarkdown;
764
- //# sourceMappingURL=chunk-NFVBT3UZ.cjs.map
845
+
846
+ exports.MARKDOWN_MENTION_SPLIT_REGEX = MARKDOWN_MENTION_SPLIT_REGEX; exports.splitMarkdownMentionParts = splitMarkdownMentionParts; exports.stripMarkdownCode = stripMarkdownCode; exports.extractMentions = extractMentions; exports.parseMentionSegments = parseMentionSegments; exports.preprocessMarkdownMentions = preprocessMarkdownMentions; exports.escapeHtml = escapeHtml; exports.plainTextToTipTapHtml = plainTextToTipTapHtml; exports.plainTextToSlice = plainTextToSlice; exports.htmlToPlainText = htmlToPlainText; exports.getEditorText = getEditorText; exports.extractMentionsFromDoc = extractMentionsFromDoc; exports.sanitizeHtml = sanitizeHtml; exports.preprocessMarkdownFileLinks = preprocessMarkdownFileLinks; exports.markdownToTipTapHtml = markdownToTipTapHtml; exports.getEditorMarkdown = getEditorMarkdown;
847
+ //# sourceMappingURL=chunk-66IMF6OD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-66IMF6OD.cjs","../src/serializers/plain-text.ts","../src/serializers/html-scan.ts","../src/serializers/mentions.ts","../src/serializers/markdown.ts"],"names":["match"],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-66IMF6OD.cjs","sourcesContent":[null,"import { DOMSerializer, Slice, Fragment, Node as ProsemirrorNode, type Schema } from \"@tiptap/pm/model\";\nimport {\n BLOCK_CHANGE_ATTRIBUTE,\n MOVE_ID_ATTRIBUTE,\n resolveTrackedChanges,\n type TrackedChangesView,\n} from \"../extensions/track-changes\";\nimport { findTagSpans, replaceTagSpans, stripTags } from \"./html-scan\";\n\nexport interface PlainTextOptions {\n /**\n * How tracked changes (`TrackChanges` extension) appear in the text.\n * Defaults to `\"accepted\"`: deleted text is dropped and inserted text kept,\n * so suggested deletions never leak into exported text. `\"original\"` drops\n * insertions and keeps deletions; `\"all\"` keeps both.\n */\n trackedChanges?: TrackedChangesView;\n}\n\n/**\n * Match tracked deletion / insertion elements. They never nest within\n * themselves, so a lazy match is enough.\n */\nconst TRACKED_DELETION_ATTRIBUTE = /\\bdata-change-type=[\"']deletion[\"']/i;\nconst TRACKED_INSERTION_ATTRIBUTE = /\\bdata-change-type=[\"']insertion[\"']/i;\n\nfunction stripTrackedChanges(html: string, view: TrackedChangesView): string {\n if (view === \"all\" || !html.includes(\"data-change-type\")) return html;\n return view === \"accepted\"\n ? replaceTagSpans(html, \"del\", TRACKED_DELETION_ATTRIBUTE, () => \"\")\n : replaceTagSpans(html, \"ins\", TRACKED_INSERTION_ATTRIBUTE, () => \"\");\n}\n\n/**\n * Escapes characters that have syntactic meaning in HTML text.\n */\nexport function escapeHtml(text: string): string {\n if (!text) return \"\";\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n/**\n * Matches strings that are already TipTap paragraph HTML (start with `<p>` and\n * end with `</p>`), as opposed to plain text that merely mentions a `<p>` tag.\n */\nconst PARAGRAPH_HTML_REGEX = /^\\s*<p[\\s>][\\s\\S]*<\\/p>\\s*$/i;\n\n/**\n * Converts a plain-text string with newlines into HTML paragraphs suitable\n * for TipTap initial content or setContent, preserving line breaks.\n * Input that is already paragraph HTML (`<p>…</p>`) is passed through as-is.\n */\nexport function plainTextToTipTapHtml(text: string): string {\n if (!text) return \"<p></p>\";\n if (PARAGRAPH_HTML_REGEX.test(text)) {\n return text;\n }\n return text\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\r/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => `<p>${escapeHtml(line)}</p>`)\n .join(\"\");\n}\n\n/**\n * Builds an open ProseMirror slice with one paragraph per line, so inserting it\n * splits the surrounding paragraph like typing Enter would. Use it for\n * plain-text paste handling (`clipboardTextParser`) and literal text insertion.\n */\nexport function plainTextToSlice(text: string, schema: Schema): Slice {\n const nodes: ProsemirrorNode[] = [];\n text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n nodes.push(ProsemirrorNode.fromJSON(schema, nodeJson));\n } catch {\n // Ignore invalid node\n }\n });\n return Slice.maxOpen(Fragment.fromArray(nodes));\n}\n\n/**\n * Block-level elements that start a new line in plain-text output.\n */\nconst BLOCK_TAGS = new Set([\n \"P\", \"DIV\", \"LI\", \"UL\", \"OL\", \"PRE\", \"BLOCKQUOTE\", \"TABLE\", \"TR\",\n \"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\", \"HR\",\n]);\n\nconst MENTION_TYPE_ATTRIBUTE = /\\bdata-type=[\"']mention[\"']/i;\n\nfunction getHtmlAttribute(attrs: string, name: string): string | undefined {\n const match = new RegExp(`\\\\b${name}=(?:\"([^\"]*)\"|'([^']*)')`, \"i\").exec(\n attrs,\n );\n return match ? (match[1] ?? match[2]) : undefined;\n}\n\n/**\n * Parses HTML into an inert fragment. Content of a `<template>` element is not\n * rendered, so `<img onerror>` and similar handlers never run.\n */\nfunction parseHtmlFragment(html: string): DocumentFragment {\n const template = document.createElement(\"template\");\n template.innerHTML = html;\n return template.content;\n}\n\nfunction decodeHtmlEntities(text: string): string {\n return text\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&#0*39;/g, \"'\")\n .replace(/&nbsp;/g, \"\\u00a0\")\n .replace(/&amp;/g, \"&\");\n}\n\nconst CELL_TAGS = new Set([\"TD\", \"TH\"]);\n\n/**\n * Walks DOM nodes and emits one line per *innermost* block, so nested blocks\n * (e.g. TipTap's `<li><p>…</p></li>`) are not repeated. A table row is one\n * line of tab-separated cells: a cell's paragraphs are joined with spaces and\n * a cell spanning several columns is followed by empty cells.\n */\nfunction blockLines(nodes: readonly ChildNode[]): string[] {\n const lines: string[] = [];\n let current: string | null = null;\n const flush = () => {\n if (current !== null) {\n lines.push(current);\n current = null;\n }\n };\n const visit = (node: Node) => {\n if (node.nodeType === 3) {\n const value = node.nodeValue ?? \"\";\n // Skip formatting whitespace between blocks.\n if (current === null && value.trim() === \"\") return;\n current = (current ?? \"\") + value;\n return;\n }\n if (node.nodeType !== 1) return;\n const element = node as Element;\n const tag = element.tagName.toUpperCase();\n if (tag === \"BR\") {\n current = (current ?? \"\") + \"\\n\";\n return;\n }\n if (tag === \"TR\") {\n flush();\n const cells: string[] = [];\n element.childNodes.forEach((child) => {\n if (child.nodeType !== 1 || !CELL_TAGS.has((child as Element).tagName.toUpperCase())) return;\n const cell = child as Element;\n cells.push(\n blockLines(Array.from(cell.childNodes))\n .filter((line) => line !== \"\")\n .join(\" \")\n .replace(/[\\t\\n]+/g, \" \"),\n );\n const span = parseInt(cell.getAttribute(\"colspan\") ?? \"1\", 10);\n for (let i = 1; i < Math.min(Number.isFinite(span) ? span : 1, 1000); i++) cells.push(\"\");\n });\n lines.push(cells.join(\"\\t\"));\n return;\n }\n if (!BLOCK_TAGS.has(tag)) {\n node.childNodes.forEach(visit);\n return;\n }\n flush();\n const linesBefore = lines.length;\n node.childNodes.forEach(visit);\n flush();\n // An empty block (e.g. <p></p>) is an empty line.\n if (lines.length === linesBefore && tag !== \"HR\" && tag !== \"TABLE\") {\n lines.push(\"\");\n }\n };\n nodes.forEach(visit);\n flush();\n return lines;\n}\n\n/**\n * Extracts plain text from an HTML string or TipTap HTML, preserving newlines\n * between block tags. Table rows become lines of tab-separated cells.\n */\nexport function htmlToPlainText(html: string, options: PlainTextOptions = {}): string {\n if (!html) return \"\";\n\n // Normalize mentions so data-label is preserved as @label even if textContent is empty\n const trackedResolved = stripTrackedChanges(\n html,\n options.trackedChanges ?? \"accepted\",\n );\n const mentionNormalized = replaceTagSpans(trackedResolved, \"span\", MENTION_TYPE_ATTRIBUTE, (match, attrs) => {\n const label =\n getHtmlAttribute(attrs, \"data-label\") || getHtmlAttribute(attrs, \"data-id\");\n const role = getHtmlAttribute(attrs, \"data-role\");\n // A role is written as `@label:role`, the text syntax `extractMentions` reads.\n const validRole = role && /^[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9])?$/.test(role) ? role : \"\";\n return label ? `@${label}${validRole ? `:${validRole}` : \"\"}` : match;\n });\n\n if (typeof document !== \"undefined\") {\n const fragment = parseHtmlFragment(mentionNormalized);\n fragment\n .querySelectorAll(\"br.ProseMirror-trailingBreak\")\n .forEach((br) => br.remove());\n\n return blockLines(Array.from(fragment.childNodes)).join(\"\\n\");\n }\n\n // Node.js fallback regex. Table cells are tab-separated and rows end lines\n // (a cell's own closing paragraph is dropped first so it does not end the line).\n const text = mentionNormalized\n .replace(/<\\/p>\\s*<\\/t([dh])>/gi, \"</t$1>\")\n .replace(/<\\/t[dh]>\\s*(?=<t[dh][\\s>])/gi, \"\\t\")\n .replace(/<\\/tr>/gi, \"\\n\")\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n // A closing block, plus any closing wrappers right after it, ends one line.\n .replace(\n /<\\/(?:p|div|li|pre|blockquote|h[1-6])>(?:\\s*<\\/(?:li|ul|ol|blockquote|div)>)*/gi,\n \"\\n\",\n );\n return decodeHtmlEntities(stripTags(text)).replace(/\\n$/, \"\");\n}\n\n/**\n * Whole-block insertions/deletions (`blockChange.operation`, from document\n * comparison) have no `<ins>`/`<del>` elements to strip, and resolving a\n * moved paragraph removes its whole block, so documents with either are\n * resolved first and serialized from the resolved document.\n */\nfunction resolvedBlockOperationsHtml(editor: any, view: TrackedChangesView): string | null {\n const doc: ProsemirrorNode | undefined = editor?.state?.doc;\n if (!doc || view === \"all\" || typeof document === \"undefined\") return null;\n let found = false;\n doc.descendants((node) => {\n if (found) return false;\n if (node.isTextblock) {\n if (node.attrs?.[BLOCK_CHANGE_ATTRIBUTE]?.operation) found = true;\n else node.forEach((child) => {\n if (child.marks.some((mark) => mark.attrs?.[MOVE_ID_ATTRIBUTE])) found = true;\n });\n return false;\n }\n return true;\n });\n if (!found) return null;\n const resolved = resolveTrackedChanges(doc, view);\n const container = document.createElement(\"div\");\n container.appendChild(DOMSerializer.fromSchema(doc.type.schema).serializeFragment(resolved.content));\n return container.innerHTML;\n}\n\n/**\n * Extracts plain text from a TipTap editor instance, preserving newline separators\n * between block nodes and converting mention nodes to @label. Tracked changes\n * are rendered as accepted unless `options.trackedChanges` says otherwise.\n */\nexport function getEditorText(editor: any, options: PlainTextOptions = {}): string {\n if (!editor) return \"\";\n const html =\n resolvedBlockOperationsHtml(editor, options.trackedChanges ?? \"accepted\") ??\n (typeof editor.getHTML === \"function\" ? editor.getHTML() : \"\");\n if (html) {\n return htmlToPlainText(html, options);\n }\n if (typeof editor.getText === \"function\") {\n try {\n return editor.getText({ blockSeparator: \"\\n\" });\n } catch {\n return editor.getText();\n }\n }\n return \"\";\n}\n\nexport interface ExtractedMention {\n id: string;\n label: string;\n type?: string;\n /** Asset role (`data-role`), when set. */\n role?: string;\n avatarUrl?: string;\n color?: string;\n [key: string]: any;\n}\n\nimport { extractMentions } from \"./mentions\";\n\n/**\n * Traverses an editor instance, ProseMirror document, JSONContent, or HTML string\n * and extracts all mention attributes without needing regex.\n */\nexport function extractMentionsFromDoc(docOrEditor: any): ExtractedMention[] {\n if (!docOrEditor) return [];\n const mentions: ExtractedMention[] = [];\n\n // TipTap editor or ProseMirror Node with .descendants()\n const doc =\n docOrEditor?.state?.doc ||\n (typeof docOrEditor?.descendants === \"function\" ? docOrEditor : null);\n\n if (doc && typeof doc.descendants === \"function\") {\n doc.descendants((node: any) => {\n if (node.type?.name === \"mention\" && node.attrs) {\n mentions.push({\n id: node.attrs.id || \"\",\n label: node.attrs.label || \"\",\n type: node.attrs.type,\n avatarUrl: node.attrs.avatarUrl,\n color: node.attrs.color,\n ...node.attrs,\n ...(node.attrs.assetRole ? { role: node.attrs.assetRole } : {}),\n });\n }\n });\n return mentions;\n }\n\n // JSONContent tree\n if (typeof docOrEditor === \"object\" && docOrEditor.content) {\n const walk = (nodes: any[]) => {\n for (const node of nodes) {\n if (node.type === \"mention\" && node.attrs) {\n mentions.push({\n id: node.attrs.id || \"\",\n label: node.attrs.label || \"\",\n type: node.attrs.type,\n avatarUrl: node.attrs.avatarUrl,\n color: node.attrs.color,\n ...node.attrs,\n ...(node.attrs.assetRole ? { role: node.attrs.assetRole } : {}),\n });\n }\n if (Array.isArray(node.content)) {\n walk(node.content);\n }\n }\n };\n walk(docOrEditor.content);\n return mentions;\n }\n\n // HTML String\n if (typeof docOrEditor === \"string\") {\n if (typeof document !== \"undefined\") {\n parseHtmlFragment(docOrEditor).querySelectorAll('span[data-type=\"mention\"]').forEach((el) => {\n mentions.push({\n id: el.getAttribute(\"data-id\") || \"\",\n label: el.getAttribute(\"data-label\") || \"\",\n type: el.getAttribute(\"data-type-name\") || undefined,\n avatarUrl: el.getAttribute(\"data-avatar-url\") || undefined,\n color: el.getAttribute(\"data-color\") || undefined,\n ...(el.getAttribute(\"data-role\") ? { role: el.getAttribute(\"data-role\")! } : {}),\n });\n });\n } else {\n for (const span of findTagSpans(docOrEditor, \"span\", MENTION_TYPE_ATTRIBUTE, false)) {\n const attrStr = span.attrs;\n const getAttr = (name: string) => {\n const m = new RegExp(`${name}=[\"']([^\"']*)[\"']`, \"i\").exec(attrStr);\n return m ? m[1] : undefined;\n };\n mentions.push({\n id: getAttr(\"data-id\") || \"\",\n label: getAttr(\"data-label\") || \"\",\n type: getAttr(\"data-type-name\") || getAttr(\"data-entity-type\"),\n avatarUrl: getAttr(\"data-avatar-url\"),\n color: getAttr(\"data-color\"),\n ...(getAttr(\"data-role\") ? { role: getAttr(\"data-role\") } : {}),\n });\n }\n }\n\n // Fallback: If no HTML mention tags were found, extract raw @mentions from text or markdown\n if (mentions.length === 0) {\n const rawHandles = extractMentions(docOrEditor);\n for (const handle of rawHandles) {\n mentions.push({\n id: handle,\n label: handle,\n type: \"user\",\n });\n }\n }\n }\n\n return mentions;\n}\n","/**\n * Linear-time scanners for the HTML-ish strings the serializers handle.\n *\n * Regexes like `/<span\\b[^>]*…[^>]*>/`, `/<[^>]+>/g` or `/<pre[\\s\\S]*?<\\/pre>/g` are\n * quadratic (or worse) on malformed input: every opener without its closer\n * rescans to the end of the string. These helpers find each closer once and\n * reuse it, so untrusted input can't stall the main thread (see GOTCHAS).\n */\n\nexport interface TagSpan {\n /** Index of the opening `<tag`. */\n start: number;\n /** Index just past the opening tag's `>`. */\n openEnd: number;\n /** Index just past the close tag (or `openEnd` when the close tag isn't required). */\n end: number;\n /** Attribute text between `<span` and `>`. */\n attrs: string;\n}\n\n/**\n * Finds `<tag …attrs…>…</tag>` elements whose attributes match `attrTest`, in\n * one linear pass. A regex such as `/<span\\b(?=[^>]*…)([^>]*)>[\\s\\S]*?<\\/span>/g`\n * rescans to the end of the input from every `<span` when a `>` or `</span>` is\n * missing, which is quadratic on malformed input. Here the next `>` and the next\n * close tag are found once and reused until the scan passes them, and the scan\n * stops as soon as either no longer exists further on. The first close tag after\n * the opening tag ends the element (no nesting), as with a lazy regex.\n */\nexport function findTagSpans(\n html: string,\n tag: string,\n attrTest: RegExp | ((attrs: string) => boolean),\n requireClose: boolean,\n): TagSpan[] {\n const matches = typeof attrTest === \"function\" ? attrTest : (attrs: string) => attrTest.test(attrs);\n const spans: TagSpan[] = [];\n const openTag = new RegExp(`<${tag}\\\\b`, \"gi\");\n const closeTag = new RegExp(`</${tag}>`, \"gi\");\n const closeLength = tag.length + 3;\n let nextGt = -1;\n let nextClose = -1;\n let from = 0;\n for (;;) {\n openTag.lastIndex = from;\n const open = openTag.exec(html);\n if (!open) break;\n const start = open.index;\n const attrsStart = start + open[0].length;\n if (nextGt < attrsStart) {\n nextGt = html.indexOf(\">\", attrsStart);\n if (nextGt === -1) break;\n }\n const attrs = html.slice(attrsStart, nextGt);\n const openEnd = nextGt + 1;\n if (!matches(attrs)) {\n from = attrsStart;\n continue;\n }\n if (!requireClose) {\n spans.push({ start, openEnd, end: openEnd, attrs });\n from = openEnd;\n continue;\n }\n if (nextClose < openEnd) {\n closeTag.lastIndex = openEnd;\n const close = closeTag.exec(html);\n if (!close) break;\n nextClose = close.index;\n }\n const end = nextClose + closeLength;\n spans.push({ start, openEnd, end, attrs });\n from = end;\n }\n return spans;\n}\n\n/** Replaces each matching element (opening tag through close tag) using `replace`. */\nexport function replaceTagSpans(\n html: string,\n tag: string,\n attrTest: RegExp | ((attrs: string) => boolean),\n replace: (match: string, attrs: string) => string,\n): string {\n const spans = findTagSpans(html, tag, attrTest, true);\n if (spans.length === 0) return html;\n let out = \"\";\n let last = 0;\n for (const span of spans) {\n out += html.slice(last, span.start) + replace(html.slice(span.start, span.end), span.attrs);\n last = span.end;\n }\n return out + html.slice(last);\n}\n\n/**\n * Replaces `<…>` tags in one pass. Same result as `.replace(/<[^>]+>/g, replacement)`,\n * which is quadratic when many `<` have no closing `>`: each one rescans to the\n * end. Once no `>` is left, the rest of the input is plain text.\n */\nexport function stripTags(html: string, replacement = \"\"): string {\n let out = \"\";\n let from = 0;\n for (;;) {\n const lt = html.indexOf(\"<\", from);\n if (lt === -1) break;\n const gt = html.indexOf(\">\", lt + 1);\n if (gt === -1) break;\n if (gt === lt + 1) {\n // \"<>\" is not a tag (the regex needs at least one character).\n out += html.slice(from, gt + 1);\n } else {\n out += html.slice(from, lt) + replacement;\n }\n from = gt + 1;\n }\n return out + html.slice(from);\n}\n\n/**\n * Removes (or replaces) regions that start with a match of `open` and end at the\n * next `close`, in one pass. Equivalent to `.replace(/<open>[\\s\\S]*?<close>/g, …)`\n * but linear: such a lazy regex rescans to the end of the input from every\n * unclosed opener. The next close position is cached until the scan passes it,\n * and scanning stops once no close is left. `open` must have the `g` flag.\n */\nexport function replaceDelimited(\n text: string,\n open: RegExp,\n close: string,\n replace: (match: string) => string = () => \"\",\n caseInsensitiveClose = false,\n): string {\n const haystack = caseInsensitiveClose ? asciiLower(text) : text;\n const needle = caseInsensitiveClose ? close.toLowerCase() : close;\n let out = \"\";\n let last = 0;\n let from = 0;\n let nextClose = -1;\n for (;;) {\n open.lastIndex = from;\n const opened = open.exec(text);\n if (!opened) break;\n const afterOpen = opened.index + Math.max(opened[0].length, 1);\n if (nextClose < afterOpen) {\n nextClose = haystack.indexOf(needle, afterOpen);\n if (nextClose === -1) break;\n }\n const end = nextClose + close.length;\n out += text.slice(last, opened.index) + replace(text.slice(opened.index, end));\n last = from = end;\n }\n return out + text.slice(last);\n}\n\n/** Lower-cases ASCII letters only, so indices match the original string. */\nexport function asciiLower(text: string): string {\n return text.replace(/[A-Z]+/g, (m) => m.toLowerCase());\n}\n","import { escapeHtml } from \"./plain-text\";\nimport {\n MENTION_REGEX,\n NODE_ROLE_MARK,\n formatMentionHandle,\n readRoleSuffix,\n type MentionRoleSyntax,\n} from \"./mention-text\";\n\nexport {\n MENTION_REGEX,\n findTextMentions,\n formatMentionText,\n setMentionRoleInText,\n type MentionRoleSyntax,\n type TextMention,\n} from \"./mention-text\";\nimport { asciiLower, findTagSpans, replaceDelimited, stripTags } from \"./html-scan\";\nimport type { MentionEntity } from \"../extensions/mention\";\n\n\n/**\n * Split regex identifying markdown code blocks, inline code, URLs, and existing HTML tags:\n * the regions preprocessMarkdownMentions leaves untouched. Kept as the reference definition;\n * preprocessMarkdownMentions uses {@link splitMarkdownMentionParts}, because this regex is\n * quadratic (lazy closers) and cubic (mention spans) on malformed input.\n */\nexport const MARKDOWN_MENTION_SPLIT_REGEX =\n /(```[\\s\\S]*?```|~~~[\\s\\S]*?~~~|<pre[\\s\\S]*?<\\/pre>|<code[\\s\\S]*?<\\/code>|`[^`\\n]+`|<span\\b[^>]*\\b(?:data-type=[\"']mention[\"']|class=[\"'][^\"']*pixerate-mention-node[^\"']*[\"']|data-mention=[\"'][^\"']*[\"'])[^>]*>[\\s\\S]*?<\\/span>|<[^>]+>|\\]\\([^)]+\\)|(?:https?|file|vscode|cursor|mailto):\\/\\/[^\\s<)]+)/gi;\n\n// The mention-span attribute test. Unlike the regex above, quoted values can't run past\n// the tag's `>`: a tag ends at its first `>` here, which keeps the scan linear.\nconst MENTION_SPAN_ATTRIBUTE =\n /\\b(?:data-type=[\"']mention[\"']|class=[\"'][^\"'>]*pixerate-mention-node[^\"'>]*[\"']|data-mention=[\"'][^\"'>]*[\"'])/gi;\nconst URL_SCHEMES = [\"https://\", \"http://\", \"file://\", \"vscode://\", \"cursor://\", \"mailto://\"];\nconst URL_STOP = /[\\s<)]/;\n\n/**\n * `text.split(MARKDOWN_MENTION_SPLIT_REGEX)` in linear time: alternating plain text and\n * protected parts (code fences, `<pre>` / `<code>`, inline code, mention spans, HTML tags,\n * markdown link targets, URLs), starting and ending with text (possibly empty). Each\n * closer is found through a cached \"next occurrence\" that only moves forward, so\n * unclosed fences or tags cost O(1) per position instead of a scan to the end.\n */\nexport function splitMarkdownMentionParts(text: string): string[] {\n const lower = asciiLower(text);\n const n = text.length;\n const cache = new Map<string, { from: number; at: number }>();\n /** First index ≥ from of needle in the lower-cased text, or -1. */\n const next = (needle: string, from: number) => {\n const cached = cache.get(needle);\n if (cached && cached.from <= from && (cached.at === -1 || cached.at >= from)) return cached.at;\n const at = lower.indexOf(needle, from);\n cache.set(needle, { from, at });\n return at;\n };\n let attrFrom = -1;\n let attrAt = -1;\n /** Start of the first mention attribute at or after from, or -1. */\n const nextAttribute = (from: number) => {\n if (attrFrom !== -1 && (attrAt === -1 || attrAt >= from) && attrFrom <= from) return attrAt;\n MENTION_SPAN_ATTRIBUTE.lastIndex = from;\n attrAt = MENTION_SPAN_ATTRIBUTE.exec(text)?.index ?? -1;\n attrFrom = from;\n return attrAt;\n };\n /** End (exclusive) of a protected part starting at i, or -1. */\n const matchAt = (i: number): number => {\n const c = text[i];\n if (c === \"`\" || c === \"~\") {\n const fence = c.repeat(3);\n if (text.startsWith(fence, i)) {\n const close = next(fence, i + 3);\n if (close !== -1) return close + 3;\n }\n if (c === \"`\") {\n const tick = next(\"`\", i + 1);\n const newline = next(\"\\n\", i + 1);\n if (tick > i + 1 && (newline === -1 || tick < newline)) return tick + 1;\n }\n return -1;\n }\n if (c === \"<\") {\n for (const tag of [\"pre\", \"code\"]) {\n if (lower.startsWith(`<${tag}`, i)) {\n const close = next(`</${tag}>`, i + tag.length + 1);\n if (close !== -1) return close + tag.length + 3;\n }\n }\n const gt = next(\">\", i + 1);\n if (lower.startsWith(\"<span\", i) && !/\\w/.test(text[i + 5] ?? \"\") && gt !== -1) {\n const attr = nextAttribute(i + 5);\n if (attr !== -1 && attr < gt) {\n const close = next(\"</span>\", gt + 1);\n if (close !== -1) return close + 7;\n }\n }\n return gt > i + 1 ? gt + 1 : -1;\n }\n if (c === \"]\") {\n if (text[i + 1] !== \"(\") return -1;\n const close = next(\")\", i + 2);\n return close > i + 2 ? close + 1 : -1;\n }\n for (const scheme of URL_SCHEMES) {\n if (!lower.startsWith(scheme, i)) continue;\n let end = i + scheme.length;\n while (end < n && !URL_STOP.test(text[end])) end++;\n return end > i + scheme.length ? end : -1;\n }\n return -1;\n };\n const parts: string[] = [];\n let last = 0;\n for (let i = 0; i < n; ) {\n const end = matchAt(i);\n if (end === -1) {\n i++;\n continue;\n }\n parts.push(text.slice(last, i), text.slice(i, end));\n last = i = end;\n }\n parts.push(text.slice(last));\n return parts;\n}\n\nexport interface MentionSegment {\n type: \"text\" | \"mention\";\n value: string;\n handle?: string;\n /** Role written after the handle (`@fox:style`), when role syntax is enabled. */\n role?: string;\n}\n\n\n/** A mention with its role (asset mentions: `@fox:style`, or `data-role` on a mention node). */\nexport interface MentionDetail {\n handle: string;\n role?: string;\n}\n\nexport interface ExtractMentionsOptions {\n /** Return `{ handle, role }` objects instead of handles. */\n details?: boolean;\n /** Read `@handle:role` suffixes in text (see {@link MentionRoleSyntax}). Mention nodes' `data-role` is always read in `details` mode. */\n roles?: MentionRoleSyntax;\n}\n\nexport interface PreprocessMarkdownMentionsOptions {\n /**\n * Known pool of mentionable entities (users, agents, teams, etc.).\n */\n mentionables?: MentionEntity[];\n /**\n * Default entity type assigned when a handle does not match any entry in mentionables.\n * Defaults to 'user'.\n */\n defaultType?: string;\n /**\n * Custom resolver callback to derive entity metadata for unmatched handles.\n */\n resolveMention?: (handle: string) => Partial<MentionEntity> | null | undefined;\n /**\n * Read `@handle:role` and emit the role as `data-role` (the `:role` text is\n * consumed). Off by default. See {@link MentionRoleSyntax}.\n */\n roles?: MentionRoleSyntax;\n}\n\n/**\n * Strips code blocks and inline code from markdown or HTML content:\n * - Fenced code blocks: ```...``` and ~~~...~~~\n * - Inline backtick code: `...`\n * - HTML code/pre tags: <pre>...</pre>, <code>...</code>\n * Used to avoid false-positive mention parsing and keyword analysis within code snippets, terminal logs, or stack traces.\n */\nexport function stripMarkdownCode(content: string): string {\n if (!content || typeof content !== \"string\") return \"\";\n // Linear scans instead of lazy `[\\s\\S]*?` regexes, which rescan to the end of the\n // input from every unclosed opener (see html-scan.ts).\n let text = replaceDelimited(content, /```/g, \"```\");\n text = replaceDelimited(text, /~~~/g, \"~~~\");\n text = replaceDelimited(text, /<pre/gi, \"</pre>\", undefined, true);\n text = replaceDelimited(text, /<code/gi, \"</code>\", undefined, true);\n return text.replace(/`[^`\\n]*`/g, \"\");\n}\n\n/** True for the attributes of a mention node: `data-type=\"mention\"` or the `pixerate-mention-node` class. */\nfunction isMentionNodeAttributes(attrs: string): boolean {\n if (/\\bdata-type=[\"']mention[\"']/i.test(attrs)) return true;\n const classAttr = /\\bclass=([\"'])/i.exec(attrs);\n if (!classAttr) return false;\n const valueStart = classAttr.index + classAttr[0].length;\n const valueEnd = attrs.indexOf(classAttr[1], valueStart);\n return valueEnd !== -1 && attrs.slice(valueStart, valueEnd).includes(\"pixerate-mention-node\");\n}\n\n/**\n * Extracts unique mention handles from a text, markdown, or HTML string,\n * ignoring trailing punctuation, code blocks, URLs, and pre-existing HTML mention attributes.\n *\n * With `{ details: true }` it returns `{ handle, role }` objects, unique per\n * handle and role: roles come from mention nodes' `data-role` and, when\n * `roles` is set, from `@handle:role` text. Without options the result is\n * unchanged.\n */\nexport function extractMentions(content: string, options?: ExtractMentionsOptions & { details?: false }): string[];\nexport function extractMentions(content: string, options: ExtractMentionsOptions & { details: true }): MentionDetail[];\nexport function extractMentions(content: string, options?: ExtractMentionsOptions): string[] | MentionDetail[];\nexport function extractMentions(content: string, options: ExtractMentionsOptions = {}): string[] | MentionDetail[] {\n if (!content || typeof content !== \"string\") return [];\n const details = options.details === true;\n\n // Normalize existing HTML mention nodes (e.g. from TipTap or rich text serializers)\n // so data-mention-suggestion-char=\"@\" does not trigger spurious quoted matches against attributes,\n // and ensure the actual mentioned label or ID is preserved cleanly.\n // A linear scan: the equivalent regex (`<span\\b[^>]*\\b(?:…)[^>]*>`) backtracks\n // cubically on malformed input without a `>` (see html-scan.ts).\n let normalizedMentionNodes = \"\";\n let last = 0;\n for (const span of findTagSpans(content, \"span\", isMentionNodeAttributes, true)) {\n const labelMatch = /\\bdata-label=[\"']([^\"']+)[\"']/i.exec(span.attrs);\n const idMatch = labelMatch ? null : /\\bdata-id=[\"']([^\"']+)[\"']/i.exec(span.attrs);\n const inner = content.slice(span.openEnd, span.end - \"</span>\".length);\n let replacement = labelMatch ? ` @${labelMatch[1]} ` : idMatch ? ` @${idMatch[1]} ` : ` ${inner} `;\n if (details && (labelMatch || idMatch)) {\n const roleMatch = /\\bdata-role=[\"']([^\"']+)[\"']/i.exec(span.attrs);\n const role = roleMatch && /^[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9])?$/.test(roleMatch[1]) ? roleMatch[1] : null;\n const label = (labelMatch ?? idMatch)![1];\n replacement = ` ${formatMentionHandle(label)}${role ? NODE_ROLE_MARK + role : \"\"} `;\n }\n normalizedMentionNodes += content.slice(last, span.start) + replacement;\n last = span.end;\n }\n normalizedMentionNodes += content.slice(last);\n\n const cleanContent = stripMarkdownCode(normalizedMentionNodes);\n // Strip URLs and markdown link destinations so @package or @handles in URLs are not extracted as mentions\n const withoutUrls = cleanContent\n .replace(/(?:https?|ftp|file|vscode|cursor|mailto):\\/\\/[^\\s<>()]+/gi, \"\");\n // Markdown link destinations: `](…)` → `]` (linear; `/\\]\\([^)]*\\)/g` is quadratic\n // when many `](` have no `)`).\n const withoutLinks = replaceDelimited(withoutUrls, /\\]\\(/g, \")\", () => \"]\");\n // Strip remaining HTML tags so attributes containing '@' or '>' aren't treated as mention triggers\n const textOnly = stripTags(withoutLinks, \" \");\n\n const mentions: string[] = [];\n const detailed: MentionDetail[] = [];\n const seen = new Set<string>();\n\n const regex = new RegExp(MENTION_REGEX.source, \"g\");\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(textOnly)) !== null) {\n const handle = (match[1] ?? match[2] ?? match[3] ?? \"\").trim();\n if (details) {\n const suffix = readRoleSuffix(textOnly, regex.lastIndex, options.roles);\n if (suffix) regex.lastIndex = suffix.end;\n const key = `${handle}\\u0000${suffix?.role ?? \"\"}`;\n if (handle && !seen.has(key)) {\n seen.add(key);\n detailed.push(suffix ? { handle, role: suffix.role } : { handle });\n }\n continue;\n }\n if (handle && !seen.has(handle)) {\n seen.add(handle);\n mentions.push(handle);\n }\n }\n\n return details ? detailed : mentions;\n}\n\n/**\n * Segments a text string into plain text parts and mention tokens.\n * Correctly leaves trailing punctuation (like \".\") as part of the following text segment.\n */\nexport function parseMentionSegments(\n content: string,\n options: { roles?: MentionRoleSyntax } = {},\n): MentionSegment[] {\n if (!content) return [];\n\n const segments: MentionSegment[] = [];\n const regex = new RegExp(MENTION_REGEX.source, \"g\");\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n const fullMatch = match[0];\n const atIndex = fullMatch.indexOf(\"@\");\n const prefix = atIndex > 0 ? fullMatch.slice(0, atIndex) : \"\";\n let mentionToken = fullMatch.slice(atIndex);\n const handle = match[1] ?? match[2] ?? match[3];\n const matchStart = match.index + prefix.length;\n const suffix =\n options.roles && content.charAt(regex.lastIndex) === \":\"\n ? readRoleSuffix(content, regex.lastIndex, options.roles)\n : null;\n if (suffix) {\n mentionToken = content.slice(matchStart, suffix.end);\n regex.lastIndex = suffix.end;\n }\n\n if (matchStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: content.slice(lastIndex, matchStart),\n });\n }\n\n segments.push({\n type: \"mention\",\n value: mentionToken,\n handle,\n ...(suffix ? { role: suffix.role } : {}),\n });\n\n lastIndex = matchStart + mentionToken.length;\n }\n\n if (lastIndex < content.length) {\n segments.push({\n type: \"text\",\n value: content.slice(lastIndex),\n });\n }\n\n return segments;\n}\n\n/**\n * Preprocesses markdown text by turning raw @mention handles into TipTap-compatible\n * mention HTML spans (<span class=\"pixerate-mention-node\" data-type=\"mention\" ...>),\n * ensuring code blocks, inline code, existing mention nodes, and URLs remain immune.\n */\nexport function preprocessMarkdownMentions(\n content: string,\n optionsOrMentionables: MentionEntity[] | PreprocessMarkdownMentionsOptions = []\n): string {\n if (!content || typeof content !== \"string\") return \"\";\n\n const options: PreprocessMarkdownMentionsOptions = Array.isArray(optionsOrMentionables)\n ? { mentionables: optionsOrMentionables }\n : optionsOrMentionables;\n\n const allMentionables = options.mentionables || [];\n const defaultType = options.defaultType || \"user\";\n const resolveMention = options.resolveMention;\n\n const parts = splitMarkdownMentionParts(content);\n\n return parts\n .map((part) => {\n if (!part) return \"\";\n\n // Preserve code fences, code tags, inline backticks, existing mention spans,\n // HTML tags, markdown link targets, and raw URLs untouched.\n if (\n part.startsWith(\"```\") ||\n part.startsWith(\"~~~\") ||\n part.startsWith(\"`\") ||\n part.startsWith(\"](\") ||\n /^(?:https?|file|vscode|cursor|mailto):\\/\\//i.test(part) ||\n /^<pre[\\s>]/i.test(part) ||\n /^<code[\\s>]/i.test(part) ||\n /^<[^>]+>$/i.test(part) ||\n /^<span\\b[^>]*\\b(?:data-type=[\"']mention[\"']|class=[\"'][^\"']*pixerate-mention-node[^\"']*[\"']|data-mention=[\"'][^\"']*[\"'])/i.test(\n part\n )\n ) {\n return part;\n }\n\n const regex = new RegExp(MENTION_REGEX.source, \"g\");\n const render = (\n match: string,\n quotedHandle: string | undefined,\n escapedQuotedHandle: string | undefined,\n standardHandle: string | undefined,\n role?: string,\n ): string => {\n const atIndex = match.indexOf(\"@\");\n const prefix = atIndex > 0 ? match.slice(0, atIndex) : \"\";\n const rawHandle = (quotedHandle || escapedQuotedHandle || standardHandle || \"\").trim();\n if (!rawHandle) return match;\n\n const lowerHandle = rawHandle.toLowerCase();\n const entity = allMentionables.find((e) => {\n if (e.label?.toLowerCase() === lowerHandle) return true;\n if (e.handle?.toLowerCase() === lowerHandle) return true;\n if (e.name?.toLowerCase() === lowerHandle) return true;\n if (e.id?.toLowerCase() === lowerHandle) return true;\n if (e.type === \"agent\" && `agent${(e.handle || e.label || \"\").toLowerCase()}` === lowerHandle) return true;\n if (\n e.aliases?.some(\n (a: string) =>\n a.toLowerCase() === lowerHandle ||\n (e.type === \"agent\" && `agent${a.toLowerCase()}` === lowerHandle)\n )\n ) {\n return true;\n }\n return false;\n });\n\n let id = entity?.id;\n let label = entity?.label || entity?.handle || rawHandle;\n let type = entity?.type;\n let avatarUrl = entity?.avatarUrl;\n let color = entity?.color || entity?.hexColor;\n\n if (!entity) {\n const customResolved = resolveMention?.(rawHandle);\n if (customResolved) {\n id = customResolved.id ?? id;\n label = customResolved.label ?? customResolved.handle ?? label;\n type = customResolved.type ?? type;\n avatarUrl = customResolved.avatarUrl ?? avatarUrl;\n color = customResolved.color ?? customResolved.hexColor ?? color;\n } else {\n const stripped = lowerHandle.replace(/^agent[\\s\\-_]*/i, \"\");\n if (\n [\"supervisor\", \"coordinator\", \"planner\", \"validator\", \"executor\"].includes(stripped)\n ) {\n type = \"agent\";\n id = stripped;\n label = stripped;\n } else if (lowerHandle === \"agents\") {\n type = \"team\";\n id = \"agents\";\n label = \"agents\";\n } else {\n type = defaultType;\n id = rawHandle;\n label = rawHandle;\n }\n }\n }\n\n const avatarAttr = avatarUrl ? ` data-avatar-url=\"${escapeHtml(avatarUrl)}\"` : \"\";\n const colorAttr = color ? ` data-color=\"${escapeHtml(color)}\"` : \"\";\n\n return `${prefix}<span class=\"pixerate-mention-node\" data-type=\"mention\" data-id=\"${escapeHtml(\n id || label\n )}\" data-label=\"${escapeHtml(label)}\" data-mention-suggestion-char=\"@\" data-type-name=\"${escapeHtml(\n type || defaultType\n )}\"${avatarAttr}${colorAttr}${role ? ` data-role=\"${escapeHtml(role)}\"` : \"\"}>@${escapeHtml(label)}</span>`;\n };\n if (!options.roles) {\n return part.replace(regex, (match, quotedHandle, escapedQuotedHandle, standardHandle) =>\n render(match, quotedHandle, escapedQuotedHandle, standardHandle),\n );\n }\n // Role syntax: consume a `:role` suffix after each mention.\n let out = \"\";\n let last = 0;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(part)) !== null) {\n const suffix = readRoleSuffix(part, regex.lastIndex, options.roles);\n const rendered = render(match[0], match[1], match[2], match[3], suffix?.role);\n if (suffix && rendered !== match[0]) regex.lastIndex = suffix.end;\n out += part.slice(last, match.index) + rendered;\n last = regex.lastIndex;\n }\n return out + part.slice(last);\n })\n .join(\"\");\n}\n","import MarkdownIt from \"markdown-it\";\nimport taskLists from \"markdown-it-task-lists\";\nimport { escapeHtml, getEditorText, type PlainTextOptions } from \"./plain-text\";\nimport { resolveTrackedChanges } from \"../extensions/track-changes\";\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n breaks: true,\n}).use(taskLists);\n\nconst defaultValidateLink = md.validateLink;\nmd.validateLink = (url: string) => {\n if (\n url.startsWith(\"file://\") ||\n url.startsWith(\"vscode://\") ||\n url.startsWith(\"cursor://\")\n ) {\n return true;\n }\n return defaultValidateLink.call(md, url);\n};\n\n/**\n * Tags allowed through from raw HTML embedded in markdown (e.g. mention spans\n * and file links that tiptap-markdown serializes as HTML). Anything else is\n * rendered as escaped text. `input` is limited to checkboxes, which\n * markdown-it-task-lists emits as raw HTML.\n */\nconst ALLOWED_HTML_TAGS = new Set([\n \"a\", \"abbr\", \"b\", \"blockquote\", \"br\", \"code\", \"col\", \"colgroup\", \"del\", \"details\", \"div\", \"em\",\n \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"hr\", \"i\", \"img\", \"input\", \"ins\", \"kbd\", \"li\", \"mark\",\n \"ol\", \"p\", \"pre\", \"s\", \"span\", \"strong\", \"sub\", \"summary\", \"sup\", \"table\",\n \"tbody\", \"td\", \"tfoot\", \"th\", \"thead\", \"tr\", \"u\", \"ul\",\n]);\n\nconst ALLOWED_HTML_ATTRS = new Set([\n \"alt\", \"align\", \"checked\", \"class\", \"colspan\", \"colwidth\", \"disabled\", \"height\", \"href\",\n \"rel\", \"rowspan\", \"src\", \"start\", \"target\", \"title\", \"type\", \"width\",\n]);\n\nconst HTML_TAG_REGEX =\n /<(\\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:\\s+[^\\s\"'>\\/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s\"'=<>`]+))?)*)\\s*\\/?>/g;\nconst HTML_ATTR_REGEX =\n /([^\\s\"'>\\/=]+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g;\n\nfunction sanitizeTag(\n tagText: string,\n isClosing: string,\n rawTagName: string,\n rawAttrs: string,\n stripDisallowed: boolean,\n): string {\n const tagName = rawTagName.toLowerCase();\n const reject = () => (stripDisallowed ? \"\" : escapeHtml(tagText));\n if (!ALLOWED_HTML_TAGS.has(tagName)) return reject();\n if (isClosing) return `</${tagName}>`;\n if (tagName === \"input\" && !/\\stype\\s*=\\s*[\"']?checkbox[\"']?[\\s>/]/i.test(tagText)) {\n return reject();\n }\n\n let attrs = \"\";\n let attrMatch: RegExpExecArray | null;\n HTML_ATTR_REGEX.lastIndex = 0;\n while ((attrMatch = HTML_ATTR_REGEX.exec(rawAttrs)) !== null) {\n const name = attrMatch[1].toLowerCase();\n if (!ALLOWED_HTML_ATTRS.has(name) && !/^data-[a-z0-9_.-]+$/.test(name)) {\n continue;\n }\n const rawValue = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4];\n if (rawValue === undefined) {\n attrs += ` ${name}`;\n continue;\n }\n const value = md.utils.unescapeAll(rawValue);\n if (\n (name === \"href\" || name === \"src\") &&\n // Browsers ignore control characters and whitespace inside schemes.\n !md.validateLink(value.replace(/[\\u0000-\\u0020\\u007f]/g, \"\"))\n ) {\n continue;\n }\n attrs += ` ${name}=\"${escapeHtml(value)}\"`;\n }\n return `<${tagName}${attrs}>`;\n}\n\nexport interface SanitizeHtmlOptions {\n /**\n * What to do with tags outside the allowlist: `escape` (default) renders\n * them as literal text, `strip` removes the tag but keeps its text content.\n */\n disallowedTags?: \"escape\" | \"strip\";\n}\n\n/**\n * Sanitizes raw HTML with the same allowlist the markdown renderer applies to\n * embedded HTML: event handlers, `style` and unsafe URL schemes are dropped,\n * and disallowed tags are escaped (or removed with `disallowedTags: \"strip\"`).\n * It is string-based, so it works without a DOM and never runs handlers.\n */\nexport function sanitizeHtml(html: string, options: SanitizeHtmlOptions = {}): string {\n return sanitizeEmbeddedHtml(html, options.disallowedTags === \"strip\");\n}\n\nfunction sanitizeEmbeddedHtml(html: string, stripDisallowed = false): string {\n let result = \"\";\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n HTML_TAG_REGEX.lastIndex = 0;\n while ((match = HTML_TAG_REGEX.exec(html)) !== null) {\n result += html.slice(lastIndex, match.index).replace(/</g, \"&lt;\");\n result += sanitizeTag(match[0], match[1], match[2], match[3], stripDisallowed);\n lastIndex = match.index + match[0].length;\n }\n return result + html.slice(lastIndex).replace(/</g, \"&lt;\");\n}\n\nconst defaultHtmlInline = md.renderer.rules.html_inline;\nconst defaultHtmlBlock = md.renderer.rules.html_block;\nmd.renderer.rules.html_inline = (tokens, idx, options, env, self) =>\n env?.allowUnsafeHtml\n ? defaultHtmlInline!(tokens, idx, options, env, self)\n : sanitizeEmbeddedHtml(tokens[idx].content);\nmd.renderer.rules.html_block = (tokens, idx, options, env, self) =>\n env?.allowUnsafeHtml\n ? defaultHtmlBlock!(tokens, idx, options, env, self)\n : sanitizeEmbeddedHtml(tokens[idx].content);\n\nimport {\n preprocessMarkdownMentions,\n type PreprocessMarkdownMentionsOptions,\n} from \"./mentions\";\nimport type { MentionEntity } from \"../extensions/mention\";\n\n/**\n * Preprocesses file-protocol and editor-protocol links in markdown content:\n * converts [label](file://...), vscode://, and cursor:// links into anchor tags\n * annotated with data-file-link=\"true\", preserving code backticks in labels.\n */\nexport function preprocessMarkdownFileLinks(content: string): string {\n if (!content) return \"\";\n return content.replace(\n /\\[([^\\]]+)\\]\\(((?:file|vscode|cursor):\\/\\/[^)]+)\\)/g,\n (_match, rawLabel, href) => {\n const trimmed = rawLabel.trim();\n const codeMatch = trimmed.match(/^`([^`]+)`$/);\n const labelContent = codeMatch\n ? `<code>${escapeHtml(codeMatch[1])}</code>`\n : escapeHtml(trimmed);\n return `<a href=\"${escapeHtml(href)}\" data-file-link=\"true\" title=\"Click to copy file path\">${labelContent}</a>`;\n }\n );\n}\n\nexport interface MarkdownToHtmlOptions {\n /**\n * Preprocess file, vscode, and cursor links into clickable data-file-link tags.\n */\n preprocessFileLinks?: boolean;\n /**\n * Whether to preprocess @mentions into TipTap mention node HTML spans.\n */\n preprocessMentions?: boolean;\n /**\n * Pool of mentionables to pass to preprocessMarkdownMentions.\n */\n mentionables?: MentionEntity[];\n /**\n * Full options for preprocessMarkdownMentions.\n */\n preprocessOptions?: PreprocessMarkdownMentionsOptions;\n /**\n * Render raw HTML embedded in the markdown without sanitizing it. By default,\n * embedded HTML is restricted to an allowlist of tags and attributes (event\n * handlers, `style` and unsafe URL schemes are removed). Only enable this for\n * fully trusted input.\n */\n allowUnsafeHtml?: boolean;\n}\n\n/**\n * Converts a markdown string into HTML suitable for TipTap initial content or setContent.\n * Raw HTML embedded in the markdown is sanitized unless `allowUnsafeHtml` is set.\n */\nexport function markdownToTipTapHtml(\n markdown: string,\n options?: MarkdownToHtmlOptions\n): string {\n if (!markdown) return \"<p></p>\";\n let content = markdown;\n if (options?.preprocessFileLinks) {\n content = preprocessMarkdownFileLinks(content);\n }\n if (\n options?.preprocessMentions ||\n options?.mentionables ||\n options?.preprocessOptions\n ) {\n const mentionOpts = options.preprocessOptions || {\n mentionables: options.mentionables,\n };\n content = preprocessMarkdownMentions(content, mentionOpts);\n }\n return md.render(content, { allowUnsafeHtml: options?.allowUnsafeHtml });\n}\n\n/**\n * Extracts clean markdown from a TipTap editor instance.\n * If the editor has the Markdown extension configured, it uses editor.storage.markdown.getMarkdown().\n * Otherwise, it falls back to getEditorText(editor).\n * Tracked changes are exported as accepted unless `options.trackedChanges`\n * says otherwise (`\"all\"` renders deletions as `~~strikethrough~~`).\n */\nexport function getEditorMarkdown(editor: any, options: PlainTextOptions = {}): string {\n if (!editor) return \"\";\n const markdown = editor.storage?.markdown;\n if (typeof markdown?.getMarkdown === \"function\") {\n const doc = editor.state?.doc;\n if (doc && typeof markdown.serializer?.serialize === \"function\") {\n const resolved = resolveTrackedChanges(doc, options.trackedChanges ?? \"accepted\");\n if (resolved !== doc) return markdown.serializer.serialize(resolved);\n }\n return markdown.getMarkdown();\n }\n return getEditorText(editor, options);\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
- var _chunkNFVBT3UZcjs = require('./chunk-NFVBT3UZ.cjs');
4
+ var _chunk66IMF6ODcjs = require('./chunk-66IMF6OD.cjs');
5
5
 
6
6
  // src/serializers/html.ts
7
7
  var DROP_WITH_CONTENT_REGEX = /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
@@ -15,12 +15,12 @@ function sanitizeImportedHtml(html) {
15
15
  const body = BODY_REGEX.exec(content);
16
16
  if (body) content = body[1];
17
17
  content = content.replace(DROP_WITH_CONTENT_REGEX, "").replace(UNCLOSED_RAW_TEXT_REGEX, "").replace(NAMESPACED_TAG_REGEX, "");
18
- const sanitized = _chunkNFVBT3UZcjs.sanitizeHtml.call(void 0, content, { disallowedTags: "strip" }).trim();
18
+ const sanitized = _chunk66IMF6ODcjs.sanitizeHtml.call(void 0, content, { disallowedTags: "strip" }).trim();
19
19
  return sanitized || "<p></p>";
20
20
  }
21
21
  function plainTextToParagraphHtml(text) {
22
22
  if (!text) return "<p></p>";
23
- return text.replace(/\r\n?/g, "\n").split("\n").map((line) => `<p>${_chunkNFVBT3UZcjs.escapeHtml.call(void 0, line)}</p>`).join("");
23
+ return text.replace(/\r\n?/g, "\n").split("\n").map((line) => `<p>${_chunk66IMF6ODcjs.escapeHtml.call(void 0, line)}</p>`).join("");
24
24
  }
25
25
 
26
26
  // src/serializers/rtf.ts
@@ -296,4 +296,4 @@ function readRichClipboard(event, schema, mimeType = PIXERATE_DOCUMENT_MIME) {
296
296
 
297
297
 
298
298
  exports.sanitizeImportedHtml = sanitizeImportedHtml; exports.plainTextToParagraphHtml = plainTextToParagraphHtml; exports.rtfToPlainText = rtfToPlainText; exports.PIXERATE_DOCUMENT_MIME = PIXERATE_DOCUMENT_MIME; exports.getSchemaSignature = getSchemaSignature; exports.serializeRichClipboardPayload = serializeRichClipboardPayload; exports.parseRichClipboardPayload = parseRichClipboardPayload; exports.getClipboardData = getClipboardData; exports.readRichClipboard = readRichClipboard;
299
- //# sourceMappingURL=chunk-RRREAOVM.cjs.map
299
+ //# sourceMappingURL=chunk-HNDSPZ5T.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-RRREAOVM.cjs","../src/serializers/html.ts","../src/serializers/rtf.ts","../src/serializers/clipboard.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-RRREAOVM.cjs","sourcesContent":[null,"import { sanitizeHtml } from \"./markdown\";\nimport { escapeHtml } from \"./plain-text\";\n\n/**\n * Elements whose content is never document text (scripts, styles, metadata,\n * embedded objects, form controls). They are removed together with their\n * content before sanitizing, so e.g. CSS rules don't show up as paragraphs.\n */\nconst DROP_WITH_CONTENT_REGEX =\n /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n/** An unclosed `<script>` / `<style>` swallows the rest of the document. */\nconst UNCLOSED_RAW_TEXT_REGEX = /<(script|style)\\b[^>]*>[\\s\\S]*$/i;\nconst BODY_REGEX = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body\\s*>|$)/i;\n/** Namespaced tags such as Word's `<o:p>`, which the sanitizer cannot parse. */\nconst NAMESPACED_TAG_REGEX = /<\\/?[a-zA-Z][\\w-]*:[\\w-]+\\b[^>]*>/g;\n\n/**\n * Sanitizes a full HTML document or fragment for insertion into the editor,\n * e.g. an imported `.html` file or HTML from another application.\n *\n * - Keeps only the `<body>` content when the input is a full document.\n * - Removes comments, doctype/XML declarations, CDATA, scripts, styles and\n * other non-text elements together with their content.\n * - Applies the same tag/attribute/URL allowlist as embedded HTML in markdown\n * (see `sanitizeHtml`), but strips disallowed tags instead of escaping them,\n * so wrappers such as `<section>` or `<font>` keep their text.\n *\n * It is string-based (no live DOM), so handlers such as `<img onerror>` never run.\n */\nexport function sanitizeImportedHtml(html: string): string {\n if (!html) return \"<p></p>\";\n let content = html.replace(/^/, \"\");\n content = content\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, \"\")\n .replace(/<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>|$)/g, \"\")\n .replace(/<![^>]*>/g, \"\")\n .replace(/<\\?[\\s\\S]*?(?:\\?>|$)/g, \"\");\n const body = BODY_REGEX.exec(content);\n if (body) content = body[1];\n content = content\n .replace(DROP_WITH_CONTENT_REGEX, \"\")\n .replace(UNCLOSED_RAW_TEXT_REGEX, \"\")\n .replace(NAMESPACED_TAG_REGEX, \"\");\n const sanitized = sanitizeHtml(content, { disallowedTags: \"strip\" }).trim();\n return sanitized || \"<p></p>\";\n}\n\n/**\n * Converts untrusted plain text to TipTap paragraph HTML, always escaping it.\n *\n * Unlike `plainTextToTipTapHtml`, text that already looks like `<p>…</p>` is\n * not passed through as HTML, which matters for imported files.\n */\nexport function plainTextToParagraphHtml(text: string): string {\n if (!text) return \"<p></p>\";\n return text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => `<p>${escapeHtml(line)}</p>`)\n .join(\"\");\n}\n","/**\n * Destinations whose content is metadata or binary data rather than document\n * text. A group starting with one of these control words (or with `\\*`) is\n * skipped entirely.\n */\nconst SKIPPED_DESTINATIONS = new Set([\n \"author\", \"bkmkend\", \"bkmkstart\", \"colortbl\", \"comment\", \"datastore\",\n \"doccomm\", \"falt\", \"fldinst\", \"filetbl\", \"fonttbl\", \"footer\", \"footerf\",\n \"footerl\", \"footerr\", \"footnote\", \"generator\", \"header\", \"headerf\",\n \"headerl\", \"headerr\", \"info\", \"keywords\", \"latentstyles\", \"listoverridetable\",\n \"listtable\", \"nonshppict\", \"object\", \"operator\", \"pict\", \"private\", \"revtbl\",\n \"rsidtbl\", \"shppict\", \"stylesheet\", \"subject\", \"themedata\", \"title\",\n \"xmlnstbl\", \"colorschememapping\",\n]);\n\n/** Control words that map to a literal character. */\nconst CONTROL_WORD_TEXT: Record<string, string> = {\n par: \"\\n\",\n sect: \"\\n\",\n page: \"\\n\",\n line: \"\\n\",\n row: \"\\n\",\n cell: \"\\t\",\n tab: \"\\t\",\n emdash: \"—\",\n endash: \"–\",\n emspace: \" \",\n enspace: \" \",\n bullet: \"•\",\n lquote: \"‘\",\n rquote: \"’\",\n ldblquote: \"“\",\n rdblquote: \"”\",\n};\n\n/** Windows-1252 code points in 0x80–0x9F that differ from Latin-1. */\nconst CP1252: Record<number, string> = {\n 0x80: \"€\", 0x82: \"‚\", 0x83: \"ƒ\", 0x84: \"„\", 0x85: \"…\",\n 0x86: \"†\", 0x87: \"‡\", 0x88: \"ˆ\", 0x89: \"‰\", 0x8a: \"Š\",\n 0x8b: \"‹\", 0x8c: \"Œ\", 0x8e: \"Ž\", 0x91: \"‘\", 0x92: \"’\",\n 0x93: \"“\", 0x94: \"”\", 0x95: \"•\", 0x96: \"–\", 0x97: \"—\",\n 0x98: \"˜\", 0x99: \"™\", 0x9a: \"š\", 0x9b: \"›\", 0x9c: \"œ\",\n 0x9e: \"ž\", 0x9f: \"Ÿ\",\n};\n\nconst CONTROL_WORD_REGEX = /\\\\([a-zA-Z]{1,32})(-?\\d{1,10})? ?/y;\n\n/**\n * Best-effort plain-text extraction from an RTF document.\n *\n * Handles paragraphs/line breaks/tabs, escaped braces and backslashes,\n * `\\'hh` hex escapes (decoded as Windows-1252), `\\uN` Unicode escapes with\n * their `\\ucN` fallback characters, common typographic control words, and\n * skips metadata destinations (font/colour tables, `\\info`, pictures, field\n * instructions, `\\*` destinations). Formatting (bold, lists, tables) is not\n * preserved. Input that does not start with `{\\rtf` is returned unchanged.\n */\nexport function rtfToPlainText(rtf: string): string {\n if (!rtf) return \"\";\n if (!/^\\s*\\{\\\\rtf/.test(rtf)) return rtf;\n\n let out = \"\";\n const stack: Array<{ skip: boolean; uc: number }> = [];\n let skip = false;\n // Number of fallback characters that follow a \\uN escape.\n let uc = 1;\n let pendingFallback = 0;\n // True right after `{`, so a destination control word can mark the group.\n let groupStart = false;\n\n const emit = (text: string) => {\n if (skip) return;\n if (pendingFallback > 0) {\n pendingFallback--;\n return;\n }\n out += text;\n };\n\n let i = 0;\n while (i < rtf.length) {\n const ch = rtf[i];\n if (ch === \"{\") {\n stack.push({ skip, uc });\n groupStart = true;\n i++;\n continue;\n }\n if (ch === \"}\") {\n const prev = stack.pop();\n if (prev) {\n skip = prev.skip;\n uc = prev.uc;\n }\n pendingFallback = 0;\n groupStart = false;\n i++;\n continue;\n }\n const atGroupStart = groupStart;\n groupStart = false;\n\n if (ch === \"\\\\\") {\n const next = rtf[i + 1];\n if (next === \"\\\\\" || next === \"{\" || next === \"}\") {\n emit(next);\n i += 2;\n continue;\n }\n if (next === \"*\") {\n // `{\\*\\dest …}`: an ignorable destination this reader doesn't know.\n skip = true;\n i += 2;\n continue;\n }\n if (next === \"'\") {\n const code = parseInt(rtf.slice(i + 2, i + 4), 16);\n if (!Number.isNaN(code)) emit(CP1252[code] ?? String.fromCharCode(code));\n i += 4;\n continue;\n }\n if (next === \"~\") {\n emit(\" \");\n i += 2;\n continue;\n }\n if (next === \"_\") {\n emit(\"‑\");\n i += 2;\n continue;\n }\n if (next === \"\\n\" || next === \"\\r\") {\n emit(\"\\n\");\n i += 2;\n continue;\n }\n CONTROL_WORD_REGEX.lastIndex = i;\n const match = CONTROL_WORD_REGEX.exec(rtf);\n if (!match) {\n // Other control symbols (`\\-`, `\\|`, `\\:`) produce no text.\n i += 2;\n continue;\n }\n i = CONTROL_WORD_REGEX.lastIndex;\n const word = match[1];\n const param = match[2] !== undefined ? parseInt(match[2], 10) : undefined;\n if (atGroupStart && SKIPPED_DESTINATIONS.has(word)) {\n skip = true;\n } else if (word === \"u\" && param !== undefined) {\n emit(String.fromCharCode(param < 0 ? param + 65536 : param));\n if (!skip) pendingFallback = uc;\n } else if (word === \"uc\" && param !== undefined) {\n uc = param;\n } else if (word === \"bin\" && param !== undefined) {\n // Raw binary data: skip it.\n i += param;\n } else if (CONTROL_WORD_TEXT[word] !== undefined) {\n emit(CONTROL_WORD_TEXT[word]);\n }\n continue;\n }\n // Raw newlines are formatting in RTF; only control words break lines.\n if (ch === \"\\n\" || ch === \"\\r\") {\n i++;\n continue;\n }\n emit(ch);\n i++;\n }\n\n return out.replace(/\\n+$/, \"\");\n}\n","import { Slice, type Schema } from \"@tiptap/pm/model\";\n\n/**\n * Custom clipboard MIME type for content copied from a @pixerate/editor\n * rich-text editor. It carries the ProseMirror slice as JSON so pasting back\n * into a compatible editor is lossless (no HTML round trip).\n */\nexport const PIXERATE_DOCUMENT_MIME = \"application/x.pixerate.document+json\";\n\n/** Payload written under {@link PIXERATE_DOCUMENT_MIME}. */\nexport interface RichClipboardPayload {\n version: 1;\n /** {@link getSchemaSignature} of the source editor's schema. */\n schema: string;\n /** `Slice.toJSON()` of the copied selection. */\n slice: { content?: unknown[]; openStart?: number; openEnd?: number } | null;\n}\n\nconst signatureCache = new WeakMap<Schema, string>();\n\n/** 32-bit FNV-1a, enough to tell schemas apart without a crypto dependency. */\nfunction fnv1a(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Returns a stable signature for a ProseMirror schema: node names, content\n * expressions, groups, attribute names and mark names. Two editors with the\n * same extensions produce the same signature, so a slice copied from one can\n * be deserialized in the other without losing or rejecting nodes.\n */\nexport function getSchemaSignature(schema: Schema): string {\n const cached = signatureCache.get(schema);\n if (cached) return cached;\n const parts: string[] = [];\n for (const [name, type] of Object.entries(schema.nodes)) {\n const spec = type.spec;\n parts.push(\n `n:${name}|${spec.content ?? \"\"}|${spec.group ?? \"\"}|${spec.marks ?? \"\"}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`,\n );\n }\n for (const [name, type] of Object.entries(schema.marks)) {\n parts.push(`m:${name}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`);\n }\n const signature = `pm1-${fnv1a(parts.join(\";\"))}`;\n signatureCache.set(schema, signature);\n return signature;\n}\n\n/** Serializes a slice into the {@link PIXERATE_DOCUMENT_MIME} payload string. */\nexport function serializeRichClipboardPayload(slice: Slice, schema: Schema): string {\n const payload: RichClipboardPayload = {\n version: 1,\n schema: getSchemaSignature(schema),\n slice: slice.toJSON(),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Parses a {@link PIXERATE_DOCUMENT_MIME} payload into a slice for `schema`.\n * Returns `null` (so callers fall back to HTML / plain text) when the payload\n * is malformed, from another payload version, or from an editor whose schema\n * signature differs.\n */\nexport function parseRichClipboardPayload(data: string | null | undefined, schema: Schema): Slice | null {\n if (!data) return null;\n try {\n const payload = JSON.parse(data) as Partial<RichClipboardPayload>;\n if (!payload || payload.version !== 1) return null;\n if (payload.schema !== getSchemaSignature(schema)) return null;\n if (!payload.slice) return null;\n return Slice.fromJSON(schema, payload.slice as any);\n } catch {\n return null;\n }\n}\n\n/** Minimal clipboard data surface (`DataTransfer`). */\nexport interface ClipboardDataLike {\n getData(format: string): string;\n setData(format: string, data: string): void;\n clearData?(format?: string): void;\n}\n\n/**\n * Returns the `clipboardData` of a native `ClipboardEvent` or of a React\n * synthetic clipboard event (which wraps the native event in `nativeEvent`).\n */\nexport function getClipboardData(event: unknown): ClipboardDataLike | null {\n if (!event || typeof event !== \"object\") return null;\n const native = \"nativeEvent\" in event ? (event as any).nativeEvent : event;\n return ((event as any).clipboardData ?? native?.clipboardData ?? null) as ClipboardDataLike | null;\n}\n\n/**\n * Reads a slice from a paste event's {@link PIXERATE_DOCUMENT_MIME} data.\n * Returns `null` when the data is missing or its schema does not match.\n */\nexport function readRichClipboard(\n event: unknown,\n schema: Schema,\n mimeType: string = PIXERATE_DOCUMENT_MIME,\n): Slice | null {\n const data = getClipboardData(event);\n if (!data) return null;\n let raw: string | undefined;\n try {\n raw = data.getData(mimeType);\n } catch {\n return null;\n }\n return parseRichClipboardPayload(raw, schema);\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-HNDSPZ5T.cjs","../src/serializers/html.ts","../src/serializers/rtf.ts","../src/serializers/clipboard.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-HNDSPZ5T.cjs","sourcesContent":[null,"import { sanitizeHtml } from \"./markdown\";\nimport { escapeHtml } from \"./plain-text\";\n\n/**\n * Elements whose content is never document text (scripts, styles, metadata,\n * embedded objects, form controls). They are removed together with their\n * content before sanitizing, so e.g. CSS rules don't show up as paragraphs.\n */\nconst DROP_WITH_CONTENT_REGEX =\n /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n/** An unclosed `<script>` / `<style>` swallows the rest of the document. */\nconst UNCLOSED_RAW_TEXT_REGEX = /<(script|style)\\b[^>]*>[\\s\\S]*$/i;\nconst BODY_REGEX = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body\\s*>|$)/i;\n/** Namespaced tags such as Word's `<o:p>`, which the sanitizer cannot parse. */\nconst NAMESPACED_TAG_REGEX = /<\\/?[a-zA-Z][\\w-]*:[\\w-]+\\b[^>]*>/g;\n\n/**\n * Sanitizes a full HTML document or fragment for insertion into the editor,\n * e.g. an imported `.html` file or HTML from another application.\n *\n * - Keeps only the `<body>` content when the input is a full document.\n * - Removes comments, doctype/XML declarations, CDATA, scripts, styles and\n * other non-text elements together with their content.\n * - Applies the same tag/attribute/URL allowlist as embedded HTML in markdown\n * (see `sanitizeHtml`), but strips disallowed tags instead of escaping them,\n * so wrappers such as `<section>` or `<font>` keep their text.\n *\n * It is string-based (no live DOM), so handlers such as `<img onerror>` never run.\n */\nexport function sanitizeImportedHtml(html: string): string {\n if (!html) return \"<p></p>\";\n let content = html.replace(/^/, \"\");\n content = content\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, \"\")\n .replace(/<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>|$)/g, \"\")\n .replace(/<![^>]*>/g, \"\")\n .replace(/<\\?[\\s\\S]*?(?:\\?>|$)/g, \"\");\n const body = BODY_REGEX.exec(content);\n if (body) content = body[1];\n content = content\n .replace(DROP_WITH_CONTENT_REGEX, \"\")\n .replace(UNCLOSED_RAW_TEXT_REGEX, \"\")\n .replace(NAMESPACED_TAG_REGEX, \"\");\n const sanitized = sanitizeHtml(content, { disallowedTags: \"strip\" }).trim();\n return sanitized || \"<p></p>\";\n}\n\n/**\n * Converts untrusted plain text to TipTap paragraph HTML, always escaping it.\n *\n * Unlike `plainTextToTipTapHtml`, text that already looks like `<p>…</p>` is\n * not passed through as HTML, which matters for imported files.\n */\nexport function plainTextToParagraphHtml(text: string): string {\n if (!text) return \"<p></p>\";\n return text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => `<p>${escapeHtml(line)}</p>`)\n .join(\"\");\n}\n","/**\n * Destinations whose content is metadata or binary data rather than document\n * text. A group starting with one of these control words (or with `\\*`) is\n * skipped entirely.\n */\nconst SKIPPED_DESTINATIONS = new Set([\n \"author\", \"bkmkend\", \"bkmkstart\", \"colortbl\", \"comment\", \"datastore\",\n \"doccomm\", \"falt\", \"fldinst\", \"filetbl\", \"fonttbl\", \"footer\", \"footerf\",\n \"footerl\", \"footerr\", \"footnote\", \"generator\", \"header\", \"headerf\",\n \"headerl\", \"headerr\", \"info\", \"keywords\", \"latentstyles\", \"listoverridetable\",\n \"listtable\", \"nonshppict\", \"object\", \"operator\", \"pict\", \"private\", \"revtbl\",\n \"rsidtbl\", \"shppict\", \"stylesheet\", \"subject\", \"themedata\", \"title\",\n \"xmlnstbl\", \"colorschememapping\",\n]);\n\n/** Control words that map to a literal character. */\nconst CONTROL_WORD_TEXT: Record<string, string> = {\n par: \"\\n\",\n sect: \"\\n\",\n page: \"\\n\",\n line: \"\\n\",\n row: \"\\n\",\n cell: \"\\t\",\n tab: \"\\t\",\n emdash: \"—\",\n endash: \"–\",\n emspace: \" \",\n enspace: \" \",\n bullet: \"•\",\n lquote: \"‘\",\n rquote: \"’\",\n ldblquote: \"“\",\n rdblquote: \"”\",\n};\n\n/** Windows-1252 code points in 0x80–0x9F that differ from Latin-1. */\nconst CP1252: Record<number, string> = {\n 0x80: \"€\", 0x82: \"‚\", 0x83: \"ƒ\", 0x84: \"„\", 0x85: \"…\",\n 0x86: \"†\", 0x87: \"‡\", 0x88: \"ˆ\", 0x89: \"‰\", 0x8a: \"Š\",\n 0x8b: \"‹\", 0x8c: \"Œ\", 0x8e: \"Ž\", 0x91: \"‘\", 0x92: \"’\",\n 0x93: \"“\", 0x94: \"”\", 0x95: \"•\", 0x96: \"–\", 0x97: \"—\",\n 0x98: \"˜\", 0x99: \"™\", 0x9a: \"š\", 0x9b: \"›\", 0x9c: \"œ\",\n 0x9e: \"ž\", 0x9f: \"Ÿ\",\n};\n\nconst CONTROL_WORD_REGEX = /\\\\([a-zA-Z]{1,32})(-?\\d{1,10})? ?/y;\n\n/**\n * Best-effort plain-text extraction from an RTF document.\n *\n * Handles paragraphs/line breaks/tabs, escaped braces and backslashes,\n * `\\'hh` hex escapes (decoded as Windows-1252), `\\uN` Unicode escapes with\n * their `\\ucN` fallback characters, common typographic control words, and\n * skips metadata destinations (font/colour tables, `\\info`, pictures, field\n * instructions, `\\*` destinations). Formatting (bold, lists, tables) is not\n * preserved. Input that does not start with `{\\rtf` is returned unchanged.\n */\nexport function rtfToPlainText(rtf: string): string {\n if (!rtf) return \"\";\n if (!/^\\s*\\{\\\\rtf/.test(rtf)) return rtf;\n\n let out = \"\";\n const stack: Array<{ skip: boolean; uc: number }> = [];\n let skip = false;\n // Number of fallback characters that follow a \\uN escape.\n let uc = 1;\n let pendingFallback = 0;\n // True right after `{`, so a destination control word can mark the group.\n let groupStart = false;\n\n const emit = (text: string) => {\n if (skip) return;\n if (pendingFallback > 0) {\n pendingFallback--;\n return;\n }\n out += text;\n };\n\n let i = 0;\n while (i < rtf.length) {\n const ch = rtf[i];\n if (ch === \"{\") {\n stack.push({ skip, uc });\n groupStart = true;\n i++;\n continue;\n }\n if (ch === \"}\") {\n const prev = stack.pop();\n if (prev) {\n skip = prev.skip;\n uc = prev.uc;\n }\n pendingFallback = 0;\n groupStart = false;\n i++;\n continue;\n }\n const atGroupStart = groupStart;\n groupStart = false;\n\n if (ch === \"\\\\\") {\n const next = rtf[i + 1];\n if (next === \"\\\\\" || next === \"{\" || next === \"}\") {\n emit(next);\n i += 2;\n continue;\n }\n if (next === \"*\") {\n // `{\\*\\dest …}`: an ignorable destination this reader doesn't know.\n skip = true;\n i += 2;\n continue;\n }\n if (next === \"'\") {\n const code = parseInt(rtf.slice(i + 2, i + 4), 16);\n if (!Number.isNaN(code)) emit(CP1252[code] ?? String.fromCharCode(code));\n i += 4;\n continue;\n }\n if (next === \"~\") {\n emit(\" \");\n i += 2;\n continue;\n }\n if (next === \"_\") {\n emit(\"‑\");\n i += 2;\n continue;\n }\n if (next === \"\\n\" || next === \"\\r\") {\n emit(\"\\n\");\n i += 2;\n continue;\n }\n CONTROL_WORD_REGEX.lastIndex = i;\n const match = CONTROL_WORD_REGEX.exec(rtf);\n if (!match) {\n // Other control symbols (`\\-`, `\\|`, `\\:`) produce no text.\n i += 2;\n continue;\n }\n i = CONTROL_WORD_REGEX.lastIndex;\n const word = match[1];\n const param = match[2] !== undefined ? parseInt(match[2], 10) : undefined;\n if (atGroupStart && SKIPPED_DESTINATIONS.has(word)) {\n skip = true;\n } else if (word === \"u\" && param !== undefined) {\n emit(String.fromCharCode(param < 0 ? param + 65536 : param));\n if (!skip) pendingFallback = uc;\n } else if (word === \"uc\" && param !== undefined) {\n uc = param;\n } else if (word === \"bin\" && param !== undefined) {\n // Raw binary data: skip it.\n i += param;\n } else if (CONTROL_WORD_TEXT[word] !== undefined) {\n emit(CONTROL_WORD_TEXT[word]);\n }\n continue;\n }\n // Raw newlines are formatting in RTF; only control words break lines.\n if (ch === \"\\n\" || ch === \"\\r\") {\n i++;\n continue;\n }\n emit(ch);\n i++;\n }\n\n return out.replace(/\\n+$/, \"\");\n}\n","import { Slice, type Schema } from \"@tiptap/pm/model\";\n\n/**\n * Custom clipboard MIME type for content copied from a @pixerate/editor\n * rich-text editor. It carries the ProseMirror slice as JSON so pasting back\n * into a compatible editor is lossless (no HTML round trip).\n */\nexport const PIXERATE_DOCUMENT_MIME = \"application/x.pixerate.document+json\";\n\n/** Payload written under {@link PIXERATE_DOCUMENT_MIME}. */\nexport interface RichClipboardPayload {\n version: 1;\n /** {@link getSchemaSignature} of the source editor's schema. */\n schema: string;\n /** `Slice.toJSON()` of the copied selection. */\n slice: { content?: unknown[]; openStart?: number; openEnd?: number } | null;\n}\n\nconst signatureCache = new WeakMap<Schema, string>();\n\n/** 32-bit FNV-1a, enough to tell schemas apart without a crypto dependency. */\nfunction fnv1a(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Returns a stable signature for a ProseMirror schema: node names, content\n * expressions, groups, attribute names and mark names. Two editors with the\n * same extensions produce the same signature, so a slice copied from one can\n * be deserialized in the other without losing or rejecting nodes.\n */\nexport function getSchemaSignature(schema: Schema): string {\n const cached = signatureCache.get(schema);\n if (cached) return cached;\n const parts: string[] = [];\n for (const [name, type] of Object.entries(schema.nodes)) {\n const spec = type.spec;\n parts.push(\n `n:${name}|${spec.content ?? \"\"}|${spec.group ?? \"\"}|${spec.marks ?? \"\"}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`,\n );\n }\n for (const [name, type] of Object.entries(schema.marks)) {\n parts.push(`m:${name}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`);\n }\n const signature = `pm1-${fnv1a(parts.join(\";\"))}`;\n signatureCache.set(schema, signature);\n return signature;\n}\n\n/** Serializes a slice into the {@link PIXERATE_DOCUMENT_MIME} payload string. */\nexport function serializeRichClipboardPayload(slice: Slice, schema: Schema): string {\n const payload: RichClipboardPayload = {\n version: 1,\n schema: getSchemaSignature(schema),\n slice: slice.toJSON(),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Parses a {@link PIXERATE_DOCUMENT_MIME} payload into a slice for `schema`.\n * Returns `null` (so callers fall back to HTML / plain text) when the payload\n * is malformed, from another payload version, or from an editor whose schema\n * signature differs.\n */\nexport function parseRichClipboardPayload(data: string | null | undefined, schema: Schema): Slice | null {\n if (!data) return null;\n try {\n const payload = JSON.parse(data) as Partial<RichClipboardPayload>;\n if (!payload || payload.version !== 1) return null;\n if (payload.schema !== getSchemaSignature(schema)) return null;\n if (!payload.slice) return null;\n return Slice.fromJSON(schema, payload.slice as any);\n } catch {\n return null;\n }\n}\n\n/** Minimal clipboard data surface (`DataTransfer`). */\nexport interface ClipboardDataLike {\n getData(format: string): string;\n setData(format: string, data: string): void;\n clearData?(format?: string): void;\n}\n\n/**\n * Returns the `clipboardData` of a native `ClipboardEvent` or of a React\n * synthetic clipboard event (which wraps the native event in `nativeEvent`).\n */\nexport function getClipboardData(event: unknown): ClipboardDataLike | null {\n if (!event || typeof event !== \"object\") return null;\n const native = \"nativeEvent\" in event ? (event as any).nativeEvent : event;\n return ((event as any).clipboardData ?? native?.clipboardData ?? null) as ClipboardDataLike | null;\n}\n\n/**\n * Reads a slice from a paste event's {@link PIXERATE_DOCUMENT_MIME} data.\n * Returns `null` when the data is missing or its schema does not match.\n */\nexport function readRichClipboard(\n event: unknown,\n schema: Schema,\n mimeType: string = PIXERATE_DOCUMENT_MIME,\n): Slice | null {\n const data = getClipboardData(event);\n if (!data) return null;\n let raw: string | undefined;\n try {\n raw = data.getData(mimeType);\n } catch {\n return null;\n }\n return parseRichClipboardPayload(raw, schema);\n}\n"]}