justify, which the site could always render

`rt-justify` has been in the sanitiser's allowlist all along, and styles.css gives it `text-align:
justify` with `hyphens: auto` — hyphenation being what stops justified text opening rivers of white
space between words. Only the toolbar was missing it.

The four are now a list beside the toolbar rather than a ternary chain, and the comment says what it has
to match: ALIGN_CLASSES in the site's rich-text.ts. A fifth offered here would be stripped on save,
silently, leaving somebody pressing a button that appears to do nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 05:54:44 +01:00
co-authored by Claude Opus 5
parent 43a447801d
commit 5c2d3baac0
+28 -13
View File
@@ -5,6 +5,7 @@ import Link from '@tiptap/extension-link';
import TextAlign from '@tiptap/extension-text-align'; import TextAlign from '@tiptap/extension-text-align';
import { import {
AlignCenter, AlignCenter,
AlignJustify,
AlignLeft, AlignLeft,
AlignRight, AlignRight,
Bold, Bold,
@@ -30,7 +31,7 @@ import {
* *
* ── The toolbar is what the site can actually render ── * ── The toolbar is what the site can actually render ──
* *
* Bold, italic, strike, a heading, two lists, three alignments, a link. Not because tiptap cannot do * Bold, italic, strike, a heading, two lists, four alignments, a link. Not because tiptap cannot do
* more, but because anything offered here that the site's stylesheet has no rule for produces copy that * more, but because anything offered here that the site's stylesheet has no rule for produces copy that
* looks right while editing and wrong when published — the worst kind of editor, since the person only * looks right while editing and wrong when published — the worst kind of editor, since the person only
* finds out after saying it is done. * finds out after saying it is done.
@@ -75,6 +76,19 @@ const Tool = ({
</button> </button>
); );
/**
* The alignments the toolbar offers.
*
* The same four the site's sanitiser accepts — ALIGN_CLASSES in src/rich-text.ts. A fifth offered here
* would be stripped on save, silently, leaving somebody pressing a button that appears to do nothing.
*/
const ALIGNMENTS = [
{ value: 'left' as const, Icon: AlignLeft },
{ value: 'center' as const, Icon: AlignCenter },
{ value: 'right' as const, Icon: AlignRight },
{ value: 'justify' as const, Icon: AlignJustify },
];
const Toolbar = ({ editor }: { editor: Editor }) => ( const Toolbar = ({ editor }: { editor: Editor }) => (
<div className="flex flex-wrap items-center gap-0.5 border-b border-border px-1 py-1"> <div className="flex flex-wrap items-center gap-0.5 border-b border-border px-1 py-1">
<Tool label="Bold" onClick={() => editor.chain().focus().toggleBold().run()} active={editor.isActive('bold')}> <Tool label="Bold" onClick={() => editor.chain().focus().toggleBold().run()} active={editor.isActive('bold')}>
@@ -117,20 +131,21 @@ const Toolbar = ({ editor }: { editor: Editor }) => (
<span className="mx-1 h-4 w-px bg-border" /> <span className="mx-1 h-4 w-px bg-border" />
{(['left', 'center', 'right'] as const).map((align) => ( {/*
Four alignments, justify included.
The site has always been able to render it: `rt-justify` is in the sanitiser's allowlist and
styles.css gives it `text-align: justify` with `hyphens: auto` — hyphenation being what stops
justified text opening rivers of white space between words. Only the toolbar was missing.
*/}
{ALIGNMENTS.map(({ value, Icon }) => (
<Tool <Tool
key={align} key={value}
label={`Align ${align}`} label={`Align ${value}`}
onClick={() => editor.chain().focus().setTextAlign(align).run()} onClick={() => editor.chain().focus().setTextAlign(value).run()}
active={editor.isActive({ textAlign: align })} active={editor.isActive({ textAlign: value })}
> >
{align === 'left' ? ( <Icon className="size-3.5" />
<AlignLeft className="size-3.5" />
) : align === 'center' ? (
<AlignCenter className="size-3.5" />
) : (
<AlignRight className="size-3.5" />
)}
</Tool> </Tool>
))} ))}