diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..4926eec --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "vite", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev:web"], + "port": 5173 + } + ] +} diff --git a/package.json b/package.json index c19282a..ddf1deb 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "scripts": { "predev": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts\\kill-stale-instances.ps1 && tsc -p tsconfig.electron.json && copy electron\\preload.cjs dist-electron\\preload.cjs", "dev": "concurrently -k \"vite --host 127.0.0.1\" \"wait-on tcp:5173 && cross-env VITE_DEV_SERVER_URL=http://127.0.0.1:5173 electron .\"", + "dev:web": "vite --host 127.0.0.1", "dev:admin": ".\\dev-admin.cmd", "build": "tsc && vite build && tsc -p tsconfig.electron.json && copy electron\\\\preload.cjs dist-electron\\\\preload.cjs", "preview": "vite preview --host 127.0.0.1", diff --git a/src/features/layout/AppLayout.tsx b/src/features/layout/AppLayout.tsx index 170c776..6d33249 100644 --- a/src/features/layout/AppLayout.tsx +++ b/src/features/layout/AppLayout.tsx @@ -90,7 +90,7 @@ export function AppTopbar({

Lokaler Windows-Assistent

-

Scanne dein Inventar, triff einfache Artifact-Entscheidungen.

+

Inventar scannen, Artifacts entscheiden.

{topbarStatus && {topbarStatus}} @@ -107,15 +107,6 @@ export function AppTopbar({ {overlayIcon} {overlayButtonLabel} -
); diff --git a/src/features/layout/navigation.ts b/src/features/layout/navigation.ts index 9d1854e..8880a6d 100644 --- a/src/features/layout/navigation.ts +++ b/src/features/layout/navigation.ts @@ -1,4 +1,4 @@ -import { Layers3, Radar, Wand2, Eye } from "lucide-react"; +import { Layers3, Radar, Wand2, Eye, Wrench } from "lucide-react"; import type { AppNavigationItem } from "./types"; export const appNavigationItems: AppNavigationItem[] = [ @@ -6,5 +6,6 @@ export const appNavigationItems: AppNavigationItem[] = [ { id: "triage", label: "Triage", icon: Layers3 }, { id: "builds", label: "Builds", icon: Wand2 }, { id: "overlay", label: "Overlay", icon: Eye }, + { id: "diagnose", label: "Diagnose", icon: Wrench }, ]; diff --git a/src/features/layout/types.ts b/src/features/layout/types.ts index 74ce470..5b6e564 100644 --- a/src/features/layout/types.ts +++ b/src/features/layout/types.ts @@ -1,7 +1,7 @@ import { type ComponentType, type ReactNode } from "react"; import type { LucideProps } from "lucide-react"; -export type NavigationId = "scan" | "triage" | "builds" | "overlay"; +export type NavigationId = "scan" | "triage" | "builds" | "overlay" | "diagnose"; export interface AppNavigationItem { id: NavigationId; diff --git a/src/features/scan/ScanView.tsx b/src/features/scan/ScanView.tsx index 26b391f..54eca1e 100644 --- a/src/features/scan/ScanView.tsx +++ b/src/features/scan/ScanView.tsx @@ -1,9 +1,29 @@ -import { ScanViewLayout } from "./components/ScanViewLayout"; +import { ScanViewLayout } from "./components/ScanViewLayout"; +import { DiagnosticsView } from "./components/DiagnosticsView"; import { useScanViewController } from "./hooks/useScanViewController"; import type { ScanViewProps } from "./types"; -export function ScanView(props: ScanViewProps) { +interface ScanViewExtraProps { + /** "workspace" shows the clean scan surface; "diagnose" shows all dev info. */ + mode?: "workspace" | "diagnose"; + onDemoScan?: () => void; + canDemoScan?: boolean; +} + +export function ScanView({ mode = "workspace", onDemoScan, canDemoScan, ...props }: ScanViewProps & ScanViewExtraProps) { const controller = useScanViewController(props); + if (mode === "diagnose") { + return ( + + ); + } + return ; } diff --git a/src/features/scan/components/DiagnosticsView.tsx b/src/features/scan/components/DiagnosticsView.tsx new file mode 100644 index 0000000..ebbb992 --- /dev/null +++ b/src/features/scan/components/DiagnosticsView.tsx @@ -0,0 +1,203 @@ +import { AlertTriangle, Play, 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; +} + +// 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, + 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, + }); + + return ( +
+
+
+

Diagnose & Dev

+

Laufzeit, Erkennung & Rohdaten

+
+
+ + {onDemoScan && ( + + )} +
+
+ +
+
+

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}

} +
+
+ +
+

Automation log

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

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}

} +
+
+ ); +} diff --git a/src/features/scan/components/ScanMainSection.tsx b/src/features/scan/components/ScanMainSection.tsx index 600f9b4..0246328 100644 --- a/src/features/scan/components/ScanMainSection.tsx +++ b/src/features/scan/components/ScanMainSection.tsx @@ -1,4 +1,4 @@ -import { AlertTriangle, Camera, Eye } from "lucide-react"; +import { AlertTriangle, Camera } from "lucide-react"; import { ArtifactResultCard } from "./ScanResultCards"; import { useScanMainSectionModel } from "./hooks/useScanMainSectionModel"; import type { ScanMainSectionProps } from "./types"; @@ -63,9 +63,7 @@ export function ScanMainSection({
Quelle{sourceLabel}
-
Grid{gridLabel}
Inventar{inventoryLabel}
-
Modus{captureModeText}
@@ -81,17 +79,8 @@ export function ScanMainSection({ {targetLabel} {dbLabel} {reviewLabel} - {rulesLabel}
-
{bridgeStatusText} @@ -118,15 +117,6 @@ export function ScanTopControlsSection({ Scan-Setup -
diff --git a/src/features/scan/components/modals/ScanDetailsModal.tsx b/src/features/scan/components/modals/ScanDetailsModal.tsx deleted file mode 100644 index 30b2d53..0000000 --- a/src/features/scan/components/modals/ScanDetailsModal.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { FieldConfidenceList } from "../ScanResultCards"; -import type { ScanDetailsModalProps } from "./types"; -import { useScanDetailsModalModel } from "./hooks/useScanDetailsModalModel"; - -export function ScanDetailsModal({ - open, - latestCapture, - controller, - setDetailsOpen, -}: ScanDetailsModalProps) { - const { parsedArtifact } = controller; - const { - closeDetails, - stopPropagation, - parsedNotes, - showParsedNotes, - cropRows, - ocrRows, - debugText, - showCrops, - showOcr, - } = useScanDetailsModalModel({ - setDetailsOpen, - parsedArtifact, - latestCapture, - }); - - if (!open || !latestCapture) return null; - - return ( -
-
-
-
-

Dev

-

Crops, OCR & Confidence

-
- -
-
- {parsedArtifact && ( - <> - - {showParsedNotes && ( -
- {parsedNotes.map((note) => {note})} -
- )} - - )} - {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}
-
- ))} -
- )} -

{debugText}

-
-
-
- ); -} diff --git a/src/features/scan/components/modals/ScanDiagnosticsModal.tsx b/src/features/scan/components/modals/ScanDiagnosticsModal.tsx deleted file mode 100644 index 45bfe6b..0000000 --- a/src/features/scan/components/modals/ScanDiagnosticsModal.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { AlertTriangle, Eye, Wrench } from "lucide-react"; -import type { ScanDiagnosticsModalProps } from "./types"; -import { useScanDiagnosticsModalModel } from "./hooks/useScanDiagnosticsModalModel"; - -export function ScanDiagnosticsModal({ - open, - captureStatus, - latestCapture, - controller, - setDetailsOpen, - setDiagnosticsOpen, -}: ScanDiagnosticsModalProps) { - const { - toggleDevMode, - } = controller; - - const { - closeDiagnostics, - openDetails, - handleSaveReviewSample, - stopPropagation, - canOpenDetails, - statusTitle, - rightsClassName, - rightsValue, - genshinClassName, - genshinValue, - shouldShowAdminBanner, - gridSourceClass, - gridMainValue, - gridMetaValue, - learningRulesText, - learningRulesSubtext, - autoScanModeLabel, - autoScanRunning, - fingerprintText, - runtimeRows, - scanLimitText, - scanTipText, - autoScanStatsLines, - playerProgress, - showDevRows, - reviewStatus, - automationLogLines, - canSaveReviewSample, - } = useScanDiagnosticsModalModel({ - setDetailsOpen, - setDiagnosticsOpen, - saveReviewSample: controller.saveReviewSample, - canSaveReviewSample: controller.canSaveReviewSample, - latestCapture, - controller, - captureStatus, - }); - - if (!open) return null; - - return ( -
-
-
-
-

Scanner Diagnose

-

Input, Grid & Lernstatus

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

- App laeuft im Standard-Modus. Auto-Scan braucht Administrator-Rechte: App schliessen und als Administrator neu starten (z.B. Terminal per Rechtsklick "Als Administrator ausfuehren" und darin "npm run dev"). -

- )} - -
- Tile grid - {gridMainValue} - {gridMetaValue} -
- -
- Learning - {learningRulesText} - {learningRulesSubtext} -
- - {playerProgress.show && ( -
- {autoScanModeLabel} - {autoScanStatsLines.map((entry) => ( - - {entry.value} - {entry.label} - - ))} -
- )} - -
- Fingerprint - {fingerprintText} - Active capture fingerprint used for deterministic duplicate guard checks. -
- - {showDevRows && ( -
- {runtimeRows.map((row) => ( - {row} - ))} -
- )} - - {autoScanRunning && playerProgress.show && ( -
-
-
-
-
- )} - - {reviewStatus && ( -

{reviewStatus}

- )} - -
- Automation -
- {automationLogLines.length > 0 ? ( - automationLogLines.map((line, index) => ( - {line} - )) - ) : ( - No scan activity yet. - )} -
-
- -
- - -
- -

{scanLimitText}

-

{scanTipText}

-
-
-
- ); -} diff --git a/src/pages/app/AppPageLayout.tsx b/src/pages/app/AppPageLayout.tsx index e9648bd..483add4 100644 --- a/src/pages/app/AppPageLayout.tsx +++ b/src/pages/app/AppPageLayout.tsx @@ -53,9 +53,12 @@ export function AppPageLayout({ controller }: AppPageLayoutProps) { overlayIcon={} demoIcon={} /> - - {activeView === "scan" && ( + {activeView !== "diagnose" && } + {(activeView === "scan" || activeView === "diagnose") && (