From 679e96b8659bc18e5ec0d463531423060a118ffd Mon Sep 17 00:00:00 2001 From: "amirmohammd.ahmadi" Date: Sat, 27 Sep 2025 10:21:11 +0330 Subject: [PATCH 1/2] fix: RTL and Bidirectional Text Issues --- src/components/TextEditor.vue | 98 +++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/components/TextEditor.vue b/src/components/TextEditor.vue index c84c4ddf23..542abe5c4d 100644 --- a/src/components/TextEditor.vue +++ b/src/components/TextEditor.vue @@ -202,6 +202,11 @@ export default { beforeMount() { this.loadEditorTranslations(getLanguage()) }, + mounted() { + this.$nextTick(() => { + this.setupRTLSupport() + }) + }, methods: { getLink(text) { const results = searchProvider(text) @@ -446,6 +451,70 @@ export default { } this.editorInstance.execute('insertItem', { content, isHtml: this.html }, '!') }, + setupRTLSupport() { + // Function to detect RTL characters + const isRTL = (text) => { + const rtlChars = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/ + return rtlChars.test(text) + } + + // Wait for editor to be ready + setTimeout(() => { + const editorElement = this.$el?.querySelector('.ck-editor__editable') + if (!editorElement) return + + // Add input event listener for auto RTL/LTR detection + editorElement.addEventListener('input', (e) => { + const selection = window.getSelection() + if (!selection.rangeCount) return + + const range = selection.getRangeAt(0) + const container = range.commonAncestorContainer + const textNode = container.nodeType === Node.TEXT_NODE ? container : container.firstChild + + if (textNode && textNode.textContent) { + const text = textNode.textContent + const parentElement = textNode.parentElement || textNode.parentNode + + if (isRTL(text)) { + parentElement.style.direction = 'rtl' + parentElement.style.textAlign = 'right' + parentElement.style.unicodeBidi = 'embed' + } else if (/^[a-zA-Z0-9\s.,!?;:'"()\-_+=<>{}[\]|\\/@#$%^&*`~]+$/.test(text)) { + parentElement.style.direction = 'ltr' + parentElement.style.textAlign = 'left' + parentElement.style.unicodeBidi = 'embed' + } + } + }) + + // Set initial direction based on content + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === Node.TEXT_NODE && node.textContent) { + const text = node.textContent + const parentElement = node.parentElement || node.parentNode + + if (isRTL(text)) { + parentElement.style.direction = 'rtl' + parentElement.style.textAlign = 'right' + parentElement.style.unicodeBidi = 'embed' + } + } + }) + } + }) + }) + + observer.observe(editorElement, { + childList: true, + subtree: true, + characterData: true + }) + }, 1000) + }, }, } @@ -728,5 +797,34 @@ https://github.com/ckeditor/ckeditor5/issues/1142 background: var(--color-primary-element-light) !important; color: var(--color-main-text) !important; } +/* RTL Support for mixed content */ +.ck-editor__editable { + unicode-bidi: plaintext !important; + text-align: start !important; +} + +/* Better RTL handling for mixed Persian/English text */ +.ck-editor__editable p, +.ck-editor__editable div, +.ck-editor__editable span { + unicode-bidi: isolate !important; + text-align: start !important; +} + +/* Ensure proper text flow for mixed content */ +.ck-editor__editable * { + unicode-bidi: isolate !important; +} + +/* Force proper direction for text nodes */ +.ck-editor__editable [style*="direction: rtl"] { + direction: rtl !important; + text-align: right !important; +} + +.ck-editor__editable [style*="direction: ltr"] { + direction: ltr !important; + text-align: left !important; +} From 278d501d58ff58604600be2cf4bce0a4b33722ef Mon Sep 17 00:00:00 2001 From: "amirmohammd.ahmadi" Date: Mon, 20 Oct 2025 12:10:30 +0330 Subject: [PATCH 2/2] fix comments --- src/components/TextEditor.vue | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/components/TextEditor.vue b/src/components/TextEditor.vue index 542abe5c4d..4438e54201 100644 --- a/src/components/TextEditor.vue +++ b/src/components/TextEditor.vue @@ -203,9 +203,6 @@ export default { this.loadEditorTranslations(getLanguage()) }, mounted() { - this.$nextTick(() => { - this.setupRTLSupport() - }) }, methods: { getLink(text) { @@ -420,6 +417,7 @@ export default { this.bus.on('append-to-body-at-cursor', this.appendToBodyAtCursor) this.bus.on('insert-text-block', this.insertTextBlock) this.$emit('ready', editor) + this.setupRTLSupport(editor.ui.view.editable.element) }, onEditorInput(text) { if (text !== this.value) { @@ -451,20 +449,17 @@ export default { } this.editorInstance.execute('insertItem', { content, isHtml: this.html }, '!') }, - setupRTLSupport() { + setupRTLSupport(editorElement) { // Function to detect RTL characters const isRTL = (text) => { const rtlChars = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/ return rtlChars.test(text) } - // Wait for editor to be ready - setTimeout(() => { - const editorElement = this.$el?.querySelector('.ck-editor__editable') - if (!editorElement) return + if (!editorElement) return - // Add input event listener for auto RTL/LTR detection - editorElement.addEventListener('input', (e) => { + // Add input event listener for auto RTL/LTR detection + editorElement.addEventListener('input', () => { const selection = window.getSelection() if (!selection.rangeCount) return @@ -488,8 +483,8 @@ export default { } }) - // Set initial direction based on content - const observer = new MutationObserver((mutations) => { + // Set initial direction based on content + const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { @@ -506,14 +501,13 @@ export default { }) } }) - }) + }) - observer.observe(editorElement, { - childList: true, - subtree: true, - characterData: true - }) - }, 1000) + observer.observe(editorElement, { + childList: true, + subtree: true, + characterData: true + }) }, }, }