import { useState } from "react"; import { AlertTriangle, BadgeCheck, Camera, ClipboardList, Download, Gauge, Play, Target, Upload, Wrench } from "lucide-react"; import type { CaptureResult } from "../../../types/global"; import type { ScanViewControllerResult } from "../types"; import { FieldConfidenceList } from "./ScanResultCards"; import { useScanDiagnosticsModalModel } from "./modals/hooks/useScanDiagnosticsModalModel"; import { useScanDetailsModalModel } from "./modals/hooks/useScanDetailsModalModel"; interface DiagnosticsViewProps { controller: ScanViewControllerResult; latestCapture: CaptureResult | null; captureStatus: string; onDemoScan?: () => void; canDemoScan?: boolean; } const appDiagnosisSections = [ { title: "Was die App kann", tone: "ok", icon: BadgeCheck, items: [ "Genshin-Fenster erkennen, Smart Capture ausfuehren und fokussierte Artifact-Crops erzeugen.", "Artifact-Felder deterministisch gegen das lokale Genshin-Datenpaket parsen.", "Auto-Scan read-only aus der sichtbaren Inventory-Seite starten, inklusive Grid, Verifikation, Dedupe und Store.", "Review-Samples, lokale Lernregeln, GOOD Import/Export, Equipped-Footer und Lock-Status im Store nutzen.", ], }, { title: "Was noch fehlt", tone: "warn", icon: ClipboardList, items: [ "Empfehlungen und Build-UX sind bewusst noch nicht der naechste Hauptfokus.", "Auto-Entry-Modi wie paimon-menu und direct-inventory bleiben Dev-Control Experimente, nicht Produktionspfad.", "3 Artifacts/Sekunde ist noch nicht bewiesen; der sichtbare Pfad liegt knapp darunter.", "Mehr bestaetigte OCR-/Review-Corpus-Faelle fehlen, bevor wir breite Qualitaet behaupten.", ], }, { title: "Wo es Probleme macht", tone: "risk", icon: AlertTriangle, items: [ "Scanner ist stark im aktuellen sichtbaren Inventory-Pfad; Repeatability ueber spaetere Sessions ist der naechste Pruefpunkt.", "OCR ist schnell genug fuer gute Runs, bleibt aber der groesste Qualitaets- und Speed-Hebel.", "Capture-Roundtrip ist sichtbar: Roundtrip und Overhead muessen bei 20/45/100 Runs mit bewertet werden.", "Groessere Runs brauchen weiter Beobachtung auf Scroll-Uebergaenge, Wiederholseiten, Duplicate-Rate und Review-Quote.", ], }, { title: "Naechste Verbesserungen", tone: "next", icon: Target, items: [ "Mehr Live-Wiederholungen: 20/45/100 Runs in spaeteren Sessions, sauber als Repeatability statt IK-Claim markieren.", "Review-Samples sauber labeln und ueber `npm run eval:prepare-confirmed` ins Eval-Corpus uebernehmen.", "Scanner-UI weiter beruhigen, aber den funktionierenden visible-inventory Pfad nicht umbauen.", "Capture-Roundtrip-Overhead als eigenen Optimierungspunkt pruefen, bevor weitere OCR-Engine-Wechsel priorisiert werden.", "Erst danach Empfehlungen wieder staerker nach vorne ziehen.", ], }, ]; // All developer / diagnostic surfaces live here, separated from the Scan // workspace: runtime + rights, grid detection, learning + data-package status, // fingerprint, auto-scan counters, the automation log, and the raw crop/OCR/ // confidence dump. Read-only; it drives no scan action except the demo snapshot. export function DiagnosticsView({ controller, latestCapture, captureStatus, onDemoScan, canDemoScan }: DiagnosticsViewProps) { const { statusTitle, rightsClassName, rightsValue, genshinClassName, genshinValue, shouldShowAdminBanner, gridSourceClass, gridMainValue, gridMetaValue, learningRulesText, learningRulesSubtext, autoScanModeLabel, fingerprintText, runtimeRows, scanLimitText, autoScanStatsLines, playerProgress, reviewStatus, automationLogLines, diagnosticEvents, canSaveReviewSample, handleSaveReviewSample, } = useScanDiagnosticsModalModel({ setDetailsOpen: controller.setDetailsOpen, setDiagnosticsOpen: controller.setDiagnosticsOpen, saveReviewSample: controller.saveReviewSample, canSaveReviewSample: controller.canSaveReviewSample, latestCapture, controller, captureStatus, }); const { parsedNotes, showParsedNotes, cropRows, ocrRows, debugText, showCrops, showOcr } = useScanDetailsModalModel({ setDetailsOpen: controller.setDetailsOpen, parsedArtifact: controller.parsedArtifact, latestCapture, }); const [interopStatus, setInteropStatus] = useState(""); const handleExportGood = async () => { setInteropStatus("Exportiere GOOD..."); const result = await controller.exportGoodFromStore(); setInteropStatus( result.ok ? `GOOD exportiert: ${result.count} Artifacts${result.path ? ` -> ${result.path}` : ""}` : "GOOD-Export fehlgeschlagen (App im Electron-Fenster oeffnen).", ); }; const handleImportGood = async () => { setInteropStatus("Waehle GOOD-Datei..."); const result = await controller.importGoodFromFile(); if (result.canceled) { setInteropStatus("GOOD-Import abgebrochen."); return; } setInteropStatus( result.ok ? `Importiert: ${result.added} neu, ${result.updated} aktualisiert (${result.count} gelesen).` : result.error === "No valid GOOD artifacts found." ? "Keine gueltigen Artifacts in der Datei gefunden." : "Import fehlgeschlagen (Datei ist kein gueltiges GOOD/JSON oder Bridge fehlt).", ); }; return (

