From 7fc7feb1edc3ab57f6895f562421946a8b0f3dc3 Mon Sep 17 00:00:00 2001 From: vchuser Date: Tue, 28 Jul 2026 18:24:07 +0200 Subject: [PATCH] Konsolidiere Spec-Kurzbeschreibung in ein gemeinsames Modul main.js, result-overlay.js und session-list.js loesten dieselbe Aufgabe (Spec-Felder zu einer Lesehilfe zusammenfassen) bislang mit drei leicht unterschiedlichen Implementierungen - unterschiedliche Feldauswahl und unterschiedlicher Umgang mit fehlenden Werten. Neues src/ui/describe-spec.js buendelt das in describeSpec(spec, { placeholders }): ohne Platzhalter fuer die kurze Treffer-Rueckmeldung und die "Zuletzt"-Zeile, mit Platzhalter fuer die Sitzungsliste, wo feste Feldreihenfolge Wiedererkennung schafft. Nutzt bei main.js zugleich die bereits vorhandene, jetzt exportierte nextStackId aus session.js statt einer Zeile-fuer-Zeile-Kopie derselben Buchstaben-Vergabe (nextFreeStackId entfaellt komplett). Co-Authored-By: Claude Opus 5 (1M context) --- src/main.js | 22 ++++------------------ src/ui/describe-spec.js | 39 +++++++++++++++++++++++++++++++++++++++ src/ui/result-overlay.js | 34 +++++++++++++--------------------- src/ui/session-list.js | 15 +++------------ 4 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 src/ui/describe-spec.js diff --git a/src/main.js b/src/main.js index 725087d..a1c16d5 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,7 @@ import { runOcr, isOcrAvailable } from './ocr.js'; import { recognize } from './pipeline.js'; import { createSession, proposeAssignment, commitAssignment, - undoLast, moveEntry, removeEntry, + undoLast, moveEntry, removeEntry, nextStackId, } from './session.js'; import { saveSession, loadSession, clearSession } from './storage.js'; import { renderScanView } from './ui/scan-view.js'; @@ -14,6 +14,7 @@ import { showResult } from './ui/result-overlay.js'; import { askForStack } from './ui/ambiguous-dialog.js'; import { askResumeSession } from './ui/resume-dialog.js'; import { renderSessionList } from './ui/session-list.js'; +import { describeSpec } from './ui/describe-spec.js'; const app = document.querySelector('#app'); const store = window.localStorage; @@ -64,22 +65,7 @@ function syncView() { /** Kurzbeschreibung fuer die "Zuletzt"-Zeile. */ function describeEntry(spec, stackId) { - const parts = [ - spec.capacityGb ? `${spec.capacityGb}GB` : '?', - spec.speed ?? '?', - spec.formFactor ?? '', - ].filter(Boolean); - return `${parts.join(' ')} → Stapel ${stackId}`; -} - -/** Naechster noch nicht vergebener Stapel-Buchstabe - fuer den Fall, dass der Nutzer im Rot-Dialog ausdruecklich einen neuen Stapel waehlt. */ -function nextFreeStackId(targetSession) { - const used = new Set(targetSession.stacks.map((stack) => stack.id)); - for (const letter of 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') { - if (!used.has(letter)) return letter; - } - // Nach Z weiter mit A2, B2, ... - in der Praxis nie erreicht (siehe session.js). - return `A${targetSession.stacks.length}`; + return `${describeSpec(spec)} → Stapel ${stackId}`; } /** @@ -124,7 +110,7 @@ async function processCapture(getFrame) { stackId = answer.stackId; } else { // "neuer Stapel": unabhaengig von jeder Vermutung der naechste freie Buchstabe. - stackId = nextFreeStackId(targetSession); + stackId = nextStackId(targetSession); } } diff --git a/src/ui/describe-spec.js b/src/ui/describe-spec.js new file mode 100644 index 0000000..a39a38c --- /dev/null +++ b/src/ui/describe-spec.js @@ -0,0 +1,39 @@ +// Rein darstellend: baut aus Spec-Feldern eine kurze, deutsche Lesehilfe. +// Kein window/document noetig - liefert nur eine Zeichenkette. + +const PLACEHOLDER = '?'; + +/** + * Fasst Kapazitaet, Rank, Geschwindigkeit und Bauform eines Specs zu einer + * kurzen Lesehilfe zusammen. Frueher gab es davon drei leicht + * unterschiedliche Fassungen (main.js, result-overlay.js, session-list.js) - + * mit unterschiedlichen Feldern und unterschiedlichem Umgang mit fehlenden + * Werten. Diese eine Fassung deckt beide Beduerfnisse ueber einen Schalter ab: + * + * - `placeholders: false` (Vorgabe): fehlende Felder werden weggelassen, + * ein vollstaendig leeres Spec ergibt "ohne Angaben". Passend fuer die + * kurz eingeblendete Treffer-Rueckmeldung und die "Zuletzt"-Zeile, wo ein + * Platzhalter pro fehlendem Feld mehr verwirrt als hilft. + * - `placeholders: true`: jedes Feld erscheint, fehlende als "?". Passend + * fuer die Sitzungsliste, wo die feste Reihenfolge der vier Felder ueber + * viele Stapel hinweg Wiedererkennung schafft. + * + * @param {object} spec + * @param {{placeholders?: boolean}} [options] + * @returns {string} + */ +export function describeSpec(spec, { placeholders = false } = {}) { + const fields = [ + spec.capacityGb ? `${spec.capacityGb}GB` : null, + spec.rank ?? null, + spec.speed ?? null, + spec.formFactor ?? null, + ]; + + if (placeholders) { + return fields.map((value) => value ?? PLACEHOLDER).join(' '); + } + + const present = fields.filter(Boolean); + return present.length > 0 ? present.join(' ') : 'ohne Angaben'; +} diff --git a/src/ui/result-overlay.js b/src/ui/result-overlay.js index d00e034..ea5b635 100644 --- a/src/ui/result-overlay.js +++ b/src/ui/result-overlay.js @@ -1,27 +1,19 @@ -function beschreibung(spec) { - const teile = [ - spec.capacityGb ? `${spec.capacityGb}GB` : null, - spec.rank, - spec.speed, - spec.formFactor, - ].filter(Boolean); - return teile.join(' ') || 'ohne Angaben'; -} +import { describeSpec } from './describe-spec.js'; /** Haelt pro Root die aktuell sichtbare Rueckmeldung samt Zeitgeber fest. */ -const aktuelleRueckmeldung = new WeakMap(); +const currentFeedback = new WeakMap(); /** * Blendet die Treffer-Rueckmeldung ein und nach kurzer Zeit wieder aus. * Gruen bei Barcode, gelb bei OCR. Keine Eingabe noetig. */ -export function showResult(root, { stackId, spec, confidence }, dauerMs = 1200) { - const vorherige = aktuelleRueckmeldung.get(root); - if (vorherige) { - clearTimeout(vorherige.timer); - vorherige.overlay.remove(); - vorherige.resolve(); - aktuelleRueckmeldung.delete(root); +export function showResult(root, { stackId, spec, confidence }, durationMs = 1200) { + const previous = currentFeedback.get(root); + if (previous) { + clearTimeout(previous.timer); + previous.overlay.remove(); + previous.resolve(); + currentFeedback.delete(root); } const overlay = document.createElement('div'); @@ -31,16 +23,16 @@ export function showResult(root, { stackId, spec, confidence }, dauerMs = 1200)
`; - overlay.querySelector('.detail').textContent = beschreibung(spec); + overlay.querySelector('.detail').textContent = describeSpec(spec); overlay.querySelector('.pn').textContent = spec.partNumber ?? ''; root.appendChild(overlay); return new Promise((resolve) => { const timer = setTimeout(() => { overlay.remove(); - aktuelleRueckmeldung.delete(root); + currentFeedback.delete(root); resolve(); - }, dauerMs); - aktuelleRueckmeldung.set(root, { overlay, timer, resolve }); + }, durationMs); + currentFeedback.set(root, { overlay, timer, resolve }); }); } diff --git a/src/ui/session-list.js b/src/ui/session-list.js index c021ca1..83938f8 100644 --- a/src/ui/session-list.js +++ b/src/ui/session-list.js @@ -3,16 +3,7 @@ // Sitzung. Kennt weder Kamera noch Erkennung - alles kommt ueber die // Rueckrufe, genau wie bei scan-view.js und ambiguous-dialog.js. -/** Kurzbeschreibung eines Specs fuer Stapel-Kopfzeilen und Eintrags-Zeilen ohne Teilenummer. */ -function describeSpec(spec) { - const parts = [ - spec.capacityGb ? `${spec.capacityGb}GB` : '?', - spec.rank ?? '?', - spec.speed ?? '?', - spec.formFactor ?? '?', - ]; - return parts.join(' '); -} +import { describeSpec } from './describe-spec.js'; /** * Kurzbezeichnung der Erkennungsquelle eines Eintrags - beim Sortieren @@ -52,7 +43,7 @@ export function renderSessionList(root, session, { onMove, onRemove, onEndSessio header.className = 'detail'; header.style.textAlign = 'left'; header.style.marginTop = '12px'; - header.textContent = `Stapel ${stack.id} — ${stack.count}x ${describeSpec(stack.spec)}`; + header.textContent = `Stapel ${stack.id} — ${stack.count}x ${describeSpec(stack.spec, { placeholders: true })}`; body.appendChild(header); for (const entry of session.entries.filter((candidate) => candidate.stackId === stack.id)) { @@ -65,7 +56,7 @@ export function renderSessionList(root, session, { onMove, onRemove, onEndSessio label.style.flex = '1'; label.style.fontSize = '14px'; label.style.textAlign = 'left'; - label.textContent = entry.spec.partNumber ?? describeSpec(entry.spec); + label.textContent = entry.spec.partNumber ?? describeSpec(entry.spec, { placeholders: true }); row.appendChild(label); const sourceLabel = document.createElement('span');