133 lines
5.2 KiB
Markdown
133 lines
5.2 KiB
Markdown
# 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
|