Vorstellung von runio/mde: Ein RTL-fähiges Markdown-Editor-Bundle für Symfony

Mar 15, 2026 10 Min. Lesezeit 92 Aufrufe
Vorstellung von runio/mde: Ein RTL-fähiges Markdown-Editor-Bundle für Symfony

Jeder Entwickler baut irgendwann etwas, weil nichts anderes genau passt. Für mich war das ein Symfony-Bundle für einen Rich-Markdown-Editor, der korrekt mit arabischen (RTL) Inhalten funktioniert. Das Ergebnis ist runio/mde, verfügbar auf Packagist.

Dieser Beitrag erklärt das Warum und das Wie. Für den breiteren Kontext — warum ich das überhaupt brauchte — siehe Ein mehrsprachiges Portfolio mit Symfony entwickeln.

Was runio/mde leistet

Im Kern verpackt runio/mde den Toast UI Editor in einem ordentlichen Symfony-Bundle. Der Toast UI Editor ist eine ausgereifte, aktiv gewartete JavaScript-Bibliothek, die Folgendes unterstützt:

  • WYSIWYG- und Raw-Markdown-Bearbeitungsmodi, zur Laufzeit umschaltbar
  • Split-View-Vorschau (Markdown-Quelle links, gerendertes HTML rechts)
  • Tabellen, Aufgabenlisten, Code-Blöcke, Bilder, Links
  • Eine saubere, zugängliche Toolbar

Das Bundle fügt hinzu, was der rohen Bibliothek in einem Symfony-Kontext fehlt:

  • Symfony-Formulartyp (MarkdownEditorType) mit allen konfigurierbaren Optionen
  • Twig-Filter |markdown zum Rendern gespeicherter Markdown auf Anzeige-Seiten
  • RTL/LTR-CSS-Schicht für korrekte Darstellung von Arabisch, Hebräisch, Persisch, Urdu
  • Alle Assets lokal bereitgestellt — kein uicdn.toast.com, kein fonts.googleapis.com
  • Symfony Asset Mapper-Kompatibilität
  • Symfony UX Turbo-Unterstützung
  • Mobile-responsive Layout über CSS-Media-Queries
  • Korrekte Service-Registrierung (keine manuellen services.yaml-Workarounds)

Installation

composer require runio/mde
php bin/console assets:install --symlink

Falls Ihr Projekt Asset Mapper verwendet (Symfony 6.3+), fügen Sie den Asset-Pfad des Bundles zu config/packages/asset_mapper.yaml hinzu:

framework:
    asset_mapper:
        paths:
            assets/: ''
            vendor/runio/mde/public/: bundles/runiomarkdowneditor/

Dann kompilieren:

php bin/console asset-map:compile

Registrieren Sie das Formular-Theme in config/packages/twig.yaml:

twig:
    form_themes:
        - '@RunioMarkdownEditor/form/markdown_editor_widget.html.twig'

Den Formulartyp verwenden

use Runio\MarkdownEditorBundle\Form\Type\MarkdownEditorType;

$builder->add('inhalt', MarkdownEditorType::class, [
    'editor_mode'      => 'wysiwyg',   // wysiwyg | markdown | both
    'rtl_enabled'      => false,
    'language'         => 'de',
    'toolbar'          => 'full',      // full | basic | minimal
    'preview_style'    => 'vertical',  // vertical | tab
    'theme'            => 'light',     // light | dark
    'editor_height'    => '500px',
    'show_mode_switch' => true,
]);

// Für arabische Inhalte:
$builder->add('inhaltAr', MarkdownEditorType::class, [
    'editor_mode'  => 'wysiwyg',
    'rtl_enabled'  => true,
    'language'     => 'ar',
]);

Gespeichertes Markdown rendern

In Ihren Twig-Templates verwenden Sie den |markdown-Filter:

<div class="prose">
    {{ beitrag.inhalt | markdown }}
</div>

Der Filter verarbeitet den Inhalt durch CommonMark und eine Bereinigungsschicht, die unsichere Tags und Attribute entfernt. Die Bereinigung ist konfigurierbar:

# config/packages/runio_markdown_editor.yaml
runio_markdown_editor:
    sanitization:
        enabled: true
        allowed_tags: [p, br, strong, em, blockquote, ul, ol, li, a, img, h1, h2, h3, h4, h5, h6, pre, code, table, thead, tbody, tr, th, td, hr]
        allowed_attributes: [href, src, alt, title, class, id, dir, lang]

Architektur

Die PHP-Schicht

Das Bundle stellt drei Services bereit:

MarkdownParser — konvertiert Markdown in HTML mit league/commonmark. Konfigurierbar über den markdown-Abschnitt der Bundle-Konfiguration:

runio_markdown_editor:
    markdown:
        html_input: escape        # strip | allow | escape
        allow_unsafe_links: false
        enable_table_extension: true
        enable_strikethrough_extension: true
        enable_tasklist_extension: true
        enable_autolink_extension: true

RTLTextDetector — erkennt, ob ein String RTL-Zeichen enthält (Arabisch, Hebräisch, Persisch, Urdu Unicode-Bereiche). Wird für die automatische Erkennung der Textrichtung verwendet.

