recognize() (pipeline.js) liefert zusätzlich den ungefilterten OCR-Rohtext (rawText) zurück - leer, wenn keine Texterkennung lief. Rein additiv: die Stapelzuordnung (session.js/spec.js, unangetastet) stützt sich weiterhin ausschließlich auf die daraus abgeleiteten, verstandenen Spec-Felder. main.js hängt den Rohtext nach dem Buchen als zusätzliche Eigenschaft an den Eintrag (analog zu den bereits vorhandenen Barcode-Inhalten je Eintrag). storage.js sichert/prüft/stellt entry.rawText nach demselben optionalen Muster wie entry.codes wieder her - ein gesicherter Stand ohne dieses Feld wird nicht verworfen. In Funktion "Text erkennen" bleibt die Treffer-Rückmeldung jetzt stehen, bis der Nutzer sie wegtippt, und zeigt den vollen erkannten Text (lesbar über dasselbe Scroll-Muster wie die übrigen Vollbild-Overlays); in den beiden Barcode-Funktionen bleibt es beim automatischen Ausblenden. Das zurückgegebene Versprechen von showResult löst in jedem Fall - Zeitgeber, Wegtippen, verdrängende neue Rückmeldung - genau einmal ein, damit die Bedienung nie dauerhaft gesperrt bleibt. Die Sitzungsliste zeigt den Rohtext zusätzlich je Eintrag zum Nachlesen. Tests (pipeline.test.js, storage.test.js): rawText nur bei tatsächlich gelaufener Texterkennung, unterschiedlicher Rohtext beeinflusst die Stapelzuordnung nachweislich nicht, Rohtext übersteht Sichern/Laden inkl. Abwärtskompatibilität zu einem Stand ohne dieses Feld.
165 lines
3.8 KiB
CSS
165 lines
3.8 KiB
CSS
:root {
|
|
color-scheme: dark;
|
|
--bg: #14161a;
|
|
--fg: #f2f4f7;
|
|
--muted: #9aa3af;
|
|
--green: #1f8a4c;
|
|
--yellow: #b8860b;
|
|
--red: #a02c2c;
|
|
--line: #2a2e36;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
#app { display: flex; flex-direction: column; height: 100dvh; }
|
|
|
|
.mode-button {
|
|
min-height: 56px;
|
|
width: 100%;
|
|
border: none;
|
|
border-bottom: 1px solid var(--line);
|
|
background: #1c2027;
|
|
color: var(--fg);
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
text-align: left;
|
|
padding: 0 14px;
|
|
}
|
|
|
|
.camera { position: relative; flex: 1; overflow: hidden; background: #000; }
|
|
.camera video { width: 100%; height: 100%; object-fit: cover; }
|
|
|
|
.frame {
|
|
/* Lage (inset) wird von scan-view.js aus FRAME_INSET (camera.js) gesetzt -
|
|
einzige Quelle, die sich auch der native Ausschnitt teilt. */
|
|
position: absolute;
|
|
border: 3px solid rgba(255, 255, 255, 0.7);
|
|
border-radius: 8px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Dauersuche hat einen Barcode im Ausschnitt gefunden - schlichte
|
|
Farbaenderung, keine Animation. */
|
|
.frame.detected {
|
|
border-color: var(--green);
|
|
}
|
|
|
|
.stacks {
|
|
display: flex;
|
|
gap: 8px;
|
|
padding: 10px 12px;
|
|
overflow-x: auto;
|
|
border-top: 1px solid var(--line);
|
|
min-height: 76px;
|
|
align-items: center;
|
|
}
|
|
|
|
.stacks button {
|
|
flex: none;
|
|
min-height: 56px;
|
|
padding: 0 14px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 8px;
|
|
background: #1c2027;
|
|
color: var(--fg);
|
|
font-size: 17px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.last {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 12px;
|
|
border-top: 1px solid var(--line);
|
|
min-height: 56px;
|
|
font-size: 15px;
|
|
color: var(--muted);
|
|
}
|
|
|
|
.last span { flex: 1; }
|
|
|
|
.action {
|
|
min-height: 56px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
background: #2b6cb0;
|
|
color: #fff;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
padding: 0 18px;
|
|
}
|
|
|
|
.action.secondary { background: #2a2e36; }
|
|
|
|
.capture { margin: 12px; }
|
|
|
|
.overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: safe center;
|
|
gap: 18px;
|
|
padding: 24px;
|
|
text-align: center;
|
|
z-index: 10;
|
|
overflow-y: auto;
|
|
overscroll-behavior: contain;
|
|
}
|
|
|
|
.overlay.green { background: var(--green); }
|
|
.overlay.yellow { background: var(--yellow); }
|
|
.overlay.red { background: var(--red); }
|
|
|
|
.overlay .stack-id { font-size: 68px; font-weight: 800; letter-spacing: 2px; }
|
|
.overlay .detail { font-size: 20px; line-height: 1.5; }
|
|
.overlay .pn { font-size: 15px; opacity: 0.85; word-break: break-all; }
|
|
|
|
/* Voller OCR-Rohtext in der wartenden Rueckmeldung (Funktion "Text
|
|
erkennen", siehe result-overlay.js) - linksbuendig und mit Zeilenumbruch,
|
|
damit ein langer Etikettentext lesbar bleibt statt einer einzelnen,
|
|
abgeschnittenen Zeile. Waechst nicht aus dem Bildschirm heraus, weil
|
|
`.overlay` selbst scrollbar ist (overflow-y: auto, siehe oben). */
|
|
.overlay .raw-text {
|
|
font-size: 16px;
|
|
line-height: 1.5;
|
|
text-align: left;
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
max-width: 420px;
|
|
width: 100%;
|
|
}
|
|
|
|
.choices { display: grid; gap: 12px; width: 100%; max-width: 420px; }
|
|
.choices button { min-height: 64px; font-size: 20px; }
|
|
|
|
/* Auswahl der Scan-Funktion vor dem Scannen (mode-dialog.js): drei grosse,
|
|
gleich gestaltete Flaechen statt schmaler Schaltflaechen. */
|
|
.mode-tile {
|
|
min-height: 96px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
padding: 12px;
|
|
}
|
|
|
|
.mode-tile-label { font-size: 24px; font-weight: 700; }
|
|
.mode-tile-hint { font-size: 14px; opacity: 0.8; }
|
|
|
|
.mode-tile-current { outline: 3px solid var(--fg); outline-offset: -3px; }
|
|
|
|
.status { padding: 8px 12px; font-size: 14px; color: var(--muted); }
|
|
.status.warn { color: #f0b429; }
|