Skip to content

Commit dc54de7

Browse files
lx-0claude
andcommitted
docs: translate German docs + code comments to English
The project is open source; English is the lingua franca for OSS. Germany-only content that belongs in German stays (the `de` i18n dictionary + DE tagline list). - AGENTS.md, CONTEXT.md, DECISIONS.md, ROADMAP.md, CONTRIBUTING.md all rewritten in English, content unchanged - CHANGELOG.md updated to reflect the merged-SW architecture (no more separate coi-serviceworker) and TruffleHog swap - Source comments translated in: project.ts, opfs-cache.ts, mediapipe-llm.ts, model-catalog.ts, prompt-template.ts, taglines.ts - OPFS error messages (thrown from opfs-cache.ts) now English; will be i18n-keyed in a later pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e212778 commit dc54de7

12 files changed

Lines changed: 284 additions & 282 deletions

AGENTS.md

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,86 @@
11
# Agents Guide
22

3-
Dieses Repo ist ein **Browser-LLM-Chat-Scaffold** (Yesterday). Du bist hier richtig gelandet — lies diesen File, dann nach Bedarf weiter.
3+
This repo is a **browser-local LLM chat scaffold** maintained by Yesterday. You've landed in the right place — read this file first, then branch out as needed.
44

5-
## Reading Order
5+
## Reading order
66

7-
1. **AGENTS.md** (hier) — Regeln, Tasks, Gotchas
8-
2. **CONTEXT.md**Architektur-Big-Picture, Datenfluss, kritische Patterns
9-
3. **DECISIONS.md**Warum die wichtigen Entscheidungen so fielen (DEC-001 … DEC-009)
10-
4. **ROADMAP.md**Was kommt, mit Acceptance-Criteria
11-
5. **CONTRIBUTING.md**Dev-Workflow + PR-Regeln
12-
6. Source-Code: `src/lib/` (Domain-Logik) → `src/hooks/` (Adapter) → `src/components/` (UI)
7+
1. **AGENTS.md** (here) — rules, tasks, gotchas
8+
2. **CONTEXT.md**architecture big picture, data flow, critical patterns
9+
3. **DECISIONS.md**why the important decisions went the way they did (DEC-001 … DEC-009)
10+
4. **ROADMAP.md**what's coming, with acceptance criteria
11+
5. **CONTRIBUTING.md**dev workflow + PR rules
12+
6. Source: `src/lib/` (domain logic) → `src/hooks/` (adapters) → `src/components/` (UI)
1313

14-
## Build & Test
14+
## Build & test
1515

1616
```bash
17-
pnpm install # einmalig
18-
pnpm dev # Dev-Server, COOP/COEP-Headers gesetzt
17+
pnpm install # once
18+
pnpm dev # dev server, COOP/COEP headers set
1919
pnpm lint # Biome
2020
pnpm typecheck # tsc -b --noEmit
21-
pnpm build # Production + PWA-SW
21+
pnpm build # production + PWA SW
2222
```
2323

24-
Vor jedem PR: `pnpm lint && pnpm typecheck && pnpm build`alle drei gruen.
24+
Before every PR: `pnpm lint && pnpm typecheck && pnpm build`all three green.
2525

26-
## Regeln
26+
## Rules
2727

28-
- **Verifikation vor Claim**: nach jeder relevanten Aenderung `pnpm typecheck` UND `pnpm build`, nicht nur typecheck.
29-
- **Keine Modelle committen**: OPFS-Cache bleibt im Browser, nie nach Disk exportieren. `public/wasm/` ist `.gitignore`d.
30-
- **Keine destruktiven Datei-Operationen**siehe globale CLAUDE.md.
31-
- **Biome fuer Formatting + Lint**nicht ESLint/Prettier dazumixen.
32-
- **Async-Generator Bridge in `mediapipe-llm.ts`** nicht anfassen ohne Verstaendnis — MediaPipe-Callback und AbortSignal-Handling sind zusammen fragil.
33-
- **Stream tee() im OPFS-Cache** nicht umbauen — beide Branches muessen parallel konsumiert werden, sonst backpressure-Deadlock.
34-
- **External-Store-Runtime, nicht LocalRuntime**wir halten Messages selbst in Dexie + Zustand.
28+
- **Verify before claiming**: after any relevant change run `pnpm typecheck` AND `pnpm build`, not just typecheck.
29+
- **Never commit models**: the OPFS cache stays in the browser, never export to disk. `public/wasm/` is gitignored.
30+
- **No destructive file operations**see the global CLAUDE.md.
31+
- **Biome for formatting + lint**don't mix in ESLint/Prettier.
32+
- **Don't touch the async-generator bridge in `mediapipe-llm.ts`** without understanding it — MediaPipe's callback API and the AbortSignal handling are fragile together.
33+
- **Don't restructure the stream `tee()` in the OPFS cache** — both branches must be consumed in parallel or backpressure deadlocks.
34+
- **External-store runtime, not LocalRuntime**we hold messages ourselves in Dexie + Zustand.
3535

