diff --git a/__tests__/tutorial/tutorial-configs.test.ts b/__tests__/tutorial/tutorial-configs.test.ts index 9b865f69..b8c4504b 100644 --- a/__tests__/tutorial/tutorial-configs.test.ts +++ b/__tests__/tutorial/tutorial-configs.test.ts @@ -23,13 +23,15 @@ describe('Tutorial Configurations', () => { expect(dashboardTutorial).toBeDefined(); expect(dashboardTutorial.id).toBe('dashboard-intro'); expect(dashboardTutorial.name).toBe('GameStringer Dashboard'); - expect(dashboardTutorial.steps).toHaveLength(9); + expect(dashboardTutorial.steps).toHaveLength(8); expect(dashboardTutorial.autoStart).toBe(true); expect(dashboardTutorial.canSkip).toBe(true); // Check first and last steps expect(dashboardTutorial.steps[0].id).toBe('welcome'); - expect(dashboardTutorial.steps[8].id).toBe('completion'); + // Indice calcolato, non cablato: l'asserzione dice «l'ultimo passo e' + // completion», che e' l'intento, e sopravvive a passi aggiunti o tolti. + expect(dashboardTutorial.steps[dashboardTutorial.steps.length - 1].id).toBe('completion'); }); it('should have valid library tutorial config', () => { @@ -165,7 +167,6 @@ describe('Tutorial Configurations', () => { const testCases = [ { path: '/', expectedCount: 1 }, { path: '/library', expectedCount: 1 }, - { path: '/injekt-translator', expectedCount: 1 }, { path: '/editor', expectedCount: 1 }, { path: '/patches', expectedCount: 1 }, { path: '/community-hub', expectedCount: 1 }, diff --git a/components/injekt-overlay-config.tsx b/components/injekt-overlay-config.tsx deleted file mode 100644 index 3f66ed84..00000000 --- a/components/injekt-overlay-config.tsx +++ /dev/null @@ -1,728 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Label } from '@/components/ui/label'; -import { Input } from '@/components/ui/input'; -import { Slider } from '@/components/ui/slider'; -import { Switch } from '@/components/ui/switch'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { - Monitor, - Palette, - Move, - Settings2, - Sparkles, - Layers, - Download, - Upload, - RotateCcw -} from 'lucide-react'; -import { useTranslation } from '@/lib/i18n'; -import { clientLogger } from '@/lib/client-logger'; - -export interface OverlayConfig { - enabled: boolean; - position: 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'; - offset: { x: number; y: number }; - opacity: number; - backgroundColor: string; - textColor: string; - borderColor: string; - borderWidth: number; - borderRadius: number; - fontSize: number; - fontFamily: string; - padding: number; - showOriginal: boolean; - showTranslated: boolean; - animationEnabled: boolean; - animationType: 'fade' | 'slide' | 'scale' | 'none'; - animationDuration: number; - blurBackground: boolean; - blurAmount: number; - shadow: boolean; - shadowColor: string; - shadowBlur: number; - maxWidth: number; - maxHeight: number; - autoHide: boolean; - autoHideDelay: number; - hotkey: string; -} - -const defaultConfig: OverlayConfig = { - enabled: true, - position: 'bottom-center', - offset: { x: 0, y: -50 }, - opacity: 90, - backgroundColor: '#000000', - textColor: '#FFFFFF', - borderColor: '#333333', - borderWidth: 1, - borderRadius: 8, - fontSize: 16, - fontFamily: 'Arial', - padding: 16, - showOriginal: true, - showTranslated: true, - animationEnabled: true, - animationType: 'fade', - animationDuration: 300, - blurBackground: true, - blurAmount: 4, - shadow: true, - shadowColor: '#000000', - shadowBlur: 10, - maxWidth: 600, - maxHeight: 200, - autoHide: false, - autoHideDelay: 5000, - hotkey: 'Ctrl+Shift+T' -}; - -interface InjektOverlayConfigProps { - config: OverlayConfig; - onConfigChange: (config: OverlayConfig) => void; -} - -export function InjektOverlayConfig({ config, onConfigChange }: InjektOverlayConfigProps) { - const { t } = useTranslation(); - const [localConfig, setLocalConfig] = useState(config); - const [previewText, _setPreviewText] = useState({ - original: 'Hello, adventurer!', - translated: 'Ciao, avventuriero!' - }); - - useEffect(() => { - setLocalConfig(config); - }, [config]); - - const handleChange = (key: keyof OverlayConfig, value: unknown) => { - const newConfig = { ...localConfig, [key]: value }; - setLocalConfig(newConfig); - onConfigChange(newConfig); - }; - - const handleOffsetChange = (axis: 'x' | 'y', value: number) => { - const newOffset = { ...localConfig.offset, [axis]: value }; - handleChange('offset', newOffset); - }; - - const resetToDefaults = () => { - setLocalConfig(defaultConfig); - onConfigChange(defaultConfig); - }; - - const exportConfig = () => { - const blob = new Blob([JSON.stringify(localConfig, null, 2)], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'overlay-config.json'; - a.click(); - URL.revokeObjectURL(url); - }; - - const importConfig = (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - try { - const imported = JSON.parse(e.target?.result as string); - setLocalConfig(imported); - onConfigChange(imported); - } catch (error: unknown) { - clientLogger.error('error importazione configurazione:', error); - } - }; - reader.readAsText(file); - } - }; - - const positions = [ - { value: 'top-left', label: 'In alto a sinistra' }, - { value: 'top-center', label: 'In alto al centro' }, - { value: 'top-right', label: 'In alto a destra' }, - { value: 'center-left', label: 'Centro sinistra' }, - { value: 'center', label: 'Centro' }, - { value: 'center-right', label: 'Centro destra' }, - { value: 'bottom-left', label: 'In basso a sinistra' }, - { value: 'bottom-center', label: 'In basso al centro' }, - { value: 'bottom-right', label: 'In basso a destra' } - ]; - - const fonts = [ - 'Arial', 'Helvetica', 'Times New Roman', 'Georgia', - 'Courier New', 'Verdana', 'Tahoma', 'Trebuchet MS', - 'Impact', 'Comic Sans MS' - ]; - - const animations = [ - { value: 'none', label: 'Nessuna' }, - { value: 'fade', label: 'Dissolvenza' }, - { value: 'slide', label: 'Scorrimento' }, - { value: 'scale', label: 'Scala' } - ]; - - return ( -
- - - {t('injektOverlayConfigComp.appearance')} - {t('injektOverlayConfigComp.position')} - {t('injektOverlayConfigComp.animation')} - {t('injektOverlayConfigComp.behavior')} - - - - - - - - {t('injektOverlayConfigComp.colorsStyle')} - - -
-
- -
- handleChange('backgroundColor', e.target.value)} - className="w-20 h-10" - /> - handleChange('backgroundColor', e.target.value)} - className="flex-1" - /> -
-
- -
- -
- handleChange('textColor', e.target.value)} - className="w-20 h-10" - /> - handleChange('textColor', e.target.value)} - className="flex-1" - /> -
-
-
- -
-
- - {localConfig.opacity}% -
- handleChange('opacity', value)} - min={0} - max={100} - step={5} - /> -
- -
-
- - -
- -
-
- - {localConfig.fontSize}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('fontSize', value)} - min={10} - max={32} - step={1} - /> -
-
- -
-
- - {localConfig.borderRadius}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('borderRadius', value)} - min={0} - max={20} - step={1} - /> -
- -
-
- - {localConfig.padding}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('padding', value)} - min={8} - max={32} - step={2} - /> -
-
-
- - - - - - {t('injektOverlayConfigComp.effects')} - - -
-
- -

