diff --git a/index.html b/index.html index d1aea2d..1cda005 100644 --- a/index.html +++ b/index.html @@ -210,6 +210,8 @@

diff --git a/package.json b/package.json index ab834f0..72d9813 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "afkode", "private": true, - "version": "0.8.4", + "version": "0.8.5", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c2cac33..36c4f16 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "afkode" -version = "0.8.4" +version = "0.8.5" dependencies = [ "base64 0.22.1", "core-foundation", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7c24657..fc459d9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "afkode" -version = "0.8.4" +version = "0.8.5" description = "In-game overlay to supervise AI coding agents while you play" authors = ["Omar Hernandez"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 384fc09..d511518 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "AFKode", - "version": "0.8.4", + "version": "0.8.5", "identifier": "app.afkode.overlay", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/hud.ts b/src/hud.ts index 201fff9..3beeb61 100644 --- a/src/hud.ts +++ b/src/hud.ts @@ -65,7 +65,13 @@ const altX = navigator.platform.toUpperCase().includes("MAC") ? "⌥X" : "Alt+X" function localizeTooltip() { try { const lang = JSON.parse(localStorage.getItem("settings") ?? "{}").lang; - openBtn.title = lang === "es" ? `Abrir overlay (${altX})` : `Open overlay (${altX})`; + const open: Record = { + es: `Abrir overlay (${altX})`, + en: `Open overlay (${altX})`, + fr: `Ouvrir l'overlay (${altX})`, + it: `Apri l'overlay (${altX})`, + }; + openBtn.title = open[lang] ?? open.en; replyBtn.title = altX.replace("X", "P"); } catch { /* keep default */ diff --git a/src/main.ts b/src/main.ts index b91f079..11ba4ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -264,7 +264,7 @@ const FONT_SIZES = [11, 12, 13, 14, 15, 16, 18]; // ── i18n ────────────────────────────────────────────────── -type Lang = "es" | "en"; +type Lang = "es" | "en" | "fr" | "it"; const I18N: Record> = { es: { @@ -499,6 +499,238 @@ const I18N: Record> = { awaySummary: "While you were away ({away} min): {turns} turns completed · {tools} tools · {files} files · it waited for you {waits} times ({waitMin} min)", }, + fr: { + tooltipNew: "Nouvel onglet", + tooltipHelp: "Aide et raccourcis", + tooltipSettings: "Paramètres", + tooltipGhost: "Mode fantôme (Alt+G) : les clics passent au jeu", + tooltipHide: "Masquer l'overlay (Alt+X)", + tooltipMaximize: "Agrandir / restaurer", + tooltipClose: "Réduire dans la zone de notification (quitter : menu de l'icône)", + close: "Fermer", + tooltipOpacity: "Opacité du fond", + hintAltXOverlay: "overlay", + hintAltXWindow: "afficher/masquer", + hintGhost: "fantôme", + hintPrompt: "prompt", + hintApprove: "approuver", + modeOverlay: "Overlay", + modeWindow: "Fenêtre", + tooltipModeBadge: "Cliquez pour basculer entre le mode overlay et fenêtre", + ghostBadge: "👻 MODE FANTÔME — Alt+G pour désactiver", + helpTitle: "Pourquoi utiliser AFKode ?", + help1: + "Sans quitter le jeu. L'overlay flotte au-dessus de votre jeu (mode fenêtré ou borderless). Appuyez sur Alt+X pour l'afficher ou le masquer instantanément, sans alt-tab.", + help2: + "Claude travaille pendant que vous jouez. Lancez une longue tâche à Claude Code, masquez l'overlay et continuez à jouer ; revenez avec Alt+X pour suivre la progression.", + help3: + "Mode fantôme (Alt+G). L'overlay reste visible comme un HUD semi-transparent et vos clics et touches passent au jeu.", + help4: + "Onglets et votre dossier. Le bouton + ouvre Claude Code, OpenCode, Codex ou PowerShell ; vous choisissez d'abord le dossier du projet via la boîte de dialogue native de Windows.", + help5: + "Vrai terminal. ConPTY + rendu GPU (WebGL) : couleurs complètes, applications interactives et performances de Windows Terminal avec un impact minimal sur vos FPS.", + help6: + "À votre goût. Thèmes, police et langue dans ⚙ ; faites glisser la barre supérieure pour le déplacer et réglez l'opacité avec le curseur de la barre inférieure.", + settingsTitle: "Paramètres", + theme: "Thème", + opacity: "Opacité", + font: "Police", + fontSize: "Taille", + language: "Langue", + ended: "(terminée)", + sessionEnded: "— session terminée — appuyez sur × pour fermer", + spawnError: "Échec du démarrage de la session", + pickFolder: "Choisissez le dossier du projet", + emptyTitle: "Où voulez-vous travailler ?", + noFolder: "Choisir un dossier… (par défaut : accueil)", + wizTitle: "Configurer Claude Code", + wizIntro: + "AFKode a besoin de deux éléments pour fonctionner. Cet assistant les installe pour vous — chaque étape ouvre un onglet où vous pouvez suivre la progression.", + wizStep1: "Node.js", + wizStep1Note: "Le moteur dont Claude Code a besoin. Installé via winget (installateur officiel de Windows).", + wizStep2: "Claude Code", + wizStep2Note: "L'agent de codage IA d'Anthropic. Installé via npm.", + wizStep3: "Tout est prêt", + wizStep3Note: + "Remarque : à la première utilisation, Claude Code vous demandera de vous connecter (votre navigateur s'ouvre, connectez-vous avec votre compte) — cette partie est gérée par Claude, pas par AFKode.", + wizInstall: "Installer", + wizLaunch: "Ouvrir Claude Code", + wizInstalling: "Installation", + wizInstallRunning: "Installation… (le journal ci-dessous se met à jour en direct)", + wizInstallOk: "✓ Installé avec succès", + wizInstallFailed: "✗ L'installation a échoué — vérifiez le journal ci-dessus", + wizInstallTimeout: "✗ Bloqué sans réponse — vous pouvez réessayer ou l'installer manuellement", + argsPlaceholder: "Options supplémentaires pour Claude Code (appliquées au lancement)…", + starting: "Démarrage", + notifications: "Notifications", + sound: "Son", + notifWaiting: "attend votre réponse", + notifDone: "semble avoir terminé", + notifExit: "session terminée", + installHint: "non installé — cliquez pour l'installer", + installing: "Installation", + hud: "Mini-HUD (pastille d'état)", + autoLaunch: "Lancer Claude automatiquement au démarrage", + hooksLabel: "Intégration avec Claude Code", + hooksNote: + "Lance Claude Code avec des hooks qui rapportent son état réel à AFKode : quel outil il exécute, quand il attend votre permission et quand un tour se termine. Le HUD, Alt+A et le résumé sont ainsi exacts au lieu d'estimés. Tout est local (127.0.0.1) ; s'applique aux nouvelles sessions.", + trayToggle: "Afficher / Masquer", + trayGhost: "Mode fantôme", + trayPalette: "Palette de prompts", + trayQuit: "Quitter AFKode", + hudOpen: "Ouvrir l'overlay (Alt+X)", + overlayModeLabel: "Mode overlay", + overlayModeNote: + "Activé : la fenêtre flotte toujours au-dessus de tout et n'apparaît pas dans la barre des tâches — pensé pour jouer. Désactivé : elle se comporte comme une fenêtre normale (barre des tâches, Alt+Tab) — mieux si vous ne jouez pas et voulez éviter qu'elle vous gêne avec d'autres applications en plein écran.", + matchModeLabel: "Ne pas déranger en partie", + matchModeNote: + "Quand un jeu en plein écran a le focus, AFKode reste silencieux : ni toasts ni bips. Les éléments en attente s'accumulent dans l'inbox et vous recevez une seule notification à la levée du silence. Comme les lobbies sont aussi en plein écran, Alt+N bascule le silence manuellement (🔕 sur le mini-HUD) ; à la fermeture du jeu, le mode automatique revient.", + searchPlaceholder: "Rechercher dans le terminal… (Entrée suivant · Maj+Entrée précédent)", + tooltipGlobalSearch: "Rechercher des sessions (Ctrl+K)", + globalSearchPlaceholder: "Rechercher des sessions…", + globalSearchEmpty: "Aucun résultat", + linkOpening: "Ouverture du lien dans votre navigateur…", + linkOpenFailed: "Impossible d'ouvrir le navigateur — lien copié, collez-le où vous voulez", + pastedImageInstead: "Votre presse-papiers ne contenait pas de texte, une image a donc été collée à la place", + filePreviewError: "Impossible d'ouvrir le fichier", + filePreviewNotFoundBare: + "Fichier introuvable dans le dossier de la session. Claude l'a probablement mentionné sans indiquer son chemin complet.", + filePreviewTriedIn: "recherché dans", + inboxTitle: "Vos agents ont besoin de vous", + inboxApprove: "Approuver", + inboxOpen: "Aller", + ttsLabel: "Annonces vocales (TTS)", + ttsNote: + "Style copilote : au lieu d'un bip, une voix annonce \"Claude Code attend votre réponse\" par-dessus l'audio du jeu. Windows ne la supprime pas en plein écran.", + queuedSummary: "{n} élément(s) en attente de vos agents — Alt+X pour vérifier", + hudWorking: "au travail", + hudWaiting: "vous attend", + hudDone: "terminé", + updateReady: + "AFKode {v} installé — appliqué au redémarrage de l'app (menu de la zone de notification → Quitter)", + updateAvailable: "AFKode {v} est disponible", + updateInstallBtn: "Mettre à jour et redémarrer", + updateInstalling: "Téléchargement de la mise à jour… l'app redémarrera toute seule", + updateFailed: "La mise à jour a échoué", + awaySummary: + "Pendant votre absence ({away} min) : {turns} tours terminés · {tools} outils · {files} fichiers · il vous a attendu {waits} fois ({waitMin} min)", + }, + it: { + tooltipNew: "Nuova scheda", + tooltipHelp: "Aiuto e scorciatoie", + tooltipSettings: "Impostazioni", + tooltipGhost: "Modalità fantasma (Alt+G): i clic passano al gioco", + tooltipHide: "Nascondi l'overlay (Alt+X)", + tooltipMaximize: "Ingrandisci / ripristina", + tooltipClose: "Nascondi nella barra di sistema (esci: menu dell'icona)", + close: "Chiudi", + tooltipOpacity: "Opacità dello sfondo", + hintAltXOverlay: "overlay", + hintAltXWindow: "mostra/nascondi", + hintGhost: "fantasma", + hintPrompt: "prompt", + hintApprove: "approva", + modeOverlay: "Overlay", + modeWindow: "Finestra", + tooltipModeBadge: "Clicca per passare dalla modalità overlay a finestra", + ghostBadge: "👻 MODALITÀ FANTASMA — Alt+G per disattivare", + helpTitle: "Perché usare AFKode?", + help1: + "Senza uscire dal gioco. L'overlay fluttua sopra il tuo gioco (in modalità finestra o borderless). Premi Alt+X per mostrarlo o nasconderlo all'istante, senza alt-tab.", + help2: + "Claude lavora mentre giochi. Avvia un compito lungo con Claude Code, nascondi l'overlay e continua a giocare; torna con Alt+X per controllare i progressi.", + help3: + "Modalità fantasma (Alt+G). L'overlay resta visibile come un HUD semitrasparente e i tuoi clic e tasti passano al gioco.", + help4: + "Schede e la tua cartella. Il pulsante + apre Claude Code, OpenCode, Codex o PowerShell; prima scegli la cartella del progetto con la finestra di dialogo nativa di Windows.", + help5: + "Terminale vero. ConPTY + rendering GPU (WebGL): colori completi, app interattive e prestazioni da Windows Terminal con impatto minimo sui tuoi FPS.", + help6: + "Su misura per te. Temi, font e lingua in ⚙; trascina la barra superiore per spostarlo e regola l'opacità con il cursore della barra inferiore.", + settingsTitle: "Impostazioni", + theme: "Tema", + opacity: "Opacità", + font: "Font", + fontSize: "Dimensione", + language: "Lingua", + ended: "(terminata)", + sessionEnded: "— sessione terminata — premi × per chiudere", + spawnError: "Avvio della sessione non riuscito", + pickFolder: "Scegli la cartella del progetto", + emptyTitle: "Dove vuoi lavorare?", + noFolder: "Scegli cartella… (predefinita: home)", + wizTitle: "Configura Claude Code", + wizIntro: + "AFKode ha bisogno di due componenti per funzionare. Questa procedura li installa per te — ogni passaggio apre una scheda dove puoi seguire i progressi.", + wizStep1: "Node.js", + wizStep1Note: "Il motore di cui Claude Code ha bisogno. Installato con winget (installer ufficiale di Windows).", + wizStep2: "Claude Code", + wizStep2Note: "L'agente di coding IA di Anthropic. Installato con npm.", + wizStep3: "Tutto pronto", + wizStep3Note: + "Nota: al primo avvio Claude Code ti chiederà di accedere (si apre il browser, entra con il tuo account) — quella parte la gestisce Claude, non AFKode.", + wizInstall: "Installa", + wizLaunch: "Apri Claude Code", + wizInstalling: "Installazione", + wizInstallRunning: "Installazione… (il log qui sotto si aggiorna in tempo reale)", + wizInstallOk: "✓ Installato correttamente", + wizInstallFailed: "✗ Installazione non riuscita — controlla il log qui sopra", + wizInstallTimeout: "✗ Bloccato senza risposta — puoi riprovare o installarlo manualmente", + argsPlaceholder: "Flag extra per Claude Code (applicati all'avvio)…", + starting: "Avvio", + notifications: "Notifiche", + sound: "Suono", + notifWaiting: "sta aspettando la tua risposta", + notifDone: "sembra aver finito", + notifExit: "sessione terminata", + installHint: "non installato — clicca per installarlo", + installing: "Installazione", + hud: "Mini-HUD (indicatore di stato)", + autoLaunch: "Avvia Claude automaticamente all'apertura", + hooksLabel: "Integrazione con Claude Code", + hooksNote: + "Avvia Claude Code con hook che riportano il suo stato reale ad AFKode: quale strumento esegue, quando aspetta il tuo permesso e quando finisce un turno. Così l'HUD, Alt+A e il riepilogo sono esatti invece che stimati. Tutto è locale (127.0.0.1); si applica alle nuove sessioni.", + trayToggle: "Mostra / Nascondi", + trayGhost: "Modalità fantasma", + trayPalette: "Palette dei prompt", + trayQuit: "Esci da AFKode", + hudOpen: "Apri l'overlay (Alt+X)", + overlayModeLabel: "Modalità overlay", + overlayModeNote: + "Attiva: la finestra fluttua sempre sopra tutto e non appare nella barra delle applicazioni — pensata per giocare. Disattiva: si comporta come una finestra normale (barra delle applicazioni, Alt+Tab) — meglio se non stai giocando e vuoi evitare che copra o venga coperta da altre app a schermo intero.", + matchModeLabel: "Non disturbare in partita", + matchModeNote: + "Quando un gioco a schermo intero ha il focus, AFKode resta in silenzio: niente toast né bip. Gli elementi in sospeso si accumulano nell'inbox e ricevi un solo avviso quando il silenzio termina. Poiché anche le lobby sono a schermo intero, Alt+N attiva il silenzio manualmente (🔕 sul mini-HUD); alla chiusura del gioco torna la modalità automatica.", + searchPlaceholder: "Cerca nel terminale… (Invio successivo · Maiusc+Invio precedente)", + tooltipGlobalSearch: "Cerca sessioni (Ctrl+K)", + globalSearchPlaceholder: "Cerca sessioni…", + globalSearchEmpty: "Nessun risultato", + linkOpening: "Apertura del link nel browser…", + linkOpenFailed: "Impossibile aprire il browser — link copiato, incollalo dove vuoi", + pastedImageInstead: "Gli appunti non contenevano testo, quindi è stata incollata un'immagine", + filePreviewError: "Impossibile aprire il file", + filePreviewNotFoundBare: + "File non trovato nella cartella della sessione. Probabilmente Claude l'ha citato senza indicarne il percorso completo.", + filePreviewTriedIn: "cercato in", + inboxTitle: "I tuoi agenti hanno bisogno di te", + inboxApprove: "Approva", + inboxOpen: "Vai", + ttsLabel: "Annunci vocali (TTS)", + ttsNote: + "Stile copilota: invece di un bip, una voce annuncia \"Claude Code sta aspettando la tua risposta\" sopra l'audio del gioco. Windows non la sopprime a schermo intero.", + queuedSummary: "{n} elemento/i in sospeso dai tuoi agenti — Alt+X per controllare", + hudWorking: "al lavoro", + hudWaiting: "ti aspetta", + hudDone: "fatto", + updateReady: + "AFKode {v} installato — si applica al riavvio dell'app (menu della barra di sistema → Esci)", + updateAvailable: "AFKode {v} è disponibile", + updateInstallBtn: "Aggiorna e riavvia", + updateInstalling: "Download dell'aggiornamento… l'app si riavvierà da sola", + updateFailed: "Aggiornamento non riuscito", + awaySummary: + "Mentre non c'eri ({away} min): {turns} turni completati · {tools} strumenti · {files} file · ti ha aspettato {waits} volte ({waitMin} min)", + }, }; // ── Settings ────────────────────────────────────────────── @@ -1304,7 +1536,9 @@ function speak(text: string) { } try { const u = new SpeechSynthesisUtterance(text); - u.lang = settings.lang === "es" ? "es-ES" : "en-US"; + u.lang = + ({ es: "es-ES", en: "en-US", fr: "fr-FR", it: "it-IT" } as const)[settings.lang] ?? + "en-US"; u.rate = 1.05; u.volume = 1; speechSynthesis.cancel(); @@ -2080,6 +2314,9 @@ function applyI18n() { el.setAttribute("aria-label", label); }); updateStatusHint(); + // The mode badge is otherwise only re-rendered when the mode toggles, so a + // language switch would leave it in the previous language. + $("#mode-badge").textContent = settings.overlayMode ? t("modeOverlay") : t("modeWindow"); ghostBadge.textContent = t("ghostBadge"); $("#help-title").textContent = t("helpTitle"); $("#help-list").innerHTML = [1, 2, 3, 4, 5, 6] @@ -2219,7 +2456,12 @@ chkTts.checked = settings.tts; chkTts.addEventListener("change", () => { settings.tts = chkTts.checked; saveSettings(); - if (settings.tts) speak(settings.lang === "es" ? "Voz activada" : "Voice enabled"); + if (settings.tts) + speak( + ({ es: "Voz activada", en: "Voice enabled", fr: "Voix activée", it: "Voce attivata" } as const)[ + settings.lang + ] ?? "Voice enabled", + ); }); // ── CLI detection & one-click install ───────────────────── diff --git a/src/palette.ts b/src/palette.ts index ba528ff..e0e88ed 100644 --- a/src/palette.ts +++ b/src/palette.ts @@ -49,18 +49,22 @@ let sugs: Suggestion[] = []; let sel = -1; let debounceTimer = 0; -function isEs(): boolean { +function uiLang(): string { try { - return JSON.parse(localStorage.getItem("settings") ?? "{}").lang === "es"; + return JSON.parse(localStorage.getItem("settings") ?? "{}").lang ?? "en"; } catch { - return false; + return "en"; } } function placeholder(): string { - return isEs() - ? "Escribe un prompt… ( / comandos · @ archivos · Tab completa )" - : "Type a prompt… ( / commands · @ files · Tab completes )"; + const map: Record = { + es: "Escribe un prompt… ( / comandos · @ archivos · Tab completa )", + en: "Type a prompt… ( / commands · @ files · Tab completes )", + fr: "Écrivez un prompt… ( / commandes · @ fichiers · Tab complète )", + it: "Scrivi un prompt… ( / comandi · @ file · Tab completa )", + }; + return map[uiLang()] ?? map.en; } function resize() {