@tiptap/extension-paragraph 3.20.1 → 3.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/extension-paragraph",
3
3
  "description": "paragraph extension for tiptap",
4
- "version": "3.20.1",
4
+ "version": "3.20.3",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -31,10 +31,10 @@
31
31
  "dist"
32
32
  ],
33
33
  "devDependencies": {
34
- "@tiptap/core": "^3.20.1"
34
+ "@tiptap/core": "^3.20.3"
35
35
  },
36
36
  "peerDependencies": {
37
- "@tiptap/core": "^3.20.1"
37
+ "@tiptap/core": "^3.20.3"
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",
package/src/paragraph.ts CHANGED
@@ -88,7 +88,7 @@ export const Paragraph = Node.create<ParagraphOptions>({
88
88
  return helpers.createNode('paragraph', undefined, content)
89
89
  },
90
90
 
91
- renderMarkdown: (node, h) => {
91
+ renderMarkdown: (node, h, ctx) => {
92
92
  if (!node) {
93
93
  return ''
94
94
  }
@@ -96,9 +96,14 @@ export const Paragraph = Node.create<ParagraphOptions>({
96
96
  // Normalize content: treat undefined/null as empty array
97
97
  const content = Array.isArray(node.content) ? node.content : []
98
98
 
99
- // If the paragraph is empty, render a non-breaking space to preserve blank lines
100
99
  if (content.length === 0) {
101
- return EMPTY_PARAGRAPH_MARKDOWN
100
+ // Emit &nbsp; for the second and later empty paragraphs in a consecutive
101
+ // run at the current nesting level. The first empty paragraph stays empty
102
+ // so markdown spacing is preserved naturally.
103
+ const previousContent = Array.isArray(ctx?.previousNode?.content) ? ctx.previousNode.content : []
104
+ const previousNodeIsEmptyParagraph = ctx?.previousNode?.type === 'paragraph' && previousContent.length === 0
105
+
106
+ return previousNodeIsEmptyParagraph ? EMPTY_PARAGRAPH_MARKDOWN : ''
102
107
  }
103
108
 
104
109
  return h.renderChildren(content)
package/dist/index.cjs DELETED
@@ -1,89 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Paragraph: () => Paragraph,
24
- default: () => index_default
25
- });
26
- module.exports = __toCommonJS(index_exports);
27
-
28
- // src/paragraph.ts
29
- var import_core = require("@tiptap/core");
30
- var EMPTY_PARAGRAPH_MARKDOWN = "&nbsp;";
31
- var NBSP_CHAR = "\xA0";
32
- var Paragraph = import_core.Node.create({
33
- name: "paragraph",
34
- priority: 1e3,
35
- addOptions() {
36
- return {
37
- HTMLAttributes: {}
38
- };
39
- },
40
- group: "block",
41
- content: "inline*",
42
- parseHTML() {
43
- return [{ tag: "p" }];
44
- },
45
- renderHTML({ HTMLAttributes }) {
46
- return ["p", (0, import_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes), 0];
47
- },
48
- parseMarkdown: (token, helpers) => {
49
- const tokens = token.tokens || [];
50
- if (tokens.length === 1 && tokens[0].type === "image") {
51
- return helpers.parseChildren([tokens[0]]);
52
- }
53
- const content = helpers.parseInline(tokens);
54
- if (content.length === 1 && content[0].type === "text" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) {
55
- return helpers.createNode("paragraph", void 0, []);
56
- }
57
- return helpers.createNode("paragraph", void 0, content);
58
- },
59
- renderMarkdown: (node, h) => {
60
- if (!node) {
61
- return "";
62
- }
63
- const content = Array.isArray(node.content) ? node.content : [];
64
- if (content.length === 0) {
65
- return EMPTY_PARAGRAPH_MARKDOWN;
66
- }
67
- return h.renderChildren(content);
68
- },
69
- addCommands() {
70
- return {
71
- setParagraph: () => ({ commands }) => {
72
- return commands.setNode(this.name);
73
- }
74
- };
75
- },
76
- addKeyboardShortcuts() {
77
- return {
78
- "Mod-Alt-0": () => this.editor.commands.setParagraph()
79
- };
80
- }
81
- });
82
-
83
- // src/index.ts
84
- var index_default = Paragraph;
85
- // Annotate the CommonJS export names for ESM import in node:
86
- 0 && (module.exports = {
87
- Paragraph
88
- });
89
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/paragraph.ts"],"sourcesContent":["import { Paragraph } from './paragraph.js'\n\nexport * from './paragraph.js'\n\nexport default Paragraph\n","import { mergeAttributes, Node } from '@tiptap/core'\n\nexport interface ParagraphOptions {\n /**\n * The HTML attributes for a paragraph node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n paragraph: {\n /**\n * Toggle a paragraph\n * @example editor.commands.toggleParagraph()\n */\n setParagraph: () => ReturnType\n }\n }\n}\n\n/**\n * Markdown marker for empty paragraphs to preserve blank lines.\n * Using &nbsp; (non-breaking space HTML entity) ensures the paragraph\n * is not collapsed by markdown parsers while remaining human-readable.\n */\nconst EMPTY_PARAGRAPH_MARKDOWN = '&nbsp;'\n\n/**\n * Unicode character for non-breaking space (U+00A0).\n * Some markdown parsers may convert &nbsp; entities to this literal character.\n */\nconst NBSP_CHAR = '\\u00A0'\n\n/**\n * This extension allows you to create paragraphs.\n * @see https://www.tiptap.dev/api/nodes/paragraph\n */\nexport const Paragraph = Node.create<ParagraphOptions>({\n name: 'paragraph',\n\n priority: 1000,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n group: 'block',\n\n content: 'inline*',\n\n parseHTML() {\n return [{ tag: 'p' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['p', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n parseMarkdown: (token, helpers) => {\n const tokens = token.tokens || []\n\n // Special case: if paragraph contains only a single image token,\n // unwrap it to avoid nesting block elements incorrectly\n if (tokens.length === 1 && tokens[0].type === 'image') {\n // Parse the image token directly as a block element\n return helpers.parseChildren([tokens[0]])\n }\n\n // Parse the inline tokens\n const content = helpers.parseInline(tokens)\n\n // Special case: if paragraph contains only &nbsp; (non-breaking space),\n // treat it as an empty paragraph to preserve blank lines\n if (\n content.length === 1 &&\n content[0].type === 'text' &&\n (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)\n ) {\n return helpers.createNode('paragraph', undefined, [])\n }\n\n // Convert 'paragraph' token to paragraph node\n return helpers.createNode('paragraph', undefined, content)\n },\n\n renderMarkdown: (node, h) => {\n if (!node) {\n return ''\n }\n\n // Normalize content: treat undefined/null as empty array\n const content = Array.isArray(node.content) ? node.content : []\n\n // If the paragraph is empty, render a non-breaking space to preserve blank lines\n if (content.length === 0) {\n return EMPTY_PARAGRAPH_MARKDOWN\n }\n\n return h.renderChildren(content)\n },\n\n addCommands() {\n return {\n setParagraph:\n () =>\n ({ commands }) => {\n return commands.setNode(this.name)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-0': () => this.editor.commands.setParagraph(),\n }\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAsC;AA4BtC,IAAM,2BAA2B;AAMjC,IAAM,YAAY;AAMX,IAAM,YAAY,iBAAK,OAAyB;AAAA,EACrD,MAAM;AAAA,EAEN,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,EAEP,SAAS;AAAA,EAET,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,EACtB;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO,CAAC,SAAK,6BAAgB,KAAK,QAAQ,gBAAgB,cAAc,GAAG,CAAC;AAAA,EAC9E;AAAA,EAEA,eAAe,CAAC,OAAO,YAAY;AACjC,UAAM,SAAS,MAAM,UAAU,CAAC;AAIhC,QAAI,OAAO,WAAW,KAAK,OAAO,CAAC,EAAE,SAAS,SAAS;AAErD,aAAO,QAAQ,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,IAC1C;AAGA,UAAM,UAAU,QAAQ,YAAY,MAAM;AAI1C,QACE,QAAQ,WAAW,KACnB,QAAQ,CAAC,EAAE,SAAS,WACnB,QAAQ,CAAC,EAAE,SAAS,4BAA4B,QAAQ,CAAC,EAAE,SAAS,YACrE;AACA,aAAO,QAAQ,WAAW,aAAa,QAAW,CAAC,CAAC;AAAA,IACtD;AAGA,WAAO,QAAQ,WAAW,aAAa,QAAW,OAAO;AAAA,EAC3D;AAAA,EAEA,gBAAgB,CAAC,MAAM,MAAM;AAC3B,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC;AAG9D,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,WAAO,EAAE,eAAe,OAAO;AAAA,EACjC;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,cACE,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,QAAQ,KAAK,IAAI;AAAA,MACnC;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,aAAa,MAAM,KAAK,OAAO,SAAS,aAAa;AAAA,IACvD;AAAA,EACF;AACF,CAAC;;;ADrHD,IAAO,gBAAQ;","names":[]}
package/dist/index.d.cts DELETED
@@ -1,28 +0,0 @@
1
- import { Node } from '@tiptap/core';
2
-
3
- interface ParagraphOptions {
4
- /**
5
- * The HTML attributes for a paragraph node.
6
- * @default {}
7
- * @example { class: 'foo' }
8
- */
9
- HTMLAttributes: Record<string, any>;
10
- }
11
- declare module '@tiptap/core' {
12
- interface Commands<ReturnType> {
13
- paragraph: {
14
- /**
15
- * Toggle a paragraph
16
- * @example editor.commands.toggleParagraph()
17
- */
18
- setParagraph: () => ReturnType;
19
- };
20
- }
21
- }
22
- /**
23
- * This extension allows you to create paragraphs.
24
- * @see https://www.tiptap.dev/api/nodes/paragraph
25
- */
26
- declare const Paragraph: Node<ParagraphOptions, any>;
27
-
28
- export { Paragraph, type ParagraphOptions, Paragraph as default };
package/dist/index.d.ts DELETED
@@ -1,28 +0,0 @@
1
- import { Node } from '@tiptap/core';
2
-
3
- interface ParagraphOptions {
4
- /**
5
- * The HTML attributes for a paragraph node.
6
- * @default {}
7
- * @example { class: 'foo' }
8
- */
9
- HTMLAttributes: Record<string, any>;
10
- }
11
- declare module '@tiptap/core' {
12
- interface Commands<ReturnType> {
13
- paragraph: {
14
- /**
15
- * Toggle a paragraph
16
- * @example editor.commands.toggleParagraph()
17
- */
18
- setParagraph: () => ReturnType;
19
- };
20
- }
21
- }
22
- /**
23
- * This extension allows you to create paragraphs.
24
- * @see https://www.tiptap.dev/api/nodes/paragraph
25
- */
26
- declare const Paragraph: Node<ParagraphOptions, any>;
27
-
28
- export { Paragraph, type ParagraphOptions, Paragraph as default };
package/dist/index.js DELETED
@@ -1,62 +0,0 @@
1
- // src/paragraph.ts
2
- import { mergeAttributes, Node } from "@tiptap/core";
3
- var EMPTY_PARAGRAPH_MARKDOWN = "&nbsp;";
4
- var NBSP_CHAR = "\xA0";
5
- var Paragraph = Node.create({
6
- name: "paragraph",
7
- priority: 1e3,
8
- addOptions() {
9
- return {
10
- HTMLAttributes: {}
11
- };
12
- },
13
- group: "block",
14
- content: "inline*",
15
- parseHTML() {
16
- return [{ tag: "p" }];
17
- },
18
- renderHTML({ HTMLAttributes }) {
19
- return ["p", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
20
- },
21
- parseMarkdown: (token, helpers) => {
22
- const tokens = token.tokens || [];
23
- if (tokens.length === 1 && tokens[0].type === "image") {
24
- return helpers.parseChildren([tokens[0]]);
25
- }
26
- const content = helpers.parseInline(tokens);
27
- if (content.length === 1 && content[0].type === "text" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) {
28
- return helpers.createNode("paragraph", void 0, []);
29
- }
30
- return helpers.createNode("paragraph", void 0, content);
31
- },
32
- renderMarkdown: (node, h) => {
33
- if (!node) {
34
- return "";
35
- }
36
- const content = Array.isArray(node.content) ? node.content : [];
37
- if (content.length === 0) {
38
- return EMPTY_PARAGRAPH_MARKDOWN;
39
- }
40
- return h.renderChildren(content);
41
- },
42
- addCommands() {
43
- return {
44
- setParagraph: () => ({ commands }) => {
45
- return commands.setNode(this.name);
46
- }
47
- };
48
- },
49
- addKeyboardShortcuts() {
50
- return {
51
- "Mod-Alt-0": () => this.editor.commands.setParagraph()
52
- };
53
- }
54
- });
55
-
56
- // src/index.ts
57
- var index_default = Paragraph;
58
- export {
59
- Paragraph,
60
- index_default as default
61
- };
62
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/paragraph.ts","../src/index.ts"],"sourcesContent":["import { mergeAttributes, Node } from '@tiptap/core'\n\nexport interface ParagraphOptions {\n /**\n * The HTML attributes for a paragraph node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n paragraph: {\n /**\n * Toggle a paragraph\n * @example editor.commands.toggleParagraph()\n */\n setParagraph: () => ReturnType\n }\n }\n}\n\n/**\n * Markdown marker for empty paragraphs to preserve blank lines.\n * Using &nbsp; (non-breaking space HTML entity) ensures the paragraph\n * is not collapsed by markdown parsers while remaining human-readable.\n */\nconst EMPTY_PARAGRAPH_MARKDOWN = '&nbsp;'\n\n/**\n * Unicode character for non-breaking space (U+00A0).\n * Some markdown parsers may convert &nbsp; entities to this literal character.\n */\nconst NBSP_CHAR = '\\u00A0'\n\n/**\n * This extension allows you to create paragraphs.\n * @see https://www.tiptap.dev/api/nodes/paragraph\n */\nexport const Paragraph = Node.create<ParagraphOptions>({\n name: 'paragraph',\n\n priority: 1000,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n group: 'block',\n\n content: 'inline*',\n\n parseHTML() {\n return [{ tag: 'p' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['p', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n parseMarkdown: (token, helpers) => {\n const tokens = token.tokens || []\n\n // Special case: if paragraph contains only a single image token,\n // unwrap it to avoid nesting block elements incorrectly\n if (tokens.length === 1 && tokens[0].type === 'image') {\n // Parse the image token directly as a block element\n return helpers.parseChildren([tokens[0]])\n }\n\n // Parse the inline tokens\n const content = helpers.parseInline(tokens)\n\n // Special case: if paragraph contains only &nbsp; (non-breaking space),\n // treat it as an empty paragraph to preserve blank lines\n if (\n content.length === 1 &&\n content[0].type === 'text' &&\n (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)\n ) {\n return helpers.createNode('paragraph', undefined, [])\n }\n\n // Convert 'paragraph' token to paragraph node\n return helpers.createNode('paragraph', undefined, content)\n },\n\n renderMarkdown: (node, h) => {\n if (!node) {\n return ''\n }\n\n // Normalize content: treat undefined/null as empty array\n const content = Array.isArray(node.content) ? node.content : []\n\n // If the paragraph is empty, render a non-breaking space to preserve blank lines\n if (content.length === 0) {\n return EMPTY_PARAGRAPH_MARKDOWN\n }\n\n return h.renderChildren(content)\n },\n\n addCommands() {\n return {\n setParagraph:\n () =>\n ({ commands }) => {\n return commands.setNode(this.name)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-0': () => this.editor.commands.setParagraph(),\n }\n },\n})\n","import { Paragraph } from './paragraph.js'\n\nexport * from './paragraph.js'\n\nexport default Paragraph\n"],"mappings":";AAAA,SAAS,iBAAiB,YAAY;AA4BtC,IAAM,2BAA2B;AAMjC,IAAM,YAAY;AAMX,IAAM,YAAY,KAAK,OAAyB;AAAA,EACrD,MAAM;AAAA,EAEN,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,EAEP,SAAS;AAAA,EAET,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,EACtB;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO,CAAC,KAAK,gBAAgB,KAAK,QAAQ,gBAAgB,cAAc,GAAG,CAAC;AAAA,EAC9E;AAAA,EAEA,eAAe,CAAC,OAAO,YAAY;AACjC,UAAM,SAAS,MAAM,UAAU,CAAC;AAIhC,QAAI,OAAO,WAAW,KAAK,OAAO,CAAC,EAAE,SAAS,SAAS;AAErD,aAAO,QAAQ,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,IAC1C;AAGA,UAAM,UAAU,QAAQ,YAAY,MAAM;AAI1C,QACE,QAAQ,WAAW,KACnB,QAAQ,CAAC,EAAE,SAAS,WACnB,QAAQ,CAAC,EAAE,SAAS,4BAA4B,QAAQ,CAAC,EAAE,SAAS,YACrE;AACA,aAAO,QAAQ,WAAW,aAAa,QAAW,CAAC,CAAC;AAAA,IACtD;AAGA,WAAO,QAAQ,WAAW,aAAa,QAAW,OAAO;AAAA,EAC3D;AAAA,EAEA,gBAAgB,CAAC,MAAM,MAAM;AAC3B,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC;AAG9D,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,WAAO,EAAE,eAAe,OAAO;AAAA,EACjC;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,cACE,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,QAAQ,KAAK,IAAI;AAAA,MACnC;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,aAAa,MAAM,KAAK,OAAO,SAAS,aAAa;AAAA,IACvD;AAAA,EACF;AACF,CAAC;;;ACrHD,IAAO,gBAAQ;","names":[]}