{t('injektOverlayConfigComp.aggiungiOmbraAlloverlay')}

-
- handleChange('shadow', checked)} - /> -
- - {localConfig.shadow && ( -
-
- -
- handleChange('shadowColor', e.target.value)} - className="w-20 h-10" - /> - handleChange('shadowColor', e.target.value)} - className="flex-1" - /> -
-
- -
-
- - {localConfig.shadowBlur}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('shadowBlur', value)} - min={0} - max={30} - step={1} - /> -
-
- )} - -
-
- -

{t('injektOverlayConfigComp.sfocaLoSfondoDietroLoverlay')}

-
- handleChange('blurBackground', checked)} - /> -
- - {localConfig.blurBackground && ( -
-
- - {localConfig.blurAmount}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('blurAmount', value)} - min={0} - max={20} - step={1} - /> -
- )} -
-
-
- - - - - - - {t('injektOverlayConfigComp.positioning')} - - -
- - -
- -
-
-
- - {localConfig.offset.x}{t('injektOverlayConfigComp.pxUnit')} -
- handleOffsetChange('x', value)} - min={-200} - max={200} - step={10} - /> -
- -
-
- - {localConfig.offset.y}{t('injektOverlayConfigComp.pxUnit')} -
- handleOffsetChange('y', value)} - min={-200} - max={200} - step={10} - /> -
-
- -
-
-
- - {localConfig.maxWidth}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('maxWidth', value)} - min={200} - max={1200} - step={50} - /> -
- -
-
- - {localConfig.maxHeight}{t('injektOverlayConfigComp.pxUnit')} -
- handleChange('maxHeight', value)} - min={100} - max={600} - step={50} - /> -
-
-
-
-
- - - - - - - {t('injektOverlayConfigComp.animations')} - - -
-
- -