36-
## Typische Tasks
36+
## Typical tasks
3737

38-
| Task | Pfad |
38+
| Task | Where |
3939
|---|---|
40-
| Neues Modell hinzufuegen | `src/lib/model-catalog.ts` erweitern |
41-
| UI-Komponente hinzufuegen | `src/components/` + assistant-ui Primitives |
42-
| Persistenz-Schema erweitern | `src/lib/db.ts` Version bumpen (Dexie migration) |
43-
| Branding anpassen | `src/lib/project.ts`, `public/icons/icon.svg`, `index.html` |
44-
| Neue Sprache hinzufuegen | neue `src/lib/i18n/<locale>.ts` + `LOCALES` + `LOCALE_LABELS` + `DICTS` in `index.ts` + `detectLocale` |
45-
| Neue Tagline dazu | `src/lib/taglines.ts` pro Sprache |
46-
| Default-Sampling aendern | `src/lib/mediapipe-llm.ts#DEFAULT_OPTIONS` |
47-
| Deploy-Workflow | `.github/workflows/deploy.yml`; Base-Path via `PROJECT.basePath` |
48-
| Prompt-Format fuer anderes Modell | `src/lib/prompt-template.ts` |
49-
| PWA-Caching anpassen | `vite.config.ts#VitePWA.workbox.runtimeCaching` |
50-
51-
## Nicht tun
52-
53-
- Kein HTTP-Backend-Layer — der Point ist Serverless-Browser.
54-
- Keine OpenAI-SDK-Stubs — wir nutzen ausschliesslich `@mediapipe/tasks-genai`.
55-
- Keine `alert()` / `confirm()` — UI via React-Components.
56-
- Keine Model-Files in `public/` oder Git-LFS — nur WASM (via `copy-wasm`).
57-
- Keine harten `localhost`-URLs — Modelle kommen aus HF Hub (direkt) oder zukuenftig R2-Mirror (via Env).
40+
| Add a new model | extend `src/lib/model-catalog.ts` |
41+
| Add a UI component | `src/components/` + assistant-ui primitives |
42+
| Extend the persistence schema | bump `src/lib/db.ts` version (Dexie migration) |
43+
| Change branding | `src/lib/project.ts`, `public/icons/icon.svg`, `index.html` |
44+
| Add a language | new `src/lib/i18n/<locale>.ts` + `LOCALES` + `LOCALE_LABELS` + `DICTS` in `index.ts` + `detectLocale` |
45+
| Add a tagline | `src/lib/taglines.ts` per language |
46+
| Change default sampling | `src/lib/mediapipe-llm.ts#DEFAULT_OPTIONS` |
47+
| Deploy pipeline | `.github/workflows/deploy.yml`; base path via `PROJECT.basePath` |
48+
| Prompt format for a different model | `src/lib/prompt-template.ts` |
49+
| Adjust PWA caching | `src/sw.ts` (routes, plugins, COI header injection) |
50+
51+
## Don't do
52+
53+
- No HTTP backend layer — the point is serverless-in-browser.
54+
- No OpenAI-SDK stubs — we use `@mediapipe/tasks-genai` exclusively.
55+
- No `alert()` / `confirm()` — UI through React components only.
56+
- No model files in `public/` or Git-LFS — only WASM (via `copy-wasm`).
57+
- No hardcoded `localhost` URLs — models come from HF Hub directly or (future) an R2 mirror.
5858

5959
## Debugging
6060

61-
- Chrome DevTools → Application → Storage → OPFS: Model-Files sichtbar
62-
- Application → IndexedDB → `browser-llm-demo`: Conversations + Messages
63-
- Application → Service Workers: SW Status (nur in Production-Build aktiv)
64-
- Network → Filter `wasm`: sieht WASM-CacheFirst-Hits
65-
- `navigator.storage.estimate()` in Console: Quota-Status
61+
- Chrome DevTools → Application → Storage → OPFS: model files visible
62+
- Application → IndexedDB → `browser-llm-demo`: conversations + messages
63+
- Application → Service Workers: SW status (only active in production builds)
64+
- Network → filter `wasm`: WASM CacheFirst hits
65+
- `navigator.storage.estimate()` in console: quota status
66+
- `window.crossOriginIsolated` in console: must be `true` for threaded WASM
6667