SanitizationService — entfernt nicht erlaubte HTML-Tags und Attribute aus der geparsten Ausgabe.

MarkdownRuntime — die Twig-Runtime, die die |markdown- und |markdown_inline-Filter registriert. Dieser ist als ordentlicher Service in der services.yaml des Bundles registriert — keine manuelle Registrierung in der Anwendung erforderlich.

Die JavaScript-Schicht

Das JavaScript (public/js/markdown-editor.js) ist ein dünnes Wrapper um toastui.Editor. Es:

  1. Prüft, ob toastui verfügbar ist, bevor es initialisiert — fällt elegant auf ein einfaches Textarea zurück, wenn das JS nicht geladen werden kann
  2. Wählt responsive Höhe und Vorschau-Stil basierend auf dem Viewport beim ersten Laden
  3. Synchronisiert den versteckten <textarea>-Wert bei jeder Editor-Änderung und beim Formular-Submit
  4. Handhabt Bild-Uploads über fetch, wenn eine Upload-URL konfiguriert ist
  5. Unterstützt Turbo-Navigation über turbo:load- und turbo:before-cache-Lifecycle-Events

Der Editor initialisiert sich automatisch auf jedem .markdown-editor-rtl-wrapper-Element im DOM und re-initialisiert sich bei dynamisch hinzugefügtem Inhalt über MutationObserver.

Die CSS-Schicht

Das RTL-CSS (public/css/markdown-editor-rtl.css) wendet korrekte Stile an, wenn der Wrapper dir="rtl" hat:

  • Textausrichtung und -richtung für alle Inhaltsbereiche
  • Zitatrahmen auf die rechte Seite gespiegelt
  • Listen-Einrückung auf die rechte Seite gespiegelt
  • Code-Blöcke immer LTR, unabhängig von der umgebenden Richtung
  • Splitter (Split-View-Trennlinie) korrekt positioniert — das war die wichtigste Bug-Behebung

Der Mobile-Responsive-Abschnitt behandelt:

  • Toolbar-Icon-Größenanpassung bei <768px und <480px
  • Dropdown-Toolbar auf Editor-Breite beschränkt (verhindert Überlauf)
  • Split-View-Panels stapeln sich vertikal auf Mobilgeräten
  • Popup-Positionierung für kleine Viewports korrigiert

Die Bugs, die ich behoben habe

Die vorherige Open-Source-Symfony-Integration für Toast UI hatte zwei bedeutende Probleme:

1. Unsichtbarer RTL-Splitter

Im Split-View-Modus mit aktiviertem RTL war die Trennlinie zwischen dem Markdown-Eingabefeld und der Vorschau unsichtbar. Ursache: Eine CSS-Regel, die left: auto; right: 0 auf .toastui-editor-md-splitter setzte. Das verschob das Element auf left: 859px in einem 860px-Container — ein Pixel außerhalb des Bildschirms. Das Entfernen des Overrides lässt Toast UI es korrekt bei 50% positionieren.

2. Nicht registrierte Twig-Runtime

Die services.yaml im vorherigen Bundle hatte die MarkdownRuntime-Service-Registrierung auskommentiert. Der |markdown-Filter schien zu existieren (die Extension war registriert), aber beim Aufruf wurde ein Laufzeitfehler ausgelöst. Die Lösung ist einfach sicherzustellen, dass der Service korrekt registriert ist — eine Zeile in services.yaml.

Mehrsprachige Konfiguration

Die Standardkonfiguration zielt auf arabische Inhalte ab, aber jede RTL-Sprache funktioniert:

runio_markdown_editor:
    default_config:
        rtl_enabled: true
        language: ar
    rtl:
        languages: [ar, he, fa, ur]
        auto_detect: true
        arabic_font: 'Noto Naskh Arabic'

Pro-Feld-Overrides ermöglichen die Mischung von RTL- und LTR-Editoren im selben Formular — genau das, was ich im Blog-Beitrags-Formular von sym-port mache — ein Editor pro Sprache, jeweils entsprechend konfiguriert.

Ausblick

Das Bundle ist funktionsfähig und wird aktiv auf meinem Portfolio verwendet. Geplante Verbesserungen:

  • Bild-Upload-UI — ein ordentliches Upload-Dialog statt eines externen Endpunkts
  • Benutzerdefinierte Toolbar-Buttons — Anwendungen können zusätzliche Toolbar-Elemente registrieren
  • Atom-Feed-Helfer — eine Twig-Funktion zum Rendern von Markdown-Inhalten in Feed-Kontexten

Der Quellcode ist auf GitHub und das Paket ist auf Packagist unter runio/mde verfügbar. Issues und Pull Requests sind willkommen.

Diesen Beitrag teilen

AM

Autor Amer Malik Mohammed

Full-Stack Entwickler mit 2+ Jahren Erfahrung in objektorientierter PHP-Entwicklung (Symfony), JavaScript und MySQL. Spezialisiert auf E-Commerce-Lösungen (Shopware 5/6), REST-API-Entwicklung und Prozessautomatisierung in agilen Teams.

Autor kontaktieren