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
+145
View File
@@ -0,0 +1,145 @@
import { ShieldCheck } from "lucide-react";
import type {
AppMetricsProps,
AppShellProps,
AppSidebarProps,
AppTopbarProps,
} from "./types";
import { useAppSidebarModel } from "./hooks/useAppSidebarModel";
import { useAppTopbarModel } from "./hooks/useAppTopbarModel";
export function AppSidebar({ activeView, items, onSelect }: AppSidebarProps) {
const { navigationItems } = useAppSidebarModel({ items, onSelect });
return (
<aside className="sidebar">
<div className="brand">
<div className="brand-mark">GA</div>
<div>
<div className="brand-title">Artifact Assistant</div>
<div className="brand-subtitle">Scanner-first MVP</div>
</div>
</div>
<nav className="nav-list">
{navigationItems.map((item) => {
const Icon = item.icon;
return (
<button
key={item.id}
className={`nav-item ${activeView === item.id ? "active" : ""}`}
disabled={Boolean(item.disabled)}
onClick={item.onSelect}
title={item.disabled ? item.disabledReason : undefined}
>
<Icon size={18} />
{item.label}
</button>
);
})}
</nav>
<div className="safety-card">
<ShieldCheck size={18} />
<div>
<strong>Sicherheits-Grenzen</strong>
<span>Keine Eingriffe am Spielinventar, keine Ressourcen-Aktionen, nur Analyse und Scan.</span>
</div>
</div>
</aside>
);
}
export function AppTopbar({
topbarStatus,
canExportGood,
canShowOverlay,
isScanning,
artifactCount,
onShowOverlay,
onDemoScan,
onExportGood,
exportIcon,
overlayIcon,
demoIcon,
}: AppTopbarProps) {
const {
handleShowOverlay,
handleExportGood,
handleDemoScan,
isExportDisabled,
isDemoDisabled,
isOverlayDisabled,
exportButtonLabel,
exportButtonTitle,
overlayButtonLabel,
overlayButtonTitle,
demoButtonLabel,
demoButtonTitle,
} = useAppTopbarModel({
onExportGood,
onShowOverlay,
onDemoScan,
canExportGood,
canShowOverlay,
isScanning,
artifactCount,
});
return (
<header className="topbar">
<div>
<p className="eyebrow">Lokaler Windows-Assistent</p>
<h1>Scanne dein Inventar, triff einfache Artifact-Entscheidungen.</h1>
</div>
<div className="topbar-actions">
{topbarStatus && <span className="topbar-status">{topbarStatus}</span>}
<button className="ghost-button" onClick={handleExportGood} disabled={isExportDisabled} title={exportButtonTitle}>
{exportIcon}
<span>{exportButtonLabel}</span>
</button>
<button
className="ghost-button"
onClick={handleShowOverlay}
disabled={isOverlayDisabled}
title={overlayButtonTitle}
>
{overlayIcon}
<span>{overlayButtonLabel}</span>
</button>
<button
className="ghost-button"
onClick={handleDemoScan}
disabled={isDemoDisabled}
title={demoButtonTitle}
>
{demoIcon}
{demoButtonLabel}
</button>
</div>
</header>
);
}
export function AppMetrics({ metricCards }: AppMetricsProps) {
return (
<section className="metrics-grid">
{metricCards.map((metric) => (
<div className="metric" key={metric.label}>
<span>{metric.label}</span>
<strong>{metric.value}</strong>
<small>{metric.detail}</small>
</div>
))}
</section>
);
}
export function AppShell({ sidebar, children }: AppShellProps) {
return (
<div className="app-shell">
{sidebar}
<main className="main-panel">{children}</main>
</div>
);
}
@@ -0,0 +1,27 @@
import { useMemo } from "react";
import type { NavigationId } from "../types";
import type { AppNavigationItem, AppSidebarItemAction } from "../types";
export interface AppSidebarModel {
navigationItems: AppSidebarItemAction[];
}
interface UseAppSidebarModelInput {
items: AppNavigationItem[];
onSelect: (id: NavigationId) => void;
}
export function useAppSidebarModel({ items, onSelect }: UseAppSidebarModelInput): AppSidebarModel {
const navigationItems = useMemo(
() =>
items.map((item) => ({
...item,
onSelect: () => onSelect(item.id),
})),
[items, onSelect],
);
return {
navigationItems,
};
}
@@ -0,0 +1,67 @@
import { useCallback } from "react";
export interface AppTopbarModel {
handleShowOverlay: () => void;
handleExportGood: () => void;
handleDemoScan: () => void;
isExportDisabled: boolean;
isDemoDisabled: boolean;
isOverlayDisabled: boolean;
exportButtonLabel: string;
exportButtonTitle: string;
overlayButtonLabel: string;
overlayButtonTitle: string;
demoButtonLabel: string;
demoButtonTitle: string;
}
interface UseAppTopbarModelInput {
onExportGood: () => Promise<void> | void;
onShowOverlay: () => Promise<void>;
onDemoScan: () => void;
canExportGood: boolean;
canShowOverlay: boolean;
isScanning: boolean;
artifactCount: number;
}
export function useAppTopbarModel({
onExportGood,
onShowOverlay,
onDemoScan,
canExportGood,
canShowOverlay,
isScanning,
artifactCount,
}: UseAppTopbarModelInput): AppTopbarModel {
const handleShowOverlay = useCallback(() => {
void onShowOverlay();
}, [onShowOverlay]);
const handleExportGood = useCallback(() => {
void onExportGood();
}, [onExportGood]);
const handleDemoScan = useCallback(() => {
onDemoScan();
}, [onDemoScan]);
const isExportDisabled = !canExportGood || artifactCount === 0;
const isDemoDisabled = isScanning;
const isOverlayDisabled = !canShowOverlay;
return {
handleShowOverlay,
handleExportGood,
handleDemoScan,
isExportDisabled,
isDemoDisabled,
isOverlayDisabled,
exportButtonLabel: "GOOD Export",
exportButtonTitle: canExportGood ? "GOOD-Export starten." : "GOOD-Export ist nur in der Electron-App verfuegbar.",
overlayButtonLabel: "Overlay",
overlayButtonTitle: canShowOverlay ? "Overlay oeffnen." : "Overlay benoetigt die Electron-Bridge.",
demoButtonLabel: isScanning ? "Laedt..." : "Demo-Daten",
demoButtonTitle: "Laedt Beispieldaten fuer die Triage- und Build-Ansicht.",
};
}
+12
View File
@@ -0,0 +1,12 @@
export { AppShell, AppMetrics, AppSidebar, AppTopbar } from "./AppLayout";
export {
type AppMetricCard,
type AppNavigationItem,
type AppBarAction,
type NavigationId,
type AppSidebarProps,
type AppTopbarProps,
type AppMetricsProps,
type AppShellProps,
} from "./types";
export { appNavigationItems } from "./navigation";
+10
View File
@@ -0,0 +1,10 @@
import { Layers3, Radar, Wand2, Eye } from "lucide-react";
import type { AppNavigationItem } from "./types";
export const appNavigationItems: AppNavigationItem[] = [
{ id: "scan", label: "Scan", icon: Radar },
{ id: "triage", label: "Triage", icon: Layers3 },
{ id: "builds", label: "Builds", icon: Wand2 },
{ id: "overlay", label: "Overlay", icon: Eye },
];
+60
View File
@@ -0,0 +1,60 @@
import { type ComponentType, type ReactNode } from "react";
import type { LucideProps } from "lucide-react";
export type NavigationId = "scan" | "triage" | "builds" | "overlay";
export interface AppNavigationItem {
id: NavigationId;
label: string;
icon: ComponentType<LucideProps>;
disabled?: boolean;
disabledReason?: string;
}
export interface AppSidebarItemAction extends AppNavigationItem {
onSelect: () => void;
}
export interface AppMetricCard {
label: string;
value: number;
detail: string;
}
export interface AppBarAction {
id: string;
icon?: ReactNode;
label: string;
disabled?: boolean;
onAction: () => void;
title?: string;
}
export interface AppSidebarProps {
activeView: NavigationId;
items: AppNavigationItem[];
onSelect: (id: NavigationId) => void;
}
export interface AppTopbarProps {
topbarStatus: string;
canExportGood: boolean;
canShowOverlay: boolean;
isScanning: boolean;
artifactCount: number;
onExportGood: () => Promise<void> | void;
onShowOverlay: () => Promise<void>;
onDemoScan: () => void;
exportIcon?: ReactNode;
overlayIcon?: ReactNode;
demoIcon?: ReactNode;
}
export interface AppMetricsProps {
metricCards: AppMetricCard[];
}
export interface AppShellProps {
sidebar: ReactNode;
children: ReactNode;
}