{t('injektOverlayConfigComp.animaLapparizioneDelloverlay')}

-
- handleChange('animationEnabled', checked)} - /> -
- - {localConfig.animationEnabled && ( - <> -
- - -
- -
-
- - {localConfig.animationDuration}{t('injektOverlayConfigComp.msUnit')} -
- handleChange('animationDuration', value)} - min={100} - max={1000} - step={50} - /> -
- - )} -
-
-
- - - - - - - {t('injektOverlayConfigComp.behavior')} - - -
-
- -

{t('injektOverlayConfigComp.displayOriginalTextInOverlay')}

-
- handleChange('showOriginal', checked)} - /> -
- -
-
- -

{t('injektOverlayConfigComp.displayTranslatedTextInOverlay')}

-
- handleChange('showTranslated', checked)} - /> -
- -
-
- -

{t('injektOverlayConfigComp.hideOverlayAfterACertainTime')}

-
- handleChange('autoHide', checked)} - /> -
- - {localConfig.autoHide && ( -
-
- - {localConfig.autoHideDelay}{t('injektOverlayConfigComp.msUnit')} -
- handleChange('autoHideDelay', value)} - min={1000} - max={10000} - step={500} - /> -
- )} - -
- - handleChange('hotkey', e.target.value)} - placeholder={t('injektOverlayConfigComp.hotkeyPh')} - /> -
-
-
- -
-
- - - - - -
-
-
-
- - {/* Preview */} - - - - - {t('injektOverlayConfigComp.overlayPreview')} - - {t('injektOverlayConfigComp.previewDesc')} - - -
- {/* Simulated game background */} -
-

{t('injektOverlayConfigComp.gameBackground')}

-
- - {/* Overlay preview */} -
-
- {localConfig.showOriginal && ( -
{previewText.original}
- )} - {localConfig.showTranslated && ( -
{previewText.translated}
- )} -
-
-
-
-
-
- ); -} - -function getOverlayPosition(position: string, offset: { x: number; y: number }) { - const base: Record = { - 'top-left': { top: 20, left: 20 }, - 'top-center': { top: 20, left: '50%', transform: 'translateX(-50%)' }, - 'top-right': { top: 20, right: 20 }, - 'center-left': { top: '50%', left: 20, transform: 'translateY(-50%)' }, - 'center': { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }, - 'center-right': { top: '50%', right: 20, transform: 'translateY(-50%)' }, - 'bottom-left': { bottom: 20, left: 20 }, - 'bottom-center': { bottom: 20, left: '50%', transform: 'translateX(-50%)' }, - 'bottom-right': { bottom: 20, right: 20 }, - }; - - const style = { ...(base[position] as Record) }; - - // Apply offset - if (style.left !== undefined && typeof style.left === 'number') { - style.left += offset.x; - } - if (style.right !== undefined && typeof style.right === 'number') { - style.right -= offset.x; - } - if (style.top !== undefined && typeof style.top === 'number') { - style.top += offset.y; - } - if (style.bottom !== undefined && typeof style.bottom === 'number') { - style.bottom -= offset.y; - } - - return style; -} - - - - diff --git a/components/injekt-realtime-stats.tsx b/components/injekt-realtime-stats.tsx deleted file mode 100644 index 950d1eee..00000000 --- a/components/injekt-realtime-stats.tsx +++ /dev/null @@ -1,333 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Progress } from '@/components/ui/progress'; -import { Badge } from '@/components/ui/badge'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { - Zap, - Database, - Activity, - Timer, - FileText, - Cpu, - HardDrive -} from 'lucide-react'; -import { AreaChart, Area, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; -import { useTranslation } from '@/lib/i18n'; -import { clientLogger } from '@/lib/client-logger'; -import { isTauri } from '@/lib/tauri-api'; - -interface RealtimeStats { - processId: number; - processName: string; - gameName: string; - totalTranslations: number; - translationsPerMinute: number; - memoryUsage: number; - cpuUsage: number; - activeTime: number; - lastTranslation: { - original: string; - translated: string; - timestamp: Date; - } | null; - translationHistory: Array<{ - time: string; - count: number; - }>; - languageDistribution: Array<{ - language: string; - count: number; - percentage: number; - }>; - performanceMetrics: { - avgTranslationTime: number; - cacheHitRate: number; - memoryEfficiency: number; - }; -} - -interface InjektRealtimeStatsProps { - processId: number | null; - isActive: boolean; -} - -export function InjektRealtimeStats({ processId, isActive }: InjektRealtimeStatsProps) { - const { t } = useTranslation(); - const [stats, setStats] = useState(null); - const [updateInterval, setUpdateInterval] = useState(null); - - useEffect(() => { - if (processId && isActive) { - fetchStats(); - const interval = setInterval(fetchStats, 1000); // Aggiorna ogni secondo - setUpdateInterval(interval); - return () => { - if (interval) clearInterval(interval); - }; - } else { - if (updateInterval) { - clearInterval(updateInterval); - setUpdateInterval(null); - } - setStats(null); - } - }, [processId, isActive]); - - const fetchStats = async () => { - if (!processId) return; - // /api non esiste nel desktop impacchettato: le stat live via HTTP valgono solo nel - // build web. In Tauri si degrada (nessuna stat) invece di generare un 501. - if (isTauri()) return; - - try { - const response = await fetch(`/api/injekt/stats/${processId}`); - if (response.ok) { - const data = await response.json(); - setStats(data); - } - } catch (error: unknown) { - clientLogger.error('error Loading...atistiche:', error); - } - }; - - if (!stats) { - return ( - - -

- {isActive ? t('injektRealtimeStatsComp.caricamentoStatistiche') : t('injektRealtimeStatsComp.nessunaSessioneAttiva')} -

-
-
- ); - } - - const formatTime = (seconds: number) => { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const secs = seconds % 60; - return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; - }; - - const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8']; - - return ( -
- {/* Header Stats */} -
- - - - - {t('injektRealtimeStatsComp.traduzioniTotali')} - - - -
{stats.totalTranslations}
-

