@tiptap/core 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/core",
3
3
  "description": "headless rich text editor",
4
- "version": "3.20.1",
4
+ "version": "3.20.3",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -52,10 +52,10 @@
52
52
  "jsx-dev-runtime"
53
53
  ],
54
54
  "devDependencies": {
55
- "@tiptap/pm": "^3.20.1"
55
+ "@tiptap/pm": "^3.20.3"
56
56
  },
57
57
  "peerDependencies": {
58
- "@tiptap/pm": "^3.20.1"
58
+ "@tiptap/pm": "^3.20.3"
59
59
  },
60
60
  "repository": {
61
61
  "type": "git",
package/src/Extendable.ts CHANGED
@@ -291,6 +291,28 @@ export interface ExtendableConfig<
291
291
  * Defines if this markdown element should indent it's child elements
292
292
  */
293
293
  indentsContent?: boolean
294
+
295
+ /**
296
+ * Lets a mark tell the Markdown serializer which inline HTML tags it can
297
+ * safely use when plain markdown delimiters would become ambiguous.
298
+ *
299
+ * This is mainly useful for overlapping marks. For example, bold followed
300
+ * by bold+italic followed by italic cannot always be written back with only
301
+ * `*` and `**` in a way that still parses correctly. In that case, the
302
+ * serializer can close the overlapping section with markdown and reopen the
303
+ * remaining tail with HTML instead.
304
+ *
305
+ * Example:
306
+ * - desired formatting: `**123` + `*456*` + `789 italic`
307
+ * - serialized result: `**123*456***<em>789</em>`
308
+ *
309
+ * If your extension defines custom mark names, set `htmlReopen` on that
310
+ * extension so the serializer can reuse its HTML form for overlap cases.
311
+ */
312
+ htmlReopen?: {
313
+ open: string
314
+ close: string
315
+ }
294
316
  }
295
317
 
296
318
  /**
@@ -25,7 +25,7 @@ export function isNodeEmpty(
25
25
  return true
26
26
  }
27
27
  if (node.isText) {
28
- return /^\s*$/m.test(node.text ?? '')
28
+ return !/\S/.test(node.text ?? '')
29
29
  }
30
30
  }
31
31
 
package/src/types.ts CHANGED
@@ -898,6 +898,8 @@ export type MarkdownParseHelpers = {
898
898
  parseInline: (tokens: MarkdownToken[]) => JSONContent[]
899
899
  /** Parse an array of block-level tokens */
900
900
  parseChildren: (tokens: MarkdownToken[]) => JSONContent[]
901
+ /** Parse block-level tokens while preserving implicit empty paragraphs from blank lines */
902
+ parseBlockChildren?: (tokens: MarkdownToken[]) => JSONContent[]
901
903
  /** Create a text node with optional marks */
902
904
  createTextNode: (text: string, marks?: Array<{ type: string; attrs?: any }>) => JSONContent
903
905
  /** Create any node type with attributes and content */
@@ -945,6 +947,7 @@ export type RenderContext = {
945
947
  level: number
946
948
  meta?: Record<string, any>
947
949
  parentType?: string | null
950
+ previousNode?: JSONContent | null
948
951
  }
949
952
 
950
953
  /** Extension contract for markdown parsing/serialization. */
@@ -956,6 +959,10 @@ export interface MarkdownExtensionSpec {
956
959
  parseMarkdown?: (token: MarkdownToken, helpers: MarkdownParseHelpers) => MarkdownParseResult
957
960
  renderMarkdown?: (node: any, helpers: MarkdownRendererHelpers, ctx: RenderContext) => string
958
961
  isIndenting?: boolean
962
+ htmlReopen?: {
963
+ open: string
964
+ close: string
965
+ }
959
966
  /** Custom tokenizer for marked.js to handle non-standard markdown syntax */
960
967
  tokenizer?: MarkdownTokenizer
961
968
  }
@@ -1004,6 +1011,9 @@ export type MarkdownRendererHelpers = {
1004
1011
  */
1005
1012
  renderChildren: (nodes: JSONContent | JSONContent[], separator?: string) => string
1006
1013
 
1014
+ /** Render a single child node with its sibling index preserved */
1015
+ renderChild?: (node: JSONContent, index: number) => string
1016
+
1007
1017
  /**
1008
1018
  * Render a text token to a markdown string
1009
1019
  * @param prefix The prefix to add before the content
@@ -57,6 +57,7 @@ export function renderNestedMarkdownContent(
57
57
  node: JSONContent,
58
58
  h: {
59
59
  renderChildren: (nodes: JSONContent[]) => string
60
+ renderChild?: (node: JSONContent, index: number) => string
60
61
  indent: (text: string) => string
61
62
  },
62
63
  prefixOrGenerator: string | ((ctx: any) => string),
@@ -73,22 +74,23 @@ export function renderNestedMarkdownContent(
73
74
 
74
75
  // Render the main content (typically a paragraph)
75
76
  const mainContent = h.renderChildren([content])
76
- const output = [`${prefix}${mainContent}`]
77
+ let output = `${prefix}${mainContent}`
77
78
 
78
79
  // Handle nested children with proper indentation
79
80
  if (children && children.length > 0) {
80
- children.forEach(child => {
81
- const childContent = h.renderChildren([child])
82
- if (childContent) {
81
+ children.forEach((child, index) => {
82
+ const childContent = h.renderChild?.(child, index + 1) ?? h.renderChildren([child])
83
+ if (childContent !== undefined && childContent !== null) {
83
84
  // Split the child content by lines and indent each line
84
85
  const indentedChild = childContent
85
86
  .split('\n')
86
- .map(line => (line ? h.indent(line) : ''))
87
+ .map(line => (line ? h.indent(line) : h.indent('')))
87
88
  .join('\n')
88
- output.push(indentedChild)
89
+
90
+ output += child.type === 'paragraph' ? `\n\n${indentedChild}` : `\n${indentedChild}`
89
91
  }
90
92
  })
91
93
  }
92
94
 
93
- return output.join('\n')
95
+ return output
94
96
  }