From 02a4fc8d6c349b15e967e5876d54d29d91a107ed Mon Sep 17 00:00:00 2001 From: rouges78 Date: Fri, 21 Aug 2026 01:36:36 +0200 Subject: [PATCH 1/3] chore: archive Injekt, the memory-injection subsystem gs-hook replaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Injekt was never reachable. Its 645-line UI had no importer, so the three components held only each other, and the fourteen registered commands had no caller. The backend explains why it was never wired: perform_hook wrote to 0x401000, an address the code itself labels "indirizzo fittizio", with NOP placeholders for the original bytes. It was scaffolding, not a broken feature. Removing it uncovered a chain. injekt-ui-enhanced was the only importer of translation-profile-manager and game-profiles; those two were the only importers of game-translations and game-translation-profiles. Four modules existing solely to serve something nothing reached. The dead-module check found each layer as the one above it went, which is the argument for running it between steps rather than at the end. The naming hid all of this. The live "Universal Injector" in the sidebar calls inject_translation_hook, which despite the name patches files on disk and never touches Injekt. Three separate systems wore the same word. Not touched: translation_bridge. It shares nothing with Injekt — zero references to shared_memory_ipc in injekt.rs — and it is alive, with a page and twelve commands that all have callers. Kept: claude/bold-banach. Its injekt.rs work dies with this commit, but it also carries a thousand lines of shared_memory_ipc fixes for the bridge that is still running: the circular-buffer wrap, the stats TOCTOU, and the unsafe Send/Sync that main still has at line 94. That is a separate decision, on a separate subsystem. Also drops two tutorial steps pointing at /injekt-translator, a route route-config.ts already documented as gone. 6085 lines out. Suite 1545 passed, typecheck clean, 849 commands with no invoke pointing at nothing, both baselines follow the code down. Co-Authored-By: Claude Opus 5 --- components/injekt-overlay-config.tsx | 728 --------------- components/injekt-realtime-stats.tsx | 333 ------- components/injekt-ui-enhanced.tsx | 645 ------------- components/translation-profile-manager.tsx | 789 ---------------- lib/game-profiles.ts | 223 ----- lib/game-translation-profiles.ts | 382 -------- lib/game-translations.ts | 828 ----------------- lib/tutorial-configs/index.ts | 8 - scripts/.dead-modules-baseline.json | 1 - scripts/.i18n-baseline.json | 2 +- src-tauri/src/commands/injekt.rs | 645 ------------- src-tauri/src/commands/mod.rs | 2 - src-tauri/src/commands/platform_stubs.rs | 79 -- src-tauri/src/injekt.rs | 996 --------------------- src-tauri/src/lib.rs | 2 - src-tauri/src/main.rs | 16 - src-tauri/src/multi_process_injekt.rs | 407 --------- 17 files changed, 1 insertion(+), 6085 deletions(-) delete mode 100644 components/injekt-overlay-config.tsx delete mode 100644 components/injekt-realtime-stats.tsx delete mode 100644 components/injekt-ui-enhanced.tsx delete mode 100644 components/translation-profile-manager.tsx delete mode 100644 lib/game-profiles.ts delete mode 100644 lib/game-translation-profiles.ts delete mode 100644 lib/game-translations.ts delete mode 100644 src-tauri/src/commands/injekt.rs delete mode 100644 src-tauri/src/injekt.rs delete mode 100644 src-tauri/src/multi_process_injekt.rs diff --git a/components/injekt-overlay-config.tsx b/components/injekt-overlay-config.tsx deleted file mode 100644 index 3f66ed842..000000000 --- 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 950d1eee6..000000000 --- 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 899c1c07d..000000000 --- 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 672afc69e..000000000 --- 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')} - /> -
-
- - -
-
- -