From 6b17c67f11df04b59e0a4a6beee05a845ae2c00d Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 10:42:03 +0200 Subject: [PATCH 1/3] fix(TextDirection): Only regard changed nodes in `appendTransaction` No need to iterate over all nodes of the document each time. Upstream PR: https://github.com/amirhhashemi/tiptap-text-direction/pull/23 Signed-off-by: Jonas --- src/extensions/TextDirection.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/extensions/TextDirection.ts b/src/extensions/TextDirection.ts index 5142b171306..4be8d079042 100644 --- a/src/extensions/TextDirection.ts +++ b/src/extensions/TextDirection.ts @@ -3,8 +3,13 @@ * SPDX-License-Identifier: MIT */ -import { Extension } from '@tiptap/core' -import { Plugin, PluginKey } from '@tiptap/pm/state' +import { + Extension, + combineTransactionSteps, + findChildrenInRange, + getChangedRanges, +} from '@tiptap/core' +import { Plugin, PluginKey, Transaction } from '@tiptap/pm/state' const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC' const LTR = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' @@ -53,11 +58,21 @@ function TextDirectionPlugin({ types }: { types: string[] }) { } let modified = false - const tr = newState.tr + const { tr } = newState + const transform = combineTransactionSteps( + oldState.doc, + transactions as Transaction[], + ) + const changes = getChangedRanges(transform) + tr.setMeta('addToHistory', false) - newState.doc.descendants((node, pos) => { - if (types.includes(node.type.name)) { + changes.forEach(({ newRange }) => { + const nodes = findChildrenInRange(newState.doc, newRange, (node) => + types.includes(node.type.name), + ) + + nodes.forEach(({ node, pos }) => { if (node.attrs.dir !== null && node.textContent.length > 0) { return } @@ -73,7 +88,7 @@ function TextDirectionPlugin({ types }: { types: string[] }) { tr.addStoredMark(mark) } modified = true - } + }) }) return modified ? tr : null From 17c9b6c5084deabfd0a8ac5567510b70f39f6c48 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 10:52:39 +0200 Subject: [PATCH 2/3] fix(Keymap): Add Backspace shortcut to undo input rules The upstream Tiptap Keymap extension that we don't use (anymore) also provides this. It allows to undo the automatic changes from input rules directly after they got applied by hitting . Unfortunately this doesn't work for nodes that get changed by the TextDirection extension due to a bug, but there's still plenty of input rules where it works, so let's bring it in. Signed-off-by: Jonas --- src/extensions/Keymap.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/extensions/Keymap.js b/src/extensions/Keymap.js index 81883699329..e2fe7fed634 100644 --- a/src/extensions/Keymap.js +++ b/src/extensions/Keymap.js @@ -11,7 +11,13 @@ const Keymap = Extension.create({ name: 'customkeymap', addKeyboardShortcuts() { - return this.options + return { + /** + * + * Allows to undo input rules after they got automatically applied + */ + Backspace: () => this.editor.commands.undoInputRule(), + } }, addProseMirrorPlugins() { From ab9ddf8a208cd99822410387afc2c9366c883337 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 29 Jul 2025 12:13:41 +0200 Subject: [PATCH 3/3] fix(TextDirection): Regard table cell, details summary and callouts Fixes styling for details and callouts with RTL script. Signed-off-by: Jonas --- src/extensions/RichText.js | 8 +++- src/nodes/Callout.vue | 6 ++- src/nodes/Table/TableCellView.vue | 8 +++- src/nodes/Table/TableHeaderView.vue | 8 +++- .../fixtures/tables/handbook/handbook.html | 4 +- .../tables/handbook/handbook.out.html | 26 ++++++------ src/tests/nodes/Table.spec.js | 42 ++++++++++++------- 7 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/extensions/RichText.js b/src/extensions/RichText.js index e131ad9b1ab..c67b7790d37 100644 --- a/src/extensions/RichText.js +++ b/src/extensions/RichText.js @@ -118,11 +118,15 @@ export default Extension.create({ TrailingNode, TextDirection.configure({ types: [ + 'blockquote', + 'callout', + 'detailsSummary', 'heading', - 'paragraph', 'listItem', + 'paragraph', + 'tableCell', + 'tableHeader', 'taskItem', - 'blockquote', ], }), ] diff --git a/src/nodes/Callout.vue b/src/nodes/Callout.vue index 6bc58d2af25..0bf82b23209 100644 --- a/src/nodes/Callout.vue +++ b/src/nodes/Callout.vue @@ -6,6 +6,7 @@ @@ -41,7 +42,10 @@ export default { return ICONS_MAP[this.type] || Info }, type() { - return this.node?.attrs?.type || 'info' + return this.node.attrs.type || 'info' + }, + dir() { + return this.node.attrs.dir || '' }, }, } diff --git a/src/nodes/Table/TableCellView.vue b/src/nodes/Table/TableCellView.vue index 2293dd5d31a..87ae63fad9b 100644 --- a/src/nodes/Table/TableCellView.vue +++ b/src/nodes/Table/TableCellView.vue @@ -4,7 +4,10 @@ -->