Files
whiteboard-url-refs/docs/01-reference.md
T

199 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Whiteboard Image Limits — Komplettdokumentation
> **Letztes Update:** 2026-07-02 · **Status:** 6 JS-Patches aktiv, OOM-Problem identifiziert, URL-Referenzen als Lösung geplant
---
## 1. Problem
Das Nextcloud Whiteboard (Excalidraw) hat mehrere hartkodierte Limits für Bildimporte:
| Limit | Default | Effekt |
|---|---|---|
| Downscale auf max. Kantenlänge | **1440px** (Excalidraw) + **1440px** (Whiteboard-eigener `image-blob-reduce`) | Bilder schrumpfen auf Briefmarken-Größe |
| Dateigrößen-Check | **4 MB** (Excalidraw) + **4 MB** (Whiteboard) | Größere Bilder werden abgelehnt |
| RAM-Verbrauch (Base64 im JSON) | **~50 MB/Bild** (4000×3000px decoded) | Ab ~3 Bildern → Chrome OOM-Crash |
Alle diese Limits sind **doppelt** vorhanden: einmal im Excalidraw-Core (`percentages-*.chunk.mjs`), einmal in der Whiteboard-Bridge (`NcSelect-*.chunk.mjs`).
---
## 2. Architektur: Warum kein Docker-Neustart nötig ist
```
Docker Image (read-only) Docker Volume (read-write)
┌──────────────────────────┐ ┌─────────────────────────────┐
│ Nextcloud PHP │ │ /custom_apps/whiteboard/js/ │
│ Apache/Nginx Config │ │ ├─ NcSelect-CknHatsl.mjs │ ← Patches #3#6
│ System-Libs │ │ ├─ percentages-*.mjs │ ← Patches #1#2
│ │ │ └─ whiteboard-*-patch.js │ ← Scroll + Quality
│ (NIE angefasst) │ │ │
└──────────────────────────┘ └─────────────────────────────┘
Persistiert über Restarts,
NICHT über App-Updates
```
**Der gesamte Bildverarbeitungs-Pipeline läuft im Browser:**
1. User dropped Bild → Whiteboard JS (`B0()` mit `image-blob-reduce`)
2. → Excalidraw JS (`initializeImage()`)
3. → Canvas-Rendering + Base64-Encode
4. → Upload JSON an Nextcloud (PHP macht **nichts** mit dem Bild)
Deshalb reicht ein **Browser-Cache-Refresh (Cachebuster)** — kein Container-Neustart.
---
## 3. Alle Patches (Stand 2026-07-02)
### 3.1 Scroll-Zoom (Miro-like)
**Datei:** `scripts/whiteboard-scroll-patch.js`
- Scrollen = Zoomen, Mittelklick-Drag = Pannen
- Registriert in `LoadViewerListener.php`, `WhiteboardDirectEditor.php`, `RecordingController.php`
### 3.2 Image Quality (Post-Processing)
**Datei:** `scripts/whiteboard-image-patch.js`
- Verhindert Qualitätsverlust durch Excalidraws internes `imageBlobReduce`-Postprocessing
### 3.3 `IM` und `TM` — Whiteboard-eigene Limits
**Datei:** `percentages-BXMCSKIN-D6x-nnv3.chunk.mjs`
```
...Cl=2,Ff=[1,2,3],dl=10,IM=1/0,TM=100*1024*1024,je=...
```
| Konstante | Vorher | Nachher | Bedeutung |
|---|---|---|---|
| `IM` | `1440` | `1/0` (=∞) | Max. Kantenlänge für `image-blob-reduce` |
| `TM` | `4*1024*1024` | `100*1024*1024` | Max. Dateigröße für Whiteboard-Preprocessing |
### 3.4 `maxImageSizeBytes` — Excalidraw-Fallback
**Datei:** `NcSelect-CknHatsl.chunk.mjs`
```
maxImageSizeBytes:F||9e15
```
| Vorher | Nachher |
|---|---|
| `F` (Nextcloud Config, `undefined` → 4MB Hardcoded) | `F\|\|9e15` (Fallback ≈ unendlich) |
### 3.5 `maxWidthOrHeight` + `imageOptions` — Excalidraw-Limits
**Datei:** `NcSelect-CknHatsl.chunk.mjs`
```
maxWidthOrHeight:8192,imageOptions:{maxWidthOrHeight:1/0,maxFileSizeBytes:1/0}
```
| Property | Vorher | Nachher |
|---|---|---|
| `maxWidthOrHeight` | `2048` (harter Reject) | `8192` |
| `imageOptions.maxWidthOrHeight` | `1440` (Downscale) | `1/0` (=∞) |
| `imageOptions.maxFileSizeBytes` | `4*1024*1024` (fileTooBig) | `1/0` (=∞) |
### 3.6 Infrastruktur
| Ebene | Einstellung | Wert |
|---|---|---|
| Nextcloud DB | `occ config whiteboard maxFileSize` | `100` (MB) |
| PHP | `upload_max_filesize` | `512M` |
| PHP | `post_max_size` | `512M` |
| PHP | `memory_limit` | `512M` |
| Nginx | `client_max_body_size` | `0` (unlimited) |
| Nginx | Whiteboard JS/CSS `Cache-Control` | `no-cache, no-store, must-revalidate` |
| Cache | `theming cachebuster` | **26** |
---
## 4. Warum das nicht reicht (OOM-Problem)
Mit `IM=1/0` und `imageOptions.maxWidthOrHeight=1/0` wird kein Downscaling mehr gemacht. Das bedeutet aber: **Jedes importierte Bild wird in voller原生auflösung als Base64 im Excalidraw-JSON gespeichert.**
**RAM-Verbrauch pro Bild:**
```
Breite × Höhe × 4 Bytes (RGBA decoded) + Base64-String (~33% Overhead)
5000 × 2000 × 4 = 40 MB decoded + 13 MB Base64 = ~53 MB RAM/Bild
4000 × 3000 × 4 = 48 MB decoded + 16 MB Base64 = ~64 MB RAM/Bild
```
**Bei 3 Bildern → ~160 MB + UI-Overhead → OOM-Crash nach ~2 Minuten.**
---
## 5. Excalidraw vs. Miro: Der fundamentale Unterschied
| | Excalidraw/Whiteboard | Miro |
|---|---|---|
| Bild-Speicherung | **Base64 inline im JSON** | URL-Referenz auf Server |
| Rendering | Canvas 2D, alle Bilder immer im RAM | WebGL + Tiled-Streaming |
| LOD | ❌ Nicht vorhanden | ✅ Nur sichtbare Kacheln geladen |
| RAM pro 4K-Bild | ~50 MB | ~0 MB (lazy, GPU-Textur) |
| 50× 4K-Bilder | 💥 unmöglich | ✅ problemlos |
Miro speichert nur **URLs** (100 Bytes statt 50 MB) und lädt Bilder erst beim Hinscrollen.
---
## 6. Geplanter Fix: URL-Referenzen statt Base64
### Ansatz
Statt das Bild als `data:image/png;base64,...` im JSON zu speichern, soll nur eine **URL** gespeichert werden. Der Browser lädt das Bild dann nur bei Bedarf.
```
// JETZT:
"files": { "abc123": { "dataURL": "data:image/png;base64,iVBORw0K...", ... } }
// ZIEL:
"files": { "abc123": { "url": "/remote.php/dav/files/.../image.png", ... } }
```
### Was geändert werden muss
| Komponente | Änderung | Aufwand |
|---|---|---|
| **Whiteboard Import** | Bild als Nextcloud-Datei speichern (WebDAV), nicht als Base64 | JS-Patch |
| **Excalidraw JSON-Format** | `dataURL``url` (relativer Pfad) | JS-Patch |
| **Excalidraw Rendering** | `Image()` vom Server laden statt aus Base64 | JS-Patch |
| **Nextcloud CORS** | `Access-Control-Allow-Origin` für Bild-URLs | Config |
| **File-ID Mapping** | `fileId` → Nextcloud-Dateipfad | JS-Patch |
### Risiken
- Alles JS-Patches auf dem Volume → gehen bei App-Update verloren
- Startup-Script zur Wiederherstellung nötig
- Langfristig: Upstream-PR an Excalidraw/Whiteboard
---
## 7. Direkter Link zum Excalidraw-Upstream
- **DEFAULT_IMAGE_OPTIONS** (Source of Truth): [`packages/common/src/constants.ts#L340-L342`](https://github.com/excalidraw/excalidraw/blob/master/packages/common/src/constants.ts)
- **LOD Feature Request** (seit 2022 offen): [Issue #5334](https://github.com/excalidraw/excalidraw/issues/5334)
- **Image Resolution Bug**: [Issue #5334](https://github.com/excalidraw/excalidraw/issues/5334)
---
## 8. Verifikation
Server-seitig verifizieren (nach jedem Patch):
```bash
# NcSelect
curl -s "https://nextcloud.niklashmotion.art/custom_apps/whiteboard/js/NcSelect-CknHatsl.chunk.mjs?v=26" \
| grep -oP 'maxImageSizeBytes[^,]+|imageOptions:\{[^}]+\}'
# Percentages
curl -s "https://nextcloud.niklashmotion.art/custom_apps/whiteboard/js/percentages-BXMCSKIN-D6x-nnv3.chunk.mjs?v=26" \
| grep -oP 'IM=[^,]+|TM=[^,]+'
```
Automatisiert: `scripts/verify-whiteboard-patches.sh` (im Skill-Verzeichnis).
---
## 9. Nächste Schritte
1. [x] Alle Limits beseitigt (6 JS-Patches + Infrastruktur)
2. [ ] **4096px-Cap setzen** (Quick-Fix gegen OOM) — 2 Minuten, 12+ Bilder stabil
3. [ ] **URL-Referenzen implementieren** (echte Lösung) — 12 Tage Entwicklungszeit
4. [ ] Startup-Script für Persistenz über App-Updates
5. [ ] Langfristig: Upstream-PR für konfigurierbare Limits + URL-Referenzen