@rimori/react-client 0.4.11-next.0 → 0.4.11-next.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,6 +12,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { useRimori } from '../../providers/PluginProvider';
13
13
  import { Markdown } from 'tiptap-markdown';
14
14
  import StarterKit from '@tiptap/starter-kit';
15
+ import { Paragraph } from '@tiptap/extension-paragraph';
15
16
  import Table from '@tiptap/extension-table';
16
17
  import TableCell from '@tiptap/extension-table-cell';
17
18
  import TableHeader from '@tiptap/extension-table-header';
@@ -26,6 +27,28 @@ import { AiOutlineUnorderedList } from 'react-icons/ai';
26
27
  import { LuClipboardPaste, LuHeading1, LuHeading2, LuHeading3, LuLink, LuUnlink } from 'react-icons/lu';
27
28
  import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from 'react-icons/fa';
28
29
  import { ImageUploadExtension, triggerImageUpload } from './ImageUploadExtension';
30
+ // Extends TipTap's Paragraph to serialize empty paragraphs as <p></p>.
31
+ // Standard markdown collapses consecutive blank lines, losing empty paragraph nodes.
32
+ // Since tiptap-markdown enables html:true by default, <p></p> survives the round-trip.
33
+ const ParagraphPreserveEmpty = Paragraph.extend({
34
+ addStorage() {
35
+ return {
36
+ markdown: {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ serialize(state, node) {
39
+ if (node.childCount === 0) {
40
+ state.write('<p></p>');
41
+ }
42
+ else {
43
+ state.renderInline(node);
44
+ }
45
+ state.closeBlock(node);
46
+ },
47
+ parse: {},
48
+ },
49
+ };
50
+ },
51
+ });
29
52
  function getMarkdown(editor) {
30
53
  return editor.storage.markdown.getMarkdown();
31
54
  }
@@ -160,7 +183,8 @@ export const MarkdownEditor = ({ content, editable, className, onUpdate, labels,
160
183
  return data.url;
161
184
  }), [storage]);
