docs: status/handoff/readme/claude.md auf multilingual-stand
- README: neue plan-referenzen (3x multilingual), repo-struktur auf
content/posts/<lang>/<slug>/, svelte-i18n + translations im app-tree
- STATUS: event-count 27 (26 de + 1 en), kurzfassung um mehrsprachigkeit,
multilingual-abschnitt in „erledigt" (pipeline, spa, i18n + bugfixes)
- HANDOFF: option D entfernt (erledigt), neuer abschnitt „wie man eine
übersetzung anlegt", frontmatter-template um a:-platzhalter,
deploy-target-stolperfalle verschärft, vr-post-pfad aktualisiert
- Multilingual-spec: status von „noch nicht implementiert" auf „umgesetzt"
+ anmerkung zum post-switcher (📖 DE | EN statt text-hinweis)
- CLAUDE.md neu: knapper einstieg für agent-sessions mit commit-konvention,
deploy-falle, zsh-globbing, forgejo-mirror-timing
- workflow-skill generalüberholt: post-cutover-stand, multilingual,
publish-pipeline, activeLocale, 73 pipeline-tests
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9040e5ac02
commit
d12ed3c40e
|
|
@ -1,80 +1,89 @@
|
|||
---
|
||||
name: joerglohrerde-workflow
|
||||
description: Repo-spezifischer Skill für joerglohrerde. Nutze ihn bei jedem Session-Start, um den aktuellen Zustand zu erfassen, Konventionen zu verstehen und wiederkehrende Workflows (Deploy, Publish, Tests) effizient auszuführen.
|
||||
description: Repo-spezifischer Skill für joerglohrerde. Nutze ihn bei jedem Session-Start, um den aktuellen Zustand zu erfassen, Konventionen zu verstehen und wiederkehrende Workflows (Deploy, Publish, Tests, Multilingual) effizient auszuführen.
|
||||
---
|
||||
|
||||
# joerglohrerde — Session-Skill
|
||||
|
||||
Dieses Repo ist die persönliche Webseite von Jörg Lohrer — in Transition
|
||||
von Hugo zu einer dezentralen Nostr-basierten SvelteKit-SPA.
|
||||
Dieses Repo ist die persönliche Webseite von Jörg Lohrer: eine dezentrale
|
||||
Nostr-basierte SvelteKit-SPA, die NIP-23-Langform-Events live von Public-
|
||||
Relays rendert. Seit 2026-04-21 mehrsprachig (DE/EN) via `svelte-i18n` +
|
||||
NIP-33-`a`-Tags.
|
||||
|
||||
## Beim Session-Start IMMER zuerst
|
||||
|
||||
1. **Lies `docs/STATUS.md`** — aktueller Projektstand, live-URLs, Branches.
|
||||
2. **Lies `docs/HANDOFF.md`** — was wartet, nächste Schritte, Stolperfallen.
|
||||
3. Bei konkreten Aufgaben: entsprechende Spec unter `docs/superpowers/specs/`
|
||||
1. **Lies `CLAUDE.md`** — Agent-spezifische Konventionen (Commit-Stil,
|
||||
Deploy-Falle, Globbing-Hinweise).
|
||||
2. **Lies `docs/STATUS.md`** — aktueller Projektstand, Live-URLs.
|
||||
3. **Lies `docs/HANDOFF.md`** — nächste Schritte, Stolperfallen,
|
||||
Alltags-Workflow für neue Posts + Übersetzungen.
|
||||
4. Bei konkreten Aufgaben: zugehörige Spec unter `docs/superpowers/specs/`
|
||||
oder Plan unter `docs/superpowers/plans/`.
|
||||
4. Branch-Zustand prüfen: `git log --oneline -10 spa main hugo-archive`.
|
||||
5. Branch-Check: `git log --oneline -10 main`.
|
||||
|
||||
Dann erst Rückfragen oder Vorschläge formulieren.
|
||||
|
||||
## Drei Live-Webseiten
|
||||
## Live-URLs
|
||||
|
||||
| URL | Inhalt | Wann anfassen |
|
||||
|---|---|---|
|
||||
| `joerg-lohrer.de` | Hugo-Seite (alt) | nur im finalen Cutover |
|
||||
| `spa.joerg-lohrer.de` | Vanilla-Mini-Spike | als Referenz, aber nicht weiterentwickeln |
|
||||
| `svelte.joerg-lohrer.de` | SvelteKit-SPA | **Haupt-Arbeitsziel** |
|
||||
| URL | Rolle |
|
||||
|---|---|
|
||||
| `joerg-lohrer.de` | **Produktion**, SvelteKit-SPA (Cutover 2026-04-18, multilingual seit 2026-04-21) |
|
||||
| `staging.joerg-lohrer.de` | Pre-Prod-Build |
|
||||
| `svelte.joerg-lohrer.de` | Entwicklungs-Deploy-Target (historischer Default) |
|
||||
| `spa.joerg-lohrer.de` | Vanilla-HTML-Spike (historisch) |
|
||||
|
||||
**Wichtig:** `scripts/deploy-svelte.sh` hat `DEPLOY_TARGET=svelte` als
|
||||
Default — das zielt auf `svelte.joerg-lohrer.de`, NICHT auf die
|
||||
Produktion. Für Prod-Deploy IMMER `DEPLOY_TARGET=prod` explizit setzen.
|
||||
|
||||
## Git-Branches
|
||||
|
||||
- `main` — kanonisch (Content, Specs, Pläne, Deploy-Scripts)
|
||||
- `spa` — aktueller Arbeitszweig mit allen SvelteKit-Commits
|
||||
- `hugo-archive` — Orphan, eingefrorener Hugo-Zustand
|
||||
- `main` — kanonisch, alle Arbeit läuft hier direkt.
|
||||
- `hugo-archive` — Orphan, eingefrorener Hugo-Zustand (Rollback-Option).
|
||||
|
||||
Specs und Pläne gehören auf `main`; SvelteKit-Code auf `spa`. Typischer
|
||||
Workflow: committe Spec-Updates auf `main`, merge `main` → `spa` um
|
||||
sie überall zu haben.
|
||||
`spa` aus der Pre-Cutover-Phase ist gemerged und historisch.
|
||||
|
||||
## Sprache und Ton
|
||||
|
||||
- Antworten und Commit-Messages auf **Deutsch** (Kundensprache).
|
||||
- Code-Kommentare auch auf Deutsch (wenn überhaupt).
|
||||
- Identifier, Variablen, Funktionen auf **Englisch**.
|
||||
- Code-Identifier (Variablen, Funktionen, Typen) auf Englisch.
|
||||
- Kurz und konkret — Jörg ist technisch versiert, erwartet keine
|
||||
Grundlagen-Erklärungen.
|
||||
- Commit-Präfixe: `feat`, `fix`, `chore`, `docs`, `test` (conventional).
|
||||
- Co-Author: `Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>`.
|
||||
|
||||
## Kernkonventionen
|
||||
|
||||
### Kanonisches URL-Schema
|
||||
### Content-Struktur
|
||||
|
||||
- Post-URL ist **kurz**: `/<dtag>/` (z. B. `/dezentrale-oep-oer/`).
|
||||
- Legacy-Hugo-URLs `/YYYY/MM/DD/<dtag>.html/` werden per SvelteKit-Load
|
||||
auf die kurze Form 301-redirected (Backlink-Kompatibilität).
|
||||
- Markdown-Posts pro Sprache: `content/posts/<lang>/<slug>/index.md`.
|
||||
- Slug ist global eindeutig (also NICHT identisch zwischen Sprach-Varianten).
|
||||
Der Slug wird zum `d`-Tag des Events und zur URL (`/<slug>/`).
|
||||
- Sprach-Differenzierung über `l`-Tag (NIP-32), nicht über den Slug.
|
||||
- Bidirektionale Verlinkung zwischen Sprach-Varianten via `a:`-Frontmatter,
|
||||
wird als `['a', '<coord>', '', 'translation']` ins Event geschrieben.
|
||||
|
||||
### URL-Schema
|
||||
|
||||
- Post-URL: `/<slug>/` (z. B. `/bibel-selfies/`, `/bible-selfies/`). Keine
|
||||
Sprach-Präfixe in der URL.
|
||||
- Legacy-Hugo-URLs `/YYYY/MM/DD/<dtag>.html/` werden 301-redirected.
|
||||
- Tag-Route: `/tag/<name>/`.
|
||||
|
||||
### Slug-Regel
|
||||
|
||||
Alle Slugs sind lowercase (Frontmatter `slug:`). Commit `d17410f` hat das
|
||||
normalisiert. Keine Runtime-Transformation, beim Publishen 1:1 übernehmen.
|
||||
|
||||
### Nostr-Konstanten
|
||||
|
||||
- Pubkey (hex): `4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41`
|
||||
- npub: `npub1f7jar3qnu269uyx5p0e4v24hqxjnxysxudvujza2ur5ehltvdeqsly2fx9`
|
||||
- Bootstrap-Relay: `wss://relay.damus.io`
|
||||
- Vollständige Relay-Liste: aus `kind:10002` des Autors (on-the-fly).
|
||||
- Relay-Liste: aus `kind:10002` des Autors (zur Laufzeit geladen).
|
||||
- Blossom-Server: aus `kind:10063` des Autors.
|
||||
|
||||
Zentralisiert in `app/src/lib/nostr/config.ts`.
|
||||
- Zentralisiert in `app/src/lib/nostr/config.ts` bzw. `.env.local`.
|
||||
|
||||
### Signing
|
||||
|
||||
- **Im Browser (Kommentare):** NIP-07 via Extension (Alby, nos2x).
|
||||
- **Aus der Kommandozeile (Publish):** NIP-46 via Amber-Bunker. Bunker-URL
|
||||
in `.env.local` als `BUNKER_URL`.
|
||||
- Privater Schlüssel **nie** im Repo, nie in CI-Secrets, nie in einer
|
||||
Pipeline-Umgebung direkt.
|
||||
- **Aus der Kommandozeile (Publish):** NIP-46 via Amber-Bunker.
|
||||
- Privater Schlüssel **nie** im Repo, nie in CI-Secrets direkt.
|
||||
|
||||
## Wiederkehrende Kommandos
|
||||
|
||||
|
|
@ -83,96 +92,92 @@ Zentralisiert in `app/src/lib/nostr/config.ts`.
|
|||
```sh
|
||||
cd app
|
||||
npm run dev # Dev-Server localhost:5173
|
||||
npm run check # Type-Check (sollte 0 errors sein)
|
||||
npm run test:unit # Vitest — aktuell 29 Tests
|
||||
npm run test:e2e # Playwright — aktuell 3 Tests
|
||||
npm run check # Type-Check (svelte-check)
|
||||
npm run test:unit # Vitest
|
||||
npm run test:e2e # Playwright
|
||||
npm run build # Prod-Build nach app/build/
|
||||
```
|
||||
|
||||
### Deploy nach `svelte.joerg-lohrer.de`
|
||||
### Publish-Pipeline
|
||||
|
||||
```sh
|
||||
cd app && npm run build && cd ..
|
||||
./scripts/deploy-svelte.sh
|
||||
cd publish
|
||||
deno task check # pre-flight (Bunker, Relays, Blossom)
|
||||
deno task publish --dry-run # diff-modus simulation
|
||||
deno task publish # diff-modus real
|
||||
deno task publish --force-all # alle 27 Posts
|
||||
deno task publish --post <slug> # einzelner Post
|
||||
deno task delete --event-id <hex> --reason "…" # NIP-09-Löschung
|
||||
deno task validate-post ../content/posts/<lang>/<dir>/index.md
|
||||
deno task test # Tests (73)
|
||||
```
|
||||
|
||||
Das Script:
|
||||
- liest `SVELTE_FTP_*` aus `.env.local`
|
||||
- uploaded `app/build/*` per FTPS (TLS 1.2-Cap wegen All-Inkl-Bug)
|
||||
- checkt `HTTP/2 200` am Ende
|
||||
### Deploy
|
||||
|
||||
### Manuelles Publishen eines Posts (bis Publish-Pipeline fertig ist)
|
||||
|
||||
Siehe `docs/HANDOFF.md` Abschnitt „Manuelles Publishen". Kurz:
|
||||
- Body aus Markdown-Frontmatter extrahieren (awk-Pattern dort)
|
||||
- Bilder zu Blossom: `nak blossom upload --server https://blossom.edufeed.org --sec "$BUNKER_URL" <bild>`
|
||||
- Event bauen mit `nak event -k 30023 -d <slug> -t title=... ...`
|
||||
- Push zu allen Relays
|
||||
```sh
|
||||
DEPLOY_TARGET=staging ./scripts/deploy-svelte.sh # Pre-Prod
|
||||
DEPLOY_TARGET=prod ./scripts/deploy-svelte.sh # Prod (joerg-lohrer.de)
|
||||
```
|
||||
|
||||
### Nostr-Status checken
|
||||
|
||||
```sh
|
||||
# Alle publizierten kind:30023-Events des Autors
|
||||
nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.damus.io 2>/dev/null | jq -c '{d: (.tags[] | select(.[0]=="d") | .[1]), title: (.tags[] | select(.[0]=="title") | .[1])}'
|
||||
|
||||
# kind:10002 (Relay-Liste)
|
||||
nak req -k 10002 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.damus.io
|
||||
|
||||
# kind:10063 (Blossom-Liste)
|
||||
nak req -k 10063 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.damus.io
|
||||
# Alle publizierten kind:30023-Events des Autors (inkl. l-Tag + a-Tags)
|
||||
nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.damus.io 2>/dev/null | jq -c '{d: (.tags[] | select(.[0]=="d") | .[1]), l: (.tags[] | select(.[0]=="l") | .[1]), title: (.tags[] | select(.[0]=="title") | .[1])}'
|
||||
```
|
||||
|
||||
## Tech-Stack-Eigenheiten, die man kennen muss
|
||||
|
||||
1. **Svelte 5 Runes:** `$props()`-Werte müssen via `$derived()` in lokale
|
||||
Variablen abgeleitet werden — sonst `state_referenced_locally`-Warning.
|
||||
1. **Svelte 5 Runes:** `$props()`-Werte via `$derived()` in lokale Variablen.
|
||||
`$effect(() => { … event.id })` statt `onMount`, wenn bei Prop-Änderung
|
||||
neu geladen werden muss (siehe `[...slug]/+page.svelte`).
|
||||
|
||||
2. **applesauce-relay v5.x API:** RxJS-basiert. `pool.request(relays, filter)`
|
||||
liefert `Observable<NostrEvent>`. Die Loader in `app/src/lib/nostr/loaders.ts`
|
||||
nutzen `toArray() + lastValueFrom + timeout + catchError`-Pattern.
|
||||
**Nicht** das Tupel-Pattern `msg[0] === 'EVENT'` — das gehört in
|
||||
alte nostr-tools-Beispiele, nicht hierher.
|
||||
|
||||
3. **DOMPurify braucht DOM:** im `renderMarkdown`-Helper gibt es einen
|
||||
Early-Fail-Guard für Node-Aufrufe (SSR ist ohnehin aus).
|
||||
3. **DOMPurify braucht DOM:** Early-Fail-Guard für Node-Aufrufe im
|
||||
`renderMarkdown`-Helper. SSR ist ohnehin aus (`ssr = false` im Layout).
|
||||
|
||||
4. **All-Inkl-FTPS-Bug:** Data-Connection bricht bei TLS 1.3 ab.
|
||||
`--tls-max 1.2` im curl-Call. Sobald SSH auf All-Inkl verfügbar ist
|
||||
(Premium-Tarif angefragt), wird das Deploy-Script auf rsync umgestellt.
|
||||
(Premium-Tarif angefragt), Umstellung auf rsync möglich.
|
||||
|
||||
5. **Amber-Bunker-Session:** bei neuer Bunker-URL müssen globale
|
||||
Permissions in Amber zurückgesetzt werden. Sonst hängt `nak event`
|
||||
auf die Signatur-Response.
|
||||
Permissions in Amber auf „Allow + Always" für `get_public_key` und
|
||||
`sign_event` gesetzt werden.
|
||||
|
||||
## Was nicht in Scope ist (laut Plan/Specs)
|
||||
6. **Forgejo→GitHub Push-Mirror:** `git push` geht nach Forgejo, die
|
||||
Action läuft auf GitHub (nachdem Forgejo gespiegelt hat). Push → Mirror →
|
||||
Action braucht typisch 1–2 Minuten.
|
||||
|
||||
- Impressum-Inhalt (rechtliche Texte)
|
||||
- Meta-Stubs pro Post (kommt via Publish-Pipeline Phase 3)
|
||||
- Menü-Navigation (einfach nachrüstbar, aber nicht priorisiert)
|
||||
- Eigener Relay (ideologischer Evolutionspfad, nicht Phase 1)
|
||||
- Eigener Blossom-Server (dito)
|
||||
7. **svelte-i18n + activeLocale:** `$t('key')` in Templates, `get(t)('key')`
|
||||
in imperativem Script-Code. `activeLocale` ist der projekteigene Store
|
||||
(persistiert via `localStorage`), `locale` aus svelte-i18n wird
|
||||
automatisch synchronisiert.
|
||||
|
||||
8. **zsh-Globbing:** Pfade mit eckigen Klammern (z. B. `app/src/routes/[...slug]/`)
|
||||
müssen in `git add` in einfachen Anführungszeichen stehen, sonst
|
||||
interpretiert zsh das als Glob-Pattern.
|
||||
|
||||
## Wie mit Jörg arbeiten
|
||||
|
||||
- **Kurze Antworten**, konkrete Optionen, nicht lang umherreden.
|
||||
- **Kurze Antworten**, konkrete Optionen, keine Grundlagen-Erklärungen.
|
||||
- Bei mehreren Wegen: 2–3 Varianten mit Empfehlung nennen, nicht alles
|
||||
aufzählen.
|
||||
- Commit-Nachrichten: imperativ, auf Deutsch, mit Kontext im Body.
|
||||
Co-Author: `Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>`.
|
||||
- Vor dem Dispatchen von Subagents: kritische API-Details der Libraries
|
||||
manuell verifizieren (Plan-Annahmen können alte Versionsstände
|
||||
widerspiegeln). Beispiel: applesauce-relay API war nicht so wie im Plan
|
||||
beschrieben — Subagent mit aktueller API briefen statt blind vertrauen.
|
||||
- Nach jedem Feature-Commit: Build + Deploy, damit Jörg live sehen kann.
|
||||
Das ist in diesem Workflow wichtig, weil UI-Feedback oft Layout-Fragen
|
||||
aufwirft, die kein Test entdeckt.
|
||||
- Spec-Updates auf `main` committen, dort läuft alle Arbeit.
|
||||
- Nach Feature-Commits: Build + Deploy, damit Jörg live sehen kann.
|
||||
UI-Feedback fängt Layout-Fragen ab, die Tests nicht entdecken.
|
||||
- Vor Subagent-Dispatch: kritische API-Details verifizieren
|
||||
(Plan-Annahmen können veraltet sein).
|
||||
|
||||
## Credentials / Secrets
|
||||
|
||||
Alle in `.env.local` (gitignored). Variablen:
|
||||
Alle in `.env.local` (gitignored):
|
||||
- `BUNKER_URL` — Amber-NIP-46-Pairing für Signaturen
|
||||
- `SPA_FTP_HOST/USER/PASS/REMOTE_PATH` — FTPS nach spa.joerg-lohrer.de
|
||||
- `SVELTE_FTP_HOST/USER/PASS/REMOTE_PATH` — FTPS nach svelte.joerg-lohrer.de
|
||||
- `CLIENT_SECRET_HEX` — identisch mit GitHub-Secret (stabile App-ID in Amber)
|
||||
- `AUTHOR_PUBKEY_HEX`, `BOOTSTRAP_RELAY`
|
||||
- `SVELTE_FTP_*`, `STAGING_FTP_*` — FTPS-Credentials pro Deploy-Target
|
||||
|
||||
Falls neue Bunker-URL nötig (Amber-Session kaputt):
|
||||
- In Amber neue Bunker-URL generieren
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
# CLAUDE.md — Einstieg für Claude-Sessions
|
||||
|
||||
Dieser Einstieg ist für Claude-Code-Sessions gedacht. Für den inhaltlichen
|
||||
Projektstand siehe [`docs/STATUS.md`](docs/STATUS.md) und
|
||||
[`docs/HANDOFF.md`](docs/HANDOFF.md).
|
||||
|
||||
## Was dieses Repo ist
|
||||
|
||||
Die persönliche Webseite [`joerg-lohrer.de`](https://joerg-lohrer.de/) als
|
||||
SvelteKit-SPA, die Blog-Posts live aus Nostr-Events (NIP-23, `kind:30023`)
|
||||
auf 5 Public-Relays rendert. Seit 2026-04-21 mehrsprachig (DE/EN).
|
||||
|
||||
## Einstiegsreihenfolge
|
||||
|
||||
1. Diese Datei (Agent-Konventionen, Fallstricke).
|
||||
2. [`docs/STATUS.md`](docs/STATUS.md) — wo steht alles gerade.
|
||||
3. [`docs/HANDOFF.md`](docs/HANDOFF.md) — Alltags-Workflow, Stolperfallen.
|
||||
4. Für konkrete Aufgaben: Spec unter `docs/superpowers/specs/`, Plan unter
|
||||
`docs/superpowers/plans/`.
|
||||
|
||||
## Sprache und Ton
|
||||
|
||||
- **Antworten und Commit-Messages auf Deutsch.**
|
||||
- Code-Identifier auf Englisch.
|
||||
- Kurz, konkret, kein Grundlagen-Tutorial. Jörg ist technisch versiert.
|
||||
- Bei mehreren Wegen: 2–3 Varianten mit Empfehlung, nicht alles aufzählen.
|
||||
|
||||
## Commit-Konvention
|
||||
|
||||
- Conventional-Commit-Präfixe: `feat`, `fix`, `chore`, `docs`, `test`.
|
||||
- Imperativ, Deutsch, Body erklärt das *Warum*.
|
||||
- Co-Author immer ergänzen:
|
||||
```
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
## Kritische Fallstricke
|
||||
|
||||
### 1. Deploy-Target
|
||||
|
||||
`scripts/deploy-svelte.sh` hat `DEPLOY_TARGET=svelte` als Default —
|
||||
das zielt auf `svelte.joerg-lohrer.de`, NICHT auf die Produktion.
|
||||
|
||||
Für Live-Deploy auf `joerg-lohrer.de`:
|
||||
|
||||
```sh
|
||||
DEPLOY_TARGET=prod ./scripts/deploy-svelte.sh
|
||||
```
|
||||
|
||||
**Immer explizit setzen.** Der stumme Default-Fehler ist nur sichtbar,
|
||||
wenn man die Live-Seite kontrolliert. Reproduzierbar als Memory-Entry
|
||||
im Claude-Memory-System.
|
||||
|
||||
### 2. zsh-Globbing mit eckigen Klammern
|
||||
|
||||
SvelteKit-Routen wie `app/src/routes/[...slug]/+page.svelte` enthalten
|
||||
eckige Klammern, die zsh als Glob-Pattern interpretiert. Pfade IMMER in
|
||||
einfachen Anführungszeichen:
|
||||
|
||||
```sh
|
||||
git add 'app/src/routes/[...slug]/+page.svelte'
|
||||
```
|
||||
|
||||
### 3. Forgejo → GitHub Push-Mirror
|
||||
|
||||
`git push` landet zuerst auf Forgejo (`forgejo.joerglohrer.synology.me`).
|
||||
Der Forgejo-Mirror synct dann zu GitHub (typisch 30–90 s). Die GitHub-
|
||||
Action (Publish-Pipeline) läuft erst nach dem Mirror. Wer direkt nach
|
||||
`git push` `gh run list` aufruft, sieht evtl. noch keinen neuen Run.
|
||||
|
||||
### 4. Deno-Path-Konventionen
|
||||
|
||||
Publish-Pipeline läuft aus `publish/` (CWD), daher sind Pfade relativ
|
||||
mit `../content/posts/...`. Git-Diff liefert aber repo-root-relative
|
||||
Pfade (`content/posts/...`). `changedPostDirs` normalisiert beides —
|
||||
wenn `posts=0` obwohl Änderungen vorliegen, ist das der Hotspot.
|
||||
|
||||
### 5. Publish-Pipeline erwartet `content/posts/<lang>/<slug>/`
|
||||
|
||||
Die Zwei-Ebenen-Struktur ist Teil der Traversierung. Wer einen Post
|
||||
versehentlich in `content/posts/<slug>/` (ohne Sprach-Ordner) anlegt,
|
||||
wird von der Pipeline ignoriert.
|
||||
|
||||
## Hauptarbeitsbereiche im Repo
|
||||
|
||||
| Pfad | Inhalt |
|
||||
|---|---|
|
||||
| `content/posts/<lang>/<slug>/index.md` | Markdown-Posts pro Sprache |
|
||||
| `app/src/lib/i18n/` | UI-Lokalisierung (svelte-i18n, activeLocale-Store) |
|
||||
| `app/src/lib/nostr/` | Relay-Loader, Translations-Resolving |
|
||||
| `app/src/lib/components/` | Svelte-5-Runes-Komponenten |
|
||||
| `app/src/routes/` | SvelteKit-Routen (Layout, Home, Archiv, Post, Impressum) |
|
||||
| `publish/src/` | Deno-Publish-Pipeline (Deno-Tasks in `publish/deno.jsonc`) |
|
||||
| `publish/tests/` | Deno-Tests für die Pipeline |
|
||||
| `docs/superpowers/specs/` | Produktdesigns, Konventionen |
|
||||
| `docs/superpowers/plans/` | Implementierungspläne (alle erledigt) |
|
||||
| `scripts/deploy-svelte.sh` | FTPS-Deploy |
|
||||
|
||||
## Quick-Links
|
||||
|
||||
- [Produktspezifikation SPA](docs/superpowers/specs/2026-04-15-nostr-page-design.md)
|
||||
- [Produktspezifikation Publish-Pipeline](docs/superpowers/specs/2026-04-15-publish-pipeline-design.md)
|
||||
- [Bild-Metadaten-Konvention](docs/superpowers/specs/2026-04-16-image-metadata-convention.md)
|
||||
- [Multilingual-Design](docs/superpowers/specs/2026-04-21-multilingual-posts-design.md)
|
||||
- [Repo-Workflow-Skill](.claude/skills/joerglohrerde-workflow.md) (ausführlicher, mit Kommandos)
|
||||
29
README.md
29
README.md
|
|
@ -6,7 +6,7 @@ Blog-Posts live aus signierten Nostr-Events (NIP-23, `kind:30023`) rendert.
|
|||
|
||||
## Aktueller Stand
|
||||
|
||||
- **`https://joerg-lohrer.de/`** — SvelteKit-SPA, Cutover am 2026-04-18 erfolgt.
|
||||
- **`https://joerg-lohrer.de/`** — SvelteKit-SPA, seit 2026-04-18 live. Seit 2026-04-21 **multilingual** (Deutsch + Englisch via NIP-32 `l`-Tag und NIP-33-`a`-Tag-Verlinkung).
|
||||
- **`https://staging.joerg-lohrer.de/`** — Staging (gleicher Build, ein Schritt vor Prod).
|
||||
- **`https://svelte.joerg-lohrer.de/`** — Entwicklungs-Deploy-Target der Pipeline.
|
||||
- **`https://spa.joerg-lohrer.de/`** — Vanilla-HTML-Mini-Spike (Proof of Concept, historisch).
|
||||
|
|
@ -15,13 +15,20 @@ Detailliert in [`docs/STATUS.md`](docs/STATUS.md).
|
|||
|
||||
## Wie die Seite funktioniert
|
||||
|
||||
1. **Inhalte** liegen als Markdown in `content/posts/<slug>/index.md` mit
|
||||
1. **Inhalte** liegen als Markdown in `content/posts/<lang>/<slug>/index.md`
|
||||
(z. B. `content/posts/de/<slug>/` oder `content/posts/en/<slug>/`) mit
|
||||
strukturierten Bild-Metadaten im Frontmatter (Alt-Text, Lizenz, Autor:innen).
|
||||
Übersetzungen eines Posts werden über bidirektionale `a:`-Tags im
|
||||
Frontmatter verlinkt — Details in
|
||||
[`docs/superpowers/specs/2026-04-21-multilingual-posts-design.md`](docs/superpowers/specs/2026-04-21-multilingual-posts-design.md).
|
||||
2. **Publish-Pipeline** (`publish/`, Deno) lädt Bilder auf Blossom-Server
|
||||
(content-addressed) und publiziert signierte `kind:30023`-Events via
|
||||
NIP-46-Bunker (Amber) auf 5 Relays.
|
||||
NIP-46-Bunker (Amber) auf 5 Relays — inkl. NIP-32 `l`-Tag (Sprache) und
|
||||
NIP-33 `a`-Tag (Verlinkung zu anderssprachigen Varianten).
|
||||
3. **SvelteKit-SPA** (`app/`) lädt diese Events zur Laufzeit und rendert
|
||||
Post-Liste + Detailseiten. Keine Server-Komponente, Static-Hosting reicht.
|
||||
Post-Liste + Detailseiten. UI-Chrome via `svelte-i18n` (DE/EN), Browser-
|
||||
Locale als Default, Listen nach aktivem Locale gefiltert. Keine
|
||||
Server-Komponente, Static-Hosting reicht.
|
||||
4. **CI**: GitHub Actions triggert die Publish-Pipeline bei Push auf `main`
|
||||
(via Forgejo→GitHub Push-Mirror).
|
||||
|
||||
|
|
@ -35,11 +42,16 @@ Identität und Assets:
|
|||
|
||||
- 📍 **Stand und Live-URLs:** [`docs/STATUS.md`](docs/STATUS.md)
|
||||
- 🔜 **Wie es weitergeht:** [`docs/HANDOFF.md`](docs/HANDOFF.md)
|
||||
- 🤖 **Claude-Einstieg:** [`CLAUDE.md`](CLAUDE.md) (Agent-Konventionen, Deploy-Falle, Commit-Stil)
|
||||
- 📐 **SPA-Spec:** [`docs/superpowers/specs/2026-04-15-nostr-page-design.md`](docs/superpowers/specs/2026-04-15-nostr-page-design.md)
|
||||
- 📐 **Publish-Pipeline-Spec:** [`docs/superpowers/specs/2026-04-15-publish-pipeline-design.md`](docs/superpowers/specs/2026-04-15-publish-pipeline-design.md)
|
||||
- 📐 **Bild-Metadaten-Konvention:** [`docs/superpowers/specs/2026-04-16-image-metadata-convention.md`](docs/superpowers/specs/2026-04-16-image-metadata-convention.md)
|
||||
- 🛠 **SvelteKit-SPA-Plan:** [`docs/superpowers/plans/2026-04-15-spa-sveltekit.md`](docs/superpowers/plans/2026-04-15-spa-sveltekit.md) (35 Tasks, abgeschlossen)
|
||||
- 🛠 **Publish-Pipeline-Plan:** [`docs/superpowers/plans/2026-04-16-publish-pipeline.md`](docs/superpowers/plans/2026-04-16-publish-pipeline.md) (24 Tasks, abgeschlossen)
|
||||
- 📐 **Multilinguale Posts:** [`docs/superpowers/specs/2026-04-21-multilingual-posts-design.md`](docs/superpowers/specs/2026-04-21-multilingual-posts-design.md)
|
||||
- 🛠 **SvelteKit-SPA-Plan:** [`docs/superpowers/plans/2026-04-15-spa-sveltekit.md`](docs/superpowers/plans/2026-04-15-spa-sveltekit.md) (35 Tasks, erledigt)
|
||||
- 🛠 **Publish-Pipeline-Plan:** [`docs/superpowers/plans/2026-04-16-publish-pipeline.md`](docs/superpowers/plans/2026-04-16-publish-pipeline.md) (24 Tasks, erledigt)
|
||||
- 🛠 **Multilingual 1/3 — Pipeline:** [`docs/superpowers/plans/2026-04-21-multilingual-posts-pipeline.md`](docs/superpowers/plans/2026-04-21-multilingual-posts-pipeline.md) (10 Tasks, erledigt)
|
||||
- 🛠 **Multilingual 2/3 — SPA-Auflösung:** [`docs/superpowers/plans/2026-04-21-multilingual-posts-spa.md`](docs/superpowers/plans/2026-04-21-multilingual-posts-spa.md) (8 Tasks, erledigt)
|
||||
- 🛠 **Multilingual 3/3 — UI-i18n:** [`docs/superpowers/plans/2026-04-21-multilingual-posts-i18n.md`](docs/superpowers/plans/2026-04-21-multilingual-posts-i18n.md) (11 Tasks, erledigt)
|
||||
- 🤖 **Claude-Workflow-Skill:** [`.claude/skills/joerglohrerde-workflow.md`](.claude/skills/joerglohrerde-workflow.md)
|
||||
|
||||
## Branches
|
||||
|
|
@ -52,9 +64,11 @@ Identität und Assets:
|
|||
## Repo-Struktur
|
||||
|
||||
```
|
||||
content/posts/ Markdown-Posts (Quelle für Nostr-Events, 18 Stück)
|
||||
content/posts/<lang>/<slug>/ Markdown-Posts pro Sprache (26× de, 1× en)
|
||||
content/impressum.md Statisches Impressum (wird von SPA geladen)
|
||||
app/ SvelteKit-SPA (Laufzeit-Renderer)
|
||||
src/lib/i18n/ UI-Lokalisierung (svelte-i18n + Messages)
|
||||
src/lib/nostr/ Relay-Loader, Translations-Resolving
|
||||
publish/ Deno-Publish-Pipeline (Blossom + Nostr)
|
||||
preview/spa-mini/ Vanilla-HTML-Mini-Spike (historische Referenz)
|
||||
scripts/deploy-svelte.sh FTPS-Deploy, Targets: svelte/staging/prod
|
||||
|
|
@ -62,6 +76,7 @@ static/ Site-Assets (Favicons, Profilbild, .well-known/)
|
|||
docs/ Specs, Pläne, Status, Handoff, Wiki-Entwürfe
|
||||
.github/workflows/ GitHub-Actions CI (Publish-Pipeline-Trigger)
|
||||
.claude/ Claude-Code-Sessions (Transparenz) + Skills
|
||||
CLAUDE.md Einstiegspunkt für Claude-Sessions
|
||||
```
|
||||
|
||||
## Entwicklung
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ Dieses Dokument sagt: was ist der Zustand, was wartet, wo liegen die Fäden.
|
|||
|
||||
## Zustand (Details in `STATUS.md`)
|
||||
|
||||
**Cutover + Reimport am 2026-04-18 abgeschlossen.** `joerg-lohrer.de`
|
||||
läuft als SvelteKit-SPA, rendert 26 Nostr-Langform-Posts live aus 5
|
||||
Relays, Bilder auf Blossom. Repo ist alleinige Quelle der Wahrheit.
|
||||
Pipeline-Subcommands `publish` + `delete` decken den kompletten
|
||||
Content-Lifecycle ab.
|
||||
**Cutover + Reimport 2026-04-18, Mehrsprachigkeit live seit 2026-04-21.**
|
||||
`joerg-lohrer.de` läuft als SvelteKit-SPA, rendert 27 Nostr-Langform-Posts
|
||||
(26 DE + 1 EN) live aus 5 Relays, Bilder auf Blossom. UI-Chrome via
|
||||
`svelte-i18n` in DE/EN, Header-Switcher, Listen-Filter nach aktivem Locale,
|
||||
bidirektionale Sprach-Verlinkung der Posts via NIP-33 `a`-Tag mit Marker
|
||||
`translation`. Repo ist alleinige Quelle der Wahrheit. Pipeline-
|
||||
Subcommands `publish` + `delete` decken den kompletten Content-Lifecycle ab.
|
||||
|
||||
**Das inhaltliche Kernziel des Gesamtprojekts ist erreicht.** Der Rest
|
||||
sind optionale Verbesserungen.
|
||||
|
|
@ -18,11 +20,12 @@ sind optionale Verbesserungen.
|
|||
|
||||
**Kompletter Happy-Path, kein manueller Publish nötig:**
|
||||
|
||||
1. Neuen Ordner anlegen: `content/posts/YYYY-MM-DD-<slug>/`
|
||||
1. Neuen Ordner anlegen: `content/posts/de/YYYY-MM-DD-<slug>/` (oder
|
||||
`content/posts/en/<slug>/` für Englisch).
|
||||
2. `index.md` schreiben mit Frontmatter (siehe Template unten).
|
||||
3. Bilder in den Ordner legen und im Markdown als ``
|
||||
referenzieren.
|
||||
4. Lokal validieren: `cd publish && deno task validate-post ../content/posts/<dir>/index.md`
|
||||
4. Lokal validieren: `cd publish && deno task validate-post ../content/posts/<lang>/<dir>/index.md`
|
||||
5. Commit + `git push origin main` — fertig.
|
||||
|
||||
**Was automatisch passiert:**
|
||||
|
|
@ -45,7 +48,7 @@ gesetzt haben. Das gilt so lange, bis der Client-Key rotiert wird.
|
|||
---
|
||||
title: "Titel des Posts"
|
||||
slug: "url-freundlicher-slug"
|
||||
date: 2026-04-18
|
||||
date: 2026-04-21
|
||||
description: "Kurzbeschreibung für SEO und den summary-Tag im Event."
|
||||
image: hauptbild.jpg
|
||||
tags:
|
||||
|
|
@ -53,11 +56,17 @@ tags:
|
|||
- Tag2
|
||||
lang: de
|
||||
license: https://creativecommons.org/publicdomain/zero/1.0/deed.de
|
||||
# a:
|
||||
# - "30023:4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41:<slug-der-anderssprachigen-variante>"
|
||||
---
|
||||
|
||||
Body in Markdown…
|
||||
```
|
||||
|
||||
Der auskommentierte `a:`-Block ist **Konvention für alle neuen Posts** — so
|
||||
lässt sich später eine Übersetzung dazu verlinken, ohne das Template zu
|
||||
suchen. Siehe Abschnitt „Wie man eine Übersetzung anlegt" weiter unten.
|
||||
|
||||
Bilder mit voller Attribution (NIP-standardisiert nach unserer Konvention,
|
||||
siehe `docs/superpowers/specs/2026-04-16-image-metadata-convention.md`):
|
||||
|
||||
|
|
@ -100,24 +109,47 @@ gefiltert. Defensive Maßnahme für zukünftige Duplikate / Soft-Deletes.
|
|||
User-Task: im All-Inkl KAS als Weiterleitung anlegen. Der Link im
|
||||
Footer und in den Social-Icons zeigt bereits darauf.
|
||||
|
||||
### Option D — Mehrsprachigkeit (Translation-of)
|
||||
### Wie man eine Übersetzung anlegt (Konvention seit 2026-04-21)
|
||||
|
||||
**Grundlage steht:** Pipeline taggt seit 2026-04-18 jedes Event mit
|
||||
NIP-32 `['L', 'ISO-639-1']` + `['l', 'de', 'ISO-639-1']` (default),
|
||||
überschreibbar per `lang:`-Frontmatter.
|
||||
**Kurz:** Pro Sprache ein eigener Unterordner unter `content/posts/<lang>/`,
|
||||
pro Sprache ein eigenes `kind:30023`-Event mit eigenem Slug (= `d`-Tag).
|
||||
Die Beziehung zwischen Sprach-Varianten kommt ausschließlich über
|
||||
bidirektionale `a`-Tags im Frontmatter.
|
||||
|
||||
**Zu tun für einen bilingualen Post:**
|
||||
1. Zweiter Markdown-Ordner, z. B. `content/posts/<date>-<slug>-en/index.md`,
|
||||
mit `slug: <slug>-en`, `lang: en`, englischem Body.
|
||||
2. Publish → eigenes `kind:30023`-Event mit `lang=en`.
|
||||
3. (Noch zu bauen) Pipeline erweitern: `translation_of:`-Frontmatter-Feld,
|
||||
das ein `['a', '30023:pubkey:<slug-de>']`-Tag ins Event setzt. Damit
|
||||
erkennen Clients wie Habla die Verwandtschaft.
|
||||
4. (Optional) SPA bekommt Language-Switcher auf der Post-Detailseite.
|
||||
**Schritt für Schritt:**
|
||||
|
||||
Nicht dringend, erst wenn echter englischer Content entsteht.
|
||||
1. Neuen Ordner für die Übersetzung anlegen, z. B.
|
||||
`content/posts/en/<eigener-slug>/index.md`. **Der Slug muss global
|
||||
eindeutig sein** — also *nicht* identisch mit dem deutschen Slug. Beispiel:
|
||||
`bibel-selfies` (DE) ↔ `bible-selfies` (EN).
|
||||
|
||||
### Option E — Pipeline weg von GitHub (self-hosted CI)
|
||||
2. Frontmatter mit `lang: en` (oder jeweiliger Sprach-Code) und aktivem
|
||||
`a:`-Verweis auf den Slug der anderen Sprach-Variante:
|
||||
|
||||
```yaml
|
||||
a:
|
||||
- "30023:4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41:<slug-der-anderen-sprache>"
|
||||
```
|
||||
|
||||
3. **Bidirektional**: im Original-Post den bereits auskommentierten
|
||||
`a:`-Platzhalter aktivieren (Kommentarzeichen entfernen, Slug einsetzen).
|
||||
Beide Posts verweisen dann aufeinander.
|
||||
|
||||
4. Commit + Push — die Action re-publisht beide Events, `a`-Tags landen im
|
||||
Nostr-Event als `['a', '<coord>', '', 'translation']`. Die SPA erkennt
|
||||
die Beziehung automatisch und zeigt den Sprach-Switcher (`📖 DE | EN`)
|
||||
unter dem Post-Titel.
|
||||
|
||||
**Was die SPA automatisch tut:**
|
||||
- Listen-Seiten (Startseite + Archiv) filtern nach aktivem Locale — englische
|
||||
Besucher:innen sehen nur englische Posts.
|
||||
- Klick auf den anderen Sprachcode im Switcher setzt `activeLocale` global
|
||||
und navigiert zum verknüpften Slug.
|
||||
- UI-Chrome (Menü, Footer, Meta-Zeile, Datumsformat) wechselt mit.
|
||||
|
||||
**Details:** [`docs/superpowers/specs/2026-04-21-multilingual-posts-design.md`](superpowers/specs/2026-04-21-multilingual-posts-design.md).
|
||||
|
||||
### Option D — Pipeline weg von GitHub (self-hosted CI)
|
||||
|
||||
**Wann:** Wenn der Optiplex-Server steht und ein zentraler Ort für Dienste
|
||||
existiert.
|
||||
|
|
@ -131,7 +163,7 @@ existiert.
|
|||
Der Pipeline-Code selbst (`publish/src/**`) ist CI-agnostisch — nur die
|
||||
Trigger-Konfiguration ändert sich.
|
||||
|
||||
### Option F — Design-Refinements
|
||||
### Option E — Design-Refinements
|
||||
|
||||
**Wann:** irgendwann, wenn Lust drauf ist.
|
||||
|
||||
|
|
@ -190,20 +222,25 @@ cd publish && deno task test # tests
|
|||
- **Hugo-quotierte Dates:** `date: "2023-02-26"` ist ein YAML-String, nicht
|
||||
ein Date-Objekt. `validatePost` coerced das automatisch; in neuen Posts
|
||||
am besten ohne Quotes schreiben.
|
||||
- **Deploy-Targets:** `svelte` → Entwicklung, `staging` → Pre-Prod,
|
||||
- **Deploy-Targets:** `svelte` (Default!) → Entwicklung, `staging` → Pre-Prod,
|
||||
`prod` → `joerglohrer26/` (Produktion seit Cutover). Script parst
|
||||
`.env.local` per awk (wegen Sonderzeichen in FTP-Passwörtern).
|
||||
**Für Live-Deploy auf `joerg-lohrer.de` IMMER explizit `DEPLOY_TARGET=prod`
|
||||
setzen** — der Default zielt auf `svelte.joerg-lohrer.de` (historischer
|
||||
Cutover-Stand), ein stummer Fehler wenn man es vergisst.
|
||||
- **Slug-Hygiene:** nur `[a-z0-9-]`, keine Umlaute/Emojis/Doppelpunkte.
|
||||
Der Slug landet als `d`-Tag im Event und wird zur URL. Einmal
|
||||
publiziert, ist Umbenennen nur über Delete + Re-Publish mit neuem Slug
|
||||
möglich.
|
||||
möglich. **Sprach-Varianten brauchen eigene Slugs** (z. B. `bibel-selfies`
|
||||
/ `bible-selfies`) — die Sprache kommt über den `l`-Tag, nicht über den
|
||||
`d`-Tag.
|
||||
- **Clients, die Markdown ignorieren:** Yakihonne/Habla kennen NIP-32
|
||||
Sprach-Tags; kurzen Text in `description:` halten, damit die Vorschau
|
||||
überall sinnvoll aussieht.
|
||||
|
||||
## Offene UNKNOWN-Einträge zur späteren Recherche
|
||||
|
||||
Im VR-Post (`content/posts/2021-08-15-virtual-reality/index.md`) sind
|
||||
Im VR-Post (`content/posts/de/2021-08-15-virtual-reality/index.md`) sind
|
||||
4 Bilder als `license: UNKNOWN / authors: UNKNOWN` markiert:
|
||||
- `01-immersion-wikipedia.jpg` (Wikipedia-Screenshot)
|
||||
- `02-mittelalterliche-kirche.jpg` (Sketchfab — Lizenz ist CC BY-NC, Fotograf fehlt)
|
||||
|
|
@ -220,7 +257,7 @@ Hilfreich beim Wiedereinstieg mit Claude:
|
|||
- Live-Check: `curl -sI https://joerg-lohrer.de/`
|
||||
- Event-Count Repo vs. Relays:
|
||||
```sh
|
||||
ls content/posts/ | wc -l
|
||||
find content/posts -mindepth 3 -name index.md | wc -l
|
||||
nak req -k 30023 -a 4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41 wss://relay.edufeed.org 2>/dev/null | jq -r '.tags[]|select(.[0]=="d")|.[1]' | sort -u | wc -l
|
||||
```
|
||||
- Pipeline-Tests: `cd publish && deno task test`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Projekt-Status: joerg-lohrer.de → Nostr-basierte SPA
|
||||
|
||||
**Stand:** 2026-04-18 (Cutover abgeschlossen)
|
||||
**Stand:** 2026-04-21 (Mehrsprachigkeit live)
|
||||
|
||||
## Kurzfassung
|
||||
|
||||
|
|
@ -9,6 +9,14 @@ signierten Nostr-Events (NIP-23, `kind:30023`) auf 5 Public-Relays rendert.
|
|||
Bilder liegen content-addressed auf 2 Blossom-Servern. Die Hugo-basierte
|
||||
Altseite ist als `hugo-archive`-Branch eingefroren.
|
||||
|
||||
**Seit 2026-04-21 multilingual:** UI-Chrome (Menü, Footer, Post-Meta)
|
||||
in Deutsch und Englisch via `svelte-i18n`, mit Browser-Locale-Default,
|
||||
`localStorage`-Persistenz und Header-Sprachswitcher. Inhalte pro Sprache
|
||||
als eigene `kind:30023`-Events, verlinkt über bidirektionale
|
||||
NIP-33-`a`-Tags mit Marker `translation`; Listen-Seiten filtern nach
|
||||
aktivem Locale. Eine englische Übersetzung existiert bereits
|
||||
(`bible-selfies`) und dient als lebendes Referenzbeispiel.
|
||||
|
||||
**Das inhaltliche Kernziel des Gesamtprojekts ist erreicht.**
|
||||
|
||||
## Live-URLs
|
||||
|
|
@ -25,13 +33,15 @@ Altseite ist als `hugo-archive`-Branch eingefroren.
|
|||
- **Autoren-Pubkey:** `npub1f7jar3qnu269uyx5p0e4v24hqxjnxysxudvujza2ur5ehltvdeqsly2fx9`
|
||||
(hex: `4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41`)
|
||||
- **NIP-05:** `joerglohrer@joerg-lohrer.de` (via `/.well-known/nostr.json`)
|
||||
- **Publizierte Events:** **26 Langform-Posts** (`kind:30023`), alle mit
|
||||
sauberen ASCII-slugs, alle aus dem Repo publiziert. 18 Alt-Posts aus der
|
||||
Hugo-Migration plus 8 re-importierte Client-Posts (Habla/Yakihonne), die
|
||||
mit bereinigten d-tags neu publiziert und alte Duplikate per NIP-09
|
||||
gelöscht wurden (Commit `7186c32`).
|
||||
- **Publizierte Events:** **27 Langform-Posts** (`kind:30023`) —
|
||||
26 Deutsch + 1 Englisch. 26 Alt-Posts (18 Hugo-Migration + 8 Client-
|
||||
Reimport) tragen seit 2026-04-21 konsistent `lang: de` im Frontmatter,
|
||||
`bible-selfies` (EN, 2026-04-21) verweist bidirektional auf `bibel-selfies`
|
||||
via NIP-33-`a`-Tag mit Marker `translation`.
|
||||
- **NIP-32-Sprach-Tags:** Alle Events tragen `['L', 'ISO-639-1']` +
|
||||
`['l', 'de', 'ISO-639-1']`. Grundlage für spätere Mehrsprachigkeit.
|
||||
`['l', <lang>, 'ISO-639-1']`. Deutsche Events haben `lang=de`, englische
|
||||
`lang=en`. Ergänzt durch `['a', '<kind>:<pubkey>:<d-tag>', '', 'translation']`
|
||||
bei verknüpften Sprach-Varianten.
|
||||
- **Relay-Liste** (`kind:10002`): `relay.damus.io`, `nos.lol`,
|
||||
`relay.primal.net`, `relay.tchncs.de`, `relay.edufeed.org`
|
||||
- **Blossom-Server** (`kind:10063`): `blossom.edufeed.org`, `blossom.primal.net`
|
||||
|
|
@ -42,30 +52,32 @@ Altseite ist als `hugo-archive`-Branch eingefroren.
|
|||
|
||||
```
|
||||
joerglohrerde/
|
||||
├── content/posts/ # 18 Markdown-Posts, alle mit strukturierten images:
|
||||
├── content/impressum.md # Statisches Impressum (wird von SPA geladen)
|
||||
├── app/ # SvelteKit-SPA (Laufzeit-Renderer)
|
||||
├── publish/ # Deno-Publish-Pipeline (Blossom + Nostr)
|
||||
├── preview/spa-mini/ # Vanilla-HTML-Mini-Spike (historisch)
|
||||
├── content/posts/<lang>/<slug>/ # Markdown-Posts pro Sprache (26 de, 1 en)
|
||||
├── content/impressum.md # Statisches Impressum (wird von SPA geladen)
|
||||
├── app/
|
||||
│ ├── src/lib/i18n/ # svelte-i18n + activeLocale-Store + Messages
|
||||
│ ├── src/lib/nostr/ # Relay-Loader, Translations-Resolving
|
||||
│ └── src/lib/components/ # u. a. LanguageSwitcher, LanguageAvailability
|
||||
├── publish/ # Deno-Publish-Pipeline (Blossom + Nostr)
|
||||
├── preview/spa-mini/ # Vanilla-HTML-Mini-Spike (historisch)
|
||||
├── scripts/
|
||||
│ └── deploy-svelte.sh # FTPS-Deploy, Targets: svelte/staging/prod
|
||||
│ └── deploy-svelte.sh # FTPS-Deploy, Targets: svelte/staging/prod
|
||||
├── docs/
|
||||
│ ├── STATUS.md # Dieses Dokument
|
||||
│ ├── HANDOFF.md # Wie man hier weitermacht
|
||||
│ ├── STATUS.md # Dieses Dokument
|
||||
│ ├── HANDOFF.md # Wie man hier weitermacht
|
||||
│ ├── redaktion-bild-metadaten.md
|
||||
│ ├── wiki-entwurf-nostr-bild-metadaten.md
|
||||
│ ├── wiki-draft-nostr-image-metadata.md
|
||||
│ ├── github-ci-setup.md
|
||||
│ └── superpowers/
|
||||
│ ├── specs/ # SPA + Publish-Pipeline + Bild-Metadaten-Konvention
|
||||
│ └── plans/
|
||||
│ ├── 2026-04-15-spa-sveltekit.md # erledigt
|
||||
│ └── 2026-04-16-publish-pipeline.md # erledigt
|
||||
├── .github/workflows/ # publish.yml (Forgejo→GitHub Push-Mirror-Trigger)
|
||||
│ ├── specs/ # SPA, Publish-Pipeline, Bild-Metadaten, Multilingual
|
||||
│ └── plans/ # Alle Pläne erledigt (SPA, Pipeline, 3× Multilingual)
|
||||
├── .github/workflows/ # publish.yml (Forgejo→GitHub Push-Mirror-Trigger)
|
||||
├── .claude/
|
||||
│ ├── skills/ # Repo-spezifischer Claude-Skill
|
||||
│ └── settings.local.json # Claude-Session-State (gitignored)
|
||||
└── .env.local # Gitignored: FTP-Creds, Bunker-URL, Publish-Pipeline-Keys
|
||||
│ ├── skills/ # Repo-spezifischer Claude-Skill
|
||||
│ └── settings.local.json # Claude-Session-State (gitignored)
|
||||
├── CLAUDE.md # Einstiegspunkt für Claude-Sessions
|
||||
└── .env.local # Gitignored: FTP-Creds, Bunker-URL, Publish-Pipeline-Keys
|
||||
```
|
||||
|
||||
## Branch-Layout (Git)
|
||||
|
|
@ -93,10 +105,11 @@ Einmalig manuell erledigt (gitignored in `.env.local`):
|
|||
Nach Priorität:
|
||||
1. **Postfach `webmaster@joerg-lohrer.de`** als Weiterleitung in KAS anlegen.
|
||||
2. **SPA respektiert NIP-09-Deletion-Events** (defensiver kind:5-Filter).
|
||||
3. **Mehrsprachigkeit** — parallele `lang:en`-Versionen bei Bedarf anlegen,
|
||||
per `a`-Tag als `translation_of` verlinken (NIP-32-Grundlage steht).
|
||||
4. **Self-hosted CI** (Woodpecker / Cron auf Optiplex), weg von GitHub.
|
||||
5. **5 UNKNOWN-Einträge** im VR-Post zur späteren Recherche.
|
||||
3. **Self-hosted CI** (Woodpecker / Cron auf Optiplex), weg von GitHub.
|
||||
4. **5 UNKNOWN-Einträge** im VR-Post zur späteren Recherche.
|
||||
5. **Weitere Übersetzungen** nach Bedarf — Framework ist sprach-agnostisch,
|
||||
neuer Sprach-Unterordner (z. B. `content/posts/fr/`) genügt, UI-i18n-
|
||||
Messages ergänzen.
|
||||
|
||||
## Erledigt (chronologisch seit 2026-04-15)
|
||||
|
||||
|
|
@ -126,6 +139,20 @@ Nach Priorität:
|
|||
nutzt stabile Bunker-Identität via `CLIENT_SECRET_HEX`.
|
||||
- ✅ **NIP-32 Sprach-Tags** in `buildKind30023` (Default `de`, über
|
||||
`lang:`-Frontmatter überschreibbar).
|
||||
- ✅ **Multilinguale Posts (2026-04-21)** — drei sequentielle Pläne
|
||||
(Pipeline, SPA-Resolving, UI-i18n) abgeschlossen:
|
||||
- Publish-Pipeline traversiert `content/posts/<lang>/<slug>/`, akzeptiert
|
||||
`a:`-Tags im Frontmatter und schreibt sie als NIP-33-Koordinaten mit
|
||||
Marker `translation` ins Event.
|
||||
- SPA löst `a`-Tags auf, zeigt kompakten Switcher im Post (`📖 DE | EN`),
|
||||
Klick setzt globalen Locale-State und navigiert zur Sprach-Variante.
|
||||
- UI-Chrome via `svelte-i18n`, `activeLocale`-Store mit `localStorage`-
|
||||
Persistenz, Listen-Seiten nach aktivem Locale gefiltert.
|
||||
- Erste englische Übersetzung `bible-selfies` existiert als lebendes
|
||||
Referenzbeispiel.
|
||||
- Zwei Publisher-Pipeline-Bugfixes (`contentRoot`-Pfad-Handling) und
|
||||
ein Route-Refresh-Bug (`onMount` → `$effect`) dabei nebenbei
|
||||
bereinigt — GitHub-Action re-publisht nun wirklich auf Content-Änderung.
|
||||
|
||||
## Live-Verifikation
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
# Multilinguale Posts — Design
|
||||
|
||||
**Datum:** 2026-04-21
|
||||
**Status:** Design, noch nicht implementiert
|
||||
**Status:** Umgesetzt. Live seit 2026-04-21 via drei Pläne (Pipeline, SPA-Resolving, UI-i18n).
|
||||
**Scope:** Posts der SPA in mehreren Sprachen anbieten; UI-Chrome lokalisieren; Publish-Pipeline entsprechend anpassen.
|
||||
|
||||
## Umsetzungshinweis
|
||||
|
||||
Das Design unten beschreibt den angenommenen Produktstand. Während der
|
||||
Implementierung gab es eine kleine Abweichung beim Sprach-Hinweis im Post:
|
||||
Statt „Auch verfügbar in: English" wird ein kompakter Switcher gerendert
|
||||
(`📖 DE | EN`), der Sprachcode + globale Locale-Umschaltung in einem
|
||||
Klick kombiniert. Grund: UI-Sprache und Anzeige-Sprache bleiben
|
||||
konsistent, Switcher-Stil identisch zum Header. Siehe
|
||||
[`docs/HANDOFF.md`](../../HANDOFF.md) für das Nutzer:innen-Verhalten.
|
||||
|
||||
## Ziel
|
||||
|
||||
Posts können in beliebigen Sprachen existieren. Posts, die inhaltlich dasselbe Thema in unterschiedlichen Sprachen behandeln, werden über nostr-native Referenzen verknüpft, sodass die SPA eine Sprachwahl anbieten kann. Das Repo bleibt Quelle der Wahrheit; die GitHub-Action publisht weiterhin automatisch.
|
||||
|
|
|
|||
Loading…
Reference in New Issue