Diagnose & Dev

Laufzeit, Erkennung & Rohdaten

{onDemoScan && ( )}

Aktueller App-Stand

Scanner zuerst, Empfehlungen danach

Quelle: Docs + Live-Status
{appDiagnosisSections.map((section) => { const Icon = section.icon; return (
{section.title}
    {section.items.map((item) => (
  • {item}
  • ))}
); })}

Status

App-Rechte {rightsValue}
Genshin {genshinValue}
{shouldShowAdminBanner && (

App laeuft im Standard-Modus. Auto-Scan braucht Administrator-Rechte: App schliessen und als Administrator neu starten.

)}
Tile grid {gridMainValue} {gridMetaValue}
Learning & Daten {learningRulesText} {learningRulesSubtext}
Fingerprint {fingerprintText} Aktiver Capture-Fingerprint fuer die Duplikat-Erkennung.

{autoScanModeLabel}

{playerProgress.show ? (
{autoScanStatsLines.map((entry) => ( {entry.value} {entry.label} ))}
) : (

Noch keine Scan-Aktivitaet.

)}

{scanLimitText}

{runtimeRows.map((row) => ( {row} ))}
{reviewStatus &&

{reviewStatus}

}

GOOD Interop

Exportiert den Scan-Store als GOOD (Genshin Optimizer / Inventory Kamera / Akasha) oder importiert eine GOOD-Datei in den Store.

{interopStatus &&

{interopStatus}

}

Automation log

{automationLogLines.length > 0 ? ( automationLogLines.map((line, index) => {line}) ) : ( Keine Scan-Aktivitaet. )}

Evidence timeline

Scan-Flugschreiber

letzte {diagnosticEvents.length}
{diagnosticEvents.length > 0 ? (
{diagnosticEvents.slice().reverse().map((event) => (
{new Date(event.at).toLocaleTimeString()} {event.phase} {event.severity}

{event.message}

{event.details && (
{Object.entries(event.details).map(([key, value]) => ( {key}: {String(value ?? "-")} ))}
)} {event.capture && (
{event.capture.name} {event.capture.width}x{event.capture.height} · {event.capture.target ?? "capture"} · fp {event.capture.fingerprint} {event.capture.grid && ( grid {event.capture.grid.cols}x{event.capture.grid.rows} · {event.capture.grid.targets} targets · {event.capture.grid.confidence}% · {event.capture.grid.source} )} {event.capture.count && ( count {event.capture.count.current}/{event.capture.count.total || "?"} · {event.capture.count.confidence}% · {event.capture.count.text || "-"} )} {event.capture.artifactDetail && ( detail {event.capture.artifactDetail.present ? "yes" : "no"} · {event.capture.artifactDetail.confidence}% · orange {event.capture.artifactDetail.orangeHits} · text {event.capture.artifactDetail.textHits} )} {event.capture.paimonMenu && ( paimon {event.capture.paimonMenu.present ? "yes" : "no"} · {event.capture.paimonMenu.confidence}% )} {event.capture.layoutWarning && {event.capture.layoutWarning}}
{event.capture.screenshots?.inventory && {`${event.phase}} {event.capture.screenshots?.detail && {`${event.phase}}
)}
))}
) : (

Noch keine Evidence-Events. Starte einen Capture oder Auto-Scan, dann erscheinen hier Schritte mit Screenshots.

)}

Crops, OCR & Confidence

{controller.parsedArtifact ? ( <> {showParsedNotes && (
{parsedNotes.map((note) => ( {note} ))}
)} ) : (

Noch kein Artifact gelesen. Lies ein Artifact im Scan-Tab, um Crops und OCR zu sehen.

)} {showCrops && (
{cropRows.map((crop) => (
{crop.label}
{crop.label} {crop.x},{crop.y} - {crop.width}x{crop.height}
))}
)} {showOcr && (
OCR candidates {ocrRows.map((entry) => (
{entry.label} {entry.confidence}% confidence
{entry.text}
))}
)} {latestCapture &&

{debugText}

}
); }