5.2 KiB
Feature: Markdown-Editor Verbesserungen
Motivation
Der Wiki-Editor in Ka-Note basiert auf Tiptap v3 (ProseMirror). Für Nutzer die primär mit Tastatur und Maus arbeiten fehlten:
- Ein schneller Weg in den Edit-Modus (ohne Button-Klick)
- Kontextuelle Formatierungshilfe beim Schreiben (Slash-Menü)
- Schnell-Formatierung bei Textauswahl (Bubble Menu)
Referenz-UI: UpNote (Slash-Menü, mobiler Toolbar-Bereich).
Umgesetzte Features
1. Doppelklick → Edit-Modus
Datei: ka-note/client/src/routes/wiki/[id]/+page.svelte
Im Read-Modus aktiviert ein Doppelklick auf den Inhaltsbereich oder den Seitentitel den Editor.
- Titel (
<span>, Zeile ~118):ondblclick={() => editing = true} - Content-Container (
<div>, Zeile ~202):ondblclick={() => editing = true}+cursor-text - Einfacher Klick auf
[[Wikilinks]]→ Navigation (unverändert) - Doppelklick auf Linktext → öffnet Editor (kein Konflikt, da Browser-Doppelklick = Textauswahl, nicht Click-Event auf Anchor)
2. Slash-Command-Menü (/)
Datei: ka-note/client/src/lib/editor/tiptapSlashCommand.ts
Tippt man / am Zeilenanfang oder nach einem Leerzeichen, erscheint ein Kontext-Menü mit Block-Befehlen.
Verfügbare Befehle
| Icon | Label | Tiptap-Command |
|---|---|---|
H1 |
Heading 1 | toggleHeading({ level: 1 }) |
H2 |
Heading 2 | toggleHeading({ level: 2 }) |
H3 |
Heading 3 | toggleHeading({ level: 3 }) |
•– |
Bullet List | toggleBulletList() |
1. |
Ordered List | toggleOrderedList() |
`x` |
Code (inline) | toggleCode() |
``` |
Code Block | toggleCodeBlock() |
❝ |
Blockquote | toggleBlockquote() |
⊞ |
Table (3×3) | insertTable({ rows: 3, cols: 3, withHeaderRow: true }) |
— |
Divider | setHorizontalRule() |
Verhalten
- Trigger:
/nach Whitespace oder am Zeilenanfang - Typing filtert die Liste (Label + Keywords, deutsch + englisch)
- Arrow Up/Down → navigieren, Enter → ausführen, Escape → schließen
- Mausklick auf Eintrag → ausführen
- Vor Ausführung:
/+ getippter Query-Text werden gelöscht (deleteRange) - URLs wie
https://example.comtriggern das Menü nicht (:vor/bricht den Scan ab)
Architektur
Exakt das gleiche ProseMirror Plugin-Pattern wie tiptapMention.ts und tiptapWikiLink.ts:
Extension.create()mitaddProseMirrorPlugins()- Rückwärts-Scan im
update(view)-Hook - DOM-Dropdown in
.ka-editor-wrapper(renutzt.mention-dropdown/.mention-item/.mention-item-activeausmention.css) pointerdown+preventDefault()für Klick-Handling ohne Fokusverlust
3. Bubble Menu (Textauswahl-Toolbar)
Extension: @tiptap/extension-bubble-menu (Tiptap v3, floating-ui-basiert)
Bei Textauswahl erscheint eine Mini-Toolbar mit:
| Button | Funktion |
|---|---|
| B | Bold toggle |
| I | Italic toggle |
| H2 | Heading 2 toggle |
| ↗ | Link setzen/entfernen (window.prompt) |
- Buttons werden aktiv (blau) wenn der Cursor im entsprechenden Mark/Node sitzt
- Verschwindet automatisch wenn Auswahl aufgehoben wird
- Nicht aktiv bei Bild-Auswahl (
!e.isActive('image')) onpointerdown+preventDefault()verhindert Fokusverlust im Editor
Tiptap v3 BubbleMenu API
v3 nutzt @floating-ui/dom für Positionierung (kein tippy.js mehr).
Visibility wird über element.style.visibility gesteuert (nicht display).
Der element-Prop erwartet einen DOM-Node — in Svelte via bind:this={bubbleMenuEl}.
Neue Tiptap-Extensions (Table)
Für das Table-Slash-Command wurden hinzugefügt:
@tiptap/extension-table@tiptap/extension-table-row@tiptap/extension-table-cell@tiptap/extension-table-header
Konfiguration: Table.configure({ resizable: false })
Hinweis: tiptap-markdown v0.9 serialisiert Tabellen als GFM (| col | col |). Beim ersten produktiven Einsatz verifizieren dass Round-Trip (Edit → Save → reOpen) korrekt funktioniert.
Geänderte / neue Dateien
| Datei | Änderung |
|---|---|
client/src/routes/wiki/[id]/+page.svelte |
ondblclick auf Titel-Span + Content-Container |
client/src/lib/components/MarkdownEditor.svelte |
7 neue Imports, bubbleMenuEl-Variable, 6 neue Extensions, Bubble-Menu-Template |
client/src/lib/editor/tiptapSlashCommand.ts |
Neu — Slash-Command Extension |
client/src/lib/editor/editor.css |
Neu — Slash-Icon + Bubble-Menu-Styles |
Nicht umgesetzt (bewusste Entscheidung)
- Feste Toolbar: Passt nicht zum minimalistischen UI-Stil; Bubble Menu ist ausreichend für Desktop
- Mobile Keyboard-Toolbar: Separates Thema — erfordert
visualViewport-Handling für iOS; bei Bedarf als eigenes Feature - Link-inline-Dialog:
window.promptals MVP; langfristig ein kleines Svelte-Popover - Tabellen-Resize:
resizable: false— die Resize-Handles brauchen zusätzliches CSS-Styling
Bekannte Einschränkungen
/code(inline) — nach dem Löschen des Slash-Texts gibt es keine Auswahl; das Code-Mark wird als "stored mark" für den nächsten Tipp-Vorgang aktiviert (Tiptap-Standardverhalten)- Bubble Menu Link-Button nutzt
window.prompt— blockiert kurz den Browser