67-
## Upstream-Referenz
68+
## Upstream reference
6869

69-
Port-Quellen (Apache-2.0):
70+
Port sources (Apache-2.0):
7071

7172
- `https://github.com/google-ai-edge/mediapipe-samples/tree/main/examples/llm_inference/llm_chat_ts`
72-
- `opfs_cache.ts` und `llm_service.ts` sind die Kern-Referenzen fuer OPFS- und MediaPipe-Interop.
73+
- `opfs_cache.ts` and `llm_service.ts` are the core references for OPFS + MediaPipe interop.
7374

7475
## Gotchas
7576

76-
- **iOS Safari < 18.4**: keine WebGPU → CPU-Fallback, E2B zaeh, E4B nicht sinnvoll
77-
- **OPFS-Quota unter iOS**: ~1 GB default, via `persist()` erweiterbar
78-
- **PWA Install-Prompt**: Android-Chrome automatisch, iOS manuell "Zum Home-Bildschirm"
79-
- **SharedArrayBuffer**: braucht COOP/COEP — bei GitHub Pages via `coi-serviceworker` (siehe DEC-008) automatisch
80-
- **HF-Rate-Limits**: unwahrscheinlich bei Direct-Downloads, aber bei vielen Users evtl. Mirror noetig
81-
- **Shader-Compile-Latenz**: 10–25 s beim ersten Load pro Session, danach Browser-WebGPU-Cache greift
82-
- **Firefox ~30–50 % langsamer** als Chrome (wgpu vs Dawn + OPFS SQLite). Feature, nicht Bug.
83-
- **Stop-Token-Leak**: Gemma emittet manchmal `<end_of_turn>` literal. Regex in `mediapipe-llm.ts#GEMMA_STOP_PATTERN` + `cancelProcessing()` fangen das
84-
- **Doppel-BOS vermeiden**: MediaPipes Tokenizer prepended BOS automatisch — nicht manuell in `renderGemmaPrompt` einfuegen
77+
- **iOS Safari < 18.4**: no WebGPU → CPU fallback, E2B is slow, E4B not viable
78+
- **OPFS quota on iOS**: ~1 GB default, extendable via `persist()`
79+
- **PWA install prompt**: automatic on Android Chrome, manual "Add to Home Screen" on iOS
80+
- **SharedArrayBuffer**: needs COOP/COEP — on GitHub Pages we inject them via `src/sw.ts` (see DEC-008)
81+
- **HF rate limits**: unlikely for direct downloads, but may need a mirror at scale
82+
- **Shader compile latency**: 10–25 s on first session load, then the browser WebGPU cache kicks in
83+
- **Firefox ~30–50 % slower** than Chrome (wgpu vs Dawn + SQLite-backed OPFS). Feature, not bug.
84+
- **Stop-token leak**: Gemma sometimes emits `<end_of_turn>` as a literal string. The regex in `mediapipe-llm.ts#GEMMA_STOP_PATTERN` + `cancelProcessing()` catch it.
85+
- **Avoid double BOS**: MediaPipe's tokenizer prepends BOS automatically — don't add one manually in `renderGemmaPrompt`.
86+
- **Two SWs collide**: never register multiple service workers for the same scope. All SW logic lives in `src/sw.ts`.

