diff --git a/README.md b/README.md index 18000f2..9c823d3 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,14 @@ App nennt den Stapel. Verglichen wird über alle Merkmale, die bei Modul und Stapel bekannt sind — Kapazität, Bauform, Rank, Geschwindigkeit und, sofern gelesen, Hersteller-Teilenummer (`specsCompatible` in `src/spec.js`); ein auf einer Seite fehlendes Merkmal wird dabei übersprungen, nicht als Widerspruch -gewertet. Passt danach genau ein Stapel, wird das Modul zugewiesen; passen -mehrere, fragt die App nach (`proposeAssignment` in `src/session.js`). Das hat -eine bewusst in Kauf genommene Folge: Konnte die Teilenummer nicht gelesen +gewertet. Ein Stapel gilt dabei nur als Kandidat, wenn er mit dem Modul +mindestens ein tatsächlich erkanntes Merkmal teilt (`sharesSetField` in +`src/session.js`) — ein Stapel ganz ohne erkannte Angaben (etwa aus einem +manuell entschiedenen Rot-Fall) ist deshalb nie automatisch treffend, sonst +würde er lautlos jedes folgende Modul an sich ziehen. Passt danach genau ein +Stapel, wird das Modul zugewiesen; passen mehrere, fragt die App nach +(`proposeAssignment` in `src/session.js`). Das hat eine bewusst in Kauf +genommene Folge: Konnte die Teilenummer nicht gelesen werden — der häufige Fall bei überklebten oder beschädigten Etiketten — und passt anhand der übrigen Merkmale nur ein Stapel, landet das Modul dort, auch wenn seine tatsächliche Teilenummer eine andere wäre. Diese Abwägung ist @@ -45,6 +50,20 @@ keine Daten verlassen das Gerät. durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt oder widersprüchliche Barcodes) oder mehrdeutiger Stapelzuordnung fragt die App nach. +5. **Sitzungsfortsetzung beim Start.** Findet die App beim Laden eine in + `localStorage` gesicherte, nicht leere Sitzung vor, wird sie nicht + stillschweigend übernommen, sondern zur Fortsetzung oder zum Verwerfen + angeboten (`askResumeSession` in `src/ui/resume-dialog.js`, verdrahtet über + `initResumeOffer()` in `src/main.js`). Erst „fortsetzen“ stellt Stapel, + Zähler und die „Zuletzt“-Zeile wieder her; „verwerfen“ löscht den + gesicherten Stand und die App startet leer. +6. **Ersatzweg ohne Kamera.** Lässt sich die Kamera nicht starten (z. B. + verweigerte Berechtigung), bleibt die App bedienbar: eine dauerhaft + sichtbare Schaltfläche „Bild auswählen“ öffnet stattdessen die native + Dateiauswahl (`setFilePickerVisible` in `src/ui/scan-view.js`). Die App + versucht danach nicht automatisch erneut, die Kamera zu starten - der + Kamera-Hinweistext bleibt für den Rest der Sitzung stehen und wird nicht + von der OCR-Verfügbarkeitsanzeige überschrieben. Barcode- und OCR-Adapter sind gegen hängende Aufrufe abgesichert: Die Erkennungs-Pipeline (`src/pipeline.js`) bricht einen Barcode-Versuch nach 10 @@ -77,7 +96,7 @@ korrekt funktioniert. npm test ``` -97 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich +101 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich die reinen Module ohne Browser-Zugriff: Teilenummer-Decoder, Spec-Normalisierung und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert) und -Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline samt Zeitgrenzen @@ -118,7 +137,12 @@ nicht gesetzt ist). | `src/camera.js` | Kamerastart, Einzelbildaufnahme, Bilddatei-Ersatzweg | | `src/barcode.js` | Adapter zu `zxing-wasm` | | `src/ocr.js` | Bildaufbereitung (Otsu) und Adapter zu `tesseract.js` | -| `src/ui/` | Ansichten: Scan-Ansicht, Ergebnis-Einblendung, Rot-Dialog bei Mehrdeutigkeit, Sitzungsliste | +| `src/ui/scan-view.js` | Scan-Ansicht: Kamera-Vorschau, Scan-Knopf, Stapel-Leiste, „Zuletzt"-Zeile, Ersatzweg-Schaltfläche bei fehlender Kamera | +| `src/ui/result-overlay.js` | Kurze Treffer-Rückmeldung (grün/gelb), blendet sich nach kurzer Zeit selbst wieder aus | +| `src/ui/ambiguous-dialog.js` | Rot-Dialog bei roter Konfidenz oder mehrdeutiger Stapelzuordnung | +| `src/ui/resume-dialog.js` | Dialog beim Start: gesicherte Sitzung fortsetzen oder verwerfen | +| `src/ui/session-list.js` | Sitzungsliste: Stapel-Übersicht, Umsortieren, Entfernen, Sitzung beenden | +| `src/ui/describe-spec.js` | Gemeinsame Kurzbeschreibung eines Specs (Kapazität, Rank, Geschwindigkeit, Bauform), wahlweise mit Platzhaltern für fehlende Felder; von Treffer-Rückmeldung, „Zuletzt"-Zeile und Sitzungsliste gemeinsam genutzt | | `src/main.js` | Verdrahtung aller Module zur lauffähigen App | | `src/styles.css` | Farbvariablen (Grün/Gelb/Rot der Ampel-Rückmeldung), Layout des Kamera-Vollbilds samt Zielrahmen, Stapel-Leiste und Aktionsknöpfe sowie die Overlays für Treffer-Rückmeldung, Rot-Dialog und Sitzungsliste | @@ -198,7 +222,8 @@ werden: **Kamera und Aufnahme** - Preview-URL auf dem Handy öffnen (muss `https://` sein), Rückkamera-Bild - erscheint im Vorschaubereich, "Kamera läuft" wird angezeigt. + erscheint im Vorschaubereich; die Statuszeile bleibt leer, solange Kamera + und Texterkennung verfügbar sind. - Verweigerte Kamera-Berechtigung: verständliche Fehlermeldung erscheint *und* die Aufnahme-LED des Geräts erlischt tatsächlich wieder. - Aufnahme-Knopf antippen, bevor das Kamerabild bereit ist: verständliche @@ -232,7 +257,8 @@ werden: darauf zurück? - Stichprobe, ob die feste Zeichen-Whitelist der Texterkennung auf echten Etiketten keine tatsächlich benötigten Zeichen ausschließt. -- Bild ohne lesbaren Code: "kein Barcode gefunden" statt Fehler oder Absturz. +- Bild ohne lesbaren Barcode und ohne verwertbaren OCR-Text: Rot-Dialog + erscheint statt Fehler oder Absturz. **Bedienung und Anzeige** - Zielrahmen liegt sinnvoll über dem Kamerabild, `object-fit: cover` füllt @@ -254,8 +280,10 @@ werden: - Ein Modul zweimal scannen: beide Male derselbe Stapel, Zähler steigt. - Rücknahme-Fläche nimmt genau den zuletzt erfassten Eintrag zurück, Zähler sinkt entsprechend. -- Seite neu laden während einer laufenden Sitzung: Stapel und Zähler bleiben - erhalten. +- Seite neu laden während einer laufenden Sitzung: Dialog „Gesicherte + Sitzung gefunden“ erscheint; erst nach „fortsetzen“ bleiben Stapel und + Zähler erhalten und die „Zuletzt“-Zeile zeigt wieder den tatsächlich + letzten Eintrag; „verwerfen“ führt zu einem leeren Start. - Sitzungsliste öffnen, Eintrag umsortieren, Eintrag entfernen: Ansicht baut sich korrekt neu auf. - "Sitzung beenden": Ansicht wird leer, und ein anschließendes Neuladen