diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 9673bcc..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,241 +0,0 @@ -# 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1889eb2 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# noz-studio + +A web editor PWA for [noz](https://github.com/noz-lab/noz) — write and manage notes from any device, even without the CLI. + +## Features + +- **CodeMirror 6** editor with Markdown syntax highlighting +- **Live preview** — rendered note side by side with the editor +- **Wikilinks** — `[[Note Title]]` autocomplete and resolution +- **Diagram canvas** — visual editor for `_diagram.toml` folder diagrams +- **Full-text search** — search your garden from the sidebar +- **PWA** — installable, works offline for reading cached notes +- **Dark / light mode** +- **Multilingual** (de / en) + +## Setup + +noz-studio connects to a running noz instance via the CLI API. You need a token configured in your noz server's `.env.local`: + +``` +NOZ_CLI_TOKEN=your-secret-token +``` + +And in `noosphere.toml`: + +```toml +[studio] +auth = "token" # or "authentik" or "both" +allow_push = true +``` + +Then set the allowed origin for CORS in `.env.local` on your noz server: + +``` +NOZ_STUDIO_ORIGIN=https://studio.your-domain.com +``` + +## Running locally + +```bash +pnpm install +pnpm dev # starts on port 3001 +``` + +Open `http://localhost:3001`, enter your noz server URL and token. + +For local development, you can prefill credentials via environment variables: + +``` +NEXT_PUBLIC_DEV_SERVER=http://localhost:3000 +NEXT_PUBLIC_DEV_TOKEN=your-token +``` + +## Deployment + +noz-studio is a standard Next.js app — deploy anywhere Next.js runs (Vercel, Docker, self-hosted). + +The `infra/` folder in [noz](https://github.com/noz-lab/noz) contains a Traefik config for self-hosting behind a reverse proxy. + +## License + +[AGPL-3.0-only](LICENSE) + +For commercial licensing (proprietary use without copyleft), open an issue. diff --git a/package.json b/package.json index 887b8f1..f486764 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "name": "noz-studio", "version": "0.1.0", "description": "Web editor PWA for noz — CodeMirror 6, live preview, wikilinks, diagram canvas and full offline support.", - "author": "Bruno Deanoz ", + "author": "Bruno Deanoz", "license": "AGPL-3.0-only", "homepage": "https://noz.dev/studio", "repository": { "type": "git", - "url": "https://git.kryptomrx.de/noz/noz-studio.git" + "url": "https://github.com/noz-lab/noz-studio.git" }, "private": true, "scripts": { diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index e5629be..f288c9d 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -10,7 +10,7 @@ export default function LoginPage() { const devServer = process.env.NEXT_PUBLIC_DEV_SERVER ?? "" const devToken = process.env.NEXT_PUBLIC_DEV_TOKEN ?? "" - const [url, setUrl] = useState(devServer || "https://noz.kryptomnrx.de") + const [url, setUrl] = useState(devServer || "") const [token, setToken] = useState(devToken) const [loading, setLoading] = useState(false) const [error, setError] = useState(null)