diff --git a/AGENTS.md b/AGENTS.md
index d59fdf1dd1..380a4a99c7 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -168,6 +168,10 @@ Changelogs must describe **user-facing changes**. Avoid internal noise.
## Extra guidance (short additions)
+### Scopes (for Conventional Commits)
+
+Git conventional commit scopes live in [SCOPES.md](./SCOPES.md). Use it to pick the right scope when committing.
+
### Environment
- Recommended Node version: >=24.x. Use a node version manager (nvm, fnm) or Corepack to pin a runtime that matches the root `package.json` `engines.node` requirement.
@@ -209,4 +213,4 @@ If a single package is failing types, run a targeted build for that package (e.g
### Troubleshooting notes
- If CI fails with dependency or lockfile errors, run `pnpm reset` locally and re-run the build.
-- For flaky Playwright tests, reproduce locally with `pnpm test:e2e:open` (UI mode) or rerun with `--trace on` and inspect via `pnpm test:e2e:report`.
+- For flaky Playwright tests, reproduce locally with `pnpm test:e2e:open` (UI mode) or rerun with `--trace on` and inspect via `pnpm test:e2e:report`
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a0994c60d..dae39afd31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,563 @@
# Releases
+## v3.23.6
+
+### @tiptap/extension-drag-handle
+
+#### Patch Changes
+
+- 937ff2e: **DragHandle**: Added `dragImageProperties` option to limit which CSS properties are cloned for the drag image. By default all ~300+ computed styles are copied; setting this to a subset (e.g. `['color', 'background-color', 'font-size']`) reduces the cost when dragging selections with complex nodes.
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-collaboration@3.23.6
+ - @tiptap/extension-node-range@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/core
+
+#### Patch Changes
+
+- 937ff2e: Fix deleteSelection to properly handle inline nodes with `text*` content. The selection is now expanded to include the entire inline node boundaries when deleting, preventing incorrect collapse of inline text nodes.
+- @tiptap/pm@3.23.6
+
+### @tiptap/extensions
+
+#### Patch Changes
+
+- 937ff2e: **Placeholder**: Replaced full-document `doc.descendants()` traversal with a cursor-resolved fast path for the default config and viewport-limited scanning for the non-default config, significantly reducing decoration overhead on large documents.
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-drag-handle-react
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/react@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-drag-handle-vue-2
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/vue-2@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-drag-handle-vue-3
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/vue-3@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-audio
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-blockquote
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-bold
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-bubble-menu
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-code
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-code-block
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-code-block-lowlight
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-code-block@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-collaboration
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-collaboration-caret
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-details
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-document
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-emoji
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/suggestion@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-file-handler
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-floating-menu
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-hard-break
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-heading
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-highlight
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-horizontal-rule
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-image
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-invisible-characters
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-italic
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-link
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-list
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-mathematics
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-mention
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/suggestion@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-node-range
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-paragraph
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-strike
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-subscript
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-superscript
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-table
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-table-of-contents
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-text
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-text-align
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-text-style
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-twitch
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-typography
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-underline
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/extension-unique-id
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-youtube
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+
+### @tiptap/html
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/markdown
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/react
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/static-renderer
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/suggestion
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/vue-2
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/vue-3
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
+### @tiptap/extension-character-count
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-dropcursor
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-focus
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-gapcursor
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-history
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-placeholder
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
+### @tiptap/extension-list-item
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-list-keymap
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-task-item
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-task-list
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-bullet-list
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-ordered-list
+
+#### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
+### @tiptap/extension-table-cell
+
+#### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
+### @tiptap/extension-table-header
+
+#### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
+### @tiptap/extension-table-row
+
+#### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
+### @tiptap/extension-color
+
+#### Patch Changes
+
+- @tiptap/extension-text-style@3.23.6
+
+### @tiptap/extension-font-family
+
+#### Patch Changes
+
+- @tiptap/extension-text-style@3.23.6
+
+### @tiptap/starter-kit
+
+#### Patch Changes
+
+- Updated dependencies [937ff2e]
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extensions@3.23.6
+ - @tiptap/extension-blockquote@3.23.6
+ - @tiptap/extension-bold@3.23.6
+ - @tiptap/extension-code@3.23.6
+ - @tiptap/extension-code-block@3.23.6
+ - @tiptap/extension-document@3.23.6
+ - @tiptap/extension-hard-break@3.23.6
+ - @tiptap/extension-heading@3.23.6
+ - @tiptap/extension-horizontal-rule@3.23.6
+ - @tiptap/extension-italic@3.23.6
+ - @tiptap/extension-link@3.23.6
+ - @tiptap/extension-list@3.23.6
+ - @tiptap/extension-paragraph@3.23.6
+ - @tiptap/extension-strike@3.23.6
+ - @tiptap/extension-text@3.23.6
+ - @tiptap/extension-underline@3.23.6
+ - @tiptap/extension-dropcursor@3.23.6
+ - @tiptap/extension-gapcursor@3.23.6
+ - @tiptap/extension-list-item@3.23.6
+ - @tiptap/extension-list-keymap@3.23.6
+ - @tiptap/extension-bullet-list@3.23.6
+ - @tiptap/extension-ordered-list@3.23.6
+ - @tiptap/pm@3.23.6
+
## v3.23.5
### @tiptap/markdown
diff --git a/SCOPES.md b/SCOPES.md
new file mode 100644
index 0000000000..e95d8eb3a1
--- /dev/null
+++ b/SCOPES.md
@@ -0,0 +1,75 @@
+# Scopes
+
+## Primary scopes (monorepo packages)
+
+### Core, framework bindings, and shared utilities
+- core
+- extensions
+- html
+- markdown
+- pm
+- react
+- server-ai-toolkit
+- starter-kit
+- static-renderer
+- suggestion
+- vue-2
+- vue-3
+
+### Extensions
+- extension-audio
+- extension-blockquote
+- extension-bold
+- extension-bubble-menu
+- extension-bullet-list
+- extension-code
+- extension-code-block
+- extension-code-block-lowlight
+- extension-collaboration
+- extension-collaboration-caret
+- extension-color
+- extension-details
+- extension-document
+- extension-drag-handle
+- extension-drag-handle-react
+- extension-drag-handle-vue-2
+- extension-drag-handle-vue-3
+- extension-emoji
+- extension-file-handler
+- extension-floating-menu
+- extension-font-family
+- extension-hard-break
+- extension-heading
+- extension-highlight
+- extension-horizontal-rule
+- extension-image
+- extension-invisible-characters
+- extension-italic
+- extension-link
+- extension-list
+- extension-mathematics
+- extension-mention
+- extension-node-range
+- extension-ordered-list
+- extension-paragraph
+- extension-strike
+- extension-subscript
+- extension-superscript
+- extension-table
+- extension-table-of-contents
+- extension-text
+- extension-text-align
+- extension-text-style
+- extension-twitch
+- extension-typography
+- extension-underline
+- extension-unique-id
+- extension-youtube
+
+## Additional scopes
+- demos
+- tests
+- ci
+- config
+- docs
+- changeset
diff --git a/packages-deprecated/extension-character-count/CHANGELOG.md b/packages-deprecated/extension-character-count/CHANGELOG.md
index 7f62e84e17..f3e8b4b666 100644
--- a/packages-deprecated/extension-character-count/CHANGELOG.md
+++ b/packages-deprecated/extension-character-count/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-character-count/package.json b/packages-deprecated/extension-character-count/package.json
index 6f0bffd972..fc76a741fc 100644
--- a/packages-deprecated/extension-character-count/package.json
+++ b/packages-deprecated/extension-character-count/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-character-count",
"description": "font family extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-dropcursor/CHANGELOG.md b/packages-deprecated/extension-dropcursor/CHANGELOG.md
index dee9999c48..89f19fe0e3 100644
--- a/packages-deprecated/extension-dropcursor/CHANGELOG.md
+++ b/packages-deprecated/extension-dropcursor/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-dropcursor/package.json b/packages-deprecated/extension-dropcursor/package.json
index 38eb27d7b0..e8786f9380 100644
--- a/packages-deprecated/extension-dropcursor/package.json
+++ b/packages-deprecated/extension-dropcursor/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-dropcursor",
"description": "dropcursor extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-focus/CHANGELOG.md b/packages-deprecated/extension-focus/CHANGELOG.md
index 8a5dba31b8..4d3b06d940 100644
--- a/packages-deprecated/extension-focus/CHANGELOG.md
+++ b/packages-deprecated/extension-focus/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-focus/package.json b/packages-deprecated/extension-focus/package.json
index 7562097dbf..f8b2e0f190 100644
--- a/packages-deprecated/extension-focus/package.json
+++ b/packages-deprecated/extension-focus/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-focus",
"description": "focus extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-gapcursor/CHANGELOG.md b/packages-deprecated/extension-gapcursor/CHANGELOG.md
index d7221aa11a..c88e030ac8 100644
--- a/packages-deprecated/extension-gapcursor/CHANGELOG.md
+++ b/packages-deprecated/extension-gapcursor/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-gapcursor/package.json b/packages-deprecated/extension-gapcursor/package.json
index e752dd9f65..bf2ae91589 100644
--- a/packages-deprecated/extension-gapcursor/package.json
+++ b/packages-deprecated/extension-gapcursor/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-gapcursor",
"description": "gapcursor extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-history/CHANGELOG.md b/packages-deprecated/extension-history/CHANGELOG.md
index 1f733988b4..5b3cf85a18 100644
--- a/packages-deprecated/extension-history/CHANGELOG.md
+++ b/packages-deprecated/extension-history/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-history/package.json b/packages-deprecated/extension-history/package.json
index 984227e9e8..87fc07078e 100644
--- a/packages-deprecated/extension-history/package.json
+++ b/packages-deprecated/extension-history/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-history",
"description": "history extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-list-item/CHANGELOG.md b/packages-deprecated/extension-list-item/CHANGELOG.md
index ff14e49876..382204f8f2 100644
--- a/packages-deprecated/extension-list-item/CHANGELOG.md
+++ b/packages-deprecated/extension-list-item/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-list-item/package.json b/packages-deprecated/extension-list-item/package.json
index 2e42f51925..d6aa8c2b6c 100644
--- a/packages-deprecated/extension-list-item/package.json
+++ b/packages-deprecated/extension-list-item/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-list-item",
"description": "list item extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-list-keymap/CHANGELOG.md b/packages-deprecated/extension-list-keymap/CHANGELOG.md
index 7155f376e2..37a495ebe0 100644
--- a/packages-deprecated/extension-list-keymap/CHANGELOG.md
+++ b/packages-deprecated/extension-list-keymap/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-list-keymap/package.json b/packages-deprecated/extension-list-keymap/package.json
index bac879b3fb..b3b4f7e0c8 100644
--- a/packages-deprecated/extension-list-keymap/package.json
+++ b/packages-deprecated/extension-list-keymap/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-list-keymap",
"description": "list keymap extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-placeholder/CHANGELOG.md b/packages-deprecated/extension-placeholder/CHANGELOG.md
index 5a11817513..9a4fa010ef 100644
--- a/packages-deprecated/extension-placeholder/CHANGELOG.md
+++ b/packages-deprecated/extension-placeholder/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extensions@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-placeholder/package.json b/packages-deprecated/extension-placeholder/package.json
index 43b9cfb451..d390cdb12b 100644
--- a/packages-deprecated/extension-placeholder/package.json
+++ b/packages-deprecated/extension-placeholder/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-placeholder",
"description": "placeholder extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-table-cell/CHANGELOG.md b/packages-deprecated/extension-table-cell/CHANGELOG.md
index 1de5cea974..29297c019f 100644
--- a/packages-deprecated/extension-table-cell/CHANGELOG.md
+++ b/packages-deprecated/extension-table-cell/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-table-cell/package.json b/packages-deprecated/extension-table-cell/package.json
index 4ca742a0bd..83dd598ed3 100644
--- a/packages-deprecated/extension-table-cell/package.json
+++ b/packages-deprecated/extension-table-cell/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-table-cell",
"description": "table cell extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-table-header/CHANGELOG.md b/packages-deprecated/extension-table-header/CHANGELOG.md
index 0cb07cee83..3bb80ac436 100644
--- a/packages-deprecated/extension-table-header/CHANGELOG.md
+++ b/packages-deprecated/extension-table-header/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-table-header/package.json b/packages-deprecated/extension-table-header/package.json
index 36374bff4d..566688f2a4 100644
--- a/packages-deprecated/extension-table-header/package.json
+++ b/packages-deprecated/extension-table-header/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-table-header",
"description": "table cell extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-table-row/CHANGELOG.md b/packages-deprecated/extension-table-row/CHANGELOG.md
index 9e5591aab8..02bf820cd9 100644
--- a/packages-deprecated/extension-table-row/CHANGELOG.md
+++ b/packages-deprecated/extension-table-row/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-table@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-table-row/package.json b/packages-deprecated/extension-table-row/package.json
index 93bcd4bb6e..18177d58db 100644
--- a/packages-deprecated/extension-table-row/package.json
+++ b/packages-deprecated/extension-table-row/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-table-row",
"description": "table row extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-task-item/CHANGELOG.md b/packages-deprecated/extension-task-item/CHANGELOG.md
index f26b50e8ce..6119ef4b9f 100644
--- a/packages-deprecated/extension-task-item/CHANGELOG.md
+++ b/packages-deprecated/extension-task-item/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-task-item/package.json b/packages-deprecated/extension-task-item/package.json
index 64a65c7eef..608501e9b8 100644
--- a/packages-deprecated/extension-task-item/package.json
+++ b/packages-deprecated/extension-task-item/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-task-item",
"description": "task item extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages-deprecated/extension-task-list/CHANGELOG.md b/packages-deprecated/extension-task-list/CHANGELOG.md
index be2dc2a02f..d5e8ad3671 100644
--- a/packages-deprecated/extension-task-list/CHANGELOG.md
+++ b/packages-deprecated/extension-task-list/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages-deprecated/extension-task-list/package.json b/packages-deprecated/extension-task-list/package.json
index 1767417dfa..c0250bb218 100644
--- a/packages-deprecated/extension-task-list/package.json
+++ b/packages-deprecated/extension-task-list/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-task-list",
"description": "task list extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 7bd6b9fd6e..ba289ff40b 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- d168376: Fix deleteSelection to properly handle inline nodes with `text*` content. The selection is now expanded to include the entire inline node boundaries when deleting, preventing incorrect collapse of inline text nodes.
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/core/__tests__/delete.spec.ts b/packages/core/__tests__/delete.spec.ts
index 10c8d528a3..23a7688bc9 100644
--- a/packages/core/__tests__/delete.spec.ts
+++ b/packages/core/__tests__/delete.spec.ts
@@ -1,6 +1,7 @@
-import { Editor } from '@tiptap/core'
+import { Editor, Node } from '@tiptap/core'
import Bold from '@tiptap/extension-bold'
import Document from '@tiptap/extension-document'
+import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { describe, expect, it, vi } from 'vitest'
@@ -8,6 +9,33 @@ const InlineDocument = Document.extend({
content: 'inline*',
})
+// Test extension for the bug fix: inline node with text* content and node view
+const TestInlineNode = Node.create({
+ name: 'testInlineNode',
+ group: 'inline',
+ inline: true,
+ content: 'text*',
+ renderHTML() {
+ return ['span', { 'data-test-node': '' }, 0]
+ },
+ parseHTML() {
+ return [
+ {
+ tag: 'span[data-test-node]',
+ },
+ ]
+ },
+ addNodeView() {
+ return () => {
+ const dom = document.createElement('span')
+ dom.dataset.testNode = 'true'
+ const contentDOM = document.createElement('span')
+ dom.appendChild(contentDOM)
+ return { dom, contentDOM }
+ }
+ },
+})
+
describe('delete extension', () => {
it('should not throw when removing a mark from inline content at position 0', () => {
const onDelete = vi.fn()
@@ -45,4 +73,87 @@ describe('delete extension', () => {
editor.destroy()
})
+
+ it('should return false for empty selection', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text],
+ content: '
hello
',
+ })
+
+ // Set cursor at position 1 (empty selection)
+ editor.commands.setTextSelection({ from: 1, to: 1 })
+
+ const result = editor.commands.deleteSelection()
+ expect(result).toBe(false)
+
+ editor.destroy()
+ })
+
+ it('should delete selected text', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text],
+ content: 'hello world
',
+ })
+
+ // Select "hello "
+ editor.chain().setTextSelection({ from: 1, to: 7 }).deleteSelection().run()
+
+ expect(editor.getHTML()).toBe('world
')
+ editor.destroy()
+ })
+
+ it('should delete selection across two paragraphs', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text],
+ content: 'one
two
',
+ })
+
+ // Select from end of first paragraph to start of second
+ editor.chain().setTextSelection({ from: 2, to: 7 }).deleteSelection().run()
+
+ expect(editor.getHTML()).toBe('owo
')
+ editor.destroy()
+ })
+
+ it('should delete entire inline node with text* when selecting inside it', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text, TestInlineNode],
+ content: 'before test after
',
+ })
+
+ // Select inside the inline node (position 9-13 selects "test")
+ editor.chain().setTextSelection({ from: 9, to: 13 }).deleteSelection().run()
+
+ // The entire node should be deleted
+ expect(editor.getHTML()).toBe('before after
')
+ editor.destroy()
+ })
+
+ it('should keep inline node when selecting only half of its text content', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text, TestInlineNode],
+ content: 'before test after
',
+ })
+
+ // Select only half of the text inside the inline node (position 9-11 selects "te")
+ editor.chain().setTextSelection({ from: 9, to: 11 }).deleteSelection().run()
+
+ // The node should remain with remaining text
+ expect(editor.getHTML()).toBe('before st after
')
+ editor.destroy()
+ })
+
+ it('should delete selection spanning around entire inline node', () => {
+ const editor = new Editor({
+ extensions: [Document, Paragraph, Text, TestInlineNode],
+ content: 'before test after
',
+ })
+
+ // Select from before the inline node to after it (position 4-17)
+ editor.chain().setTextSelection({ from: 4, to: 17 }).deleteSelection().run()
+
+ // The entire selection including the inline node should be deleted
+ expect(editor.getHTML()).toBe('befter
')
+ editor.destroy()
+ })
})
diff --git a/packages/core/package.json b/packages/core/package.json
index cd9d70a724..834e07027a 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/core",
"description": "headless rich text editor",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/core/src/commands/deleteSelection.ts b/packages/core/src/commands/deleteSelection.ts
index 0c111df95f..81bd77d2f3 100644
--- a/packages/core/src/commands/deleteSelection.ts
+++ b/packages/core/src/commands/deleteSelection.ts
@@ -1,7 +1,62 @@
-import { deleteSelection as originalDeleteSelection } from '@tiptap/pm/commands'
+import type { ResolvedPos, Schema } from '@tiptap/pm/model'
import type { RawCommands } from '../types.js'
+/**
+ * Check if a node has text content based on its content specification.
+ * Returns true if the node's content spec matches text* or text+ patterns.
+ */
+const hasTextContent = (nodeSpec: { content?: string }): boolean => {
+ if (!nodeSpec.content) {
+ return false
+ }
+ const textRegex = /^text(\*|\+)/
+ return textRegex.test(nodeSpec.content)
+}
+
+/**
+ * Expand selection position for a specific side (left or right) to handle inline text nodes.
+ * This function checks if the position is within an inline node with text content and
+ * expands it to include the entire node boundaries for proper deletion.
+ * @param $pos - The resolved position to expand
+ * @param schema - The ProseMirror schema
+ * @param side - Which side to expand ('left' or 'right')
+ * @returns The expanded position for deletion
+ */
+const expandSelectionForSide = ($pos: ResolvedPos, schema: Schema, side: 'left' | 'right'): number => {
+ if (!$pos.parent.isInline) {
+ return $pos.pos
+ }
+
+ if ((side === 'left' && $pos.pos > $pos.start()) || (side === 'right' && $pos.pos < $pos.end())) {
+ return $pos.pos
+ }
+
+ const parentContent = schema.nodes[$pos.parent.type.name].spec
+ if (!hasTextContent(parentContent)) {
+ return $pos.pos
+ }
+
+ return side === 'left' ? $pos.start() - 1 : $pos.end() + 1
+}
+
+/**
+ * Expand selection range to properly handle deletion of inline text nodes.
+ * Inline text nodes don't collapse correctly when text inside is deleted,
+ * so we need to expand the selection to include the entire node.
+ * See: https://code.haverbeke.berlin/prosemirror/prosemirror/issues/1365
+ */
+const expandSelectionForInlineText = (
+ $from: ResolvedPos,
+ $to: ResolvedPos,
+ schema: Schema,
+): { from: number; to: number } => {
+ const from = expandSelectionForSide($from, schema, 'left')
+ const to = expandSelectionForSide($to, schema, 'right')
+
+ return { from, to }
+}
+
declare module '@tiptap/core' {
interface Commands {
deleteSelection: {
@@ -17,5 +72,17 @@ declare module '@tiptap/core' {
export const deleteSelection: RawCommands['deleteSelection'] =
() =>
({ state, dispatch }) => {
- return originalDeleteSelection(state, dispatch)
+ const { $from, $to } = state.selection
+ if (state.selection.empty) {
+ return false
+ }
+
+ const { from, to } = expandSelectionForInlineText($from, $to, state.schema)
+
+ if (dispatch) {
+ state.tr.deleteRange(from, to).scrollIntoView()
+ dispatch(state.tr)
+ }
+
+ return true
}
diff --git a/packages/extension-audio/CHANGELOG.md b/packages/extension-audio/CHANGELOG.md
index 46fb19330c..5d00a98433 100644
--- a/packages/extension-audio/CHANGELOG.md
+++ b/packages/extension-audio/CHANGELOG.md
@@ -1,5 +1,12 @@
# @tiptap/extension-audio
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-audio/package.json b/packages/extension-audio/package.json
index 47f84b9058..7e1a178b1a 100644
--- a/packages/extension-audio/package.json
+++ b/packages/extension-audio/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-audio",
"description": "audio extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-blockquote/CHANGELOG.md b/packages/extension-blockquote/CHANGELOG.md
index cddf974352..f4772d63d8 100644
--- a/packages/extension-blockquote/CHANGELOG.md
+++ b/packages/extension-blockquote/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-blockquote/package.json b/packages/extension-blockquote/package.json
index a6e4bd9cd0..6091fc1de1 100644
--- a/packages/extension-blockquote/package.json
+++ b/packages/extension-blockquote/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-blockquote",
"description": "blockquote extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-bold/CHANGELOG.md b/packages/extension-bold/CHANGELOG.md
index 532954a1f7..fd8b30dce6 100644
--- a/packages/extension-bold/CHANGELOG.md
+++ b/packages/extension-bold/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-bold/package.json b/packages/extension-bold/package.json
index e603071e32..58da540e08 100644
--- a/packages/extension-bold/package.json
+++ b/packages/extension-bold/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bold",
"description": "bold extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-bubble-menu/CHANGELOG.md b/packages/extension-bubble-menu/CHANGELOG.md
index 88fa24080b..c6377f52ee 100644
--- a/packages/extension-bubble-menu/CHANGELOG.md
+++ b/packages/extension-bubble-menu/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-bubble-menu/package.json b/packages/extension-bubble-menu/package.json
index 238f1d496a..a269a33fe4 100644
--- a/packages/extension-bubble-menu/package.json
+++ b/packages/extension-bubble-menu/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bubble-menu",
"description": "bubble-menu extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-bullet-list/CHANGELOG.md b/packages/extension-bullet-list/CHANGELOG.md
index 5ae6d0e232..0b9f3e16ed 100644
--- a/packages/extension-bullet-list/CHANGELOG.md
+++ b/packages/extension-bullet-list/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-bullet-list/package.json b/packages/extension-bullet-list/package.json
index 2dfbb0b955..71d2ced254 100644
--- a/packages/extension-bullet-list/package.json
+++ b/packages/extension-bullet-list/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-bullet-list",
"description": "bullet list extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-code-block-lowlight/CHANGELOG.md b/packages/extension-code-block-lowlight/CHANGELOG.md
index 469c232511..5d7bafea3e 100644
--- a/packages/extension-code-block-lowlight/CHANGELOG.md
+++ b/packages/extension-code-block-lowlight/CHANGELOG.md
@@ -1,5 +1,14 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-code-block@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-code-block-lowlight/package.json b/packages/extension-code-block-lowlight/package.json
index b04dbcdbdf..bbbf959f0e 100644
--- a/packages/extension-code-block-lowlight/package.json
+++ b/packages/extension-code-block-lowlight/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code-block-lowlight",
"description": "code block extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-code-block/CHANGELOG.md b/packages/extension-code-block/CHANGELOG.md
index 835d94b9fe..0a72fcadab 100644
--- a/packages/extension-code-block/CHANGELOG.md
+++ b/packages/extension-code-block/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-code-block/package.json b/packages/extension-code-block/package.json
index 61606f149b..2ce871dd5f 100644
--- a/packages/extension-code-block/package.json
+++ b/packages/extension-code-block/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code-block",
"description": "code block extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-code/CHANGELOG.md b/packages/extension-code/CHANGELOG.md
index d05dfb65a0..ee52d3fdc9 100644
--- a/packages/extension-code/CHANGELOG.md
+++ b/packages/extension-code/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-code/package.json b/packages/extension-code/package.json
index afa3cd6e30..e504a06c68 100644
--- a/packages/extension-code/package.json
+++ b/packages/extension-code/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-code",
"description": "code extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-collaboration-caret/CHANGELOG.md b/packages/extension-collaboration-caret/CHANGELOG.md
index f3806865f7..0b7cb3e437 100644
--- a/packages/extension-collaboration-caret/CHANGELOG.md
+++ b/packages/extension-collaboration-caret/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-collaboration-caret/package.json b/packages/extension-collaboration-caret/package.json
index 417e9629f8..f9136ee5f5 100644
--- a/packages/extension-collaboration-caret/package.json
+++ b/packages/extension-collaboration-caret/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-collaboration-caret",
"description": "collaboration caret extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-collaboration/CHANGELOG.md b/packages/extension-collaboration/CHANGELOG.md
index 7b5c4a515a..c82ad6372a 100644
--- a/packages/extension-collaboration/CHANGELOG.md
+++ b/packages/extension-collaboration/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-collaboration/package.json b/packages/extension-collaboration/package.json
index 2e1375b26a..f28b016695 100644
--- a/packages/extension-collaboration/package.json
+++ b/packages/extension-collaboration/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-collaboration",
"description": "collaboration extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-color/CHANGELOG.md b/packages/extension-color/CHANGELOG.md
index 471107d3c0..f66ab3c440 100644
--- a/packages/extension-color/CHANGELOG.md
+++ b/packages/extension-color/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-text-style@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-color/package.json b/packages/extension-color/package.json
index 06da66537a..869b3406e4 100644
--- a/packages/extension-color/package.json
+++ b/packages/extension-color/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-color",
"description": "text color extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-details/CHANGELOG.md b/packages/extension-details/CHANGELOG.md
index 236e427754..c819f12e96 100644
--- a/packages/extension-details/CHANGELOG.md
+++ b/packages/extension-details/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-details
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-details/package.json b/packages/extension-details/package.json
index 851044d0b2..7ff0affb29 100644
--- a/packages/extension-details/package.json
+++ b/packages/extension-details/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-details",
"description": "details extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/api/nodes/details",
"keywords": [
"tiptap",
diff --git a/packages/extension-document/CHANGELOG.md b/packages/extension-document/CHANGELOG.md
index e73b07c1d7..1c7e8fe75a 100644
--- a/packages/extension-document/CHANGELOG.md
+++ b/packages/extension-document/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-document/package.json b/packages/extension-document/package.json
index 83425afccc..202f8c78c3 100644
--- a/packages/extension-document/package.json
+++ b/packages/extension-document/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-document",
"description": "document extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-drag-handle-react/CHANGELOG.md b/packages/extension-drag-handle-react/CHANGELOG.md
index d26a7f1d82..7c2f5049f2 100644
--- a/packages/extension-drag-handle-react/CHANGELOG.md
+++ b/packages/extension-drag-handle-react/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-drag-handle-react
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/react@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-drag-handle-react/package.json b/packages/extension-drag-handle-react/package.json
index 314e25b495..95a16605ef 100644
--- a/packages/extension-drag-handle-react/package.json
+++ b/packages/extension-drag-handle-react/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-drag-handle-react",
"description": "drag handle extension for tiptap with react",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-drag-handle-vue-2/CHANGELOG.md b/packages/extension-drag-handle-vue-2/CHANGELOG.md
index 3b067a5952..7e161bc802 100644
--- a/packages/extension-drag-handle-vue-2/CHANGELOG.md
+++ b/packages/extension-drag-handle-vue-2/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-drag-handle-vue-2
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/vue-2@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-drag-handle-vue-2/package.json b/packages/extension-drag-handle-vue-2/package.json
index ab87133edc..904b8012c5 100644
--- a/packages/extension-drag-handle-vue-2/package.json
+++ b/packages/extension-drag-handle-vue-2/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-drag-handle-vue-2",
"description": "drag handle extension for tiptap with vue 2",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-drag-handle-vue-3/CHANGELOG.md b/packages/extension-drag-handle-vue-3/CHANGELOG.md
index 61e5cfa4f2..955ee98d06 100644
--- a/packages/extension-drag-handle-vue-3/CHANGELOG.md
+++ b/packages/extension-drag-handle-vue-3/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-drag-handle-vue-3
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [937ff2e]
+ - @tiptap/extension-drag-handle@3.23.6
+ - @tiptap/vue-3@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-drag-handle-vue-3/package.json b/packages/extension-drag-handle-vue-3/package.json
index 165a355147..c7124eb872 100644
--- a/packages/extension-drag-handle-vue-3/package.json
+++ b/packages/extension-drag-handle-vue-3/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-drag-handle-vue-3",
"description": "drag handle extension for tiptap with vue 3",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-drag-handle/CHANGELOG.md b/packages/extension-drag-handle/CHANGELOG.md
index 8d30c2fb2d..43831fc548 100644
--- a/packages/extension-drag-handle/CHANGELOG.md
+++ b/packages/extension-drag-handle/CHANGELOG.md
@@ -1,5 +1,16 @@
# @tiptap/extension-drag-handle
+## 3.23.6
+
+### Patch Changes
+
+- 937ff2e: **DragHandle**: Added `dragImageProperties` option to limit which CSS properties are cloned for the drag image. By default all ~300+ computed styles are copied; setting this to a subset (e.g. `['color', 'background-color', 'font-size']`) reduces the cost when dragging selections with complex nodes.
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-collaboration@3.23.6
+ - @tiptap/extension-node-range@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-drag-handle/__tests__/cloneElement.spec.ts b/packages/extension-drag-handle/__tests__/cloneElement.spec.ts
index 53f0b7619d..6431cd50d7 100644
--- a/packages/extension-drag-handle/__tests__/cloneElement.spec.ts
+++ b/packages/extension-drag-handle/__tests__/cloneElement.spec.ts
@@ -7,41 +7,119 @@ describe('cloneElement', () => {
vi.restoreAllMocks()
})
- it('copies computed styles to the clone', () => {
- const element = document.createElement('p')
- const child = document.createElement('span')
-
- element.appendChild(child)
-
- vi.spyOn(window, 'getComputedStyle').mockImplementation((node: Element) => {
- const styles = node === element ? ['margin-top', 'color'] : ['font-weight']
-
- return {
- length: styles.length,
- 0: styles[0],
- 1: styles[1],
- getPropertyValue: (property: string) => {
- if (property === 'margin-top') {
- return '24px'
- }
-
- if (property === 'color') {
- return 'rgb(1, 2, 3)'
- }
-
- if (property === 'font-weight') {
- return '700'
- }
-
- return ''
- },
- } as unknown as CSSStyleDeclaration
+ describe('without properties filter', () => {
+ it('copies computed styles to the clone', () => {
+ const element = document.createElement('p')
+ const child = document.createElement('span')
+
+ element.appendChild(child)
+
+ vi.spyOn(window, 'getComputedStyle').mockImplementation((node: Element) => {
+ const styles = node === element ? ['margin-top', 'color'] : ['font-weight']
+
+ return {
+ length: styles.length,
+ 0: styles[0],
+ 1: styles[1],
+ getPropertyValue: (property: string) => {
+ if (property === 'margin-top') {
+ return '24px'
+ }
+
+ if (property === 'color') {
+ return 'rgb(1, 2, 3)'
+ }
+
+ if (property === 'font-weight') {
+ return '700'
+ }
+
+ return ''
+ },
+ } as unknown as CSSStyleDeclaration
+ })
+
+ const clone = cloneElement(element)
+
+ expect(clone.style.cssText).toContain('margin-top: 24px;')
+ expect(clone.style.cssText).toContain('color: rgb(1, 2, 3);')
+ expect((clone.firstElementChild as HTMLElement).style.cssText).toContain('font-weight: 700;')
})
+ })
+
+ describe('with properties filter', () => {
+ it('copies only the listed properties to the clone', () => {
+ const element = document.createElement('p')
+ const child = document.createElement('span')
+
+ element.appendChild(child)
+
+ vi.spyOn(window, 'getComputedStyle').mockImplementation(() => {
+ return {
+ length: 6,
+ 0: 'margin-top',
+ 1: 'color',
+ 2: 'font-weight',
+ 3: 'background-color',
+ 4: 'opacity',
+ 5: 'font-size',
+ getPropertyValue: (property: string) => {
+ if (property === 'margin-top') {
+ return '24px'
+ }
+
+ if (property === 'color') {
+ return 'rgb(1, 2, 3)'
+ }
- const clone = cloneElement(element)
+ if (property === 'font-weight') {
+ return '700'
+ }
- expect(clone.style.cssText).toContain('margin-top: 24px;')
- expect(clone.style.cssText).toContain('color: rgb(1, 2, 3);')
- expect((clone.firstElementChild as HTMLElement).style.cssText).toContain('font-weight: 700;')
+ if (property === 'background-color') {
+ return 'rgb(255, 0, 0)'
+ }
+
+ if (property === 'opacity') {
+ return '0.5'
+ }
+
+ if (property === 'font-size') {
+ return '16px'
+ }
+
+ return ''
+ },
+ } as unknown as CSSStyleDeclaration
+ })
+
+ const clone = cloneElement(element, ['margin-top', 'color'])
+
+ // Listed properties should be present
+ expect(clone.style.cssText).toContain('margin-top: 24px;')
+ expect(clone.style.cssText).toContain('color: rgb(1, 2, 3);')
+
+ // Non-listed properties should not be present
+ expect(clone.style.cssText).not.toContain('font-weight')
+ expect(clone.style.cssText).not.toContain('background-color')
+ expect(clone.style.cssText).not.toContain('opacity')
+ expect(clone.style.cssText).not.toContain('font-size')
+ })
+
+ it('returns an empty cssText when the properties list is empty', () => {
+ const element = document.createElement('p')
+
+ vi.spyOn(window, 'getComputedStyle').mockImplementation(() => {
+ return {
+ length: 1,
+ 0: 'color',
+ getPropertyValue: () => 'red',
+ } as unknown as CSSStyleDeclaration
+ })
+
+ const clone = cloneElement(element, [])
+
+ expect(clone.style.cssText).toBe('')
+ })
})
})
diff --git a/packages/extension-drag-handle/package.json b/packages/extension-drag-handle/package.json
index b00935a10b..3ed3fcdda6 100644
--- a/packages/extension-drag-handle/package.json
+++ b/packages/extension-drag-handle/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-drag-handle",
"description": "drag handle extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/drag-handle",
"keywords": [
"tiptap",
diff --git a/packages/extension-drag-handle/src/drag-handle-plugin.ts b/packages/extension-drag-handle/src/drag-handle-plugin.ts
index 24f491aa3f..889a19bb7e 100644
--- a/packages/extension-drag-handle/src/drag-handle-plugin.ts
+++ b/packages/extension-drag-handle/src/drag-handle-plugin.ts
@@ -66,6 +66,7 @@ export interface DragHandlePluginProps {
computePositionConfig?: ComputePositionConfig
getReferencedVirtualElement?: () => VirtualElement | null
nestedOptions: NormalizedNestedOptions
+ dragImageProperties?: string[]
}
export const dragHandlePluginDefaultKey = new PluginKey('dragHandle')
@@ -80,6 +81,7 @@ export const DragHandlePlugin = ({
onElementDragStart,
onElementDragEnd,
nestedOptions,
+ dragImageProperties,
}: DragHandlePluginProps) => {
const wrapper = document.createElement('div')
let locked = false
@@ -132,7 +134,7 @@ export const DragHandlePlugin = ({
// Push this to the end of the event cue
// Fixes bug where incorrect drag pos is returned if drag handle has position: absolute
// Pass the current node context to avoid recalculation issues during drag start
- dragHandler(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos })
+ dragHandler(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos }, dragImageProperties)
if (element) {
element.dataset.dragging = 'true'
diff --git a/packages/extension-drag-handle/src/drag-handle.ts b/packages/extension-drag-handle/src/drag-handle.ts
index 12d2fe8bdd..6709a8b089 100644
--- a/packages/extension-drag-handle/src/drag-handle.ts
+++ b/packages/extension-drag-handle/src/drag-handle.ts
@@ -97,6 +97,21 @@ export interface DragHandleOptions {
* })
*/
nested?: boolean | NestedOptions
+ /**
+ * Limit the drag image clone to a subset of CSS properties instead of
+ * copying all computed styles. When set, only the listed properties
+ * are read from `getComputedStyle` and applied to the drag image clone.
+ *
+ * Useful for improving drag performance on selections containing complex
+ * or deeply nested nodes.
+ *
+ * @example
+ * // Only copy visual appearance, skip layout properties
+ * DragHandle.configure({
+ * dragImageProperties: ['color', 'background-color', 'font-size', 'font-family'],
+ * })
+ */
+ dragImageProperties?: string[]
}
declare module '@tiptap/core' {
@@ -138,6 +153,7 @@ export const DragHandle = Extension.create({
onElementDragStart: undefined,
onElementDragEnd: undefined,
nested: false,
+ dragImageProperties: undefined,
}
},
@@ -178,6 +194,7 @@ export const DragHandle = Extension.create({
onElementDragStart: this.options.onElementDragStart,
onElementDragEnd: this.options.onElementDragEnd,
nestedOptions,
+ dragImageProperties: this.options.dragImageProperties,
}).plugin,
]
},
diff --git a/packages/extension-drag-handle/src/helpers/cloneElement.ts b/packages/extension-drag-handle/src/helpers/cloneElement.ts
index d01a61a768..0892649e82 100644
--- a/packages/extension-drag-handle/src/helpers/cloneElement.ts
+++ b/packages/extension-drag-handle/src/helpers/cloneElement.ts
@@ -1,7 +1,15 @@
-function getCSSText(element: Element) {
- let value = ''
+function getCSSText(element: Element, properties?: string[]) {
const style = getComputedStyle(element)
+ if (properties) {
+ return properties
+ .filter(p => p.trim().length > 0)
+ .map(p => `${p}:${style.getPropertyValue(p)};`)
+ .join('')
+ }
+
+ let value = ''
+
for (let i = 0; i < style.length; i += 1) {
value += `${style[i]}:${style.getPropertyValue(style[i])};`
}
@@ -9,13 +17,13 @@ function getCSSText(element: Element) {
return value
}
-export function cloneElement(node: HTMLElement) {
+export function cloneElement(node: HTMLElement, properties?: string[]) {
const clonedNode = node.cloneNode(true) as HTMLElement
const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]
const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))] as HTMLElement[]
sourceElements.forEach((sourceElement, index) => {
- targetElements[index].style.cssText = getCSSText(sourceElement)
+ targetElements[index].style.cssText = getCSSText(sourceElement, properties)
})
return clonedNode
diff --git a/packages/extension-drag-handle/src/helpers/dragHandler.ts b/packages/extension-drag-handle/src/helpers/dragHandler.ts
index d2ca3f88eb..81d70ec40f 100644
--- a/packages/extension-drag-handle/src/helpers/dragHandler.ts
+++ b/packages/extension-drag-handle/src/helpers/dragHandler.ts
@@ -68,6 +68,7 @@ export function dragHandler(
editor: Editor,
nestedOptions?: NormalizedNestedOptions,
dragContext?: DragContext,
+ dragImageProperties?: string[],
) {
const { view } = editor
@@ -126,7 +127,7 @@ export function dragHandler(
return
}
- const clonedElement = cloneElement(element)
+ const clonedElement = cloneElement(element, dragImageProperties)
clonedElement.style.margin = '0'
diff --git a/packages/extension-emoji/CHANGELOG.md b/packages/extension-emoji/CHANGELOG.md
index dc5d48b8e2..a802d9cc47 100644
--- a/packages/extension-emoji/CHANGELOG.md
+++ b/packages/extension-emoji/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-emoji
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/suggestion@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-emoji/package.json b/packages/extension-emoji/package.json
index 002eb5fafa..208d5595f8 100644
--- a/packages/extension-emoji/package.json
+++ b/packages/extension-emoji/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-emoji",
"description": "emoji extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/api/nodes/emoji",
"keywords": [
"tiptap",
diff --git a/packages/extension-file-handler/CHANGELOG.md b/packages/extension-file-handler/CHANGELOG.md
index 1eca9da46b..f14035837e 100644
--- a/packages/extension-file-handler/CHANGELOG.md
+++ b/packages/extension-file-handler/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-file-handler
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-file-handler/package.json b/packages/extension-file-handler/package.json
index 3754e14fb4..d4d66a7b7f 100644
--- a/packages/extension-file-handler/package.json
+++ b/packages/extension-file-handler/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-file-handler",
"description": "file handler extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/filehandler",
"keywords": [
"tiptap",
diff --git a/packages/extension-floating-menu/CHANGELOG.md b/packages/extension-floating-menu/CHANGELOG.md
index aae81e3b83..e5752255cd 100644
--- a/packages/extension-floating-menu/CHANGELOG.md
+++ b/packages/extension-floating-menu/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-floating-menu/package.json b/packages/extension-floating-menu/package.json
index 213ec83139..22807de099 100644
--- a/packages/extension-floating-menu/package.json
+++ b/packages/extension-floating-menu/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-floating-menu",
"description": "floating-menu extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-font-family/CHANGELOG.md b/packages/extension-font-family/CHANGELOG.md
index 6dd13ba72e..e54a094934 100644
--- a/packages/extension-font-family/CHANGELOG.md
+++ b/packages/extension-font-family/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-text-style@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-font-family/package.json b/packages/extension-font-family/package.json
index 2cc3e02560..f2460296e9 100644
--- a/packages/extension-font-family/package.json
+++ b/packages/extension-font-family/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-font-family",
"description": "font family extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-hard-break/CHANGELOG.md b/packages/extension-hard-break/CHANGELOG.md
index e436961bb0..abd9f78e41 100644
--- a/packages/extension-hard-break/CHANGELOG.md
+++ b/packages/extension-hard-break/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-hard-break/package.json b/packages/extension-hard-break/package.json
index 18799451b0..9646a6a9d2 100644
--- a/packages/extension-hard-break/package.json
+++ b/packages/extension-hard-break/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-hard-break",
"description": "hard break extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-heading/CHANGELOG.md b/packages/extension-heading/CHANGELOG.md
index 6c1457349b..08ea370abe 100644
--- a/packages/extension-heading/CHANGELOG.md
+++ b/packages/extension-heading/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-heading/package.json b/packages/extension-heading/package.json
index 2d796d4ec1..3e0c00910c 100644
--- a/packages/extension-heading/package.json
+++ b/packages/extension-heading/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-heading",
"description": "heading extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-highlight/CHANGELOG.md b/packages/extension-highlight/CHANGELOG.md
index ab3ff2694c..86fcdb9320 100644
--- a/packages/extension-highlight/CHANGELOG.md
+++ b/packages/extension-highlight/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-highlight/package.json b/packages/extension-highlight/package.json
index c0a137872e..f017c2c56e 100644
--- a/packages/extension-highlight/package.json
+++ b/packages/extension-highlight/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-highlight",
"description": "highlight extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-horizontal-rule/CHANGELOG.md b/packages/extension-horizontal-rule/CHANGELOG.md
index a334e5bbde..13dc35c1a8 100644
--- a/packages/extension-horizontal-rule/CHANGELOG.md
+++ b/packages/extension-horizontal-rule/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-horizontal-rule/package.json b/packages/extension-horizontal-rule/package.json
index 99e83c07b7..a8da9b0891 100644
--- a/packages/extension-horizontal-rule/package.json
+++ b/packages/extension-horizontal-rule/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-horizontal-rule",
"description": "horizontal rule extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-image/CHANGELOG.md b/packages/extension-image/CHANGELOG.md
index b883ce6d0c..047f27720e 100644
--- a/packages/extension-image/CHANGELOG.md
+++ b/packages/extension-image/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-image/package.json b/packages/extension-image/package.json
index 9a7d0051d6..5b9877ed53 100644
--- a/packages/extension-image/package.json
+++ b/packages/extension-image/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-image",
"description": "image extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-invisible-characters/CHANGELOG.md b/packages/extension-invisible-characters/CHANGELOG.md
index 7be73aa8ab..a85cab794e 100644
--- a/packages/extension-invisible-characters/CHANGELOG.md
+++ b/packages/extension-invisible-characters/CHANGELOG.md
@@ -1,5 +1,14 @@
# @tiptap/extension-invisible-characters
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/extension-text-style@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-invisible-characters/package.json b/packages/extension-invisible-characters/package.json
index ae245a6049..3829af8bf0 100644
--- a/packages/extension-invisible-characters/package.json
+++ b/packages/extension-invisible-characters/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-invisible-characters",
"description": "invisible characters extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/invisiblecharacters",
"keywords": [
"tiptap",
diff --git a/packages/extension-italic/CHANGELOG.md b/packages/extension-italic/CHANGELOG.md
index e406112665..05384e0c55 100644
--- a/packages/extension-italic/CHANGELOG.md
+++ b/packages/extension-italic/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-italic/package.json b/packages/extension-italic/package.json
index c5f52be53f..43d19bf199 100644
--- a/packages/extension-italic/package.json
+++ b/packages/extension-italic/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-italic",
"description": "italic extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-link/CHANGELOG.md b/packages/extension-link/CHANGELOG.md
index 702467b28d..2f7cd9c4b9 100644
--- a/packages/extension-link/CHANGELOG.md
+++ b/packages/extension-link/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-link/package.json b/packages/extension-link/package.json
index a3ed9fd887..8dc0d09ba7 100644
--- a/packages/extension-link/package.json
+++ b/packages/extension-link/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-link",
"description": "link extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-list/CHANGELOG.md b/packages/extension-list/CHANGELOG.md
index e002ccecc5..cd15ed29e8 100644
--- a/packages/extension-list/CHANGELOG.md
+++ b/packages/extension-list/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-list/package.json b/packages/extension-list/package.json
index ca5af5c62b..462ce34c85 100644
--- a/packages/extension-list/package.json
+++ b/packages/extension-list/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-list",
"description": "List extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-mathematics/CHANGELOG.md b/packages/extension-mathematics/CHANGELOG.md
index 5f51a776e5..fdc7a0c3b9 100644
--- a/packages/extension-mathematics/CHANGELOG.md
+++ b/packages/extension-mathematics/CHANGELOG.md
@@ -1,5 +1,13 @@
# @tiptap/extension-mathematics
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-mathematics/package.json b/packages/extension-mathematics/package.json
index 07fe5dc51a..a2bbac00f0 100644
--- a/packages/extension-mathematics/package.json
+++ b/packages/extension-mathematics/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-mathematics",
"description": "latex math extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/api/extensions/mathematics",
"keywords": [
"tiptap",
diff --git a/packages/extension-mention/CHANGELOG.md b/packages/extension-mention/CHANGELOG.md
index e1f1369696..ffd5d66712 100644
--- a/packages/extension-mention/CHANGELOG.md
+++ b/packages/extension-mention/CHANGELOG.md
@@ -1,5 +1,14 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/suggestion@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-mention/package.json b/packages/extension-mention/package.json
index 9267ab9791..58e0e7c4f7 100644
--- a/packages/extension-mention/package.json
+++ b/packages/extension-mention/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-mention",
"description": "mention extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-node-range/CHANGELOG.md b/packages/extension-node-range/CHANGELOG.md
index 9c673c2948..6a3a2d93fb 100644
--- a/packages/extension-node-range/CHANGELOG.md
+++ b/packages/extension-node-range/CHANGELOG.md
@@ -1,5 +1,13 @@
# @tiptap/extension-node-range
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-node-range/package.json b/packages/extension-node-range/package.json
index f0adda3ca0..fdba848d79 100644
--- a/packages/extension-node-range/package.json
+++ b/packages/extension-node-range/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-node-range",
"description": "node range extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-ordered-list/CHANGELOG.md b/packages/extension-ordered-list/CHANGELOG.md
index c7f17738d5..4777b2a98b 100644
--- a/packages/extension-ordered-list/CHANGELOG.md
+++ b/packages/extension-ordered-list/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- @tiptap/extension-list@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-ordered-list/package.json b/packages/extension-ordered-list/package.json
index 9980a6ea1d..8284c3f051 100644
--- a/packages/extension-ordered-list/package.json
+++ b/packages/extension-ordered-list/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-ordered-list",
"description": "ordered list extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-paragraph/CHANGELOG.md b/packages/extension-paragraph/CHANGELOG.md
index 1e4b3f6098..038d86855e 100644
--- a/packages/extension-paragraph/CHANGELOG.md
+++ b/packages/extension-paragraph/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-paragraph/package.json b/packages/extension-paragraph/package.json
index 897306c82f..6ba2493cc4 100644
--- a/packages/extension-paragraph/package.json
+++ b/packages/extension-paragraph/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-paragraph",
"description": "paragraph extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-strike/CHANGELOG.md b/packages/extension-strike/CHANGELOG.md
index a908481ae6..0bcf488f74 100644
--- a/packages/extension-strike/CHANGELOG.md
+++ b/packages/extension-strike/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-strike/package.json b/packages/extension-strike/package.json
index cc002e549b..ff3e89c318 100644
--- a/packages/extension-strike/package.json
+++ b/packages/extension-strike/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-strike",
"description": "strike extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-subscript/CHANGELOG.md b/packages/extension-subscript/CHANGELOG.md
index 319e5bc386..1028f43de5 100644
--- a/packages/extension-subscript/CHANGELOG.md
+++ b/packages/extension-subscript/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-subscript/package.json b/packages/extension-subscript/package.json
index a26607859d..9c6da9a78e 100644
--- a/packages/extension-subscript/package.json
+++ b/packages/extension-subscript/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-subscript",
"description": "subscript extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-superscript/CHANGELOG.md b/packages/extension-superscript/CHANGELOG.md
index 562daab7c5..7d9ff2fba3 100644
--- a/packages/extension-superscript/CHANGELOG.md
+++ b/packages/extension-superscript/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-superscript/package.json b/packages/extension-superscript/package.json
index fa2a5293ad..e9d456aab1 100644
--- a/packages/extension-superscript/package.json
+++ b/packages/extension-superscript/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-superscript",
"description": "superscript extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-table-of-contents/CHANGELOG.md b/packages/extension-table-of-contents/CHANGELOG.md
index 8bdb05fc80..252ccf876c 100644
--- a/packages/extension-table-of-contents/CHANGELOG.md
+++ b/packages/extension-table-of-contents/CHANGELOG.md
@@ -1,5 +1,13 @@
# @tiptap/extension-table-of-contents
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-table-of-contents/package.json b/packages/extension-table-of-contents/package.json
index 5ba2aff79f..90587cedd4 100644
--- a/packages/extension-table-of-contents/package.json
+++ b/packages/extension-table-of-contents/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-table-of-contents",
"description": "table of contents extension for Tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/table-of-contents",
"keywords": [
"tiptap",
diff --git a/packages/extension-table/CHANGELOG.md b/packages/extension-table/CHANGELOG.md
index 1da77ffa18..f9654faed0 100644
--- a/packages/extension-table/CHANGELOG.md
+++ b/packages/extension-table/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-table/package.json b/packages/extension-table/package.json
index 8e0f71a548..229ec8ff46 100644
--- a/packages/extension-table/package.json
+++ b/packages/extension-table/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-table",
"description": "table extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-text-align/CHANGELOG.md b/packages/extension-text-align/CHANGELOG.md
index 59bee50666..9001fa3246 100644
--- a/packages/extension-text-align/CHANGELOG.md
+++ b/packages/extension-text-align/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-text-align/package.json b/packages/extension-text-align/package.json
index f674d8bd40..fcfcdb5080 100644
--- a/packages/extension-text-align/package.json
+++ b/packages/extension-text-align/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-text-align",
"description": "text align extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-text-style/CHANGELOG.md b/packages/extension-text-style/CHANGELOG.md
index 50dae94ead..5cd43a3109 100644
--- a/packages/extension-text-style/CHANGELOG.md
+++ b/packages/extension-text-style/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-text-style/package.json b/packages/extension-text-style/package.json
index bbb200ba8b..95c24c5d95 100644
--- a/packages/extension-text-style/package.json
+++ b/packages/extension-text-style/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-text-style",
"description": "text style extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-text/CHANGELOG.md b/packages/extension-text/CHANGELOG.md
index cd1f91287c..38242ff328 100644
--- a/packages/extension-text/CHANGELOG.md
+++ b/packages/extension-text/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-text/package.json b/packages/extension-text/package.json
index 350033c6b2..04cedec24b 100644
--- a/packages/extension-text/package.json
+++ b/packages/extension-text/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-text",
"description": "text extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-twitch/CHANGELOG.md b/packages/extension-twitch/CHANGELOG.md
index 153a882f05..11e20d3784 100644
--- a/packages/extension-twitch/CHANGELOG.md
+++ b/packages/extension-twitch/CHANGELOG.md
@@ -1,5 +1,12 @@
# @tiptap/extension-twitch
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-twitch/package.json b/packages/extension-twitch/package.json
index b53f59c030..3bdb823051 100644
--- a/packages/extension-twitch/package.json
+++ b/packages/extension-twitch/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-twitch",
"description": "a twitch embed extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-typography/CHANGELOG.md b/packages/extension-typography/CHANGELOG.md
index 8f64405158..c7a21824fc 100644
--- a/packages/extension-typography/CHANGELOG.md
+++ b/packages/extension-typography/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-typography/package.json b/packages/extension-typography/package.json
index e2cd855e6a..51b018e770 100644
--- a/packages/extension-typography/package.json
+++ b/packages/extension-typography/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-typography",
"description": "typography extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-underline/CHANGELOG.md b/packages/extension-underline/CHANGELOG.md
index 352719ed72..6536c93a94 100644
--- a/packages/extension-underline/CHANGELOG.md
+++ b/packages/extension-underline/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-underline/package.json b/packages/extension-underline/package.json
index be3511473d..725104bd88 100644
--- a/packages/extension-underline/package.json
+++ b/packages/extension-underline/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-underline",
"description": "underline extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extension-unique-id/CHANGELOG.md b/packages/extension-unique-id/CHANGELOG.md
index b0dabb584b..7b96229060 100644
--- a/packages/extension-unique-id/CHANGELOG.md
+++ b/packages/extension-unique-id/CHANGELOG.md
@@ -1,5 +1,13 @@
# @tiptap/extension-unique-id
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-unique-id/package.json b/packages/extension-unique-id/package.json
index 182c0992eb..e6fddd06d2 100644
--- a/packages/extension-unique-id/package.json
+++ b/packages/extension-unique-id/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-unique-id",
"description": "unique id extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev/api/extensions/unique-id",
"keywords": [
"tiptap",
diff --git a/packages/extension-youtube/CHANGELOG.md b/packages/extension-youtube/CHANGELOG.md
index 629dcff77a..75a804c1d7 100644
--- a/packages/extension-youtube/CHANGELOG.md
+++ b/packages/extension-youtube/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extension-youtube/package.json b/packages/extension-youtube/package.json
index ce0a53812f..ac75a15766 100644
--- a/packages/extension-youtube/package.json
+++ b/packages/extension-youtube/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extension-youtube",
"description": "a youtube embed extension for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extensions/CHANGELOG.md b/packages/extensions/CHANGELOG.md
index db10b3a348..4bdd0e0a34 100644
--- a/packages/extensions/CHANGELOG.md
+++ b/packages/extensions/CHANGELOG.md
@@ -1,5 +1,14 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- 937ff2e: **Placeholder**: Replaced full-document `doc.descendants()` traversal with a cursor-resolved fast path for the default config and viewport-limited scanning for the non-default config, significantly reducing decoration overhead on large documents.
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/extensions/__tests__/placeholder.spec.ts b/packages/extensions/__tests__/placeholder.spec.ts
index 8beeda3b33..2a6cac0e3f 100644
--- a/packages/extensions/__tests__/placeholder.spec.ts
+++ b/packages/extensions/__tests__/placeholder.spec.ts
@@ -7,7 +7,10 @@ import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import Text from '@tiptap/extension-text'
import { type PlaceholderOptions, Placeholder, preparePlaceholderAttribute } from '@tiptap/extensions'
-import { afterEach, describe, expect, it } from 'vitest'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { findScrollParent } from '../src/placeholder/utils/findScrollParent.js'
+import { throttle } from '../src/placeholder/utils/throttle.js'
describe('extension-placeholder', () => {
let editor: Editor | null = null
@@ -186,3 +189,335 @@ describe('extension-placeholder with includeChildren and wrapper nodes', () => {
expect(paragraph.hasAttribute('data-placeholder')).toBe(false)
})
})
+
+describe('extension-placeholder: fast path (default config)', () => {
+ let editor: Editor | null = null
+
+ afterEach(() => {
+ if (editor) {
+ editor.destroy()
+ editor = null
+ }
+ })
+
+ it('shows placeholder in the current empty paragraph via the fast path', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ placeholder: 'Type something...',
+ showOnlyCurrent: true,
+ includeChildren: false,
+ }),
+ ],
+ content: '',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.getAttribute('data-placeholder')).toBe('Type something...')
+ expect(paragraph.classList.contains('is-empty')).toBe(true)
+ })
+
+ it('hides placeholder when the cursor is in a non-empty paragraph', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ placeholder: 'Type something...',
+ showOnlyCurrent: true,
+ includeChildren: false,
+ }),
+ ],
+ content: 'Hello
',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.hasAttribute('data-placeholder')).toBe(false)
+ expect(paragraph.classList.contains('is-empty')).toBe(false)
+ })
+
+ it('shows placeholder only for the current empty node when multiple paragraphs exist', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ placeholder: 'Write here...',
+ showOnlyCurrent: true,
+ includeChildren: false,
+ }),
+ ],
+ content: '',
+ })
+
+ // Cursor is in the first paragraph by default (start of doc)
+ const paragraphs = editor!.view.dom.querySelectorAll('p')
+ // Only the first paragraph (where the cursor is) should have a placeholder
+ expect(paragraphs[0].getAttribute('data-placeholder')).toBe('Write here...')
+ // The second paragraph should not have a placeholder since showOnlyCurrent is true
+ expect(paragraphs[1].hasAttribute('data-placeholder')).toBe(false)
+ })
+
+ it('removes placeholder when user types into the empty paragraph', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ placeholder: 'Type something...',
+ showOnlyCurrent: true,
+ includeChildren: false,
+ }),
+ ],
+ content: '',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.getAttribute('data-placeholder')).toBe('Type something...')
+
+ // Insert text into the paragraph
+ editor!.commands.insertContent('Hello')
+ expect(paragraph.hasAttribute('data-placeholder')).toBe(false)
+ expect(paragraph.classList.contains('is-empty')).toBe(false)
+ })
+})
+
+describe('extension-placeholder: slow path (showOnlyCurrent: false)', () => {
+ let editor: Editor | null = null
+
+ afterEach(() => {
+ if (editor) {
+ editor.destroy()
+ editor = null
+ }
+ })
+
+ it('shows placeholder on all empty textblocks when showOnlyCurrent is false', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ placeholder: 'Fill me in...',
+ showOnlyCurrent: false,
+ includeChildren: false,
+ }),
+ ],
+ content: 'Content
',
+ })
+
+ const paragraphs = editor!.view.dom.querySelectorAll('p')
+ // First paragraph (empty) should have placeholder
+ expect(paragraphs[0].getAttribute('data-placeholder')).toBe('Fill me in...')
+ // Second paragraph (has content) should not
+ expect(paragraphs[1].hasAttribute('data-placeholder')).toBe(false)
+ // Third paragraph (empty) should have placeholder
+ expect(paragraphs[2].getAttribute('data-placeholder')).toBe('Fill me in...')
+ })
+})
+
+describe('extension-placeholder — empty editor class', () => {
+ let editor: Editor | null = null
+
+ afterEach(() => {
+ if (editor) {
+ editor.destroy()
+ editor = null
+ }
+ })
+
+ it('adds is-editor-empty class when the entire document is empty', () => {
+ editor = new Editor({
+ extensions: [Document, Paragraph, Text, Placeholder],
+ content: '',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.classList.contains('is-editor-empty')).toBe(true)
+ })
+
+ it('removes is-editor-empty class when content is added', () => {
+ editor = new Editor({
+ extensions: [Document, Paragraph, Text, Placeholder],
+ content: '',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.classList.contains('is-editor-empty')).toBe(true)
+
+ editor!.commands.insertContent('Hello')
+ expect(paragraph.classList.contains('is-editor-empty')).toBe(false)
+ })
+
+ it('uses custom emptyEditorClass option', () => {
+ editor = new Editor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ Placeholder.configure({
+ emptyEditorClass: 'my-empty-editor',
+ emptyNodeClass: 'my-empty-node',
+ }),
+ ],
+ content: '',
+ })
+
+ const paragraph = editor!.view.dom.querySelector('p') as HTMLElement
+ expect(paragraph.classList.contains('my-empty-editor')).toBe(true)
+ expect(paragraph.classList.contains('my-empty-node')).toBe(true)
+ })
+})
+
+describe('placeholder utility: findScrollParent', () => {
+ let container: HTMLElement
+
+ beforeEach(() => {
+ container = document.createElement('div')
+ document.body.appendChild(container)
+ })
+
+ afterEach(() => {
+ document.body.removeChild(container)
+ })
+
+ it('returns the window when no scroll parent exists', () => {
+ const el = document.createElement('span')
+ container.appendChild(el)
+ expect(findScrollParent(el)).toBe(window)
+ })
+
+ it('finds a scrollable parent element with overflow: auto', () => {
+ const scrollable = document.createElement('div')
+ scrollable.style.overflow = 'auto'
+ const child = document.createElement('span')
+ scrollable.appendChild(child)
+ container.appendChild(scrollable)
+
+ expect(findScrollParent(child)).toBe(scrollable)
+ })
+
+ it('finds a scrollable parent element with overflow: scroll', () => {
+ const scrollable = document.createElement('div')
+ scrollable.style.overflow = 'scroll'
+ const child = document.createElement('span')
+ scrollable.appendChild(child)
+ container.appendChild(scrollable)
+
+ expect(findScrollParent(child)).toBe(scrollable)
+ })
+
+ it('does not return elements with overflow: hidden or overflow: clip', () => {
+ const hidden = document.createElement('div')
+ hidden.style.overflow = 'hidden'
+ const clip = document.createElement('div')
+ clip.style.overflow = 'clip'
+ const child = document.createElement('span')
+ hidden.appendChild(clip)
+ clip.appendChild(child)
+ container.appendChild(hidden)
+
+ expect(findScrollParent(child)).toBe(window)
+ })
+
+ it('finds the nearest scrollable ancestor', () => {
+ const outer = document.createElement('div')
+ outer.style.overflow = 'scroll'
+ const middle = document.createElement('div')
+ middle.style.overflow = 'hidden'
+ const child = document.createElement('span')
+ middle.appendChild(child)
+ outer.appendChild(middle)
+ container.appendChild(outer)
+
+ // Should find `outer`, not stop at `middle` (which is hidden, not scrollable)
+ expect(findScrollParent(child)).toBe(outer)
+ })
+})
+
+describe('placeholder utility: throttle', () => {
+ it('calls the function immediately on the first invocation (leading-edge)', () => {
+ let called = false
+ const { call } = throttle(() => {
+ called = true
+ }, 250)
+ call()
+ expect(called).toBe(true)
+ })
+
+ it('ignores subsequent calls within the delay window', () => {
+ let count = 0
+ const { call } = throttle(() => {
+ count += 1
+ }, 250)
+
+ call()
+ call()
+ call()
+ // Leading-edge fires immediately, subsequent calls within the delay are blocked
+ expect(count).toBe(1)
+ })
+
+ it('allows a call after the delay has elapsed', () => {
+ vi.useFakeTimers()
+ let count = 0
+ const { call } = throttle(() => {
+ count += 1
+ }, 250)
+
+ call()
+ expect(count).toBe(1)
+
+ vi.advanceTimersByTime(250)
+ call()
+ expect(count).toBe(2)
+ vi.useRealTimers()
+ })
+
+ it('resets the timer on each call within the window', () => {
+ vi.useFakeTimers()
+ let count = 0
+ const { call } = throttle(() => {
+ count += 1
+ }, 250)
+
+ call() // fires immediately
+ expect(count).toBe(1)
+
+ vi.advanceTimersByTime(100)
+ call() // ignored (within 250ms window)
+ vi.advanceTimersByTime(100)
+ call() // ignored
+ vi.advanceTimersByTime(100)
+ // 300ms elapsed total, but the window extends 250ms from the last call
+ expect(count).toBe(1)
+
+ vi.advanceTimersByTime(150)
+ call() // 250ms has passed since last call
+ expect(count).toBe(2)
+ vi.useRealTimers()
+ })
+
+ it('cancel() clears the timer and allows immediate re-call', () => {
+ vi.useFakeTimers()
+ let count = 0
+ const { call, cancel } = throttle(() => {
+ count += 1
+ }, 250)
+
+ call() // fires, starts timer
+ expect(count).toBe(1)
+
+ cancel() // clears timer
+ call() // fires again because timer was cancelled
+ expect(count).toBe(2)
+ vi.useRealTimers()
+ })
+})
diff --git a/packages/extensions/package.json b/packages/extensions/package.json
index e8e9b22d33..a3c20888d3 100644
--- a/packages/extensions/package.json
+++ b/packages/extensions/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/extensions",
"description": "various extensions for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/extensions/src/placeholder/index.ts b/packages/extensions/src/placeholder/index.ts
index d2ea7291a7..70f8306c46 100644
--- a/packages/extensions/src/placeholder/index.ts
+++ b/packages/extensions/src/placeholder/index.ts
@@ -1 +1,2 @@
export * from './placeholder.js'
+export * from './types.js'
diff --git a/packages/extensions/src/placeholder/placeholder.ts b/packages/extensions/src/placeholder/placeholder.ts
index 3c8c483e88..232a6bd604 100644
--- a/packages/extensions/src/placeholder/placeholder.ts
+++ b/packages/extensions/src/placeholder/placeholder.ts
@@ -1,8 +1,13 @@
-import type { Editor } from '@tiptap/core'
import { Extension, isNodeEmpty } from '@tiptap/core'
-import type { Node as ProsemirrorNode } from '@tiptap/pm/model'
import { Plugin, PluginKey } from '@tiptap/pm/state'
-import { Decoration, DecorationSet } from '@tiptap/pm/view'
+import type { Decoration } from '@tiptap/pm/view'
+import { DecorationSet } from '@tiptap/pm/view'
+
+import type { PlaceholderOptions } from './types.js'
+import { createPlaceholderDecoration } from './utils/createPlaceholderDecoration.js'
+import { findScrollParent } from './utils/findScrollParent.js'
+import { getViewportBoundaryPositions } from './utils/getViewportBoundaryPositions.js'
+import { throttle } from './utils/throttle.js'
/**
* The default data attribute label
@@ -31,63 +36,7 @@ export function preparePlaceholderAttribute(attr: string): string {
)
}
-export interface PlaceholderOptions {
- /**
- * **The class name for the empty editor**
- * @default 'is-editor-empty'
- */
- emptyEditorClass: string
-
- /**
- * **The class name for empty nodes**
- * @default 'is-empty'
- */
- emptyNodeClass: string
-
- /**
- * **The data-attribute used for the placeholder label**
- * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
- * @default 'placeholder'
- */
- dataAttribute: string
-
- /**
- * **The placeholder content**
- *
- * You can use a function to return a dynamic placeholder or a string.
- * @default 'Write something …'
- */
- placeholder:
- | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)
- | string
-
- /**
- * **Checks if the placeholder should be only shown when the editor is editable.**
- *
- * If true, the placeholder will only be shown when the editor is editable.
- * If false, the placeholder will always be shown.
- * @default true
- */
- showOnlyWhenEditable: boolean
-
- /**
- * **Checks if the placeholder should be only shown when the current node is empty.**
- *
- * If true, the placeholder will only be shown when the current node is empty.
- * If false, the placeholder will be shown when any node is empty.
- * @default true
- */
- showOnlyCurrent: boolean
-
- /**
- * **Controls if the placeholder should be shown for all descendents.**
- *
- * If true, the placeholder will be shown for all descendents.
- * If false, the placeholder will only be shown for the current node.
- * @default false
- */
- includeChildren: boolean
-}
+export const PLUGIN_KEY = new PluginKey('tiptap__placeholder')
/**
* This extension allows you to add a placeholder to your editor.
@@ -116,52 +65,157 @@ export const Placeholder = Extension.create({
return [
new Plugin({
- key: new PluginKey('placeholder'),
+ state: {
+ init() {
+ return {
+ // null means "no viewport info yet" — decoration callback falls
+ // back to full document scan until the scroll handler fires.
+ topPos: null as number | null,
+ bottomPos: null as number | null,
+ }
+ },
+ apply(tr, prev) {
+ const meta = tr.getMeta(PLUGIN_KEY) as { positions?: { top: number; bottom: number } } | undefined
+
+ if (meta?.positions) {
+ return {
+ topPos: meta.positions.top,
+ bottomPos: meta.positions.bottom,
+ }
+ }
+
+ if (!tr.docChanged) {
+ return prev
+ }
+
+ // Preserve last known viewport positions across transactions.
+ // Without this, every keystroke resets back to a full document
+ // scan, defeating the viewport optimisation.
+ // Only map when we have actual positions — null means "no viewport
+ // info yet" and should stay null to fall back to full doc scan.
+ return {
+ topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null,
+ bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null,
+ }
+ },
+ },
+ key: PLUGIN_KEY,
+ view(view) {
+ const scrollContainer = findScrollParent(view.dom)
+
+ const computeAndDispatch = () => {
+ const positions = getViewportBoundaryPositions({
+ view,
+ doc: view.state.doc,
+ scrollContainer,
+ })
+
+ const prev = PLUGIN_KEY.getState(view.state)
+ if (prev.topPos === positions.top && prev.bottomPos === positions.bottom) {
+ return
+ }
+
+ const tr = view.state.tr
+ .setMeta(PLUGIN_KEY, { positions })
+ // Flag this transaction so the update() method can detect
+ // it and avoid re-entrant computation.
+ .setMeta('tiptap__viewportUpdate', true)
+ view.dispatch(tr)
+ }
+
+ const { call: throttledUpdate, cancel: cancelThrottle } = throttle(computeAndDispatch, 250)
+ const scrollParent = scrollContainer
+
+ scrollParent.addEventListener('scroll', throttledUpdate, { passive: true })
+
+ // Fire once to populate initial viewport (bypass throttle)
+ computeAndDispatch()
+
+ return {
+ update(_, prevState) {
+ // Skip re-entry: the dispatch inside computeAndDispatch would
+ // trigger this update again, but the doc didn't change so the
+ // size guard catches that. The meta flag is an extra safeguard.
+ if (view.state.doc.content.size !== prevState.doc.content.size) {
+ computeAndDispatch()
+ }
+ },
+ destroy: () => {
+ cancelThrottle()
+ scrollParent.removeEventListener('scroll', throttledUpdate)
+ },
+ }
+ },
props: {
decorations: ({ doc, selection }) => {
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable
- const { anchor } = selection
- const decorations: Decoration[] = []
if (!active) {
return null
}
+ const { anchor } = selection
+ const decorations: Decoration[] = []
const isEmptyDoc = this.editor.isEmpty
- doc.descendants((node, pos) => {
- const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize
- const isEmpty = !node.isLeaf && isNodeEmpty(node)
-
- if (!node.type.isTextblock) {
- return this.options.includeChildren
+ const useResolvedPath = this.options.showOnlyCurrent && !this.options.includeChildren
+
+ if (useResolvedPath) {
+ const resolved = doc.resolve(anchor)
+
+ if (resolved.depth > 0) {
+ const node = resolved.node(1)
+ const nodeStart = resolved.before(1)
+
+ if (node.type.isTextblock && isNodeEmpty(node)) {
+ const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize
+ const decoration = createPlaceholderDecoration({
+ node,
+ dataAttribute,
+ hasAnchor,
+ placeholder: this.options.placeholder,
+ classes: {
+ emptyEditor: this.options.emptyEditorClass,
+ emptyNode: this.options.emptyNodeClass,
+ },
+ editor: this.editor,
+ isEmptyDoc,
+ pos: resolved.before(1),
+ })
+
+ decorations.push(decoration)
+ }
}
+ } else {
+ const pluginState = PLUGIN_KEY.getState(this.editor.state)
+ const from = pluginState.topPos ?? 0
+ const to = pluginState.bottomPos ?? doc.content.size
- if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
- const classes = [this.options.emptyNodeClass]
+ doc.nodesBetween(from, to, (node, pos) => {
+ const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize
+ const isEmpty = !node.isLeaf && isNodeEmpty(node)
- if (isEmptyDoc) {
- classes.push(this.options.emptyEditorClass)
+ if (!node.type.isTextblock) {
+ return this.options.includeChildren
}
- const decoration = Decoration.node(pos, pos + node.nodeSize, {
- class: classes.join(' '),
- [dataAttribute]:
- typeof this.options.placeholder === 'function'
- ? this.options.placeholder({
- editor: this.editor,
- node,
- pos,
- hasAnchor,
- })
- : this.options.placeholder,
- })
-
- decorations.push(decoration)
- }
+ if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
+ const decoration = createPlaceholderDecoration({
+ classes: { emptyEditor: this.options.emptyEditorClass, emptyNode: this.options.emptyNodeClass },
+ editor: this.editor,
+ isEmptyDoc,
+ dataAttribute,
+ hasAnchor,
+ placeholder: this.options.placeholder,
+ node,
+ pos,
+ })
+ decorations.push(decoration)
+ }
- return this.options.includeChildren
- })
+ return this.options.includeChildren
+ })
+ }
return DecorationSet.create(doc, decorations)
},
diff --git a/packages/extensions/src/placeholder/types.ts b/packages/extensions/src/placeholder/types.ts
new file mode 100644
index 0000000000..5e9f631f13
--- /dev/null
+++ b/packages/extensions/src/placeholder/types.ts
@@ -0,0 +1,60 @@
+import type { Editor } from '@tiptap/core'
+import type { Node as ProsemirrorNode } from '@tiptap/pm/model'
+
+export interface PlaceholderOptions {
+ /**
+ * **The class name for the empty editor**
+ * @default 'is-editor-empty'
+ */
+ emptyEditorClass: string
+
+ /**
+ * **The class name for empty nodes**
+ * @default 'is-empty'
+ */
+ emptyNodeClass: string
+
+ /**
+ * **The data-attribute used for the placeholder label**
+ * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
+ * @default 'placeholder'
+ */
+ dataAttribute: string
+
+ /**
+ * **The placeholder content**
+ *
+ * You can use a function to return a dynamic placeholder or a string.
+ * @default 'Write something …'
+ */
+ placeholder:
+ | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)
+ | string
+
+ /**
+ * **Checks if the placeholder should be only shown when the editor is editable.**
+ *
+ * If true, the placeholder will only be shown when the editor is editable.
+ * If false, the placeholder will always be shown.
+ * @default true
+ */
+ showOnlyWhenEditable: boolean
+
+ /**
+ * **Checks if the placeholder should be only shown when the current node is empty.**
+ *
+ * If true, the placeholder will only be shown when the current node is empty.
+ * If false, the placeholder will be shown when any node is empty.
+ * @default true
+ */
+ showOnlyCurrent: boolean
+
+ /**
+ * **Controls if the placeholder should be shown for all descendants.**
+ *
+ * If true, the placeholder will be shown for all descendants.
+ * If false, the placeholder will only be shown for the current node.
+ * @default false
+ */
+ includeChildren: boolean
+}
diff --git a/packages/extensions/src/placeholder/utils/createPlaceholderDecoration.ts b/packages/extensions/src/placeholder/utils/createPlaceholderDecoration.ts
new file mode 100644
index 0000000000..b2ee275c83
--- /dev/null
+++ b/packages/extensions/src/placeholder/utils/createPlaceholderDecoration.ts
@@ -0,0 +1,61 @@
+import type { Editor } from '@tiptap/core'
+import type { Node } from '@tiptap/pm/model'
+import { Decoration } from '@tiptap/pm/view'
+
+import type { PlaceholderOptions } from '../types.js'
+
+/**
+ * Creates a ProseMirror node decoration that applies a placeholder
+ * CSS class and data attribute to an empty node.
+ * @param options.editor - The editor instance
+ * @param options.pos - The position of the node in the document
+ * @param options.node - The ProseMirror node
+ * @param options.isEmptyDoc - Whether the entire document is empty
+ * @param options.hasAnchor - Whether the selection anchor is within the node
+ * @param options.dataAttribute - The data attribute name (e.g. `data-placeholder`)
+ * @param options.classes - CSS classes for empty nodes and the empty editor
+ * @param options.placeholder - The placeholder text or a function that returns it
+ * @returns A ProseMirror node decoration with placeholder classes and data attribute
+ */
+export function createPlaceholderDecoration(options: {
+ editor: Editor
+ pos: number
+ node: Node
+ isEmptyDoc: boolean
+ hasAnchor: boolean
+ dataAttribute: string
+ classes: {
+ emptyEditor: PlaceholderOptions['emptyEditorClass']
+ emptyNode: PlaceholderOptions['emptyNodeClass']
+ }
+ placeholder: PlaceholderOptions['placeholder']
+}) {
+ const {
+ editor,
+ placeholder,
+ dataAttribute,
+ pos,
+ node,
+ isEmptyDoc,
+ hasAnchor,
+ classes: { emptyNode, emptyEditor },
+ } = options
+ const classes = [emptyNode]
+
+ if (isEmptyDoc) {
+ classes.push(emptyEditor)
+ }
+
+ return Decoration.node(pos, pos + node.nodeSize, {
+ class: classes.join(' '),
+ [dataAttribute]:
+ typeof placeholder === 'function'
+ ? placeholder({
+ editor,
+ node,
+ pos,
+ hasAnchor,
+ })
+ : placeholder,
+ })
+}
diff --git a/packages/extensions/src/placeholder/utils/findScrollParent.ts b/packages/extensions/src/placeholder/utils/findScrollParent.ts
new file mode 100644
index 0000000000..cc547d5b62
--- /dev/null
+++ b/packages/extensions/src/placeholder/utils/findScrollParent.ts
@@ -0,0 +1,38 @@
+/**
+ * Checks if an element is scrollable by testing its overflow properties.
+ * Elements with `overflow: hidden` or `overflow: clip` are intentionally
+ * excluded — they clip content but don't emit scroll events.
+ */
+function isScrollable(el: HTMLElement): boolean {
+ const style = getComputedStyle(el)
+ const overflow = `${style.overflow} ${style.overflowY} ${style.overflowX}`
+
+ return /auto|scroll|overlay/.test(overflow)
+}
+
+export function findScrollParent(element: HTMLElement): HTMLElement | Window {
+ let el: HTMLElement | null = element
+
+ while (el) {
+ if (isScrollable(el)) {
+ return el
+ }
+
+ // Check if we hit a Shadow DOM boundary. If so, jump to the shadow host
+ // and continue traversing the light DOM.
+ const parent = el.parentElement
+ if (!parent) {
+ const root = el.getRootNode()
+ if (root instanceof ShadowRoot) {
+ el = root.host as HTMLElement
+ continue
+ }
+
+ return window
+ }
+
+ el = parent
+ }
+
+ return window
+}
diff --git a/packages/extensions/src/placeholder/utils/getViewportBoundaryPositions.ts b/packages/extensions/src/placeholder/utils/getViewportBoundaryPositions.ts
new file mode 100644
index 0000000000..6a8fdfece0
--- /dev/null
+++ b/packages/extensions/src/placeholder/utils/getViewportBoundaryPositions.ts
@@ -0,0 +1,45 @@
+import type { Node } from '@tiptap/pm/model'
+import type { EditorView } from '@tiptap/pm/view'
+
+function getContainerRect(container: HTMLElement | Window): { top: number; bottom: number } {
+ if (container === window) {
+ return { top: 0, bottom: window.innerHeight }
+ }
+
+ return (container as HTMLElement).getBoundingClientRect()
+}
+
+export function getViewportBoundaryPositions({
+ doc,
+ view,
+ scrollContainer,
+}: {
+ doc: Node
+ view: EditorView
+ scrollContainer?: HTMLElement | Window
+}) {
+ const editorRect = view.dom.getBoundingClientRect()
+ const containerRect = scrollContainer ? getContainerRect(scrollContainer) : { top: 0, bottom: window.innerHeight }
+
+ const visibleTop = Math.max(editorRect.top, containerRect.top)
+ const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom)
+
+ if (visibleTop >= visibleBottom) {
+ // Editor is not visible — fall back to full document range
+ return { top: 0, bottom: doc.content.size }
+ }
+
+ // Pick the x-coordinate based on text direction. In LTR the content
+ // starts at the left edge; in RTL it starts at the right edge.
+ // Clamp to ensure the coordinate stays inside the editor bounds.
+ const isRTL = getComputedStyle(view.dom).direction === 'rtl'
+ const x = isRTL ? Math.max(editorRect.right - 2, editorRect.left + 2) : editorRect.left + 2
+
+ const topPos = view.posAtCoords({ left: x, top: visibleTop + 2 })
+ const bottomPos = view.posAtCoords({ left: x, top: visibleBottom - 2 })
+
+ return {
+ top: topPos ? topPos.pos : 0,
+ bottom: bottomPos ? bottomPos.pos : doc.content.size,
+ }
+}
diff --git a/packages/extensions/src/placeholder/utils/throttle.ts b/packages/extensions/src/placeholder/utils/throttle.ts
new file mode 100644
index 0000000000..fab2e19cd7
--- /dev/null
+++ b/packages/extensions/src/placeholder/utils/throttle.ts
@@ -0,0 +1,25 @@
+export function throttle void>(fn: T, delay: number): { call: T; cancel: () => void } {
+ let timer: ReturnType | null = null
+
+ const call = ((...args: any[]) => {
+ if (timer) {
+ return
+ }
+
+ // Leading-edge: fire immediately, then prevent subsequent calls
+ // until the timer fires and resets.
+ fn(...args)
+ timer = setTimeout(() => {
+ timer = null
+ }, delay)
+ }) as T
+
+ const cancel = () => {
+ if (timer) {
+ clearTimeout(timer)
+ timer = null
+ }
+ }
+
+ return { call, cancel }
+}
diff --git a/packages/html/CHANGELOG.md b/packages/html/CHANGELOG.md
index b535f1b72d..652039e387 100644
--- a/packages/html/CHANGELOG.md
+++ b/packages/html/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/html/package.json b/packages/html/package.json
index d297723b51..d106cfade7 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/html",
"description": "utility package to render tiptap JSON as HTML",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/markdown/CHANGELOG.md b/packages/markdown/CHANGELOG.md
index fd9d5258c2..561dfc4ba3 100644
--- a/packages/markdown/CHANGELOG.md
+++ b/packages/markdown/CHANGELOG.md
@@ -1,5 +1,13 @@
# @tiptap/markdown
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/markdown/package.json b/packages/markdown/package.json
index 2b9c969de3..a4b71df542 100644
--- a/packages/markdown/package.json
+++ b/packages/markdown/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/markdown",
"description": "markdown parser and serializer for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/pm/CHANGELOG.md b/packages/pm/CHANGELOG.md
index 4be9cb81d5..3247759f08 100644
--- a/packages/pm/CHANGELOG.md
+++ b/packages/pm/CHANGELOG.md
@@ -1,5 +1,7 @@
# Change Log
+## 3.23.6
+
## 3.23.5
## 3.23.4
diff --git a/packages/pm/package.json b/packages/pm/package.json
index 4940ecd726..ac935a5435 100644
--- a/packages/pm/package.json
+++ b/packages/pm/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/pm",
"description": "prosemirror wrapper package for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md
index 2b57e28fa8..e6bf2663d1 100644
--- a/packages/react/CHANGELOG.md
+++ b/packages/react/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/react/package.json b/packages/react/package.json
index a27f7a6c91..9667a41fd3 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/react",
"description": "React components for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/starter-kit/CHANGELOG.md b/packages/starter-kit/CHANGELOG.md
index 1c8d71af51..bbca91c870 100644
--- a/packages/starter-kit/CHANGELOG.md
+++ b/packages/starter-kit/CHANGELOG.md
@@ -1,5 +1,36 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+- Updated dependencies [937ff2e]
+ - @tiptap/core@3.23.6
+ - @tiptap/extensions@3.23.6
+ - @tiptap/extension-blockquote@3.23.6
+ - @tiptap/extension-bold@3.23.6
+ - @tiptap/extension-code@3.23.6
+ - @tiptap/extension-code-block@3.23.6
+ - @tiptap/extension-document@3.23.6
+ - @tiptap/extension-hard-break@3.23.6
+ - @tiptap/extension-heading@3.23.6
+ - @tiptap/extension-horizontal-rule@3.23.6
+ - @tiptap/extension-italic@3.23.6
+ - @tiptap/extension-link@3.23.6
+ - @tiptap/extension-list@3.23.6
+ - @tiptap/extension-paragraph@3.23.6
+ - @tiptap/extension-strike@3.23.6
+ - @tiptap/extension-text@3.23.6
+ - @tiptap/extension-underline@3.23.6
+ - @tiptap/extension-dropcursor@3.23.6
+ - @tiptap/extension-gapcursor@3.23.6
+ - @tiptap/extension-list-item@3.23.6
+ - @tiptap/extension-list-keymap@3.23.6
+ - @tiptap/extension-bullet-list@3.23.6
+ - @tiptap/extension-ordered-list@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/starter-kit/package.json b/packages/starter-kit/package.json
index 34feae7ddd..3d8a2a4cad 100644
--- a/packages/starter-kit/package.json
+++ b/packages/starter-kit/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/starter-kit",
"description": "starter kit for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/static-renderer/CHANGELOG.md b/packages/static-renderer/CHANGELOG.md
index a08329afc2..886ffba73f 100644
--- a/packages/static-renderer/CHANGELOG.md
+++ b/packages/static-renderer/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/static-renderer/package.json b/packages/static-renderer/package.json
index a02c20418a..a61b6cba4f 100644
--- a/packages/static-renderer/package.json
+++ b/packages/static-renderer/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/static-renderer",
"description": "statically render Tiptap JSON",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/suggestion/CHANGELOG.md b/packages/suggestion/CHANGELOG.md
index 442801f181..6e86bf7b0e 100644
--- a/packages/suggestion/CHANGELOG.md
+++ b/packages/suggestion/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/suggestion/package.json b/packages/suggestion/package.json
index 6bbeb6ea52..fa859559f8 100644
--- a/packages/suggestion/package.json
+++ b/packages/suggestion/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/suggestion",
"description": "suggestion plugin for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/vue-2/CHANGELOG.md b/packages/vue-2/CHANGELOG.md
index d2785e2f31..fd73016036 100644
--- a/packages/vue-2/CHANGELOG.md
+++ b/packages/vue-2/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/vue-2/package.json b/packages/vue-2/package.json
index 08a6ca8754..a269de8783 100644
--- a/packages/vue-2/package.json
+++ b/packages/vue-2/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/vue-2",
"description": "Vue components for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",
diff --git a/packages/vue-3/CHANGELOG.md b/packages/vue-3/CHANGELOG.md
index 60f8efabca..f91a74f18c 100644
--- a/packages/vue-3/CHANGELOG.md
+++ b/packages/vue-3/CHANGELOG.md
@@ -1,5 +1,13 @@
# Change Log
+## 3.23.6
+
+### Patch Changes
+
+- Updated dependencies [d168376]
+ - @tiptap/core@3.23.6
+ - @tiptap/pm@3.23.6
+
## 3.23.5
### Patch Changes
diff --git a/packages/vue-3/package.json b/packages/vue-3/package.json
index d5de2b5e3b..cf4078de83 100644
--- a/packages/vue-3/package.json
+++ b/packages/vue-3/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiptap/vue-3",
"description": "Vue components for tiptap",
- "version": "3.23.5",
+ "version": "3.23.6",
"homepage": "https://tiptap.dev",
"keywords": [
"tiptap",