Hi @rico.trevisan
Thanks for the update. It would be great if it could erase formatting that doesn’t come from Tiptap as well. On the video below, I copy pasted content from a website. The first paragraph seemed to be cleaned up by the plugin (?) but not the second. And the ‘cleanup’ action doesn’t remove formatting.
And also, you can see, sometimes when I click on an action, the action doesn’t happen and the cursor is sent at the end of the paragraph. It seems random but it happens a lot (but not all the time)…
OK thanks for the info. I use the very last version (3.22.5).
I don’t use a collab server at the moment. I have too many users x pieces of content. It would be too expensive for not enough value added. I’m starting to think I’ll put on guardrails on multi-editing.
Yes, the content renders the same after reload. It actually happens the same way on your test app : the pasted content’s formatting is not removed…
As for my problem with the actions pushing the cursor, it still happens event if I remove the ‘when tiptap content is updated’…
Thanks
Hey @rico.trevisan
Thanks for the update. I tried the latest version. The cursor jump seems to happen less often but it still happens sometimes and it doesn’t register the click when it happens (like before). I still don’t understand why it happens on my app and not in your demo…
Partykit + Cloudflare seems interesting
@rico.trevisan Love the app! It seems like the collaboration is no longer working or set up is a bit unclear. I have tired tiptap cloud and also liveblocks but it looks like the app is not initiating the socket properly. TipTap has update their language around keys and id’s that dont quite match with the app’s fields.
@rico.trevisan I would second this. not sure why but I can not make the collaboration work. the console says “debounce done, updating content“ but I can not see the collaboration UI when I am using two different browses. will send you the link just in case you have to check it out.
appreciate your amazin work on this plugin! thanks
The entire plugin has been upgraded from Tiptap v2 to Tiptap v3 (@tiptap/core ^3.0.9 and all extensions). This is a major upgrade that brings performance improvements, new APIs, and better extensibility.
Tiptap v3 core and all extensions updated to ^3.0.9
Hocuspocus provider updated from ^3.2.2 to ^3.4.4
BubbleMenu / FloatingMenu now use Floating UI (Tiptap v3) instead of tippy.js — menus are now explicitly hidden before being passed to the extension to prevent flash-of-unstyled-content
History → UndoRedo: Replaced the History extension with Tiptap v3’s UndoRedo extension (History is automatically disabled when collaboration is active)
TableKit → Individual table extensions: Replaced the bundled TableKit with individual Table, TableRow, TableHeader, and TableCell extensions for finer control
Major refactoring of the plugin’s internal architecture for better maintainability, reliability, and performance.
Library management overhaul
Consolidated window.tiptap namespace: All libraries are now exported as a single organized window.tiptap object instead of scattered window.tiptapXyz globals. The object is structured by category (Core, Basic nodes, Formatting, Block elements, Lists, Advanced, Styling, Interaction, Utilities, Collaboration, Third-party)
New extensions exported: UndoRedo, TrailingNode, Focus, Selection now available from @tiptap/extensions
Code reorganization
Editor setup moved to initialize.js: The ~760-line editor creation logic that previously lived in update.js has been moved to initialize.js as instance.data.setupEditor(). update.js is now a lean ~110-line file that handles property changes only
Stylesheet logic extracted: CSS generation is now a reusable instance.data.applyStylesheet(properties) function callable from both update.js and the collab retry path, eliminating duplication
Script loading
Moved from shared.html to headers.html: All scripts (dist.js, lodash, tippy.js styles) moved from the plugin-level shared.html to the element-level headers.html with defer attributes for better loading performance
shared.html cleared: No longer loads any scripts at the plugin level
Collaboration Improvements
Substantial improvements to the real-time collaboration system across all three providers (Tiptap Cloud, Custom Hocuspocus, Liveblocks).
Auth failure retry with exponential backoff: When collaboration authentication fails (e.g., JWT not yet valid on the server), the plugin now automatically retries up to 5 times with exponential backoff delays (1s, 2s, 4s, 8s, 16s). The editor and provider are torn down and re-created on each retry
Initial content for empty collab documents: New maybeSetCollabInitialContent() helper handles the race condition between provider sync and editor creation — initial content is set on whichever fires last
Collab sync polling fallback: Added an interval-based polling fallback for detecting sync completion, since onSynced may not fire reliably in all providers
Graceful JWT waiting: When collaboration is enabled but the JWT token isn’t ready yet, the editor now waits gracefully instead of erroring. A one-time debug warning is shown
New collaboration exposed states
collab_status (text) — current connection status (e.g., “connected”, “disconnected”)
collab_synced (boolean) — whether the document has synced with the server
collab_connected_users (number) — count of connected collaboration users
collab_status_changed (event) — fires when the collaboration status changes
collab_synced (event) — fires when the document syncs
Auth Token Action Rewrite
The server-side JWT action has been completely rewritten and renamed.
Improved error handling: Secret keys are now trimmed before use; error messages are more descriptive
New Features
New action: Unset All Marks — removes all formatting marks (bold, italic, etc.) from the current selection
New exposed state: is_empty (boolean) — reflects whether the editor content is empty, updated on every transaction
New exposed state: can_undo (boolean) — whether an undo operation is available
New exposed state: can_redo (boolean) — whether a redo operation is available
New field: Debug Mode — toggleable debug logging. When enabled, logs detailed [Tiptap] messages to the console. When disabled, no console output is produced (replaces scattered console.log calls)
Bug Fixes
Cursor position preservation: When content is updated programmatically (via autobinding or initialContent change), the cursor position is now saved and restored (clamped to document bounds) instead of jumping to the start
Menu elements not found on time: Fixed a race condition where bubble menu and floating menu elements weren’t detected during initialization
getSelection fixed: The selectedHTML state now uses generateHTML from the window.tiptap namespace with the editor’s actual extensions, producing correct HTML output
CSS loading condition: Fixed a condition where editor styles weren’t applied correctly
Debounce timeout cleared on programmatic updates: Prevents stale debounced writes from overwriting programmatic content changes
Extensions Refactor
The old error-prone comma-separated list of extension names has been replaced with individual yes/no toggles for each extension. Each toggle is organized into its own labeled section, making extensions easy to discover, toggle on/off, and control dynamically with Bubble expressions.
29 individual yes/no toggles replace the old text field where you had to type exact extension names
Organized into sections — Text Formatting, Highlight, Heading (with H1–H6 styling), Blockquote, Structure, Lists, Image, YouTube, Link, Table, Menus, Editor Behavior, Hard Break, Mention, Unique ID, Preserve Attributes
Extensions with settings have their related options grouped directly below the toggle (e.g., Table toggle + cell padding, border colors; Link toggle + link colors/CSS; Heading toggle + heading levels + H1–H6 styling)
Every toggle has documentation explaining what the extension does, how to use it, and relevant keyboard/markdown shortcuts
CSS Override Fields Cleanup
All advanced CSS override fields have been renamed, documented, and converted from large textareas to compact single-line inputs.
Before
After
h1_adv – h6_adv
H1 CSS override – H6 CSS override
p_adv
Paragraph CSS override
Image properties
Image CSS override
YouTube (duplicate name)
YouTube CSS override
Blockquote styling
Blockquote CSS override
Highlight CSS
Highlight CSS override
Bullet lists CSS / Number list CSS
Bullet list CSS override / Ordered list CSS override
link_adv, link_unvisited_adv, etc.
Link CSS override, Unvisited link CSS override, etc.
CSS for base div // override
Base div CSS override
All CSS override fields now use compact inputs (use arbitrary text for more room), and have documentation explaining that they inject raw CSS that overrides the preceding settings (size, color, weight, etc.).
Preserve Attributes Fields Renamed
Before
After
preserve_attributes
Preserve HTML attributes
preserved_attributes
Preserved attributes (read-only)
preserve_unknown_ta... (truncated)
Preserve unknown HTML tags
All three fields now have documentation explaining what they do and how they relate to each other.
Property Panel Reorganization
The entire property panel has been reorganized into a clean hierarchy: General config → Stylesheet → Extensions (with toggles + related settings) → Collaboration → Debug mode.
Cleanup
Proper reset handling: reset.js now cleans up collab retry timers and sync polling intervals instead of just logging
All element actions updated with consistent editor-ready guards (returnAndReportErrorIfEditorNotReady)
Console logging gated: All console.log calls replaced with the instance.data.debug() helper, controlled by the Debug Mode field
@rico.trevisan amazing work, many thanks for your efforts!
just sth that I still cannot understand (and probably not related to this new version): how can I make the collaboration cursor look more like the one that you have in the first post here rather than the one I have right now (it is just a new line with the name of the editor)? is it sth I have to do in bubble or is it some configuration in the plugin? thanks
Trailing Node (on by default) — Automatically inserts an empty paragraph at the end of the document when the last node is a block element (table, image, code block, etc.). Prevents users from getting trapped with no way to continue typing below block-level content.
Focus (off by default) — Adds a has-focus CSS class to the node where the cursor is currently positioned. Enables styling the active block — e.g., a subtle background or left border on the focused paragraph. Configurable mode: deepest (default, innermost node only), shallowest (outermost node only), or all (all ancestor nodes).
Selection (off by default) — Keeps the text selection visually highlighted when the editor loses focus. Useful when users click toolbar buttons outside the editor — the selection remains visible instead of disappearing. A default blue highlight style (.selection { background: #accef7 }) is provided automatically. Includes a Selection CSS override field to customize the highlight styling.
Collaboration
New field: Cursor label CSS override — Inject custom CSS for the collaboration cursor label (the floating name tag above each user’s cursor). Customize font-size, padding, opacity, etc.
How the collaboration cursor works
Each collaborator’s cursor renders two elements:
The caret (.collaboration-carets__caret) — a thin vertical line at the cursor position, colored with border-color from the user’s cursor_color.
The name label (.collaboration-carets__label) — a floating tag above the caret showing the user’s name, with background-color set to the user’s cursor_color.
The plugin provides sensible defaults (12px font, 600 weight, rounded corners, positioned above the caret). To customize, use the Cursor label CSS override field. Any CSS you add is injected into the .collaboration-carets__label rule and overrides the defaults.
Collaboration cursor styles not applied: Fixed CSS class name mismatch from the v3 upgrade — the stylesheet targeted .collaboration-cursor__caret / .collaboration-cursor__label but Tiptap v3’s CollaborationCaret extension renders with .collaboration-carets__caret / .collaboration-carets__label. The floating name label now displays correctly with proper positioning, styling, and background color.
Auto-binding ignored when collaboration is active: Fixed a critical conflict when both collaboration and auto-binding were enabled simultaneously. The auto-binding system works by writing editor HTML to a Bubble database field and reading changes back via setContent(). However, collaboration uses a Y.js CRDT document as the source of truth, and setContent() replaces the entire document — destroying the Y.js state, losing remote edits, and causing content duplication or cursor jumps. When both were on, every user was continuously writing HTML to the same Bubble field (their own and remote changes), creating race conditions and feedback loops that corrupted the collaborative document. Now, when collaboration is active, auto-binding writes and read-backs are suppressed. The collaborative Y.js document takes priority as the single source of truth. A one-time debugger warning is shown if both are enabled. States (contentHTML, contentText, contentJSON) and the contentUpdated event continue to work normally.
Documentation
“Enable collaboration?” field: Added documentation explaining that auto-binding is automatically ignored while collaboration is active to prevent conflicts.
CodeBlock: Tab indentation — New checkbox to allow the Tab key to indent inside code blocks instead of moving focus. Includes a configurable Tab size (default: 4 spaces).
Link: Additional protocols — New text field to specify additional protocols to recognize as valid links (e.g. tel, mailto, ftp). Comma-separated.
YouTube: Default width & height — New number fields to set the default pixel dimensions for embedded YouTube videos (defaults: 640×480).
Mention: Trigger character — New text field to change the character that triggers the mention popup (default: @). Use # for hashtags, etc.
Image: Inline images — New checkbox to make images flow inline with text instead of being block-level elements.
Table: Cell min width — New number field to set the minimum column width in pixels when resizing tables (default: 25px).
Fixed an issue where selecting text inside a table cell with the mouse could cause the entire table to appear highlighted instead of just the text within the cell. This happened because prosemirror-tables converts accidental table-level selections into a cell selection covering all cells. A new Allow table node selection option lets users control this behavior.
New Table Options
Four new configuration options for the Table extension:
Resizable columns (checkbox, default on) — Controls whether table columns can be resized by dragging column borders. Previously hardcoded to true. Disable for simpler tables with equal-width columns and no resize handles.
Allow table node selection (checkbox, default off) — Controls what happens when the table itself is selected (e.g. by clicking near its border). When off (default, previous behavior), the selection is converted to a cell selection covering all cells — which can cause the entire table to appear highlighted. When on, the table is selected as a single block, which is less disruptive and easier to dismiss.
Last column resizable (checkbox, default on) — Whether the rightmost column of the table can be resized. Disabling this prevents unexpected table expansion when dragging the last column border.
Resize handle width (number, default 5) — Width in pixels of the invisible resize handle zone on column borders. A larger value makes resizing easier to trigger; a smaller value reduces accidental resize activation near cell edges.
New Server-Side Action
Convert webhook payload to HTML — Converts Hocuspocus / Tiptap Cloud webhook payloads into clean HTML for storage in the database. When collaboration is active, the document lives on the collab server as a Y.js document and webhooks deliver changes as ProseMirror JSON — not HTML. This action bridges that gap.
Smart format detection — Accepts three input formats automatically:
Full Hocuspocus webhook body (auto-extracts payload.document)
Document object with named fields (e.g. { "default": { "type": "doc", ... } })
Raw ProseMirror JSON (e.g. { "type": "doc", "content": [...] })
Field name parameter (optional, default: "default") — for custom Hocuspocus setups using a different Y.js field name
Returns: html (text), error (text), returned_an_error (boolean)
Supports all editor node/mark types: headings, bold, italic, underline, strike, code, blockquotes, lists, task lists, tables, images, YouTube embeds, links, highlights, text color, font family, font size, subscript, superscript, details/accordion, mentions, and horizontal rules
When the collaborative document ID (collab_doc_id) changes at runtime, the editor now automatically tears down and rebuilds with a new provider connected to the new document. Previously, changing the document ID required a page reload.
Automatic teardown & rebuild — When collab_doc_id changes while the editor is active in collaboration mode, the existing provider and editor are cleanly destroyed (connections closed, timers cleared, DOM removed) and a fresh editor is created for the new document.
New event: Collaboration document changed — Fires just before the teardown begins, allowing workflows to react to the document switch (e.g., save state, show a loading indicator).
Shared teardown function — Introduced a reusable teardownEditor() helper that handles full cleanup: collab sync polling, retry timers, debounce timeouts, provider destruction, editor destruction, DOM removal, and state reset. Used by document ID change detection, collab auth failure retries, and element reset — eliminating duplicated teardown logic.
Improved reset cleanup — reset.js now uses the shared teardown for complete resource cleanup instead of only clearing timers.
Minor Improvements
App ID → Doc Server ID — The app_id collaboration field has been renamed to match Tiptap’s current naming: Doc Server ID.
Collab status values in inspector — The possible collab_status values (disconnected → connecting → connected → synced) are now documented directly in the plugin property panel for easy reference.