Repository navigation
Fix saving a TipTap rich text that ends with a list - #6520
Merged
Merged
Conversation
Contributor
|
SebiVPS
force-pushed
the
fix-tip-tap-trailing-empty-list
branch
from
October 7, 2026 05:41
0120b9c to
e1b51a1
Compare
SebiVPS
marked this pull request as draft
October 7, 2026 05:44
SebiVPS
marked this pull request as ready for review
October 7, 2026 06:38
This comment was marked as resolved.
This comment was marked as resolved.
SebiVPS
force-pushed
the
fix-tip-tap-trailing-empty-list
branch
from
October 7, 2026 06:59
873e8aa to
9f3aa03
Compare
Since 10.8.0, the editor added an empty bulletList after a list at the end of the content. The API rejects a list without items, so saving failed. Removing the list didn't fix it: the empty list stayed at the end of the content, where editors couldn't see or remove it. ProseMirror's default block type is the schema's first block node. TrailingNode inserts it after content that doesn't end with a text block, and an emptied document is filled with it. The textBlock node, which replaced the paragraph and heading nodes, had no priority, so it was registered after StarterKit's lists, and the default block type became bulletList (or horizontalRule in a heading-only block). The textBlock node gets the paragraph's priority, in the Admin and in the API, whose schema has to match. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The story clicked the editor to place the caret at the end of the list item. Now that TrailingNode adds an empty text block after a list again, that click can place the caret in the text block instead, so the nested bullet list was created outside of the ordered list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With the text block as the default block type, TrailingNode adds an empty text block after a list at the end of the content. When the list is the last block maxTextBlocks allows, that block exceeds the limit, and the limit handler can't remove it for good, because TrailingNode adds it again. The content then can't be saved. Transactions that leave the content at or over the limit now skip TrailingNode. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SebiVPS
force-pushed
the
fix-tip-tap-trailing-empty-list
branch
from
October 7, 2026 07:22
9f3aa03 to
f915a41
Compare
VPS-Obi
previously approved these changes
Oct 7, 2026
Contributor
|
@SebiVPS please resolve conflicts. |
…empty-list # Conflicts: # packages/admin/cms-admin/src/blocks/tipTap/createTipTapRichTextBlock.test.tsx
VPS-Obi
approved these changes
Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Since 10.8.0 (#6382), a TipTap rich text that ends with a list got an empty
bulletListappended by the editor. A list without items is invalid, so the API rejected the content on save with "Validation failed". Deleting the list did not help: the empty list stayed in the content, rendered as an empty<ul></ul>, and editors could not see, select or remove it until they reloaded. Selecting all content and deleting it was affected too. It produced abulletListwith alistItemand a text block instead of one empty text block, and in a block that only allows headings it produced ahorizontalRule.ProseMirror needs a default block type in some places, and no setting defines it: it is the first node of the
blockgroup in schema order (schema.topNodeType.contentMatch.defaultType). TipTap'sTrailingNodeinserts it after content that ends with a list or a child block, and ProseMirror'screateAndFillfills an emptied document with it. The schema order follows the extensions'priority. TipTap'sParagraphhas priority 1000, so the paragraph used to be the default block type. #6382 replaced the paragraph and heading nodes with thetextBlocknode, which had no priority and is registered after StarterKit's lists, sobulletListbecame the default block type.Solution
textBlocknow haspriority: 1000in the Admin and in the API (the API schema must match the Admin schema). This restores what #6382 removed:textBlocktakes the paragraph's place in the schema, so every place that uses the default block type gets a text block again, not onlyTrailingNode. The editor adds an empty text block after a trailing list, as it did before 10.8.0. Other approaches only fix part of the problem:nodeoption ofTrailingNodetotextBlockfixesTrailingNodeonly. An emptied document would still become a list.TrailingNode, nothing follows a child block at the end of the content, so editors could not type after it.textBlockbefore StarterKit gives the same schema order, but only as long as nobody changes the order of the extensions.priorityis TipTap's way to define it, with the same value as TipTap'sParagraph.With
maxTextBlocks, this empty text block must not be added when the content is already at the limit. Otherwise a list as the last allowed block is followed by a block over the limit, and the API rejects the content. 10.8.0 had the same problem for ordered lists. It only avoided it for bullet lists by accident, becauseTrailingNodenever adds a block after its own default type. ThemaxTextBlocksextension now sets TipTap'sskipTrailingNodeMetaon transactions that leave the content at or over the limit. A filter that rejects these transactions would also reject pastes over the limit instead of cutting them off.@tiptap/extensionsexportsskipTrailingNodeMeta, so it is now a direct dependency of@dextinity/cms-admin. StarterKit already depends on it.The "List Text Block Styles" story clicked the editor to put the caret at the end of the list item. With the empty text block after the list, that click lands below the list, so the story now clicks the list item text.
🤖 Generated with Claude Code