Skip to content

feat(texteditor): add text alignment toggling buttons#5677

Open
habibayman wants to merge 11 commits intolearningequality:unstablefrom
habibayman:feat/RTE-text-align
Open

feat(texteditor): add text alignment toggling buttons#5677
habibayman wants to merge 11 commits intolearningequality:unstablefrom
habibayman:feat/RTE-text-align

Conversation

@habibayman
Copy link
Member

@habibayman habibayman commented Jan 30, 2026

Summary

This PR includes the addition of a toggle alignment (Left/Right) button.
From the motives of making the RTL editor experience better and give users the ability to seamlessly mix RTL & LTR text in the same editing block.

It includes support for all nodes & Marks we have:

  • paragraphs
  • headers
  • links
  • formulas
  • inline formulas & links
  • images
  • small text (was a bit problematic since it's a custom node)

For this to work with our backward compatible dual markdown conversion pipeline; I have altered the markdown serializer to wrap non-default direction nodes with HTML tags containing the align attribute set.

And of course, updated the unit tests accordingly.


References


Reviewer guidance

  • Try different combinations of nodes & marks and see if their alignment gets aligned correctly

[!HINT]
You can use the checklist I wrote in the description section

  • Check that the alignment button's icon reflects direction correctly
  • Does everything persists on save?

Note

Check the native behavior of the extension from the official TipTap documentation to see where its limitations -if any- exist.
https://tiptap.dev/docs/editor/extensions/functionality/textalign

@learning-equality-bot
Copy link

👋 Thanks for contributing!

We will assign a reviewer within the next two weeks. In the meantime, please ensure that:

  • You ran pre-commit locally
  • All issue requirements are satisfied
  • The contribution is aligned with our Contributing guidelines. Pay extra attention to Using generative AI. Pull requests that don't follow the guidelines will be closed.

We'll be in touch! 😊

@nucleogenesis nucleogenesis self-requested a review February 6, 2026 20:59
@nucleogenesis nucleogenesis self-assigned this Feb 6, 2026
@nucleogenesis nucleogenesis removed their request for review February 6, 2026 20:59
@habibayman habibayman force-pushed the feat/RTE-text-align branch from 00187e5 to f2aa6e6 Compare March 4, 2026 23:05
@habibayman habibayman marked this pull request as ready for review March 5, 2026 02:16
Copy link

@rtibblesbot rtibblesbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feature addition for RTL/LTR text alignment toggling. The serializer refactoring from mutation-based to return-based is a nice improvement. One bug in the overflow dropdown disabled logic.

CI passing.

Blocking:

  • Inverted :disabled logic on overflow align button (see inline)

Suggestions:

  • SVG icon dimension mismatch between align-left and align-right
  • Peer dependency version mismatch for @tiptap/extension-text-align

Nitpick:

  • Double space in image markdown output when no dimensions present

@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Reviewed the pull request diff checking for:

  • Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
  • Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
  • Architecture: duplicated concerns, minimal interfaces, composition over inheritance
  • Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
  • Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
  • Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
  • Checked CI status and linked issue acceptance criteria
  • For UI changes: inspected screenshots for layout, visual completeness, and consistency

class="dropdown-item"
:class="{ active: alignAction.isActive }"
role="menuitem"
:disabled="alignAction.isAvailable"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: This is inverted. alignAction.isAvailable is true when the action IS available (not in a code block), so :disabled="alignAction.isAvailable" disables the button when it should be enabled, and vice versa.

Compare with line 262 which correctly uses :disabled="!canClearFormat" — note the negation.

Fix: :disabled="!alignAction.isAvailable"

@@ -0,0 +1,3 @@
<svg width="8" height="8" viewBox="0 0 15 17" fill="none" xmlns="http://www.w3.org/2000/svg">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This SVG has width="8" height="8" while icon-alignLeft.svg has width="10" height="10". Both share the same viewBox="0 0 15 17" so they represent the same logical space but will render at different sizes. Should these match?

return `![${alt || ''}](${IMAGE_PLACEHOLDER}/${fileName} =${width}x${height}${alignSuffix})`;
}
return `![${alt || ''}](${IMAGE_PLACEHOLDER}/${fileName})`;
return `![${alt || ''}](${IMAGE_PLACEHOLDER}/${fileName}${alignSuffix ? ` ${alignSuffix}` : ''})`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: When there are no dimensions but alignment exists, alignSuffix already starts with a space ( align=right), then ` ${alignSuffix}` adds another space, producing a double space like ![alt](file align=right). The regex still parses it correctly (\s+), but you could simplify to just ${alignSuffix} (without the extra template literal wrapping) since the leading space is already in the variable.

"@tiptap/extension-link": "^3.13.0",
"@tiptap/extension-subscript": "^3.13.0",
"@tiptap/extension-superscript": "^3.13.0",
"@tiptap/extension-text-align": "^3.18.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: @tiptap/extension-text-align@3.18.0 declares a peer dependency of @tiptap/core: ^3.18.0, but the project has @tiptap/core@3.13.0. CI passes so it likely works today, but this gap (3.13 vs 3.18) could cause subtle issues if the extension uses newer core APIs. Consider either pinning to a text-align version compatible with 3.13 (e.g., @tiptap/extension-text-align@^3.13.0) or upgrading the other tiptap packages.

}
}
break;
return parts.join('\n\n');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Nice refactoring of the serializer from mutation-based (result +=) to a return-based approach. This makes each case self-contained and much easier to reason about, especially now that alignment wrapping needs to inspect the serialized children before deciding the output format.

type: 'paragraph',
content: [{ type: 'text', text: 'n', marks: [{ type: 'subscript' }] }],
},
];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Thorough test coverage for text alignment serialization — covers right/left/center/no-attr cases, headings, inline marks inside aligned paragraphs, and mixed alignment documents. Good edge case thinking.

default: 'small-text',
},
textAlign: {
default: 'left',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question/suggestion: I wonder if this should be set based on the user's current language - when I load the page up in Arabic, the direction defaults to the left but I expected it to go to the right.

When I type Arabic into the box, it automatically aligns the text to the right and then when I insert things into it they all stay right-aligned, but the icon indicator is showing to the left:

Image

But I could do all as described (ie, right/left aligning one paragraph between others that stay put).

(cc @radinamatic @marcellamaki)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I interpret this button (outlined here) as "Right to Left"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks that way to me too... Can you reverse the alignment manually to LTR for the paragraphs that are not in Arabic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Rich Text Editor]: add a toggle text alignment (R/L) button

5 participants