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

7.8 KiB
Raw Blame History

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 dataURLurl (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


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

  1. 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