EU-Utility-V3/src/components/SetupWizard.tsx

344 lines
11 KiB
TypeScript

import { useState, useEffect, useRef } from 'react'
import { invoke } from '@tauri-apps/api/tauri'
import {
Monitor,
Crosshair,
Check,
ChevronRight,
ChevronLeft,
RefreshCw,
AlertCircle,
Settings2
} from 'lucide-react'
interface CalibrationRegion {
name: string
key: string
description: string
required: boolean
detected?: boolean
}
const REGIONS: CalibrationRegion[] = [
{
name: 'HP Bar',
key: 'hp_bar',
description: 'Health bar at bottom of screen',
required: true
},
{
name: 'Radar',
key: 'radar',
description: 'Mini-map showing location',
required: true
},
{
name: 'Skill Window',
key: 'skill_window',
description: 'Window showing skill gains',
required: false
},
{
name: 'Mob Name',
key: 'mob_name',
description: 'Floating text above creatures',
required: false
},
]
export default function SetupWizard() {
const [step, setStep] = useState(0)
const [isDetecting, setIsDetecting] = useState(false)
const [detections, setDetections] = useState<Record<string, boolean>>({})
const [calibration, setCalibration] = useState<any>(null)
const [showWizard, setShowWizard] = useState(false)
useEffect(() => {
checkFirstRun()
}, [])
const checkFirstRun = async () => {
try {
const settings = await invoke<any>('get_settings')
if (!settings?.ocr?.calibration) {
setShowWizard(true)
}
} catch (e) {
setShowWizard(true)
}
}
const autoDetect = async () => {
setIsDetecting(true)
try {
const detected = await invoke<any>('detect_ui_elements')
setDetections({
hp_bar: detected.hp_bar.found,
radar: detected.radar.found,
skill_window: detected.skill_window.found,
})
} catch (e) {
console.error('Detection failed:', e)
}
setIsDetecting(false)
}
const saveCalibration = async () => {
try {
await invoke('set_ocr_calibration', { calibration })
setShowWizard(false)
} catch (e) {
console.error('Save failed:', e)
}
}
if (!showWizard) return null
return (
<div className="fixed inset-0 bg-background/95 backdrop-blur-sm z-50 flex items-center justify-center">
<div className="w-full max-w-4xl bg-surface rounded-2xl border border-border shadow-2xl">
{/* Header */}
<div className="p-6 border-b border-border">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center">
<Settings2 className="w-6 h-6 text-primary" />
</div>
<div>
<h2 className="text-2xl font-bold text-white">EU-Utility Setup</h2>
<p className="text-text-muted">Configure screen regions for OCR recognition</p>
</div>
</div>
</div>
{/* Steps */}
<div className="p-6">
{step === 0 && (
<div className="space-y-6">
<div className="text-center py-8">
<Monitor className="w-20 h-20 text-primary mx-auto mb-6" />
<h3 className="text-xl font-bold text-white mb-3">Welcome to EU-Utility V3</h3>
<p className="text-text-muted max-w-lg mx-auto">
This setup wizard will help you configure the application to read
screen elements from Entropia Universe. This enables features like
automatic loot tracking, HP monitoring, and skill gain detection.
</p>
</div>
<div className="bg-surface-light rounded-lg p-4 border border-border">
<h4 className="font-semibold text-white mb-3">What will be configured:</h4>
<ul className="space-y-2 text-text-muted">
<li className="flex items-center gap-2">
<Check className="w-4 h-4 text-accent" />
HP bar position and reading
</li>
<li className="flex items-center gap-2">
<Check className="w-4 h-4 text-accent" />
Radar/mini-map coordinate extraction
</li>
<li className="flex items-center gap-2">
<Check className="w-4 h-4 text-accent" />
Skill window detection
</li>
<li className="flex items-center gap-2">
<Check className="w-4 h-4 text-accent" />
Mob name recognition
</li>
</ul>
</div>
</div>
)}
{step === 1 && (
<div className="space-y-6">
<div className="text-center mb-6">
<h3 className="text-xl font-bold text-white">Auto-Detect UI Elements</h3>
<p className="text-text-muted mt-2">
Make sure Entropia Universe is running and visible on your screen,
then click detect to automatically find UI elements.
</p>
</div>
<button
onClick={autoDetect}
disabled={isDetecting}
className="w-full py-4 bg-primary hover:bg-primary-hover disabled:opacity-50 text-white rounded-xl font-medium transition-colors flex items-center justify-center gap-3"
>
{isDetecting ? (
<>
<RefreshCw className="w-5 h-5 animate-spin" />
Detecting UI elements...
</>
) : (
<>
<Crosshair className="w-5 h-5" />
Auto-Detect Elements
</>
)}
</button>
<div className="grid grid-cols-2 gap-4">
{REGIONS.map((region) => (
<div
key={region.key}
className={`p-4 rounded-lg border ${
detections[region.key]
? 'bg-accent/10 border-accent/30'
: 'bg-surface-light border-border'
}`}
>
<div className="flex items-center justify-between mb-2">
<span className="font-medium text-white">{region.name}</span>
{detections[region.key] ? (
<Check className="w-5 h-5 text-accent" />
) : (
<AlertCircle className="w-5 h-5 text-text-muted" />
)}
</div>
<p className="text-sm text-text-muted">{region.description}</p>
</div>
))}
</div>
</div>
)}
{step === 2 && (
<RegionCalibrator
regions={REGIONS}
onComplete={(cal) => {
setCalibration(cal)
setStep(3)
}}
/>
)}
{step === 3 && (
<div className="text-center py-12">
<div className="w-20 h-20 rounded-full bg-accent/10 flex items-center justify-center mx-auto mb-6">
<Check className="w-10 h-10 text-accent" />
</div>
<h3 className="text-2xl font-bold text-white mb-3">Setup Complete</h3>
<p className="text-text-muted max-w-md mx-auto mb-8">
EU-Utility is now configured to read screen elements.
You can adjust these settings anytime in Settings {`->`} OCR.
</p>
<button
onClick={saveCalibration}
className="px-8 py-3 bg-primary hover:bg-primary-hover text-white rounded-lg font-medium transition-colors"
>
Finish Setup
</button>
</div>
)}
</div>
{/* Navigation */}
<div className="p-6 border-t border-border flex justify-between">
<button
onClick={() => setStep(Math.max(0, step - 1))}
disabled={step === 0}
className="flex items-center gap-2 px-4 py-2 text-text-muted hover:text-white disabled:opacity-30 transition-colors"
>
<ChevronLeft className="w-4 h-4" />
Back
</button>
<div className="flex gap-2">
{REGIONS.map((_, i) => (
<div
key={i}
className={`w-2 h-2 rounded-full ${
i <= step ? 'bg-primary' : 'bg-surface-light'
}`}
/>
))}
</div>
<button
onClick={() => setStep(Math.min(3, step + 1))}
disabled={step === 3 || (step === 1 && Object.keys(detections).length === 0)}
className="flex items-center gap-2 px-4 py-2 bg-primary hover:bg-primary-hover disabled:opacity-30 text-white rounded-lg transition-colors"
>
{step === 2 ? 'Complete' : 'Next'}
<ChevronRight className="w-4 h-4" />
</button>
</div>
</div>
</div>
)
}
interface RegionCalibratorProps {
regions: CalibrationRegion[]
onComplete: (calibration: any) => void
}
function RegionCalibrator({ regions, onComplete }: RegionCalibratorProps) {
const [activeRegion, setActiveRegion] = useState(0)
const [_regions_, _setRegions] = useState<Record<string, any>>({})
const canvasRef = useRef<HTMLCanvasElement>(null)
const captureScreen = async () => {
try {
const screenshot = await invoke<string>('capture_screen')
return screenshot
} catch (e) {
console.error('Capture failed:', e)
return null
}
}
return (
<div className="space-y-4">
<p className="text-center text-text-muted mb-4">
Click and drag on the screenshot to define each region
</p>
<div className="flex gap-2 mb-4">
{regions.map((region, i) => (
<button
key={region.key}
onClick={() => setActiveRegion(i)}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
activeRegion === i
? 'bg-primary text-white'
: regions_[region.key]
? 'bg-accent/20 text-accent'
: 'bg-surface-light text-text-muted'
}`}
>
{region.name}
{regions_[region.key] && <Check className="w-3 h-3 inline ml-1" />}
</button>
))}
</div>
<div className="relative bg-black rounded-lg overflow-hidden aspect-video">
<canvas
ref={canvasRef}
className="w-full h-full cursor-crosshair"
/>
<div className="absolute inset-0 flex items-center justify-center">
<button
onClick={captureScreen}
className="px-4 py-2 bg-primary/80 hover:bg-primary text-white rounded-lg"
>
Capture Screen
</button>
</div>
</div>
<button
onClick={() => onComplete({ regions: regions_ })}
disabled={Object.keys(regions_).length === 0}
className="w-full py-3 bg-accent hover:bg-accent-hover disabled:opacity-50 text-white rounded-lg font-medium transition-colors"
>
Save Calibration
</button>
</div>
)
}