# 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/H1–H3/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 (160–360px) + Resizable Preview (200–640px) - [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 `. ### 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