CHANGELOG.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ Initial public release. Ships as a scaffold for building browser-local LLM chat
2222
- i18n: German + English dictionaries, `navigator.language` auto-detect, `useT()` hook (~40 LOC, no framework)
2323
- Rotating privacy taglines on the landing page ("An LLM that doesn't phone home", etc.)
2424
- Key-facts footer: `0` server calls · `100 %` browser-local · `Apache 2.0` + GitHub link
25-
- PWA: manifest, service worker (CacheFirst for WASM), install prompt, offline indicator
25+
- PWA: manifest, installable, offline indicator, install prompt
26+
- Custom service worker (`src/sw.ts`) — Workbox precache + runtime cache for WASM + client-side COOP/COEP header injection so SharedArrayBuffer works on GitHub Pages (see DEC-008)
2627
- Atomic Design component structure: atoms · molecules · organisms · templates · pages
2728
- Dark-theme only (see DEC-005)
28-
- AsteriskAnimation atom for load/generation waiting states
29-
- Governance docs: DECISIONS.md, ROADMAP.md, CONTRIBUTING.md, AGENTS.md, CONTEXT.md, SECURITY.md
30-
- GitHub Actions workflows: CI (lint, typecheck, build, gitleaks) + Pages deploy
31-
- `coi-serviceworker` bundled — enables threaded WASM on hosts without custom headers (GitHub Pages)
29+
- `AsteriskAnimation` atom for load / generation waiting states
30+
- Governance docs: DECISIONS.md, ROADMAP.md, CONTRIBUTING.md, AGENTS.md, CONTEXT.md, SECURITY.md, RELEASING.md
31+
- GitHub Actions workflows: CI (lint, typecheck, build, TruffleHog secret scan) + Pages deploy
3232

3333
### Known Limitations
3434

CONTEXT.md

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Architektur
1+
# Architecture
22

3-
## Big Picture
3+
## Big picture
44

55
```
66
HF Hub (public) OPFS (Browser)
@@ -34,47 +34,54 @@
3434
Sidebar, ModelPicker)
3535
```
3636

37-
## Wichtige Dateien
37+
## Key files
3838

39-
| File | Rolle |
39+
| File | Role |
4040
|---|---|
41-
| `src/lib/model-catalog.ts` | Swappable Modell-Liste (Scaffold-Point) |
42-
| `src/lib/opfs-cache.ts` | Download + OPFS-Cache mit Progress + Abort |
43-
| `src/lib/mediapipe-llm.ts` | MediaPipe-Wrapper, Callback→Async-Generator Bridge |
44-
| `src/lib/prompt-template.ts` | Gemma-Turn-Formatting |
45-
| `src/lib/llm-store.ts` | LLM-Load-State (Zustand) |
46-
| `src/lib/chat-store.ts` | Conversation-State + Dexie-Persistenz (Zustand + Persist) |
47-
| `src/lib/db.ts` | Dexie-Schema (conversations, messages) |
48-
| `src/lib/capabilities.ts` | Browser-Fähigkeiten pruefen + Modell-Eignung |
49-
| `src/hooks/useLlmRuntime.ts` | `useExternalStoreRuntime` Adapter |
50-
| `src/components/ChatShell.tsx` | Top-Level-Layout, Model-Picker-Flow |
51-
| `src/components/ChatThread.tsx` | Thread + Composer (assistant-ui primitives) |
52-
| `scripts/copy-wasm.mjs` | `predev`/`prebuild` — MediaPipe-WASM aus node_modules nach public/wasm |
41+
| `src/lib/project.ts` | Project identity (name, displayName, GitHub URL, base path) |
42+
| `src/lib/model-catalog.ts` | Swappable model list (scaffold point) |
43+
| `src/lib/opfs-cache.ts` | HF download + OPFS cache with progress + abort |
44+
| `src/lib/mediapipe-llm.ts` | MediaPipe wrapper, callback → async-generator bridge |
45+
| `src/lib/prompt-template.ts` | Gemma turn formatting |
46+
| `src/lib/llm-store.ts` | LLM load state (Zustand) |
47+
| `src/lib/chat-store.ts` | Conversation state + Dexie persistence (Zustand + persist) |
48+
| `src/lib/db.ts` | Dexie schema (conversations, messages) |
49+
| `src/lib/capabilities.ts` | Browser capability checks + model eligibility |
50+
| `src/lib/settings-store.ts` | User-editable sampling, context, locale, system prompt |
51+
| `src/lib/i18n/{de,en,types,index}.ts` | Lean i18n (no framework) |
52+
| `src/lib/taglines.ts` | Rotating privacy taglines (per locale) |
53+
| `src/lib/asset-path.ts` | `import.meta.env.BASE_URL` prefixing for runtime asset refs |
54+
| `src/hooks/useLlmRuntime.ts` | `useExternalStoreRuntime` adapter |
55+
| `src/components/pages/ChatShell.tsx` | Top-level layout, model-picker flow |
56+
| `src/components/organisms/ChatThread.tsx` | Thread + composer (assistant-ui primitives) |
57+
| `src/sw.ts` | Service Worker — Workbox precache + COI header injection |
58+
| `scripts/copy-wasm.mjs` | `predev`/`prebuild` — copies MediaPipe WASM from node_modules to public/wasm |
5359

