Ka-Note/docs/feature-mdeditor.md

5.2 KiB
Raw Blame History

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.com triggern das Menü nicht (: vor / bricht den Scan ab)

Architektur

Exakt das gleiche ProseMirror Plugin-Pattern wie tiptapMention.ts und tiptapWikiLink.ts:

  • Extension.create() mit addProseMirrorPlugins()
  • Rückwärts-Scan im update(view)-Hook
  • DOM-Dropdown in .ka-editor-wrapper (renutzt .mention-dropdown/.mention-item/.mention-item-active aus mention.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.prompt als 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