- {stats.translationsPerMinute} {t('injektRealtimeStatsComp.alMinuto')} -

-
-
- - - - - - {t('injektRealtimeStatsComp.tempoAttivo')} - - - -
{formatTime(stats.activeTime)}
-

- {t('injektRealtimeStatsComp.sessioneCorrente')} -

-
-
- - - - - - CPU - - - -
{stats.cpuUsage.toFixed(1)}%
- -
-
- - - - - - {t('injektRealtimeStatsComp.memoria')} - - - -
{(stats.memoryUsage / 1024 / 1024).toFixed(0)} MB
- -
-
-
- - {/* Grafici e Dettagli */} - - - {t('injektRealtimeStatsComp.timeline')} - {t('injektRealtimeStatsComp.lingue')} - {t('injektRealtimeStatsComp.performance')} - {t('injektRealtimeStatsComp.recenti')} - - - - - - {t('injektRealtimeStatsComp.traduzioniNelTempo')} - - - - - - - - - - - - - - - - - - - {t('injektRealtimeStatsComp.distribuzioneLingue')} - - - - - `${language} ${percentage}%`} - outerRadius={80} - fill="#8884d8" - dataKey="count" - > - {stats.languageDistribution.map((entry, index) => ( - - ))} - - - - - - - - - -
- - - - - {t('injektRealtimeStatsComp.tempoMedioTraduzione')} - - - -
- {stats.performanceMetrics.avgTranslationTime.toFixed(0)} ms -
-

- {t('injektRealtimeStatsComp.perTraduzione')} -

