noz-studio/CLAUDE.md
kryptomrx 3f9553cff4 feat: initial commit — noz-studio web editor
Full-featured web editor PWA for noz, built with Next.js and CodeMirror 6.

Editor:
- CM6-based Markdown editor with iA Writer aesthetic and Typora-style marker hiding
- Live preview with KaTeX math, highlight.js syntax highlighting, wikilink rendering
- Wikilink autocomplete via server note graph ([[  triggers suggestion list)
- Floating selection toolbar, format toolbar, status bar
- Split / editor-only / preview-only view modes with resizable panels
- Visual frontmatter panel (no raw YAML editing required)

Navigation:
- Folder tree with topics, sub-topics, drafts section
- Create / rename / move / delete notes and folders
- Diagram editor (TOML definition → canvas visualization)

Search:
- ⌘K command palette with scoped search (@topic, @web, @draft)

Preview:
- Broken wikilink detection with visual indicator and toast notification
- Fragment links: [[note#section]] navigates and scrolls to heading
- In-page links: [[#section]] scrolls without switching notes
- Heading anchors auto-generated from heading text

Auth:
- Token / Authentik SSO / both — driven by noosphere.toml
- sessionStorage only (no localStorage — prevents credential leaks on shared devices)

i18n: German + English
2026-05-18 16:05:11 +02:00

241 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# noz-studio — Projektübersicht für Claude
> **Kommunikation:** Deutsch. Alle Konversationen, Commit-Messages, Kommentare und UI-Labels auf Deutsch.
## Was ist noz-studio?
noz-studio ist der Web-Editor für [noz](../noz) — ein vollständiger Markdown-Editor als PWA, optimiert für Desktop und Mobile (Touch). Gedacht für Situationen wo noz-cli nicht verfügbar ist (Arbeit, unterwegs, fremdes Gerät).
**noz-studio ist ein Client.** Es schreibt keinen eigenen Content-Store — es spricht ausschließlich gegen die noz-API. Die einzige Verbindung zwischen noz und noz-studio sind API-Endpunkte.
```
noz (Server + API) ←──────────────────────────
↑ ↑ ↑
noz-cli noz (public) noz-studio
(Terminal) (Leser) (Web-Editor)
```
---
## Stack
- **Next.js** (App Router) — gleicher Stack wie noz
- **CodeMirror 6** — Editor-Engine (MIT-Lizenz) — NUR als Engine, alles außen rum ist custom
- **Tailwind CSS v4** — OKLCH-Farben, gleiche Palette wie noz aber eigene Design-Sprache
- **PWA** — `manifest.json` + Service Worker — installierbar auf iOS/Android/Desktop
---
## Design-Prinzipien
- **iA Writer-Feel** — fokussiert, clean, Typografie im Vordergrund
- **Affine-like** — immer offen, kein "speichern"-Stress
- **Kein localStorage für Credentials** — Token in `sessionStorage` (Tab zu → Token weg, kein Datenleak auf Arbeitsrechnern)
- **noosphere.toml als Single Source of Truth** — Studio liest Config vom Server, nie hardcoden
- **CM6 ist swappable** — EditorEngine-Interface abstrahiert die Implementierung
---
## Auth-System
Konfiguriert in `noosphere.toml` des noz-Servers:
```toml
[studio]
auth = "token" # "token" | "authentik" | "both"
allow_delete = true
allow_push = true
allow_rebuild = false
```
Studio macht beim Start einen **public Request ohne Token**:
```
GET /api/studio/config → { auth: "token" | "authentik" | "both", name: "...", lang: "..." }
```
Daraufhin zeigt der Login-Screen die passende UI (Token-Formular, Authentik-SSO-Button, oder beides).
**Token-Handling:**
- Gespeichert in `sessionStorage` — nie `localStorage`
- Tab schließen = Token weg = kein Datenleak auf fremden Geräten
- Gleicher Token wie `[cli]` in `noosphere.toml`
---
## Draft-System (3 Stufen)
```
[ Draft ] → [ In Review ] → [ Live ]
```
| Stufe | Speicherort | Sichtbar auf noz? | Wer sieht es? |
|-------|-------------|-------------------|---------------|
| **Draft** | `content/noosphere/_drafts/notiz.md` | Nein (Index-Builder überspringt `_`-Ordner) | Nur Studio |
| **In Review** | `content/noosphere/topic/notiz.md` + `draft: true` im Frontmatter | Nur via Authentik/Vault | Eingeloggte User |
| **Live** | `content/noosphere/topic/notiz.md` (kein draft-Flag) | Ja, öffentlich | Alle |
Der **Draft-Button** im Editor-StatusBar toggelt zwischen den Stufen. Studio kümmert sich um:
- Datei verschieben (`_drafts/` → topic-Ordner)
- Frontmatter `draft: true/false` setzen
- Index-Rebuild triggern (wenn `allow_rebuild = true`)
---
## EditorEngine-Interface (CM6 swappable)
```typescript
// src/components/editor/engine.ts
interface EditorEngine {
getValue(): string
setValue(content: string): void
onSave(callback: (value: string) => void): void
focus(): void
destroy(): void
}
```
```typescript
// src/components/editor/cm6-adapter.ts
class CM6Adapter implements EditorEngine { ... }
```
```tsx
// src/components/editor/editor-wrapper.tsx
// Kennt nur EditorEngine, nie direkt CM6
```
Wenn CM6 irgendwann getauscht wird → nur `cm6-adapter.ts` anfassen.
---
## Projektstruktur
```
noz-studio/
src/
app/
page.tsx # → redirect /editor oder /login
login/
page.tsx # Login-Screen (token | authentik | both — je nach config)
editor/
page.tsx # Haupt-Editor: Sidebar + Editor + Preview
components/
editor/
engine.ts # EditorEngine Interface
cm6-adapter.ts # CM6 implementiert EditorEngine
editor-wrapper.tsx # React Component — bindet Adapter ein
toolbar.tsx # Formatting-Toolbar (Bold, Italic, Link, Bild...)
status-bar.tsx # Wortanzahl, Status-Badge, Draft-Button, Sprache
sidebar/
folder-tree.tsx # Topic/Folder Navigation (wie noz Folder-View)
note-list.tsx # Notes im aktuellen Ordner
draft-badge.tsx # Visueller Draft-Indikator
preview/
preview.tsx # Live-Preview (KaTeX, Wikilinks, Bilder, Tabellen)
auth/
token-form.tsx # Token-Login Formular
authentik-button.tsx # Authentik SSO Button
lib/
api.ts # Alle API-Calls zu noz (einzige Stelle die fetch aufruft)
auth.ts # Token lesen/schreiben (sessionStorage)
config.ts # Studio-Config vom Server cachen
hooks/
use-notes.ts # Notes laden, CRUD
use-editor.ts # Editor-State, Auto-Save
use-config.ts # noosphere.toml Config
public/
manifest.json # PWA Manifest
icons/ # App-Icons (512x512, 192x192, maskable)
next.config.ts
```
---
## Features
### Editor — gebaut ✓
- [x] Markdown-Editor (CM6, iA Writer-Feel)
- [x] Live-Preview (rechts, togglebar — raw / split / rendered)
- [x] KaTeX Math (`$...$` und `$$...$$`)
- [x] Syntax-Highlighting für Code-Blöcke (highlight.js)
- [x] Formatierungs-Toolbar (Bold/Italic/Strike/H1H3/Link/Code/Bild/Mathe/Wikilink/…)
- [x] Floating Selection-Toolbar (erscheint bei Textauswahl)
- [x] Einstellungs-Modal (Affine-Stil, vollseitig)
- [x] ⌘K Command Palette (Scoped Search: `@topic`, `@web`, `@draft`)
- [x] Resizable Sidebar (160360px) + Resizable Preview (200640px)
- [x] Glassmorphism Navbar (vereint Format-Toolbar + View-Toggle + Suche)
### Editor — geplant
- [ ] Typora-Style: `**fett**` sofort fett (CM6 Decorations)
- [ ] `/` Slash-Command-Menü (wie Notion/AFFiNE)
- [ ] Wikilink-Autocomplete (`[[` → Vorschläge)
- [ ] Bild-Upload (Drag & Drop → Upload → `![](url)`)
### Navigation — gebaut ✓
- [x] Folder-Tree (Topics → Notes, Unterordner-Unterstützung via `subTopics`)
- [x] Draft-Bereich (eigener `_drafts`-Bereich)
- [x] Note umbenennen (inline, im Tree)
- [x] Ordner erstellen (root + Unterordner)
- [x] Ordner umbenennen (inline)
- [x] Note verschieben (Kontextmenü → Ordner-Auswahl)
### Navigation — geplant
- [ ] Meilisearch-Integration (`/api/search`)
- [ ] Note löschen (mit Bestätigungs-Dialog)
### Verwaltung — geplant
- [ ] Neue Notiz (Frontmatter-Wizard)
- [ ] 3-Stufen Draft-Button (Draft → In Review → Live)
- [ ] Frontmatter visuell editieren
### PWA / Mobile — geplant
- [ ] Installierbar (manifest.json)
- [ ] Touch-optimiert (Bottom-Navigation auf Mobile)
---
## Verbindung zu noz
noz-studio braucht neue API-Routen in noz (noch nicht gebaut — werden nach dem Studio-Design spezifiziert):
```
GET /api/studio/config # public — Auth-Methode + Site-Config
GET /api/cli/notes # alle Notes (meta)
GET /api/cli/notes/:slug # Raw Markdown
POST /api/cli/notes # neue Note — { slug, title, topic, status, lang, content }
PUT /api/cli/notes/:slug # Note aktualisieren
DELETE /api/cli/notes/:slug # Note löschen
POST /api/cli/notes/:slug/rename # umbenennen — { newTitle } — Server rewritet Wikilinks
POST /api/cli/notes/:slug/move # Note verschieben — { newTopic } — Server rewritet Wikilinks
GET /api/cli/templates # Templates
POST /api/cli/rebuild # build:index triggern
POST /api/cli/folders # Neuer Ordner — { slug, parent? } — legt _index.md an
PUT /api/cli/folders/:slug # Ordner umbenennen — { newSlug } — Server rewritet ALLE internen Links
DELETE /api/cli/folders/:slug # Ordner löschen (nur wenn leer — sonst 409)
```
Alle außer `/api/studio/config` erfordern `Authorization: Bearer <token>`.
### Link-Rewriting beim Verschieben/Umbenennen (Serverseite)
Wenn eine Note verschoben wird (`/move`) oder ein Ordner umbenannt wird (`PUT /folders`):
1. Datei/Verzeichnis physisch verschieben
2. In **allen** `.md`-Dateien des Content-Stores Wikilinks ersetzen:
- Note-Move: `[[alter-topic/note-slug]]``[[neuer-topic/note-slug]]`
- Folder-Rename: `[[alter-ordner/...]]``[[neuer-ordner/...]]`
3. Index-Rebuild triggern (wenn `allow_rebuild = true`)
Kurzform: `[[slug]]` ohne Topic-Prefix bleibt unverändert — nur vollqualifizierte Slugs werden rewritten.
---
## Design-System
Gleiche OKLCH-Palette wie noz (`globals.css`), aber eigene Layout-Sprache:
- **Dark by default** (Editor-Feeling)
- **Newsreader** für Headings (italic), **Inter** für UI, **JetBrains Mono** im Editor
- Kein Prose-Layout — Editor ist das Zentrum
**Layouts:**
- Desktop: `[Sidebar 260px] [Editor flex] [Preview flex]`
- Mobile: Bottom-Nav + Fullscreen-Editor + Swipe zwischen Editor/Preview