7.8 KiB
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:
- User dropped Bild → Whiteboard JS (
B0()mitimage-blob-reduce) - → Excalidraw JS (
initializeImage()) - → Canvas-Rendering + Base64-Encode
- → 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 - LOD Feature Request (seit 2022 offen): Issue #5334
- Image Resolution Bug: Issue #5334
8. Verifikation
Server-seitig verifizieren (nach jedem Patch):
# 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
- Alle Limits beseitigt (6 JS-Patches + Infrastruktur)
- 4096px-Cap setzen (Quick-Fix gegen OOM) — 2 Minuten, 12+ Bilder stabil
- URL-Referenzen implementieren (echte Lösung) — 1–2 Tage Entwicklungszeit
- Startup-Script für Persistenz über App-Updates
- Langfristig: Upstream-PR für konfigurierbare Limits + URL-Referenzen