54-
## Datenfluss
60+
## Data flow
5561

56-
1. **Modell-Download**: `ChatShell``llm-store.load()``mediapipe-llm.load()``opfs-cache.loadModelWithCache()`Stream tee'd, eine Haelfte an MediaPipe, andere in OPFS
57-
2. **Inferenz**: User-Message via `useLlmRuntime.onNew()``chat-store.sendUserMessage()` → Dexie + Stream-Update `llm-store.llm.generate()` yields chunks → store-update → React re-render
58-
3. **Persistenz**: Messages nach Stream-Ende nach Dexie; Conversation-Liste via `useLiveQuery` reaktiv in Sidebar
62+
1. **Model download**: `ChatShell``llm-store.load()``mediapipe-llm.load()``opfs-cache.loadModelWithCache()`stream `tee()`'d, one half into MediaPipe, the other into OPFS
63+
2. **Inference**: user message via `useLlmRuntime.onNew()``chat-store.sendUserMessage()` → Dexie write + streaming state update `llm-store.llm.generate()` yields chunks → store update → React re-render
64+
3. **Persistence**: messages flushed to Dexie after stream end; conversation list reactive via `useLiveQuery` in the sidebar
5965

60-
## Kritische Patterns
66+
## Critical patterns
6167

62-
- **Callback→AsyncGenerator**: `mediapipe-llm.ts#generate()` — MediaPipe hat callback API, assistant-ui will AsyncGenerator
63-
- **Stream-tee**: `opfs-cache.ts#loadModelWithCache()` — ein Fetch, zwei Verbraucher (MediaPipe + Cache-Writer)
64-
- **Sidecar _size-File**: Cache-Validierung ohne vollstaendiges Re-Hash
65-
- **External-Store-Runtime**: statt `useLocalRuntime` — wir halten Messages selbst, assistant-ui rendert nur
68+
- **Callback → AsyncGenerator** in `mediapipe-llm.ts#generate()` — MediaPipe exposes a callback API, assistant-ui expects an AsyncGenerator
69+
- **Stream tee()** in `opfs-cache.ts#loadModelWithCache()` — one fetch, two consumers (MediaPipe + cache writer)
70+
- **Sidecar `_size` file** — cache validation without re-hashing multi-GB blobs
71+
- **External-store runtime** instead of `useLocalRuntime` — we own the message array; assistant-ui only renders
72+
- **Atomic Design layering** — atoms never import from molecules/organisms; organisms may import stores
6673

6774
## Mobile + PWA
6875

69-
- `viewport-fit=cover` + `env(safe-area-inset-*)` fuer Notches
70-
- `interactive-widget=resizes-content` fuer iOS-Keyboard
71-
- Service Worker: App-Shell precached, WASM CacheFirst, HF NetworkOnly
72-
- Model-Files leben in OPFS, NICHT im SW-Cache
76+
- `viewport-fit=cover` + `env(safe-area-inset-*)` for notches
77+
- `interactive-widget=resizes-content` for iOS keyboard
78+
- Service Worker: app-shell precached, WASM CacheFirst, HF bypasses the SW entirely
79+
- Model files live in OPFS, NOT in the SW cache
7380

74-
## COOP/COEP
81+
## COOP / COEP
7582

76-
MediaPipe nutzt SharedArrayBuffer → `Cross-Origin-Opener-Policy: same-origin` + `Cross-Origin-Embedder-Policy: require-corp` in `vite.config.ts`. HF-Hub-Downloads gehen via `fetch()` (keine Embed-Restriction).
83+
MediaPipe uses SharedArrayBuffer → needs `Cross-Origin-Opener-Policy: same-origin` + `Cross-Origin-Embedder-Policy: require-corp` + `Cross-Origin-Resource-Policy: cross-origin`. Dev server sets them via `vite.config.ts`. On GitHub Pages (which can't set custom headers), `src/sw.ts` injects them client-side via a Workbox plugin (`fetchDidSucceed` + `cachedResponseWillBeUsed`). HF Hub downloads go straight through `fetch()` and bypass the SW.
7784

78-
## Offene Entscheidungen / Roadmap
85+
## Open questions / roadmap
7986

80-
Siehe Plan-File und README. AG-UI-Protocol-Layer, MCP-Tools, RAG, i18n in der Roadmap.
87+
See `ROADMAP.md` and `README.md`. AG-UI protocol layer, MCP tools, RAG, voice I/O, light theme, Playwright tests are all tracked there.

0 commit comments

Comments
 (0)