chore: add README, remove personal references, migrate to GitHub
This commit is contained in:
parent
4cd4ee208c
commit
c1cb2e5db6
4 changed files with 67 additions and 244 deletions
241
CLAUDE.md
241
CLAUDE.md
|
|
@ -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 → ``)
|
|
||||||
|
|
||||||
### 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
|
|
||||||
64
README.md
Normal file
64
README.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
"name": "noz-studio",
|
"name": "noz-studio",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Web editor PWA for noz — CodeMirror 6, live preview, wikilinks, diagram canvas and full offline support.",
|
"description": "Web editor PWA for noz — CodeMirror 6, live preview, wikilinks, diagram canvas and full offline support.",
|
||||||
"author": "Bruno Deanoz <bruno.deanoz@protonmail.com>",
|
"author": "Bruno Deanoz",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"homepage": "https://noz.dev/studio",
|
"homepage": "https://noz.dev/studio",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.kryptomrx.de/noz/noz-studio.git"
|
"url": "https://github.com/noz-lab/noz-studio.git"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export default function LoginPage() {
|
||||||
const devServer = process.env.NEXT_PUBLIC_DEV_SERVER ?? ""
|
const devServer = process.env.NEXT_PUBLIC_DEV_SERVER ?? ""
|
||||||
const devToken = process.env.NEXT_PUBLIC_DEV_TOKEN ?? ""
|
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 [token, setToken] = useState(devToken)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue