chore: initialize repository baseline

Import the existing Electron + React + TypeScript app as the version-control
baseline before the scanner rework (C# input/capture sidecar, resolution-anchored
layout profiles, OCR preprocessing, eval harness, rescan-merge, GOOD interop).

Housekeeping in this commit:
- Remove orphaned temp_inputhelper_block.ts (duplicate of the input-helper script).
- Ignore .claude/scheduled_tasks.lock local session state.
- Add .gitattributes to normalize line endings (LF in repo).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-07-05 20:31:01 +02:00
commit e76d88e0c7
147 changed files with 26220 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
export type ArtifactSlot = "flower" | "plume" | "sands" | "goblet" | "circlet";
export type ArtifactVerdict =
| "keep"
| "maybe_level"
| "character_specific"
| "trash_candidate"
| "needs_review";
export type ScanSource = "mock" | "screen" | "overlay" | "good_import" | "manual";
export interface ArtifactSubstat {
key: string;
value: number;
unit: "%" | "flat";
}
export interface Artifact {
id: string;
setKey: string;
setName: string;
slot: ArtifactSlot;
rarity: 4 | 5;
level: number;
mainStat: string;
substats: ArtifactSubstat[];
equipped?: string;
locked: boolean;
source: ScanSource;
confidence: number;
lastSeenAt: string;
}
export interface Character {
id: string;
name: string;
owned: boolean;
level: number;
constellation: number;
rolePreference: CharacterRole;
confidence: number;
}
export type CharacterRole =
| "main_dps"
| "sub_dps"
| "support"
| "healer"
| "reaction";
export interface CharacterPreset {
characterId: string;
role: CharacterRole;
recommendedSets: string[];
alternativeSets: string[];
mainStats: Partial<Record<ArtifactSlot, string[]>>;
substatWeights: Record<string, number>;
erTarget?: number;
explanation: string;
}
export interface Recommendation {
artifactId: string;
verdict: ArtifactVerdict;
score: number;
bestCharacters: string[];
reason: string;
}
export interface BuildSuggestion {
id: string;
characterId: string;
label: string;
quality: "recommended_set" | "alternative_set" | "rainbow";
artifactIds: string[];
score: number;
warnings: string[];
explanation: string;
}
export interface ScanEvent {
id: string;
type: "environment" | "character" | "artifact" | "review" | "complete";
label: string;
detail: string;
confidence?: number;
}
export interface AppSnapshot {
artifacts: Artifact[];
characters: Character[];
recommendations: Recommendation[];
builds: BuildSuggestion[];
scanEvents: ScanEvent[];
}
+305
View File
@@ -0,0 +1,305 @@
import type { AppSnapshot } from "./domain";
import type { StoredArtifactRecord } from "./storage";
export interface CaptureOptions {
skipOcr?: boolean;
}
export interface CaptureSourceInfo {
id: string;
name: string;
isGenshinCandidate: boolean;
thumbnailDataUrl: string;
}
export interface CaptureCrop {
id: string;
label: string;
rect: { x: number; y: number; width: number; height: number };
dataUrl: string;
}
export interface OcrResult {
id: string;
label: string;
text: string;
confidence: number;
}
export type CaptureTarget = "genshin-client" | "primary-screen" | "desktop-source";
export interface CaptureResult {
id: string;
name: string;
width: number;
height: number;
dataUrl: string;
capturedAt: string;
captureTarget?: CaptureTarget;
detailDataUrl?: string;
inventoryDataUrl?: string;
ocrSkipped?: boolean;
ocrTimedOut?: boolean;
crops?: CaptureCrop[];
ocr?: OcrResult[];
inventoryGrid?: {
centers: Array<{ x: number; y: number; row: number; col: number }>;
rows: number;
cols: number;
confidence: number;
source: "detected" | "fallback" | "missing";
};
inventoryCount?: {
current: number;
total: number;
confidence: number;
source: "ocr" | "missing";
text: string;
};
}
export interface WindowBounds {
x: number;
y: number;
width: number;
height: number;
}
export interface ClickResult {
ok: boolean;
x: number;
y: number;
cursorX?: number;
cursorY?: number;
escapePressed?: boolean;
enterPressed?: boolean;
f9Pressed?: boolean;
/** Cursor verifiably reached the target position before clicking. */
moved?: boolean;
/** Mouse down+up were injected (only attempted when moved is true). */
clicked?: boolean;
/** SendInput injected zero events, e.g. blocked by UIPI (elevated game). */
inputBlocked?: boolean;
focused?: boolean;
alreadyForeground?: boolean;
foregroundProcess?: string;
targetProcess?: string;
isElevated?: boolean;
}
export interface RuntimeInfo {
ok: boolean;
isElevated: boolean;
platform: string;
hotkeys?: Record<string, boolean>;
genshinFound?: boolean;
genshinHwnd?: number;
targetProcess?: string;
foregroundProcess?: string;
foregroundHwnd?: number;
helperPid?: number;
}
export interface FocusGenshinResult {
focused: boolean;
alreadyForeground: boolean;
foregroundProcess?: string;
targetProcess?: string;
genshinFound?: boolean;
setForegroundResult?: boolean;
}
export interface BooleanResult {
ok: boolean;
}
export interface SaveResultWithPath {
ok: boolean;
path: string;
}
export type SaveSnapshotResult = SaveResultWithPath;
export interface ScannerLearningRulePayload {
textReplacements?: Record<string, string>;
}
export interface LoadScannerLearningRulesResult {
ok: boolean;
path: string;
rules: ScannerLearningRulePayload;
}
export interface SaveScannerLearningRulesResult {
ok: boolean;
path: string;
rules: ScannerLearningRulePayload;
total: number;
}
export interface AutomationGuard {
ok: boolean;
cursorX?: number;
cursorY?: number;
escapePressed: boolean;
enterPressed?: boolean;
f9Pressed?: boolean;
isElevated?: boolean;
}
export interface GdiCaptureResult {
dataUrl: string;
width: number;
height: number;
originX: number;
originY: number;
captureTarget: Extract<CaptureTarget, "primary-screen" | "genshin-client">;
}
export interface HelperOperationResponse {
id?: string;
ok: boolean;
error?: string;
[key: string]: unknown;
}
export interface ScrollResult {
ok: boolean;
notchesSent: number;
inputBlocked?: boolean;
isElevated?: boolean;
}
export type ArtifactSaveResult = ArtifactStoreSaveResult;
export interface ArtifactStoreLoadResult {
ok: boolean;
artifacts: StoredArtifactRecord[];
total: number;
path: string;
}
export interface ArtifactStoreSaveResult {
ok: boolean;
added: number;
updated: number;
total: number;
path: string;
}
export interface ReviewSampleRecord {
savedAt: string;
sample?: {
reason?: string;
parsed?: unknown;
capture?: {
id?: string;
name?: string;
width?: number;
height?: number;
dataUrl?: string;
detailDataUrl?: string;
inventoryDataUrl?: string;
captureTarget?: CaptureResult["captureTarget"];
capturedAt?: string;
crops?: Array<{ id: string; label: string; rect: { x: number; y: number; width: number; height: number }; dataUrl?: string }>;
ocr?: OcrResult[];
inventoryGrid?: CaptureResult["inventoryGrid"];
inventoryCount?: CaptureResult["inventoryCount"];
};
};
}
export interface ReviewSampleListResult {
ok: boolean;
samples: ReviewSampleRecord[];
total: number;
path: string;
}
export interface ReviewSamplePayload {
reason?: string;
parsed?: unknown;
capture?: {
id?: string;
name?: string;
width?: number;
height?: number;
dataUrl?: string;
detailDataUrl?: string;
inventoryDataUrl?: string;
captureTarget?: CaptureResult["captureTarget"];
capturedAt?: string;
crops?: Array<{ id: string; label: string; rect: { x: number; y: number; width: number; height: number }; dataUrl?: string }>;
ocr?: OcrResult[];
inventoryGrid?: CaptureResult["inventoryGrid"];
inventoryCount?: CaptureResult["inventoryCount"];
};
[key: string]: unknown;
}
export interface ScannerStatusPayload {
running: boolean;
reviewStatus: string;
captureStatus: string;
selectedSource: string | null;
stats: Record<string, number>;
summary: unknown;
snapshotArtifacts: number;
snapshotCharacters: number;
snapshotRecommendations: number;
snapshotBuilds: number;
grid: CaptureResult["inventoryGrid"] | null;
automationLog: string[];
runtimeInfo: RuntimeInfo | null;
storedTotal: number | null;
learningRuleCount: number;
updatedAt: string | null;
[key: string]: unknown;
}
export interface GoodExportArtifact {
setKey: string;
slotKey: string;
rarity: number;
level: number;
mainStatKey: string;
substats: Array<{ key: string; value: number }>;
lock: boolean;
}
export interface GoodDatabase {
format: "GOOD";
version: number;
source: string;
artifacts: GoodExportArtifact[];
}
declare global {
interface Window {
assistantApi?: {
loadSnapshot: () => Promise<AppSnapshot | null>;
saveSnapshot: (snapshot: AppSnapshot) => Promise<SaveSnapshotResult>;
runMockScan: () => Promise<AppSnapshot | null>;
listCaptureSources: () => Promise<CaptureSourceInfo[]>;
captureSource: (sourceId: string, delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult>;
clickScreen: (x: number, y: number) => Promise<ClickResult>;
scrollScreen: (notches: number, anchorX?: number, anchorY?: number) => Promise<ScrollResult>;
getAutomationGuard: () => Promise<AutomationGuard>;
focusMainWindow: () => Promise<BooleanResult>;
focusGenshin: () => Promise<FocusGenshinResult>;
getRuntimeInfo: () => Promise<RuntimeInfo>;
saveReviewSample: (sample: ReviewSamplePayload) => Promise<SaveResultWithPath>;
loadReviewSamples: (limit?: number) => Promise<ReviewSampleListResult>;
loadScannerLearningRules: () => Promise<LoadScannerLearningRulesResult>;
saveScannerLearningRules: (rules: ScannerLearningRulePayload) => Promise<SaveScannerLearningRulesResult>;
loadArtifacts: () => Promise<ArtifactStoreLoadResult>;
saveArtifacts: (records: StoredArtifactRecord[]) => Promise<ArtifactStoreSaveResult>;
exportGood: (payload: GoodDatabase) => Promise<SaveResultWithPath>;
publishScannerStatus: (status: ScannerStatusPayload) => Promise<BooleanResult>;
showOverlay: () => Promise<BooleanResult>;
hideOverlay: () => Promise<BooleanResult>;
onScannerCommand: (callback: (command: "start-auto" | "stop") => void) => () => void;
};
}
}
+17
View File
@@ -0,0 +1,17 @@
export interface StoredArtifactRecord {
id: string;
name: string;
slot: string;
level?: number;
setName: string;
mainStat: string;
mainValue: string;
substats: string[];
equipped: string;
confidence: number;
needsReview: boolean;
source: string;
firstSeenAt?: string;
lastSeenAt?: string;
timesSeen?: number;
}