# ROADMAP — Baustelle PWA Stand: 2026-10-19 · Gegenstück: `data/bericht` (Modul-Version 1.7.1) Die PWA hat kein eigenes Backend — alles läuft über `custom/bericht/api/`. Punkte, die eine API-Änderung brauchen, sind mit **[API]** markiert und stehen zusätzlich in der ROADMAP des Bericht-Moduls. --- ## Offen - [ ] `idb.queueAll()` lädt **alle** Foto-Blobs in den Speicher — auch `updateBadge()`, das nur zählen will. Bei 20 Originalen à 8 MB sind das 160 MB je Badge-Update. Braucht ein Zähl-Verfahren ohne Werte (Schlüssel + kleiner Meta-Store); Schema-Änderung, weil `sw.js`/`share.html` die DB mit fester Version 1 öffnen. - [ ] **Am Handy gegenprüfen** (geht nur auf dem Gerät, am Schreibtisch nicht nachstellbar): echte Kamera-Hardware mit „Fertig" im Funkloch, Background Sync bei ausgeschaltetem Display, und der Zwei-Finger-Zoom im PDF-Betrachter (lokal nur per CDP-Touch simuliert). Beides lief lokal nur mit nachgebildeter Kamera (`canvas.captureStream()`). Wenn etwas komisch aussieht: Tag + ungefähre Uhrzeit reichen, der Rest steht im Prod-Zugriffslog (Lesart siehe KB #208, Nachtrag 18.09.2026). **Bewusst nicht gebaut** (Entscheidungen, nicht vergessen): - Keine Schreib-Warteschlange für Auftragsdaten — bräuchte Idempotenz und Konfliktbehandlung (zwei Geräte am selben Auftrag). Lesen geht ohne Netz, ändern nur mit Netz; Fotos sind die Ausnahme (Persist-First-Queue). - Keine Zeichenfläche für echte Handschrift — am Handy ohne Stift unbrauchbar. Notizen werden getippt. - Kein Web Lock (`navigator.locks`) für die Foto-Queue — ein eingefrorener Tab hielte ihn ewig; deshalb Lease mit Ablauf in IndexedDB (Abschnitt 6.1). --- ## 10. Kamera vernünftig + offene Handy-Prüfungen im Emulator (Eddy 19.10.2026: „mach das vernünftig") Anlass: Nach der Blitz/Fokus-Gegenprobe (Punkt 9) meldet Chrome außerdem Zoom 1–10× und Belichtungskorrektur ±3. Der Emulator läuft jetzt (SVM an) — die bisher „nur am Handy" prüfbaren Punkte gehen dort mit echtem Android-Chrome. - [x] ✅ 19.10.2026 **Zoom mit zwei Fingern im Sucher** (`applyConstraints({advanced:[{zoom}]})`, nur wenn `caps.zoom` gemeldet) + Anzeige „1×"/„2,5×" unten im Sucher (gelb, sobald gezoomt), Antippen der Anzeige → zurück auf 1×. Während der Geste immer nur ein Aufruf an die Kamera, Zwischenwerte verfallen; Ende der Geste löst keinen Fokus-Tipp aus. Im Emulator mit **echter Zwei-Finger-Geste** (Emulator-Konsole → Touchscreen, siehe unten) geprüft: Fingerabstand ×2,87 → 2,9×, zusammen → 1×, kein Fokus-Aufruf/Klick während der Gesten; Foto bei 2,5× zeigt genau den Sucher-Ausschnitt. Chrome 153 zoomt über `control.zoomRatio` (nicht Ausschnitt) — die Fokus-/Belichtungs- bereiche gelten dann laut AOSP-Definition relativ zum gezoomten Bild, die Tippstelle passt also auch im Zoom. - [x] ✅ Belichtung: **kein eigener Regler** — Antippen setzt bei Chrome Fokus-, Belichtungs- und Weißabgleich-Bereich gemeinsam; im Emulator gemessen: `aeRegions` = `afRegions` = `awbRegions` = Tippstelle → Display/Typenschild antippen = dort scharf UND richtig belichtet. - [ ] Emulator: echte Kamera + „Fertig" im Funkloch (Fotos gesichert, nach Netz-Rückkehr hochgeladen, Server zählt genau +n) - [ ] Emulator: Hochladen bei ausgeschaltetem Display (Background Sync) - [ ] Emulator: 8–10 Fotos auf einmal aus der Galerie (Fix aus Punkt 7) - [ ] Emulator: Zwei-Finger-Zoom im PDF-Betrachter mit echtem Mehrfinger-Touch (statt CDP-Nachbildung) ## 8. Knopf „Geteiltes ablegen" bleibt dauerhaft stehen (gemeldet 2026-10-19) Eddy: *„bei der App Baustelle ist permanent ein Button relativ weit unten angezeigt über den aufgelisteten Aufträgen: Geteiltes ablegen"*. - Ursache: `checkPendingShare()` zeigte den Knopf, sobald IRGENDEIN `shared_payload` in IndexedDB lag — auch ein leeres (Teilen abgebrochen, nichts mitgekommen). `share.html` meldete dann „Es kam nichts an" und ließ den Rest liegen → Knopf für immer. Wer etwas Geteiltes nicht ablegen wollte, kam ebenfalls nicht davon los. - [x] ✅ 19.10.2026: Leere Pakete räumt die App selbst weg (kein Knopf); „Es kam nichts an" löscht den Rest; Knopf zeigt „(Notiz)", wenn nur Text geteilt wurde; `share.html` hat „Verwerfen" mit Rückfrage in der Seite. Auf der Testinstanz durchgespielt (leer → kein Knopf; Notiz → Knopf → Verwerfen → weg). - [x] ✅ 19.10.2026 (Eddy: „unten am Rand, nicht auf den Aufträgen"): Knopf ist jetzt eine Leiste in der App-Spalte direkt über der Navigation (`position: sticky; bottom: 0`) statt fest schwebend; der FAB rückt eine Leistenhöhe hoch. Auf 412 × 860 gemessen: kurze Liste → Leiste über der Navigation, nichts verdeckt; lange Liste → Leiste klebt am unteren Rand, am Ende lässt sich alles darüber scrollen. - [ ] Am Handy gegenprüfen: Knopf ist nach dem Update weg (beim ersten Laden räumt die App den leeren Rest ab). ### Befunde aus der AVIF-Prüfung in Bericht (19.10.2026) - [x] ✅ 19.10.2026: Ist eine Maximalgröße für Fotos gesetzt (`photo_maxside` > 0), behielt das im Browser verkleinerte JPEG seinen Originalnamen — ein `.avif`/`.webp` lehnte der Server ab. Jetzt zieht `offline.replaceQueuedBlob()` die Endung auf `.jpg` nach (gilt für Galerie, Teilen und Kamera). - [x] ✅ 19.10.2026: Beim Verkleinern im Browser (Canvas → JPEG) wurde Transparenz schwarz — jetzt weißer Grund. Lokal geprüft: Queue-Eintrag „bild.avif" (PNG mit Transparenz) → „bild.jpg", Ecke weiß. ## 9. Kamera: Blitz und Fokus einstellbar (gemeldet 2026-10-19) Eddy: *„beim Fotografieren kann man den Blitz nicht einstellen und den Fokus auch nicht"*. - [x] ✅ 19.10.2026: Blitz-Knopf in der Kopfzeile, schaltet reihum durch, was das Gerät meldet: Aus → Auto → An (`ImageCapture.getPhotoCapabilities().fillLightMode`, wirkt beim Auslösen über `takePhoto({fillLightMode})`; klappt das nicht, wird ohne Einstellung ausgelöst) → 🔦 Licht (Dauerlicht, `track.getCapabilities().torch`, nach dem Auslösen erneut gesetzt). Meldet das Gerät nichts davon, gibt es keinen Knopf. Auswahl bleibt gemerkt (`localStorage baustelle.kamera.blitz`). - [x] ✅ 19.10.2026: Antippen stellt dort scharf (`pointsOfInterest` + `focusMode`), Ring an der Stelle, einmaliger Hinweis „Antippen stellt dort scharf". - [x] Lokal geprüft mit nachgebildeter Kamera (Canvas-Strom + gemeldete Fähigkeiten): Reihenfolge der Modi, Licht an/aus, `takePhoto({fillLightMode:"flash"})`, Antippstelle richtig umgerechnet (eingepasste Vorschau, 0,803 statt 0,75 bei seitlichem Rand); ohne Fähigkeiten kein Knopf, keine Wirkung. - [x] ✅ 19.10.2026 abends: **Gegenprobe im Android-Emulator** (AVD `fluke`, Chrome 153, emulierte Rückkamera mit Blitz + AF-Bereich) — echter Camera2-Weg, gemessen per `adb shell dumpsys media.camera` („Last request sent"): | Einstellung | was bei der Kamera ankommt | Ergebnis | |---|---|---| | ⚡ Aus | `aeMode ON`, `flash.mode OFF` | ✓ | | ⚡ Auto, Auslösen | Standbild `aeMode ON_AUTO_FLASH` | ✓ Kamera entscheidet | | ⚡ An, Auslösen | Standbild `aeMode ON_ALWAYS_FLASH`, `flash.mode SINGLE` | ✓ wie Kamera-Apps | | 🔦 Licht | `flash.mode TORCH`, bleibt nach dem Auslösen an | ✓ | | Antippen (alt) | `afRegions` an falscher Stelle, `afMode` bleibt `CONTINUOUS_PICTURE` | ✗ zwei Fehler | **Fehler 1 — Tippstelle ungedreht:** Chrome rechnet `pointsOfInterest` direkt auf die Sensorfläche um (`VideoCaptureCamera2.java`, keine Drehung), das Sucherbild kommt aber gedreht an (Sensor quer, Bild hochkant). Tipp bei 67 % / 82 % → Bereich bei 67 % / 82 % des quer liegenden Sensors statt 82 % / 33 %. Jetzt dreht `sensorPunkt()` zurück (Rückkamera: Sensor 90° − Bildschirmwinkel; Nothing A059 hat 90°, siehe takePhoto- Drehmarke 6 in KB #1494). Gemessen: hochkant ✓ (zwei Stellen), quer 90° ✓ (1:1), quer 270° ✓ (180°). **Fehler 2 — „Fokus fest" log:** `single-shot` nimmt Chrome ohne Fehler an (auch mit `exact`), setzt auf Android aber nichts um — `AF_TRIGGER_START` kommt im Quelltext nicht vor, der Modus bleibt `CONTINUOUS_PICTURE`, und die gefundene Entfernung meldet Chrome nicht zurück (nur die eigene Vorgabe). Ein echtes Festhalten geht über die Web-Schnittstelle also nicht. Jetzt: Antippen = Dauer-Autofokus **auf die Tippstelle**, der Hinweis „🎯 Fokus fest" ist entfernt. - [x] ✅ 19.10.2026 (Eddy: „den hässlichen Wechselbutton lässt du da doch nicht"): 🔄-Emoji (erschien als blaues Kästchen) → weißes Material-Symbol wie ✕; Knopf nur, wenn `enumerateDevices()` mindestens zwei Kameras meldet. - [ ] **Am Handy beim nächsten Einsatz ansehen** — der Weg bis zur Kamera ist belegt, offen ist nur das Gerät selbst: blitzt das Nothing bei „An" sichtbar, und springt der Fokus nach dem Antippen sofort auf die Stelle (ohne AF-Auslöser hängt das vom Kamera-Treiber ab). Wenn nicht: Tag + Uhrzeit reichen. ## 7. Galerie-Auswahl: „permission"-Fehler ab dem 3. Foto (gemeldet 2026-09-30) Befund (Prod-Zugriffslog 30.09., Auftrag 114 / SO2609-0088, JHB Bio): erster Anlauf 19:40:21 — **nur 2** `upload_photo`-POSTs, danach rote Toasts mit „permission". Der Server hat die übrigen Fotos nie gesehen; zweiter Anlauf 19:41:48 lief mit 8 POSTs durch. Ursache: `handleFiles()` las jedes Foto erst, nachdem das vorige hochgeladen war. Chrome/Android gibt die Galerie-Dateien nur kurz frei (bzw. prüft, ob sie seit der Auswahl verändert wurden) → `NotReadableError` („could not be read, typically due to permission problems…"). Die betroffenen Fotos landeten nicht in der Queue. - [x] ✅ Fix live seit 30.09.2026 (Commit b824e95): erst **alle** Dateien sofort in den Speicher lesen (`readFileNow`, ein Wiederholversuch nach 500 ms), dann hochladen. Unlesbare Datei → klare Meldung „bitte nochmal auswählen". - [ ] Am Handy gegenprüfen: 8–10 Fotos aus der Galerie auf einmal auswählen. ## 6. Rauswurf aus dem Auftrag + unsichtbare Warteschlange (gemeldet 2026-09-18) Gemeldet von Eddy: am 17.09.2026 gegen 18 Uhr mehrere Fotos in einem Auftrag gemacht, „Fertig" getippt — die App stand danach auf der **Auftragsliste** statt im Auftrag. Wieder in den Auftrag gegangen: **keine Fotos zu sehen**, kein Hinweis, dass sie noch warten. ### Befund (Prod-Zugriffslog 17.09., Auftrag 111 — kein Raten) - 17:30:56 `POST orders.php?action=create` — Auftrag 111 über den ➕-Knopf angelegt. In derselben Sekunde laufen **zwei** Routen gleichzeitig: `orders.php` (Liste) **und** `orders.php?id=111` (Detail). Das ist die Signatur des Fehlers. - 17:40:45 `verify.php` + `orders.php` (Liste) — der Rauswurf. **Kein** `index.php` davor, also kein Reload, kein SW-Update, kein Logout (kein 401), kein Absturz. - 17:40:47 Auftrag 111 erneut geöffnet, `action=photos` liefert eine **leere** Liste; die Uploads kommen erst 17:40:54 und 17:57:25–17:58:22 an. **Ursache Rauswurf — History-Race in `openNewOrderModal`:** ```js closeModal(modal); // -> history.back() (asynchron!) router.go('#/orders/' + res.order.id); // -> location.hash = … (sofort) ``` `history.back()` wird erst nach dem Hash-Wechsel ausgeführt und springt dann **hinter** den neuen Eintrag zurück. Ergebnis: Auf dem Bildschirm steht der Auftrag (seine Route wurde als letzte fertig), in der Adresszeile steht aber `#/orders`. Alles, was danach `router.navigate()` ruft — z. B. „Fertig" in der Kamera — zeichnet die **Auftragsliste**. Nachgestellt in Chromium (18.09.2026): ohne Abwarten endet der Ablauf auf `#/orders`, mit abgewartetem `popstate` auf `#/orders/111`. Dieselbe Falle an zwei weiteren Stellen: „Neuen Bericht anlegen" (`#/reports/`) und Lieferschein-Unterschrift (`#/shipments/`). Betroffen ist nur, wer den Auftrag **in derselben Sitzung frisch angelegt** hat — deshalb fiel es nicht früher auf. **Ursache „keine Fotos, keine Meldung":** Die Auftragsseite zeigt ausschließlich, was der Server schon hat. Fotos in der Warteschlange erscheinen nirgends außer als Zahl im kleinen Badge oben rechts (🔴 3 / 🟡 3), und die Seite zeichnet sich nach einem im Hintergrund fertig gewordenen Upload nicht neu. Verloren ging nichts — alle Fotos kamen an. ### Umsetzung (2026-09-18) - [x] **Race zentral behoben:** `closeModal()` zählt selbst ausgelöste `history.back()` (`_pendingBacks`, Zähler statt Boolean, KB #1209 — der Boolean verschluckt bei zwei schnell nacheinander geschlossenen Dialogen ein `popstate`), `router.go()` wartet über `window.historySettled()`, bis alle durch sind, und wechselt erst dann den Hash (Schutzzeit 1,5 s, falls ein `popstate` ausbleibt). Alle Aufrufstellen bleiben wie sie sind. `closeModal()` nimmt den History-Eintrag jetzt **vor** dem Cleanup zurück, damit ein navigierender Cleanup den Rücksprung sieht; `pushModal()` wartet ebenfalls. - [x] **Sicherheitsnetz in Kamera und Galerie-Auswahl:** nach „Fertig" ausdrücklich `router.go('#/orders/')` statt `router.navigate()` — gezeichnet wird der Auftrag, für den fotografiert wurde, nicht „was gerade in der Adresszeile steht". - [x] **Wartende Fotos am Auftrag:** Block „⏳ Warten auf Upload (n)" über „Hochgeladene Fotos", mit Vorschaubildern direkt aus der Warteschlange (max. 12, danach „+n") und Klartext („📴 Kein Netz — 3 Fotos sind auf dem Gerät gesichert und werden gesendet, sobald wieder Netz da ist."), fehlgeschlagene rot markiert; Tipp öffnet die Warteschlange. - [x] **Auftragsseite zieht nach:** bei `photo-uploaded` für diesen Auftrag wird nach 2,5 s Ruhe neu gezeichnet (Scrollposition bleibt) — nicht, solange ein Modal offen ist, die Mehrfachauswahl läuft oder eine Sprachnotiz abgespielt wird. - [x] **Hinweis beim Schließen der Kamera:** „📴 Kein Netz — n Fotos auf dem Gerät gesichert, Upload folgt automatisch" bzw. „⏳ n Fotos warten noch auf den Upload". ### Verifiziert (2026-09-18, lokale Testinstanz, Chromium, Auftrag 62) - Ablauf „Modal zu + `router.go`": endet auf `#/orders/62` (vorher `#/orders`) - **Echtes Funkloch** (`context.setOffline(true)`): 3 Fotos gesichert, „Fertig" → bleibt im Auftrag, Offline-Band, Block mit 3 Vorschaubildern und Klartext, Badge 🔴 3 - Netz zurück: Upload läuft an, Block verschwindet von selbst, „Hochgeladene Fotos" 2 → 5, Badge 🟢, Warteschlange leer. Testfotos danach wieder entfernt. - **Nicht** getestet: die echte Kamera (`getUserMedia`) am Handy — der Test hat den Kamera-Cleanup nachgestellt, nicht die Kamera selbst bedient. ### 6.1 Doppel-Uploads: Seite und Service Worker laden dasselbe Foto hoch Log 17.09.: mehrere Antworten mit `duplicate:true`. `syncQueue()` (Seite) und `drainQueue()` (Service Worker, Background Sync) lesen dieselbe IndexedDB-Queue und wissen nichts voneinander. Dank md5-Abgleich (Bericht 1.6.0) folgenlos — kostet aber im Mobilfunk das doppelte Datenvolumen, bei 8-MB-Originalen spürbar. **Schlimmer, bisher unbemerkt:** Ist im Admin ein Verkleinern eingestellt, sichert die Kamera erst das Original und ersetzt es danach durch die kleine Fassung. Greift der Service Worker dazwischen zu, lädt er das **Original** hoch, die Seite danach die **kleine Fassung** — unterschiedlicher Inhalt, der md5-Abgleich greift nicht, es liegen **zwei Dateien** im Auftrag. Umgesetzt (2026-09-18) — Lease je Item in IndexedDB (kein Web Lock: ein eingefrorener Tab hielte den ewig, und genau für den eingefrorenen Tab gibt es den Background Sync): - [x] `idb.queueKeys()` / `queuePatch(id, fn)` / `queueClaim(id, owner, leaseMs)` / `queueRelease()` — Lesen-Ändern-Schreiben in **einer** readwrite-Transaktion (über Kontexte hinweg atomar). Gegenstück in `sw.js` (`queueClaim`/`queueRelease`) — **beide Stellen zusammen ändern**, der Worker kann `lib/idb.js` nicht laden (`window`). - [x] `syncQueue()` und `drainQueue()` holen sich jedes Item per Claim; ist es frisch vom anderen belegt, wird es übersprungen (im Worker zählt es als „offen", damit der Browser nachfasst). Lease 90 s (> längster Upload-Timeout 60 s), danach darf der andere übernehmen. Besitzer je Seiten-/Worker-Instanz eindeutig (`page-…` / `sw-…`). Nebeneffekt: es liegt nur noch **ein** Foto gleichzeitig im Speicher statt aller. - [x] Fehlschlag gibt die Lease sofort frei; Versuchszähler/Quarantäne per Patch statt „ganzes Item zurückschreiben" (überschrieb bisher eine parallele Änderung). Ebenso `retryFailed()`. - [x] `enqueuePhoto(…, {hold:true})` + `releaseHold()`: soll verkleinert werden, kommt das Foto **mit** Lease (`page-…:resize`) in die Queue; freigegeben wird im `finally` nach dem Verkleinern. Vergisst es jemand oder stirbt die Seite: Ablauf nach 90 s. - [x] `replaceQueuedBlob()` ersetzt per Patch ### 6.2 SW-Update lädt mitten in der Arbeit neu Beim Test beobachtet: `controllerchange` → `location.reload()` (index.php, Muster KB #201) feuerte, während drei Fotos in der Warteschlange lagen; der Browser zeigte dazu seinen eigenen „Seite verlassen?"-Dialog (`beforeunload`). War am 17.09. **nicht** die Ursache (kein Deploy, kein `index.php` im Log), trifft aber jeden, der nach einem Deploy arbeitet: offene Kamera weg, getippte Notiz weg, halbe Unterschrift weg. - [x] `window.appBusy()` in app.js: Modal offen, Mehrfachauswahl, laufender Upload (`offline.isSyncing()`), Eingabefeld im Fokus, laufende Sprachnotiz. **Nicht** beschäftigt: PIN-Sperre — dort ist der beste Moment zum Neuladen, sonst kommt die PIN-Abfrage zweimal. Suchfelder zählen nicht (behalten am Handy den Fokus, der Reload käme nie). - [x] index.php: Reload nur, wenn nicht beschäftigt; sonst alle 3 s und bei jedem Sichtbarkeitswechsel erneut prüfen - [x] Eigener Reload setzt `window.__selfReload` → `beforeunload` warnt dann nicht (die Fotos liegen in IndexedDB, ein Reload verliert nichts). Die Warnung beim echten Schließen bleibt. ### Verifiziert 6.1 + 6.2 (2026-09-18, lokale Testinstanz, Chromium, Auftrag 62) - **Echte Kamera-Oberfläche** mit nachgebildetem Kamerabild (`canvas.captureStream()` statt Hardware), offline: 3× Auslöser, „Fertig" → bleibt im Auftrag, Toast „📴 Kein Netz — 3 Fotos auf dem Gerät gesichert, Upload folgt automatisch", Block mit 3 Vorschaubildern, Badge 🔴 3 - **Gleichzeitiger Zugriff:** Netz an, Seite und Service Worker greifen parallel zu — der Worker lud Foto 4 und 6, die Seite Foto 5. Serverlog: **genau 3** `upload_photo`-POSTs für 3 Fotos, keine Antwort mit `duplicate:true` - **Verkleinern:** während des Verkleinerns ist das Item von `page-…:resize` belegt; auf dem Server liegt **eine** Datei mit 41.832 Bytes (die kleine Fassung), nicht das 208-KB-Original - **Fremde Lease:** frisch → wird respektiert (kein Upload, bleibt in der Queue); künstlich abgelaufen → wird übernommen und hochgeladen - **Reload:** `controllerchange` bei offenem Notiz-Dialog → 4,5 s kein Reload; Dialog zu → Reload nach 0,7 s, gleicher Auftrag, **kein Browser-Dialog**, obwohl ein Foto in der Warteschlange lag. Konsole ohne Fehler. Testdaten danach wieder entfernt. - **Nicht** getestet: echte Kamera-Hardware und echter Background Sync bei ausgeschaltetem Display am Handy — beides geht nur auf dem Gerät. --- ## Erledigt (gekürzt — Einzelheiten stehen im Commit und in der KB) | Wann | Was | Commit | KB | |---|---|---|---| | 2026-10-15 | Zurück-Knopf mit Pfeil und Wort; **ein** PDF-Betrachter mit Zoom (Knöpfe, Zwei-Finger, Doppeltipp, Strg+Rad) für Bericht, Lieferschein und Dokumente — der alte Dokumenten-Betrachter hatte keinen Zoom | `55b9a4f`, `a6cdc7b` | #478 | | 2026-09-18 | Abschnitt 6 oben: Rauswurf nach „Fertig", wartende Fotos am Auftrag, keine Doppel-Uploads, Reload erst wenn nichts offen ist | `64ebbf7`, `30202f8` (deployt `292c251`) | #1444, #1445, #1446 | | 2026-08-31 | Offline lesbar statt „Failed to fetch": Netzfehler als `offline = true`, Datenspiegel in IndexedDB (Listen + 30 Details), Band „📴 Kein Netz — Stand von HH:MM", Suche im Spiegel, Spiegel weg beim Abmelden, SW liefert immer eine echte Response, nur vollständige Antworten cachen, Precache per `PRECACHE`-Nachricht nach Deploy | `a108664` | #1261, #1037, #1041 | | 2026-08-30 | Teilen aus WhatsApp: Anmeldung über `verify.php` statt totem JWT, Anmelden in der Teilen-Seite, Text/Titel/Link werden mitgesichert, Bilder über die Queue, Text als Notiz, Banner für Unabgelegtes, „Zuletzt benutzt" + 30-Minuten-Vorauswahl | `7076388`, `eaad796` | — | | 2026-08-28 | Foto-Serie übersteht Display-Aus: Persist-First streng (erst sichern, dann verkleinern), Schutzzeit im Verkleinern, Wake Lock, Background Sync, volle Kameraauflösung über `ImageCapture` | `65bc09d`, `130d9d1`, `c2bf4e7` | #840 | | 2026-08-22 | Aus Bericht 1.5.x nachgezogen **[API]**: echte Thumbnails (`photo.php?size=thumb`), Sortierung nach Aufnahmezeit (`taken_at`), Seiten-Titel, `composite_path` + Layout-Kennzeichen | `4a53ab0` | #1142, #1133 | | 2026-08-22 | Zehn Browser-Dialoge durch `confirmDialog`/`inputDialog`/`alertDialog` ersetzt; Bild-Cache `baustelle-media` (cache-first, LRU 300, überlebt Deploys, leer beim Abmelden); Textnotizen (`api/note.php`, `notiz_*.txt`) | `4a53ab0` | — | | 2026-08-22 | Nebenbei: `.modal-header`/`.close-btn`/`.modal-body` hatten gar kein CSS; Thumbnail-Laden überschrieb Auswahl-Häkchen und Seitennummer der Kachel | `4a53ab0` | — | | davor | SSO-Migration auf awlauth, WebAuthn-Login, Neuer-Auftrag-Formular, Live-Kamera mit Persist-First-Queue, Lieferschein-Unterschrift | siehe `git log` | #208, #841, #855 |