162
185
  const extensions = useMemo(() => [
163
- StarterKit,
186
+ StarterKit.configure({ paragraph: false }),
187
+ ParagraphPreserveEmpty,
164
188
  Table.configure({ resizable: false }),
165
189
  TableRow,
166
190
  TableHeader,
@@ -184,7 +208,8 @@ export const MarkdownEditor = ({ content, editable, className, onUpdate, labels,
184
208
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(markdown);
185
209
  },
186
210
  });
187
- // Sync external content changes (e.g. AI autofill) without triggering update loop
211
+ // Sync external content changes (e.g. AI autofill) without triggering update loop.
212
+ // Pass false to setContent to prevent onUpdate from firing and re-normalizing the content.
188
213
  useEffect(() => {
189
214
  if (!editor)
190
215
  return;
@@ -192,7 +217,7 @@ export const MarkdownEditor = ({ content, editable, className, onUpdate, labels,
192
217
  if (incoming === lastEmittedRef.current)
193
218
  return;
194
219
  lastEmittedRef.current = incoming;
195
- editor.commands.setContent(incoming);
220
+ editor.commands.setContent(incoming, false);
196
221
  }, [editor, content]);
197
222
  // Sync editable prop
198
223
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/react-client",
3
- "version": "0.4.11-next.0",
3
+ "version": "0.4.11-next.1",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,7 +24,7 @@
24
24
  "format": "prettier --write ."
25
25
  },
26
26
  "peerDependencies": {
27
- "@rimori/client": "^2.5.19",
27
+ "@rimori/client": "2.5.19-next.5",
28
28
  "react": "^18.1.0",
29
29
  "react-dom": "^18.1.0"
30
30
  },
@@ -32,6 +32,7 @@
32
32
  "@tiptap/core": "^2.26.1",
33
33
  "@tiptap/extension-image": "^2.26.1",
34
34
  "@tiptap/extension-link": "^2.26.1",
35
+ "@tiptap/extension-paragraph": "^2.26.1",
35
36
  "@tiptap/extension-table": "^2.26.1",
36
37
  "@tiptap/extension-table-cell": "^2.26.1",
37
38
  "@tiptap/extension-table-header": "^2.26.1",
@@ -46,7 +47,7 @@
46
47
  },
47
48
  "devDependencies": {
48
49
  "@eslint/js": "^9.37.0",
49
- "@rimori/client": "^2.5.19",
50
+ "@rimori/client": "2.5.19-next.5",
50
51
  "@types/react": "^18.3.21",
51
52
  "eslint-config-prettier": "^10.1.8",
52
53
  "eslint-plugin-prettier": "^5.5.4",
@@ -2,6 +2,7 @@ import { JSX, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { useRimori } from '../../providers/PluginProvider';
3
3
  import { Markdown } from 'tiptap-markdown';
4
4
  import StarterKit from '@tiptap/starter-kit';
5
+ import { Paragraph } from '@tiptap/extension-paragraph';
5
6
  import Table from '@tiptap/extension-table';
6
7
  import TableCell from '@tiptap/extension-table-cell';
7
8
  import TableHeader from '@tiptap/extension-table-header';
@@ -28,6 +29,28 @@ import { LuClipboardPaste, LuHeading1, LuHeading2, LuHeading3, LuLink, LuUnlink
28
29
  import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from 'react-icons/fa';
29
30
  import { ImageUploadExtension, triggerImageUpload } from './ImageUploadExtension';
30
31
 
32
+ // Extends TipTap's Paragraph to serialize empty paragraphs as <p></p>.
33
+ // Standard markdown collapses consecutive blank lines, losing empty paragraph nodes.
34
+ // Since tiptap-markdown enables html:true by default, <p></p> survives the round-trip.
35
+ const ParagraphPreserveEmpty = Paragraph.extend({
36
+ addStorage() {
37
+ return {
38
+ markdown: {
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ serialize(state: any, node: any) {
41
+ if (node.childCount === 0) {
42
+ state.write('<p></p>');
43
+ } else {
44
+ state.renderInline(node);
45
+ }
46
+ state.closeBlock(node);
47
+ },
48
+ parse: {},
49
+ },
50
+ };
51
+ },
52
+ });
53
+
31
54
  function getMarkdown(editor: Editor): string {
32
55
  return (editor.storage as { markdown: { getMarkdown: () => string } }).markdown.getMarkdown();
33
56
  }
@@ -559,7 +582,8 @@ export const MarkdownEditor = ({
559
582
 
560
583
  const extensions = useMemo(
561
584
  () => [
562
- StarterKit,
585
+ StarterKit.configure({ paragraph: false }),
586
+ ParagraphPreserveEmpty,
563
587
  Table.configure({ resizable: false }),
564
588
  TableRow,
565
589
  TableHeader,
@@ -587,13 +611,14 @@ export const MarkdownEditor = ({
587
611
  },
588
612
  });
589
613
 
590
- // Sync external content changes (e.g. AI autofill) without triggering update loop
614
+ // Sync external content changes (e.g. AI autofill) without triggering update loop.
615
+ // Pass false to setContent to prevent onUpdate from firing and re-normalizing the content.
591
616
  useEffect(() => {
592
617
  if (!editor) return;
593
618
  const incoming = content ?? '';
594
619
  if (incoming === lastEmittedRef.current) return;
595
620
  lastEmittedRef.current = incoming;
596
- editor.commands.setContent(incoming);
621
+ editor.commands.setContent(incoming, false);
597
622
  }, [editor, content]);
598
623
 
599
624
  // Sync editable prop
package/tsconfig.json CHANGED
@@ -14,5 +14,6 @@
14
14
  },
15
15
  "include": [
16
16
  "src/**/*"
17
- ]
17
+ ],
18
+ "exclude": ["node_modules", "dist", "build"]
18
19
  }