-
-
- - - - - - {t('injektRealtimeStatsComp.cacheHitRate')} - - - -
- {(stats.performanceMetrics.cacheHitRate * 100).toFixed(1)}% -
- -
-
- - - - - - {t('injektRealtimeStatsComp.efficienzaMemoria')} - - - -
- {(stats.performanceMetrics.memoryEfficiency * 100).toFixed(1)}% -
- -
-
-
-
- - - - - {t('injektRealtimeStatsComp.ultimeTraduzioni')} - - - - {stats.lastTranslation && ( -
-
-
- {t('injektRealtimeStatsComp.piùRecente')} - - {new Date(stats.lastTranslation.timestamp).toLocaleTimeString()} - -
-
-

{stats.lastTranslation.original}

-

→ {stats.lastTranslation.translated}

-
-
-
- )} -
-
-
-
-
-
- ); -} - - - - diff --git a/components/injekt-ui-enhanced.tsx b/components/injekt-ui-enhanced.tsx deleted file mode 100644 index 899c1c07..00000000 --- a/components/injekt-ui-enhanced.tsx +++ /dev/null @@ -1,645 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Progress } from '@/components/ui/progress'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Switch } from '@/components/ui/switch'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { - Play, - Square, - RefreshCw, - Settings, - Download, - Upload, - Plus, - Trash2, - TrendingUp, - Clock, - Zap, - Database, - Globe -} from 'lucide-react'; -import { gameProfileManager, GameProfile } from '@/lib/game-profiles'; -import { gameTranslations } from '@/lib/game-translations'; -import { InjektRealtimeStats } from '@/components/injekt-realtime-stats'; -import { InjektOverlayConfig, OverlayConfig } from '@/components/injekt-overlay-config'; -import { TranslationProfileManager } from '@/components/translation-profile-manager'; -// translationProfileManager/TranslationProfile imports removed — not currently used -import { useTranslation } from '@/lib/i18n'; -import { clientLogger } from '@/lib/client-logger'; - -interface Process { - pid: number; - name: string; - windowTitle?: string; - executablePath?: string; - icon?: string; -} - -interface InjectionStats { - totalTranslations: number; - customTranslations: number; - totalInjections: number; - memoryAddresses: number; - lastUpdated: Date; - enabled: boolean; -} - -interface TranslationEntry { - original: string; - translated: string; -} - -export function InjektUIEnhanced() { - const { t } = useTranslation(); - const [processes, setProcesses] = useState([]); - const [selectedProcess, setSelectedProcess] = useState(null); - const [isInjecting, setIsInjecting] = useState(false); - const [injectionStats, setInjectionStats] = useState(null); - const [profiles, setProfiles] = useState([]); - const [activeTab, setActiveTab] = useState('injection'); - const [customTranslations, setCustomTranslations] = useState([]); - const [newTranslation, setNewTranslation] = useState({ original: '', translated: '' }); - const [_editingTranslation, _setEditingTranslation] = useState(null); - const [injectionCount, setInjectionCount] = useState(0); - const [monitoringInterval, setMonitoringInterval] = useState(null); - const [overlayConfig, setOverlayConfig] = useState(() => { - const saved = localStorage.getItem('injekt-overlay-config'); - return saved ? JSON.parse(saved) : { - enabled: true, - position: 'bottom-center', - offset: { x: 0, y: -50 }, - opacity: 90, - backgroundColor: '#000000', - textColor: '#FFFFFF', - borderColor: '#333333', - borderWidth: 1, - borderRadius: 8, - fontSize: 16, - fontFamily: 'Arial', - padding: 16, - showOriginal: true, - showTranslated: true, - animationEnabled: true, - animationType: 'fade', - animationDuration: 300, - blurBackground: true, - blurAmount: 4, - shadow: true, - shadowColor: '#000000', - shadowBlur: 10, - maxWidth: 600, - maxHeight: 200, - autoHide: false, - autoHideDelay: 5000, - hotkey: 'Ctrl+Shift+T' - }; - }); - - // Load processes - useEffect(() => { - fetchProcesses(); - const interval = setInterval(fetchProcesses, 5000); - return () => clearInterval(interval); - }, []); - - // Load profiles - useEffect(() => { - loadProfiles(); - }, []); - - // Update stats when selected process changes - useEffect(() => { - if (selectedProcess) { - updateStats(); - loadCustomTranslations(); - } - }, [selectedProcess]); - - // Save overlay config when it changes - useEffect(() => { - localStorage.setItem('injekt-overlay-config', JSON.stringify(overlayConfig)); - }, [overlayConfig]); - - const fetchProcesses = async () => { - try { - const { invoke } = await import('@/lib/tauri-api'); - const result = await invoke('get_processes') as Record; - setProcesses((result?.processes || result || []) as typeof processes); - } catch (error: unknown) { - clientLogger.error('Error loading processes:', error); - setProcesses([]); - } - }; - - const loadProfiles = () => { - const allProfiles = gameProfileManager.listProfiles(); - setProfiles(allProfiles); - }; - - const updateStats = () => { - if (!selectedProcess) return; - const stats = gameProfileManager.getStatistics(selectedProcess.name); - setInjectionStats(stats); - }; - - const loadCustomTranslations = () => { - if (!selectedProcess) return; - const profile = gameProfileManager.getOrCreateProfile(selectedProcess.name); - const translations = Object.entries(profile.customTranslations).map(([original, translated]) => ({ - original, - translated - })); - setCustomTranslations(translations); - }; - - const startInjection = async () => { - if (!selectedProcess) return; - - setIsInjecting(true); - setInjectionCount(0); - - try { - const { invoke } = await import('@/lib/tauri-api'); - const data = await invoke('start_injection', { - processId: selectedProcess.pid, - processName: selectedProcess.name, - config: { enabled: true, sourceLang: 'en', targetLang: 'it' } - }) as Record; - - if (data?.success !== false) { - // Start stats monitoring - const interval = setInterval(() => { - updateStats(); - setInjectionCount(prev => prev + Math.floor(Math.random() * 5)); - }, 1000); - setMonitoringInterval(interval); - } else { - alert((data.message as string) || 'Error during injection'); - setIsInjecting(false); - } - } catch (error: unknown) { - clientLogger.error('Injection error:', error); - setIsInjecting(false); - } - }; - - const stopInjection = async () => { - if (!selectedProcess) return; - - try { - const { invoke } = await import('@/lib/tauri-api'); - await invoke('stop_injection', { processId: selectedProcess.pid }); - - setIsInjecting(false); - if (monitoringInterval) { - clearInterval(monitoringInterval); - setMonitoringInterval(null); - } - } catch (error: unknown) { - clientLogger.error('Stop injection error:', error); - } - }; - - const addCustomTranslation = () => { - if (!selectedProcess || !newTranslation.original || !newTranslation.translated) return; - - gameProfileManager.addCustomTranslation( - selectedProcess.name, - newTranslation.original, - newTranslation.translated - ); - - loadCustomTranslations(); - updateStats(); - setNewTranslation({ original: '', translated: '' }); - }; - - const removeCustomTranslation = (original: string) => { - if (!selectedProcess) return; - - gameProfileManager.removeCustomTranslation(selectedProcess.name, original); - loadCustomTranslations(); - updateStats(); - }; - - const exportProfile = () => { - if (!selectedProcess) return; - - const data = gameProfileManager.exportProfile(selectedProcess.name); - const blob = new Blob([data], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${selectedProcess.name.replace('.exe', '')}_profile.json`; - a.click(); - URL.revokeObjectURL(url); - }; - - const importProfile = async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (!file) return; - - const text = await file.text(); - if (gameProfileManager.importProfile(text)) { - loadProfiles(); - updateStats(); - loadCustomTranslations(); - alert(t('injektUi.importSuccess')); - } else { - alert(t('injektUi.importError')); - } - }; - - const getGameIcon = (processName: string) => { - const game = gameTranslations.find(g => - g.processName.toLowerCase() === processName.toLowerCase() - ); - return game ? '🎮' : '🎯'; - }; - - return ( -
- {/* Header with futuristic stats */} -
- -
- - -
- -
- {t('injektUi.totalTranslations')}
-
- -
- {injectionStats?.totalTranslations || 0} -
-

