@bendyline/squisq-editor-react 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,11 @@ const RE_ITALIC_UNDER = /_(.+?)_/g;
19
19
  const RE_STRIKETHROUGH = /~~(.+?)~~/g;
20
20
  const RE_INLINE_CODE = /`(.+?)`/g;
21
21
  const RE_LINK = /\[(.+?)\]\((.+?)\)/g;
22
- const RE_IMAGE = /!\[(.+?)\]\((.+?)\)/g;
22
+ // `*?` on the alt — an empty alt (`![](foo.png)`) is valid markdown and
23
+ // the most common shape for pasted/uploaded images that don't yet have
24
+ // a human-picked caption. Previously required at least one alt char,
25
+ // which dropped those images on the floor during markdown→HTML.
26
+ const RE_IMAGE = /!\[(.*?)\]\((.+?)\)/g;
23
27
  // Mentions: `@[Display](scheme:id)` — scheme-part must start with a letter
24
28
  // so plain `$100` or price-style parentheticals don't accidentally match.
25
29
  // remark-stringify may round-trip the colon as `\:` — tolerate either.
@@ -33,7 +37,14 @@ const RE_S_TAG = /<s>(.*?)<\/s>/g;
33
37
  const RE_DEL_TAG = /<del>(.*?)<\/del>/g;
34
38
  const RE_CODE_TAG = /<code>(.*?)<\/code>/g;
35
39
  const RE_A_TAG = /<a[^>]+href="([^"]*)"[^>]*>(.*?)<\/a>/g;
36
- const RE_IMG_TAG = /<img[^>]+alt="([^"]*)"[^>]+src="([^"]*)"[^>]*>/g;
40
+ // Matches any `<img>` tag and captures its `src` + `alt` regardless of
41
+ // attribute order. TipTap's Image extension renders `<img src="..."
42
+ // alt="...">` (src first), while some other producers — including our
43
+ // own `markdownToTiptap` conversion — emit alt-first. The previous
44
+ // regex required alt-before-src and silently dropped every src-first
45
+ // image; `RE_STRIP_TAGS` below would then delete the unmatched tag,
46
+ // so the outgoing markdown had no image reference at all.
47
+ const RE_IMG_TAG = /<img\b([^>]*)>/g;
37
48
  const RE_STRIP_TAGS = /<[^>]+>/g;
38
49
 
39
50
  /**
@@ -462,6 +473,26 @@ export function tiptapToMarkdown(html: string): string {
462
473
  continue;
463
474
  }
464
475
 
476
+ // Block-level image. TipTap's Image extension with `inline: false`
477
+ // emits `<img src alt>` as a bare top-level element (no wrapping
478
+ // `<p>`). Without this handler the skip-unknown-tags catch-all
479
+ // below silently drops the image from the outgoing markdown —
480
+ // the bug that made the chat composer ship image-less messages
481
+ // even though the editor showed the picture. Handled here,
482
+ // before the inline walker ever sees it.
483
+ const imgMatch = remaining.match(/^<img\b([^>]*)>/);
484
+ if (imgMatch) {
485
+ const attrs = imgMatch[1] ?? '';
486
+ const src = /\bsrc="([^"]*)"/i.exec(attrs)?.[1];
487
+ if (src) {
488
+ const alt = /\balt="([^"]*)"/i.exec(attrs)?.[1] ?? '';
489
+ lines.push(`![${alt}](${src})`);
490
+ lines.push('');
491
+ }
492
+ remaining = remaining.slice(imgMatch[0].length);
493
+ continue;
494
+ }
495
+
465
496
  // Skip unknown tags or whitespace
466
497
  const skipMatch = remaining.match(/^(<[^>]+>|\s+)/);
467
498
  if (skipMatch) {
@@ -637,8 +668,14 @@ function htmlToInline(html: string): string {
637
668
  // Links
638
669
  result = result.replace(RE_A_TAG, '[$2]($1)');
639
670
 
640
- // Images
641
- result = result.replace(RE_IMG_TAG, '![$1]($2)');
671
+ // Images — order-agnostic attribute parsing (tiptap emits src-first,
672
+ // our markdown-to-html emits alt-first; either must serialize back).
673
+ result = result.replace(RE_IMG_TAG, (match, attrs: string) => {
674
+ const src = /\bsrc="([^"]*)"/i.exec(attrs)?.[1];
675
+ if (!src) return match;
676
+ const alt = /\balt="([^"]*)"/i.exec(attrs)?.[1] ?? '';
677
+ return `![${alt}](${src})`;
678
+ });
642
679
 
643
680
  // Strip remaining tags
644
681
  result = result.replace(RE_STRIP_TAGS, '');