- {injectionStats?.customTranslations || 0} {t('injektUi.customUnit')}

-
- - - -
- - -
- -
- {t('injektUi.totalInjections')}
-
- -
- {injectionStats?.totalInjections || 0} -
-

- {isInjecting ? `+${injectionCount} this session` : 'Not active'} -

-
- - - -
- - -
- -
- {t('injektUi.memoryAddresses')}
-
- -
- {injectionStats?.memoryAddresses || 0} -
-

{t('injektUiEnhancedComp.savedPositions')}

-
- - - -
- - -
- -
- {t('injektUi.lastUpdate')}
-
- -
- {injectionStats?.lastUpdated - ? new Date(injectionStats.lastUpdated).toLocaleTimeString() - : 'Mai'} -
-

- {selectedProcess ? selectedProcess.name : 'No process'} -

-
- -
- - {/* Main futuristic tabs */} - - - - - {t('injektUi.injectionTab')} - - - {t('injektUi.translationsTab')} - - - {t('injektUi.profilesTab')} - - - {t('injektUi.statisticsTab')} - - - {t('injektUi.overlayTab')} - - - {/* Tab Injection */} - - -
- - - - {t('injektUi.detectedProcesses')} - - - -
- {processes.map((process) => ( -
setSelectedProcess(process)} - > -
-
- {getGameIcon(process.name)} -
-
{process.name}
-
- {t('injektUi.pidLabel')} {process.pid} {process.windowTitle && `• ${process.windowTitle}`} -
-
-
- {selectedProcess?.pid === process.pid && ( - - {isInjecting ? 'Active' : 'Selected'} - - )} -
-
- ))} -
-
- - {selectedProcess && ( -
- - -
- )} -
- - - {/* Live Stats durante injection */} - {isInjecting && selectedProcess && ( - -
- - - - {t('injektUi.injectionInProgress')} - - -
-
- {t('injektUiEnhancedComp.injectedTranslations')} - {injectionCount} -
- -
-
- {t('injektUi.activeMonitoring')} {selectedProcess.name}... -
-
- - )} - - - {/* Tab Traduzioni */} - - -
- - - - {t('injektUi.customTranslations')} - - - {selectedProcess ? ( -
- {/* Add translation form */} -
- setNewTranslation({ ...newTranslation, original: e.target.value })} - className="bg-white/10 border-white/20 text-white placeholder-gray-400 focus:border-blue-400/50" - /> -
- setNewTranslation({ ...newTranslation, translated: e.target.value })} - className="bg-white/10 border-white/20 text-white placeholder-gray-400 focus:border-blue-400/50" - /> - -
-
- - {/* Translation list */} - -
- {customTranslations.map((trans) => ( -
-
-
{trans.original}
-
{trans.translated}
-
- -
- ))} -
-
-
- ) : ( -
- {t('injektUi.selectProcess')}
- )} -
- - - - {/* Tab Profili */} - - -
- - - - {t('injektUi.profileManagement')} - - -
- - -
- - -
- {profiles.map((profile) => ( -
-
-
-
{profile.gameName}
-
- {profile.processName} • {Object.keys(profile.customTranslations).length} {t('injektUi.customTranslationsUnit')}
-
- { - gameProfileManager.updateProfile(profile.processName, { enabled: checked }); - loadProfiles(); - }} - /> -
-
- ))} -
-
-
- - - - {/* Tab Profili Translation Manager */} - - -
- - { - // Load translations from selected profile - const translations = profile.translations.map(t => ({ - original: t.original, - translated: t.translated - })); - setCustomTranslations(translations); - - // Switch to translations tab to show loaded translations - setActiveTab('translations'); - - // Show notification - alert(`Profile "${profile.gameName}" loaded with ${profile.translations.length} translations`); - }} - /> - - - - - {/* Tab Statistiche */} - - -
- - - - - - - {/* Tab Overlay */} - - -
- - - - - - -
- ); -} - - - - diff --git a/components/translation-profile-manager.tsx b/components/translation-profile-manager.tsx deleted file mode 100644 index 672afc69..00000000 --- a/components/translation-profile-manager.tsx +++ /dev/null @@ -1,789 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Textarea } from '@/components/ui/textarea'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { - Plus, - Download, - Upload, - Edit, - Trash2, - Copy, - Star, - Search, - Globe, - GamepadIcon, - Hash, - CheckCircle, - FileText -} from 'lucide-react'; -import { translationProfileManager, TranslationProfile, TranslationEntry } from '@/lib/game-translation-profiles'; -import { useTranslation } from '@/lib/i18n'; - -interface TranslationProfileManagerProps { - processName?: string; - onProfileSelect?: (profile: TranslationProfile) => void; - selectedProfileId?: string; -} - -export function TranslationProfileManager({ - processName, - onProfileSelect, - selectedProfileId -}: TranslationProfileManagerProps) { - const { t } = useTranslation(); - const [profiles, setProfiles] = useState([]); - const [filteredProfiles, setFilteredProfiles] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); - const [selectedProfile, setSelectedProfile] = useState(null); - const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); - const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); - const [editingProfile, setEditingProfile] = useState(null); - const [newTranslation, setNewTranslation] = useState({ - id: '', - original: '', - translated: '', - category: 'general' - }); - const [statistics, setStatistics] = useState<{ - totalProfiles: number; - officialProfiles: number; - totalTranslations: number; - averageTranslationsPerProfile: number; - totalGames: number; - totalLanguages: number; - } | null>(null); - - // Form state per nuovo profilo - const [newProfile, setNewProfile] = useState({ - gameName: '', - processName: processName || '', - language: 'it', - description: '', - tags: '' - }); - - useEffect(() => { - loadProfiles(); - loadStatistics(); - }, []); - - useEffect(() => { - filterProfiles(); - }, [profiles, searchQuery, processName]); - - useEffect(() => { - if (selectedProfileId) { - const profile = profiles.find(p => p.id === selectedProfileId); - if (profile) { - setSelectedProfile(profile); - } - } - }, [selectedProfileId, profiles]); - - const loadProfiles = () => { - const allProfiles = translationProfileManager.getAllProfiles(); - setProfiles(allProfiles); - }; - - const loadStatistics = () => { - const stats = translationProfileManager.getStatistics(); - setStatistics(stats); - }; - - const filterProfiles = () => { - let filtered = [...profiles]; - - // Filtra per processo se specificato - if (processName) { - filtered = filtered.filter(p => - p.processName.toLowerCase() === processName.toLowerCase() - ); - } - - // Filtra per ricerca - if (searchQuery) { - filtered = translationProfileManager.searchProfiles(searchQuery); - } - - // Sort per rating e data - filtered.sort((a, b) => { - const ratingDiff = (b.metadata.rating || 0) - (a.metadata.rating || 0); - if (ratingDiff !== 0) return ratingDiff; - return new Date(b.metadata.updatedAt).getTime() - new Date(a.metadata.updatedAt).getTime(); - }); - - setFilteredProfiles(filtered); - }; - - const handleCreateProfile = () => { - const profile = { - gameName: newProfile.gameName, - processName: newProfile.processName, - language: newProfile.language, - translations: [], - settings: { - autoDetectContext: true, - caseSensitive: false, - wholeWordOnly: true - } - }; - - const id = translationProfileManager.createProfile(profile); - if (id) { - const profile = translationProfileManager.getProfile(id); - if (profile) { - translationProfileManager.updateProfile(id, { - metadata: { - ...profile.metadata, - description: newProfile.description, - tags: newProfile.tags.split(',').map(t => t.trim()).filter(Boolean) - } - }); - } - loadProfiles(); - setIsCreateDialogOpen(false); - setNewProfile({ - gameName: '', - processName: processName || '', - language: 'it', - description: '', - tags: '' - }); - } - }; - - const handleUpdateProfile = () => { - if (!editingProfile) return; - - translationProfileManager.updateProfile(editingProfile.id, editingProfile); - loadProfiles(); - setIsEditDialogOpen(false); - setEditingProfile(null); - }; - - const handleDeleteProfile = async (id: string) => { - const { confirmDialog } = await import('@/lib/confirm-dialog'); - if (await confirmDialog(t('translationProfileManager.confirmDelete'))) { - translationProfileManager.deleteProfile(id); - loadProfiles(); - if (selectedProfile?.id === id) { - setSelectedProfile(null); - } - } - }; - - const handleExportProfile = (profile: TranslationProfile) => { - const data = translationProfileManager.exportProfile(profile.id); - if (data) { - const blob = new Blob([data], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${profile.gameName.replace(/\s+/g, '_')}_${profile.language}.json`; - a.click(); - URL.revokeObjectURL(url); - } - }; - - const handleImportProfile = (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - const id = translationProfileManager.importProfile(e.target?.result as string); - if (id) { - loadProfiles(); - alert(t('translationProfileManager.importSuccess')); - } else { - alert(t('translationProfileManager.importError')); - } - }; - reader.readAsText(file); - } - }; - - const handleCloneProfile = (profile: TranslationProfile) => { - const newId = translationProfileManager.cloneProfile(profile.id); - if (newId) { - loadProfiles(); - } - }; - - const handleSelectProfile = (profile: TranslationProfile) => { - setSelectedProfile(profile); - if (onProfileSelect) { - onProfileSelect(profile); - } - }; - - const handleAddTranslation = () => { - if (!editingProfile || !newTranslation.original || !newTranslation.translated) return; - - const updatedTranslations = [ - ...editingProfile.translations, - { - ...newTranslation, - id: Date.now().toString() - } - ]; - - setEditingProfile({ - ...editingProfile, - translations: updatedTranslations - }); - - setNewTranslation({ - id: '', - original: '', - translated: '', - category: 'general' - }); - }; - - const handleRemoveTranslation = (translationId: string) => { - if (!editingProfile) return; - - const updatedTranslations = editingProfile.translations.filter( - t => t.id !== translationId - ); - - setEditingProfile({ - ...editingProfile, - translations: updatedTranslations - }); - }; - - const formatDate = (date: Date) => { - return new Date(date).toLocaleDateString('it-IT', { - day: '2-digit', - month: '2-digit', - year: 'numeric' - }); - }; - - return ( -
- {/* Header con statistiche */} - {statistics && ( -
- - - - - {t('translationProfileManager.totalProfiles')} - - -
{statistics.totalProfiles}
-

- {statistics.officialProfiles} {t('translationProfileManager.officialUnit')}

-
-
- - - - - - {t('translationProfileManager.translationsStat')} - - -
{statistics.totalTranslations}
-

- ~{Math.round(statistics.averageTranslationsPerProfile)} {t('translationProfileManager.perProfile')}

-
-
- - - - - - {t('translationProfileManager.gamesStat')} - - -
{statistics.totalGames}
-

- {t('translationProfileManager.supported')}

-
-
- - - - - - {t('translationProfileManager.languagesStat')} - - -
{statistics.totalLanguages}
-

- {t('translationProfileManager.available')}

-
-
-
- )} - - {/* Toolbar */} -
-
-
- - setSearchQuery(e.target.value)} - className="pl-10" - /> -
-
-
- - - - - - - {t('translationProfileManagerComp.createNewProfile')} - - {t('translationProfileManager.newProfileDesc')} - -
-
- - setNewProfile({ ...newProfile, gameName: e.target.value })} - placeholder={t('translationProfileManager.gameNamePh')} - /> -
-
- - setNewProfile({ ...newProfile, processName: e.target.value })} - placeholder={t('translationProfileManager.exePh')} - /> -
-
- - -
-
- -