diff --git a/settings.html b/settings.html index 6226b02..54c6201 100644 --- a/settings.html +++ b/settings.html @@ -16,26 +16,33 @@ + - Toggle to enable/disable the RPG Companion extension. Configure additional settings within the panel itself. + Toggle to enable/disable the RPG Companion extension. + Configure additional settings within the panel itself.
- +  Discord - +  Support
- Recommended Models: + Recommended Models:
- For the extension to work properly, it is not recommended to use any models below 20B, especially if they're old. It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini. + For the extension to work properly, it is not recommended to use any models below 20B, + especially if they're old. It works best with the SOTA models such as Deepseek, Claude, + GPT, or Gemini.
@@ -44,7 +51,8 @@ Contributors:
- SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, Amauragis, Tomt610, and Jakstein. + SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, + Chungchandev, Joenunezb, Amauragis, Tomt610, and Jakstein.
@@ -53,4 +61,4 @@ - + \ No newline at end of file diff --git a/src/i18n/en.json b/src/i18n/en.json index 1dabbb4..ba49c5a 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -182,10 +182,10 @@ "global.listView": "List view", "global.gridView": "Grid view", "global.save": "Save", - "global.status":"Status", - "global.inventory":"Inventory", - "global.quests":"Quests", - "global.info":"Info", + "global.status": "Status", + "global.inventory": "Inventory", + "global.quests": "Quests", + "global.info": "Info", "infobox.noData.title": "No data yet", "infobox.noData.instruction": "Generate a new response in the roleplay or switch to \"Separate Generation\" in Settings to access and click the \"Refresh RPG Info\" button", "infobox.recentEvents.title": "Recent Events", @@ -238,5 +238,39 @@ "musicPlayer.noMusic": "AI will suggest music when appropriate for the scene", "errors.parsingError": "RPG Companion Trackers' parsing error! The model returned an incorrect format. If the issue persists, consider changing the model for generations.", "settings.recommendedModels.title": "Recommended Models", - "settings.recommendedModels.description": "For the extension to work properly, **it is not recommended to use any models below 20B, especially if they're old.** It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini." -} + "settings.recommendedModels.description": "For the extension to work properly, **it is not recommended to use any models below 20B, especially if they're old.** It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini.", + "thoughts.addCharacter": "Add Character", + "thoughts.locked": "Locked", + "thoughts.unlocked": "Unlocked", + "thoughts.clickToEdit": "Click to edit", + "thoughts.clickToUpload": "Click to upload avatar", + "thoughts.removeCharacter": "Remove character", + "userStats.level": "LVL", + "userStats.clickToEditLevel": "Click to edit level", + "userStats.statsLocked": "Locked - AI cannot change stats", + "userStats.statsUnlocked": "Unlocked - AI can change stats", + "userStats.clickToEditStatName": "Click to edit stat name", + "userStats.clickToEditStatValue": "Click to edit", + "userStats.moodLocked": "Locked - AI cannot change mood", + "userStats.moodUnlocked": "Unlocked - AI can change mood", + "userStats.clickToEditEmoji": "Click to edit emoji", + "userStats.skillsLocked": "Locked - AI cannot change skills", + "userStats.skillsUnlocked": "Unlocked - AI can change skills", + "userStats.clickToEditSkills": "Click to edit skills", + "infoBox.clickToEdit": "Click to edit", + "infoBox.locked": "Locked - AI cannot change this", + "infoBox.unlocked": "Unlocked - AI can change this", + "infoBox.weatherFallback": "Weather", + "infoBox.locationFallback": "Location", + "stats.health": "Health", + "stats.satiety": "Satiety", + "stats.energy": "Energy", + "stats.hygiene": "Hygiene", + "stats.arousal": "Arousal", + "stats.str": "STR", + "stats.dex": "DEX", + "stats.con": "CON", + "stats.int": "INT", + "stats.wis": "WIS", + "stats.cha": "CHA" +} \ No newline at end of file diff --git a/src/i18n/fr.json b/src/i18n/fr.json new file mode 100644 index 0000000..dfbb2e3 --- /dev/null +++ b/src/i18n/fr.json @@ -0,0 +1,277 @@ +{ + "settings.language.label": "Langue", + "settings.language.option.en": "English", + "settings.language.option.zh-tw": "繁體中文", + "settings.language.option.ru": "Русский", + "settings.language.option.fr": "Français", + "settings.extensionEnabled": "Activer RPG Companion", + "settings.note": "Basculez pour activer/désactiver l'extension RPG Companion. Configurez des paramètres supplémentaires dans le panneau lui-même.", + "template.settingsTitle": "Paramètres RPG Companion", + "template.settingsModal.themeTitle": "Thème", + "template.settingsModal.themeLabel": "Thème Visuel :", + "template.settingsModal.themeOptions.default": "Défaut", + "template.settingsModal.themeOptions.sciFi": "Sci-Fi (Synthwave)", + "template.settingsModal.themeOptions.fantasy": "Fantasy (Parchemin Rustique)", + "template.settingsModal.themeOptions.cyberpunk": "Cyberpunk (Grille Néon)", + "template.settingsModal.themeOptions.custom": "Personnalisé", + "template.settingsModal.themeOptions.custom.background": "Arrière-plan :", + "template.settingsModal.themeOptions.custom.accent": "Accent :", + "template.settingsModal.themeOptions.custom.text": "Texte :", + "template.settingsModal.themeOptions.custom.highlight": "Surlignage :", + "template.settingsModal.theme.statBarLow": "Couleur Barre Stat (Bas) :", + "template.settingsModal.theme.statBarLowNote": "Couleur lorsque les stats sont à 0%.", + "template.settingsModal.theme.statBarHigh": "Couleur Barre Stat (Haut) :", + "template.settingsModal.theme.statBarHighNote": "Couleur lorsque les stats sont à 100%.", + "template.settingsModal.displayTitle": "Options d'Affichage", + "template.settingsModal.displayNote": "Vous pouvez activer/désactiver l'extension complète RPG Companion dans l'onglet Extensions de SillyTavern.", + "template.settingsModal.display.panelPosition": "Position du Panneau :", + "template.settingsModal.display.panelPositionOptions.right": "Barre Latérale Droite", + "template.settingsModal.display.panelPositionOptions.left": "Barre Latérale Gauche", + "template.settingsModal.display.toggleAutoUpdate": "Mise à jour auto après messages", + "template.settingsModal.display.toggleAutoUpdateNote": "Rafraîchir automatiquement les infos RPG après chaque message.", + "template.settingsModal.display.showUserStats": "Afficher Stats Utilisateur", + "template.settingsModal.display.showUserStatsNote": "Activer les Stats Utilisateur pour suivre les statistiques, l'humeur, les attributs, les compétences, etc. de votre persona.", + "template.settingsModal.display.showInfoBox": "Afficher Boîte Info", + "template.settingsModal.display.showInfoBoxNote": "Afficher le lieu, l'heure, la météo et les événements récents.", + "template.settingsModal.display.showPresentCharacters": "Afficher Personnages Présents", + "template.settingsModal.display.showPresentCharactersNote": "Afficher les portraits des personnages avec leurs pensées actuelles et leur statut.", + "template.settingsModal.display.narratorMode": "Mode Narrateur", + "template.settingsModal.display.narratorModeNote": "Utiliser la carte de personnage comme narrateur. Déduire les personnages du contexte au lieu d'utiliser des références de personnages fixes.", + "template.settingsModal.display.showInventory": "Afficher Inventaire", + "template.settingsModal.display.showInventoryNote": "Suivre les objets transportés, les vêtements portés, les objets stockés et les biens.", + "template.settingsModal.display.showQuests": "Afficher Quêtes", + "template.settingsModal.display.showQuestsNote": "Gérer les quêtes principales et optionnelles avec des objectifs.", + "template.settingsModal.display.showLockIcons": "Afficher Icônes Verrouillage", + "template.settingsModal.display.showLockIconsNote": "Afficher les icônes de verrouillage/déverrouillage sur les éléments de suivi pour empêcher l'IA de les modifier.", + "template.settingsModal.display.showThoughtsInChat": "Afficher Pensées", + "template.settingsModal.display.showThoughtsInChatNote": "Afficher les pensées des personnages sous forme de bulles superposées à côté de leurs messages.", + "template.settingsModal.display.alwaysShowThoughtBubble": "Toujours Afficher Bulle Pensée", + "template.settingsModal.display.alwaysShowThoughtBubbleNote": "Développer automatiquement la bulle de pensée sans cliquer sur l'icône d'abord", + "template.settingsModal.display.enableAnimations": "Activer Animations", + "template.settingsModal.display.enableAnimationsNote": "Transitions fluides pour les stats, les mises à jour de contenu et les lancers de dés.", + "template.settingsModal.display.showImmersiveHtmlToggle": "Afficher HTML Immersif", + "template.settingsModal.display.showImmersiveHtmlToggleNote": "Afficher un bouton pour activer/désactiver le formatage HTML dans les messages.", + "template.settingsModal.display.showDialogueColoringToggle": "Afficher Dialogues Colorés", + "template.settingsModal.display.showDialogueColoringToggleNote": "Afficher un bouton pour activer/désactiver le formatage des dialogues colorés.", + "template.settingsModal.display.showDeceptionToggle": "Afficher Système Déception", + "template.settingsModal.display.showDeceptionToggleNote": "Afficher un bouton pour activer/désactiver le Système de Déception pour marquer les mensonges.", + "template.settingsModal.display.showOmniscienceToggle": "Afficher Filtre Omniscience", + "template.settingsModal.display.showOmniscienceToggleNote": "Afficher un bouton pour activer/désactiver le Filtre d'Omniscience pour filtrer les événements cachés.", + "template.settingsModal.display.showSpotifyMusicToggle": "Afficher Musique Spotify", + "template.settingsModal.display.showSpotifyMusicToggleNote": "Afficher le lecteur Spotify avec des pistes suggérées par l'IA appropriées à la scène.", + "template.settingsModal.display.showSnowflakesToggle": "Afficher Effet Flocons", + "template.settingsModal.display.showDynamicWeatherToggle": "Afficher Effets Météo Dynamiques", + "template.settingsModal.display.showDynamicWeatherToggleNote": "Afficher un bouton pour activer/désactiver les effets météo animés.", + "template.settingsModal.display.showNarratorMode": "Afficher Mode Narrateur", + "template.settingsModal.display.showNarratorModeNote": "Afficher un bouton pour activer/désactiver le mode narrateur (déduire les personnages du contexte).", + "template.settingsModal.display.showAutoAvatars": "Afficher Génération Auto Avatars", + "template.settingsModal.display.showAutoAvatarsNote": "Afficher un bouton pour générer automatiquement des avatars pour les personnages sans image.", + "template.settingsModal.display.showRandomizedPlot": "Afficher Progression Intrigue Aléatoire", + "template.settingsModal.display.showRandomizedPlotNote": "Afficher un bouton pour des invites de progression d'intrigue générées aléatoirement par l'IA.", + "template.settingsModal.display.showNaturalPlot": "Afficher Progression Intrigue Naturelle", + "template.settingsModal.display.showNaturalPlotNote": "Afficher un bouton pour des invites de continuation narrative conscientes du contexte.", + "template.settingsModal.display.showStartEncounter": "Afficher Démarrer Rencontre", + "template.settingsModal.display.showStartEncounterNote": "Afficher un bouton pour initier des rencontres de combat interactives.", + "template.settingsModal.display.showDiceDisplay": "Afficher Lancer de Dés", + "template.settingsModal.display.showDiceDisplayNote": "Afficher l'indicateur \"Dernier Lancer\" dans le panneau.", + "template.mainPanel.autoAvatars": "Avatars Auto", + "template.settingsModal.advancedTitle": "Avancé", + "template.settingsModal.advanced.encounterHistoryDepth": "Profondeur Historique Rencontre :", + "template.settingsModal.advanced.encounterHistoryDepthNote": "Nombre de messages récents à inclure dans l'initialisation du combat.", + "template.settingsModal.advanced.autoSaveCombatLogs": "Sauvegarde Auto Journaux Combat", + "template.settingsModal.advanced.autoSaveCombatLogsNote": "Sauvegarder les journaux de combat détaillés dans un fichier pour référence future et analyse.", + "template.settingsModal.advanced.clearCacheNote": "Efface les données de suivi validées et affichées pour votre chat actuellement actif.", + "template.settingsModal.advanced.generationMode": "Mode de Génération :", + "template.settingsModal.advanced.generationModeOptions.together": "Ensemble avec Génération Principale", + "template.settingsModal.advanced.generationModeOptions.separate": "Génération Séparée", + "template.settingsModal.advanced.generationModeNote": "Ensemble : Ajoute le suivi RPG au jeu de rôle principal. Séparé : Génère les données RPG séparément (manuel ou auto). Externe : Se connecte directement à un point de terminaison compatible OpenAI.", + "template.settingsModal.advanced.generationModeOptions.external": "API Externe", + "template.settingsModal.advanced.externalApi.title": "Paramètres API Externe", + "template.settingsModal.advanced.externalApi.baseUrl": "URL de base API", + "template.settingsModal.advanced.externalApi.baseUrlNote": "Point de terminaison compatible OpenAI (ex: OpenAI, OpenRouter, serveur LLM local).", + "template.settingsModal.advanced.externalApi.apiKey": "Clé API", + "template.settingsModal.advanced.externalApi.apiKeyNote": "Votre clé API pour le service externe.", + "template.settingsModal.advanced.externalApi.model": "Modèle", + "template.settingsModal.advanced.externalApi.modelNote": "Identifiant du modèle (ex: gpt-4o-mini, claude-3-haiku, mistral-7b).", + "template.settingsModal.advanced.externalApi.maxTokens": "Max Tokens", + "template.settingsModal.advanced.externalApi.temperature": "Température", + "template.settingsModal.advanced.externalApi.testConnection": "Tester Connexion", + "template.settingsModal.advanced.contextMessages": "Messages de Contexte :", + "template.settingsModal.advanced.contextMessagesNote": "Nombre de messages récents à inclure.", + "template.settingsModal.advanced.useSeparatePreset": "Utiliser modèle connecté au preset RPG Companion Trackers", + "template.settingsModal.advanced.useSeparatePresetNote": "Si activé, la génération de suivi utilisera le modèle du preset \"RPG Companion Trackers\" au lieu de votre modèle API principal. Le preset sera commuté automatiquement pendant la génération et restauré après. Sélectionnez le modèle souhaité dans ce preset et assurez-vous que l'option \"Lier les presets aux connexions API\" est activée (à côté des boutons import/export preset).", + "template.settingsModal.advanced.skipInjections": "Sauter Injections Pendant Générations Guidées :", + "template.settingsModal.advanced.skipInjectionsOptions.none": "Ne jamais sauter", + "template.settingsModal.advanced.skipInjectionsOptions.impersonation": "Seulement sur demandes d'imitation", + "template.settingsModal.advanced.skipInjectionsOptions.guided": "Toujours pour invites guidées ou silencieuses", + "template.settingsModal.advanced.skipInjectionsNote": "Si défini, l'extension n'injectera pas les invites de suivi, exemples ou instructions HTML selon le mode sélectionné lorsqu'une génération guidée (via `instruct` ou `quiet_prompt`) est détectée. Utile lors de l'utilisation de GuidedGenerations ou extensions similaires.", + "template.settingsModal.advanced.customHtmlPromptTitle": "Invite HTML Personnalisée :", + "template.settingsModal.advanced.restoreDefaultHtmlPrompt": "Restaurer Défaut", + "template.settingsModal.advanced.customHtmlPromptNote": "Personnalisez l'invite HTML injectée lorsque \"Activer HTML Immersif\" est activé. L'invite par défaut est affichée ci-dessus - vous pouvez l'éditer directement ou la remplacer entièrement. Cliquez sur \"Restaurer Défaut\" pour réinitialiser. Cela affecte tous les modes de génération (ensemble, séparé et progression d'intrigue).", + "template.settingsModal.advanced.clearCache": "Effacer Cache Extension", + "template.settingsModal.advanced.resetFabPositions": "Réinitialiser Positions Boutons", + "template.settingsModal.advanced.resetFabPositionsNote": "Réinitialise tous les boutons d'action flottants (bascule, rafraîchir, debug) à leurs positions par défaut en haut à gauche. Utile si les boutons sont hors écran.", + "template.trackerEditorModal.title": "Éditer Suivis", + "template.trackerEditorModal.tabs.userStats": "Stats Utilisateur", + "template.trackerEditorModal.tabs.infoBox": "Boîte Info", + "template.trackerEditorModal.tabs.presentCharacters": "Personnages Présents", + "template.trackerEditorModal.buttons.reset": "Réinitialiser", + "template.trackerEditorModal.buttons.cancel": "Annuler", + "template.trackerEditorModal.buttons.save": "Sauvegarder & Appliquer", + "template.trackerEditorModal.buttons.export": "Exporter", + "template.trackerEditorModal.buttons.import": "Importer", + "template.trackerEditorModal.messages.exportSuccess": "Preset de suivi exporté avec succès !", + "template.trackerEditorModal.messages.exportError": "Échec de l'exportation du preset. Vérifiez la console pour les détails.", + "template.trackerEditorModal.messages.importSuccess": "Preset de suivi importé avec succès !", + "template.trackerEditorModal.messages.importError": "Échec de l'importation du preset", + "template.trackerEditorModal.messages.importConfirm": "Ceci remplacera votre configuration actuelle de suivi. Continuer ?", + "template.trackerEditorModal.userStatsTab.customStatsTitle": "Stats Personnalisées", + "template.trackerEditorModal.userStatsTab.addCustomStatButton": "Ajouter Stat Perso", + "template.trackerEditorModal.userStatsTab.rpgAttributesTitle": "Attributs RPG", + "template.trackerEditorModal.userStatsTab.enableRpgAttributes": "Activer Section Attributs RPG", + "template.trackerEditorModal.userStatsTab.alwaysIncludeAttributes": "Toujours Inclure Attributs dans Invite", + "template.trackerEditorModal.userStatsTab.alwaysIncludeAttributesNote": "Si désactivé, les attributs ne sont envoyés que lorsqu'un lancer de dé est actif.", + "template.trackerEditorModal.userStatsTab.addAttributeButton": "Ajouter Attribut", + "template.trackerEditorModal.userStatsTab.statusSectionTitle": "Section Statut", + "template.trackerEditorModal.userStatsTab.enableStatusSection": "Activer Section Statut", + "template.trackerEditorModal.userStatsTab.showMoodEmoji": "Afficher Emoji Humeur", + "template.trackerEditorModal.userStatsTab.statusFieldsLabel": "Champs Statut (séparés par virgule) :", + "template.trackerEditorModal.userStatsTab.skillsSectionTitle": "Section Compétences", + "template.trackerEditorModal.userStatsTab.enableSkillsSection": "Activer Section Compétences", + "template.trackerEditorModal.userStatsTab.skillsLabelLabel": "Libellé Compétences :", + "template.trackerEditorModal.userStatsTab.skillsListLabel": "Liste Compétences (séparées par virgule) :", + "template.trackerEditorModal.infoBoxTab.widgetsTitle": "Widgets", + "template.trackerEditorModal.infoBoxTab.dateWidget": "Date", + "template.trackerEditorModal.infoBoxTab.weatherWidget": "Météo", + "template.trackerEditorModal.infoBoxTab.temperatureWidget": "Température", + "template.trackerEditorModal.infoBoxTab.timeWidget": "Heure", + "template.trackerEditorModal.infoBoxTab.locationWidget": "Lieu", + "template.trackerEditorModal.infoBoxTab.recentEventsWidget": "Événements Récents", + "template.trackerEditorModal.presentCharactersTab.relationshipStatusTitle": "Champs Statut Relation", + "template.trackerEditorModal.presentCharactersTab.enableRelationshipStatus": "Activer Champs Statut Relation", + "template.trackerEditorModal.presentCharactersTab.relationshipStatusHint": "Définir les types de relation avec les emojis correspondants affichés sur les portraits des personnages.", + "template.trackerEditorModal.presentCharactersTab.newRelationshipButton": "Nouvelle Relation", + "template.trackerEditorModal.presentCharactersTab.appearanceDemeanorTitle": "Champs Apparence/Comportement", + "template.trackerEditorModal.presentCharactersTab.appearanceDemeanorHint": "Champs affichés sous le nom du personnage.", + "template.trackerEditorModal.presentCharactersTab.addCustomFieldButton": "Ajouter Champ Perso", + "template.trackerEditorModal.presentCharactersTab.thoughtsConfigTitle": "Configuration Pensées", + "template.trackerEditorModal.presentCharactersTab.enableCharacterThoughts": "Activer Pensées Personnage", + "template.trackerEditorModal.presentCharactersTab.thoughtsLabelLabel": "Libellé Pensées :", + "template.trackerEditorModal.presentCharactersTab.aiInstructionLabel": "Instruction IA :", + "template.trackerEditorModal.presentCharactersTab.characterStatsTitle": "Stats Personnage", + "template.trackerEditorModal.presentCharactersTab.trackCharacterStats": "Suivre Stats Personnage", + "template.trackerEditorModal.presentCharactersTab.characterStatsHint": "Créer des stats à suivre pour chaque personnage (affichées comme nombres colorés).", + "template.trackerEditorModal.presentCharactersTab.addCharacterStatButton": "Ajouter Stat Personnage", + "template.mainPanel.title": "RPG Companion", + "template.mainPanel.lastRoll": "Dernier Lancer :", + "template.mainPanel.clearLastRoll": "Effacer dernier lancer", + "template.mainPanel.immersiveHtml": "HTML Immersif", + "template.mainPanel.coloredDialogues": "Dialogues Colorés", + "template.mainPanel.deceptionSystem": "Système Déception", + "template.mainPanel.omniscienceFilter": "Filtre Omniscience", + "template.mainPanel.spotifyMusic": "Musique Spotify", + "template.mainPanel.snowflakesEffect": "Effet Flocons", + "template.mainPanel.dynamicWeatherEffects": "Météo Dynamique", + "template.mainPanel.narratorMode": "Mode Narrateur", + "template.mainPanel.refreshRpgInfo": "Rafraîchir Infos RPG", + "template.mainPanel.updating": "Mise à jour...", + "template.mainPanel.editTrackersButton": "Éditer Suivis", + "template.mainPanel.settingsButton": "Paramètres", + "global.none": "Aucun", + "global.add": "Ajouter", + "global.cancel": "Annuler", + "global.listView": "Vue liste", + "global.gridView": "Vue grille", + "global.save": "Sauvegarder", + "global.status": "Statut", + "global.inventory": "Inventaire", + "global.quests": "Quêtes", + "global.info": "Info", + "infobox.noData.title": "Pas de données", + "infobox.noData.instruction": "Générez une nouvelle réponse dans le jeu de rôle ou basculez vers \"Génération Séparée\" dans les Paramètres pour accéder et cliquer sur le bouton \"Rafraîchir Infos RPG\"", + "infobox.recentEvents.title": "Événements Récents", + "infobox.recentEvents.addEventPlaceholder": "Ajouter événement...", + "inventory.section.onPerson": "Sur Soi", + "inventory.section.clothing": "Vêtements", + "inventory.section.stored": "Stocké", + "inventory.section.assets": "Biens", + "inventory.onPerson.empty": "Aucun objet porté", + "inventory.onPerson.title": "Objets Actuellement Portés", + "inventory.onPerson.addItemButton": "Ajouter Objet", + "inventory.onPerson.addItemPlaceholder": "Entrer nom objet...", + "inventory.clothing.empty": "Ne porte rien", + "inventory.clothing.title": "Vêtements & Armure", + "inventory.clothing.addItemButton": "Ajouter Vêtement", + "inventory.clothing.addItemPlaceholder": "Entrer vêtement...", + "inventory.stored.title": "Lieux de Stockage", + "inventory.stored.addLocationButton": "Ajouter Lieu", + "inventory.stored.addLocationPlaceholder": "Entrer nom lieu...", + "inventory.stored.saveButton": "Sauvegarder", + "inventory.stored.empty": "Aucun lieu de stockage. Cliquez sur \"Ajouter Lieu\" pour en créer un.", + "inventory.stored.noItems": "Aucun objet stocké ici", + "inventory.stored.addItemToLocationPlaceholder": "Entrer nom objet...", + "inventory.stored.addItemButton": "Ajouter Objet", + "inventory.stored.confirmRemoveLocationMessage": "Supprimer \"${location}\" ? Cela supprimera tous les objets stockés ici.", + "inventory.stored.confirmRemoveLocationConfirmButton": "Confirmer", + "inventory.assets.empty": "Aucun bien possédé", + "inventory.assets.title": "Véhicules, Propriétés & Possessions Majeures", + "inventory.assets.addAssetModalTitle": "Ajouter Bien", + "inventory.assets.addAssetButton": "Ajouter Bien", + "inventory.assets.addAssetPlaceholder": "Entrer nom bien...", + "inventory.assets.description": "Les biens incluent les véhicules (voitures, motos), les propriétés (maisons, appartements) et les équipements majeurs (outils d'atelier, objets spéciaux).", + "quests.section.main": "Quête Principale", + "quests.section.optional": "Quêtes Optionnelles", + "quests.main.title": "Quêtes Principales", + "quests.main.addQuestButton": "Ajouter Quête", + "quests.main.addQuestPlaceholder": "Entrer titre quête principale...", + "quests.main.empty": "Aucune quête principale active", + "quests.main.hint": "La quête principale représente votre objectif principal dans l'histoire.", + "quests.optional.title": "Quêtes Optionnelles", + "quests.optional.addQuestButton": "Ajouter Quête", + "quests.optional.addQuestPlaceholder": "Entrer titre quête optionnelle...", + "quests.optional.empty": "Aucune quête optionnelle active", + "quests.optional.hint": "Les quêtes optionnelles sont des objectifs secondaires qui complètent votre histoire principale.", + "checkpoint.setChapterStart": "Définir Début Chapitre", + "checkpoint.clearChapterStart": "Effacer Début Chapitre", + "checkpoint.indicator": "Début Chapitre", + "checkpoint.tooltip": "Les messages avant ce point sont exclus du contexte", + "musicPlayer.title": "Musique de Scène", + "musicPlayer.noMusic": "L'IA suggérera de la musique quand approprié pour la scène", + "errors.parsingError": "Erreur de parsing RPG Companion Trackers ! Le modèle a renvoyé un format incorrect. Si le problème persiste, envisagez de changer le modèle pour les générations.", + "settings.recommendedModels.title": "Modèles Recommandés", + "settings.recommendedModels.description": "Pour que l'extension fonctionne correctement, **il n'est pas recommandé d'utiliser des modèles de moins de 20B, surtout s'ils sont anciens.** Elle fonctionne mieux avec les modèles SOTA tels que Deepseek, Claude, GPT ou Gemini.", + "thoughts.addCharacter": "Ajouter un personnage", + "thoughts.locked": "Verrouillé", + "thoughts.unlocked": "Déverrouillé", + "thoughts.clickToEdit": "Cliquer pour modifier", + "thoughts.clickToUpload": "Cliquer pour télécharger un avatar", + "thoughts.removeCharacter": "Supprimer le personnage", + "userStats.level": "NIV", + "userStats.clickToEditLevel": "Cliquer pour modifier le niveau", + "userStats.statsLocked": "Verrouillé - L'IA ne peut pas modifier les stats", + "userStats.statsUnlocked": "Déverrouillé - L'IA peut modifier les stats", + "userStats.clickToEditStatName": "Cliquer pour modifier le nom", + "userStats.clickToEditStatValue": "Cliquer pour modifier", + "userStats.moodLocked": "Verrouillé - L'IA ne peut pas modifier l'humeur", + "userStats.moodUnlocked": "Déverrouillé - L'IA peut modifier l'humeur", + "userStats.clickToEditEmoji": "Cliquer pour modifier l'émoji", + "userStats.skillsLocked": "Verrouillé - L'IA ne peut pas modifier les compétences", + "userStats.skillsUnlocked": "Déverrouillé - L'IA peut modifier les compétences", + "userStats.clickToEditSkills": "Cliquer pour modifier les compétences", + "infoBox.clickToEdit": "Cliquer pour modifier", + "infoBox.locked": "Verrouillé - L'IA ne peut pas modifier à ceci", + "infoBox.unlocked": "Déverrouillé - L'IA peut modifier à ceci", + "infoBox.weatherFallback": "Météo", + "infoBox.locationFallback": "Lieu", + "stats.health": "Santé", + "stats.satiety": "Satiété", + "stats.energy": "Énergie", + "stats.hygiene": "Hygiène", + "stats.arousal": "Excitation", + "stats.str": "FOR", + "stats.dex": "DEX", + "stats.con": "CON", + "stats.int": "INT", + "stats.wis": "VOL", + "stats.cha": "CHA" +} \ No newline at end of file diff --git a/src/systems/rendering/infoBox.js b/src/systems/rendering/infoBox.js index 48a633e..c0ca5f4 100644 --- a/src/systems/rendering/infoBox.js +++ b/src/systems/rendering/infoBox.js @@ -159,171 +159,171 @@ export function renderInfoBox() { const lines = infoBoxData.split('\n'); // console.log('[RPG Companion] Info Box split into lines:', lines); - // Track which fields we've already parsed to avoid duplicates from mixed formats - const parsedFields = { - date: false, - temperature: false, - time: false, - location: false, - weather: false - }; + // Track which fields we've already parsed to avoid duplicates from mixed formats + const parsedFields = { + date: false, + temperature: false, + time: false, + location: false, + weather: false + }; - for (const line of lines) { - // console.log('[RPG Companion] Processing line:', line); + for (const line of lines) { + // console.log('[RPG Companion] Processing line:', line); - // Support both new text format (Date:) and legacy emoji format (🗓️:) - // Prioritize text format over emoji format - if (line.startsWith('Date:')) { - if (!parsedFields.date) { - // console.log('[RPG Companion] → Matched DATE (text format)'); - const dateStr = line.replace('Date:', '').trim(); - const dateParts = dateStr.split(',').map(p => p.trim()); - data.weekday = dateParts[0] || ''; - data.month = dateParts[1] || ''; - data.year = dateParts[2] || ''; - data.date = dateStr; - parsedFields.date = true; - } - } else if (line.includes('🗓️:')) { - if (!parsedFields.date) { - // console.log('[RPG Companion] → Matched DATE (emoji format)'); - const dateStr = line.replace('🗓️:', '').trim(); - const dateParts = dateStr.split(',').map(p => p.trim()); - data.weekday = dateParts[0] || ''; - data.month = dateParts[1] || ''; - data.year = dateParts[2] || ''; - data.date = dateStr; - parsedFields.date = true; - } - } else if (line.startsWith('Temperature:')) { - if (!parsedFields.temperature) { - // console.log('[RPG Companion] → Matched TEMPERATURE (text format)'); - const tempStr = line.replace('Temperature:', '').trim(); - data.temperature = tempStr; - const tempMatch = tempStr.match(/(-?\d+)/); - if (tempMatch) { - data.tempValue = parseInt(tempMatch[1]); + // Support both new text format (Date:) and legacy emoji format (🗓️:) + // Prioritize text format over emoji format + if (line.startsWith('Date:')) { + if (!parsedFields.date) { + // console.log('[RPG Companion] → Matched DATE (text format)'); + const dateStr = line.replace('Date:', '').trim(); + const dateParts = dateStr.split(',').map(p => p.trim()); + data.weekday = dateParts[0] || ''; + data.month = dateParts[1] || ''; + data.year = dateParts[2] || ''; + data.date = dateStr; + parsedFields.date = true; } - parsedFields.temperature = true; - } - } else if (line.includes('🌡️:')) { - if (!parsedFields.temperature) { - // console.log('[RPG Companion] → Matched TEMPERATURE (emoji format)'); - const tempStr = line.replace('🌡️:', '').trim(); - data.temperature = tempStr; - const tempMatch = tempStr.match(/(-?\d+)/); - if (tempMatch) { - data.tempValue = parseInt(tempMatch[1]); + } else if (line.includes('🗓️:')) { + if (!parsedFields.date) { + // console.log('[RPG Companion] → Matched DATE (emoji format)'); + const dateStr = line.replace('🗓️:', '').trim(); + const dateParts = dateStr.split(',').map(p => p.trim()); + data.weekday = dateParts[0] || ''; + data.month = dateParts[1] || ''; + data.year = dateParts[2] || ''; + data.date = dateStr; + parsedFields.date = true; } - parsedFields.temperature = true; - } - } else if (line.startsWith('Time:')) { - if (!parsedFields.time) { - // console.log('[RPG Companion] → Matched TIME (text format)'); - const timeStr = line.replace('Time:', '').trim(); - data.time = timeStr; - const timeParts = timeStr.split('→').map(t => t.trim()); - data.timeStart = timeParts[0] || ''; - data.timeEnd = timeParts[1] || ''; - parsedFields.time = true; - } - } else if (line.includes('🕒:')) { - if (!parsedFields.time) { - // console.log('[RPG Companion] → Matched TIME (emoji format)'); - const timeStr = line.replace('🕒:', '').trim(); - data.time = timeStr; - const timeParts = timeStr.split('→').map(t => t.trim()); - data.timeStart = timeParts[0] || ''; - data.timeEnd = timeParts[1] || ''; - parsedFields.time = true; - } - } else if (line.startsWith('Location:')) { - if (!parsedFields.location) { - // console.log('[RPG Companion] → Matched LOCATION (text format)'); - data.location = line.replace('Location:', '').trim(); - parsedFields.location = true; - } - } else if (line.includes('🗺️:')) { - if (!parsedFields.location) { - // console.log('[RPG Companion] → Matched LOCATION (emoji format)'); - data.location = line.replace('🗺️:', '').trim(); - parsedFields.location = true; - } - } else if (line.startsWith('Weather:')) { - if (!parsedFields.weather) { - // New text format: Weather: [Emoji], [Forecast] OR Weather: [Emoji][Forecast] (no separator - FIXED) - const weatherStr = line.replace('Weather:', '').trim(); - const { emoji, text } = separateEmojiFromText(weatherStr); - - if (emoji && text) { - data.weatherEmoji = emoji; - data.weatherForecast = text; - } else if (weatherStr.includes(',')) { - // Fallback to comma split if emoji detection failed - split only on FIRST comma - const firstCommaIndex = weatherStr.indexOf(','); - data.weatherEmoji = weatherStr.substring(0, firstCommaIndex).trim(); - data.weatherForecast = weatherStr.substring(firstCommaIndex + 1).trim(); - } else { - // No clear separation - assume it's all forecast text - data.weatherEmoji = '🌤️'; // Default emoji - data.weatherForecast = weatherStr; + } else if (line.startsWith('Temperature:')) { + if (!parsedFields.temperature) { + // console.log('[RPG Companion] → Matched TEMPERATURE (text format)'); + const tempStr = line.replace('Temperature:', '').trim(); + data.temperature = tempStr; + const tempMatch = tempStr.match(/(-?\d+)/); + if (tempMatch) { + data.tempValue = parseInt(tempMatch[1]); + } + parsedFields.temperature = true; } + } else if (line.includes('🌡️:')) { + if (!parsedFields.temperature) { + // console.log('[RPG Companion] → Matched TEMPERATURE (emoji format)'); + const tempStr = line.replace('🌡️:', '').trim(); + data.temperature = tempStr; + const tempMatch = tempStr.match(/(-?\d+)/); + if (tempMatch) { + data.tempValue = parseInt(tempMatch[1]); + } + parsedFields.temperature = true; + } + } else if (line.startsWith('Time:')) { + if (!parsedFields.time) { + // console.log('[RPG Companion] → Matched TIME (text format)'); + const timeStr = line.replace('Time:', '').trim(); + data.time = timeStr; + const timeParts = timeStr.split('→').map(t => t.trim()); + data.timeStart = timeParts[0] || ''; + data.timeEnd = timeParts[1] || ''; + parsedFields.time = true; + } + } else if (line.includes('🕒:')) { + if (!parsedFields.time) { + // console.log('[RPG Companion] → Matched TIME (emoji format)'); + const timeStr = line.replace('🕒:', '').trim(); + data.time = timeStr; + const timeParts = timeStr.split('→').map(t => t.trim()); + data.timeStart = timeParts[0] || ''; + data.timeEnd = timeParts[1] || ''; + parsedFields.time = true; + } + } else if (line.startsWith('Location:')) { + if (!parsedFields.location) { + // console.log('[RPG Companion] → Matched LOCATION (text format)'); + data.location = line.replace('Location:', '').trim(); + parsedFields.location = true; + } + } else if (line.includes('🗺️:')) { + if (!parsedFields.location) { + // console.log('[RPG Companion] → Matched LOCATION (emoji format)'); + data.location = line.replace('🗺️:', '').trim(); + parsedFields.location = true; + } + } else if (line.startsWith('Weather:')) { + if (!parsedFields.weather) { + // New text format: Weather: [Emoji], [Forecast] OR Weather: [Emoji][Forecast] (no separator - FIXED) + const weatherStr = line.replace('Weather:', '').trim(); + const { emoji, text } = separateEmojiFromText(weatherStr); - parsedFields.weather = true; - } - } else { - // Check if it's a legacy weather line (emoji format) - // Only parse if we haven't already found weather in text format - if (!parsedFields.weather) { - // Since \p{Emoji} doesn't work reliably, use a simpler approach - const hasColon = line.includes(':'); - const notInfoBox = !line.includes('Info Box'); - const notDivider = !line.includes('---'); - const notCodeFence = !line.trim().startsWith('```'); + if (emoji && text) { + data.weatherEmoji = emoji; + data.weatherForecast = text; + } else if (weatherStr.includes(',')) { + // Fallback to comma split if emoji detection failed - split only on FIRST comma + const firstCommaIndex = weatherStr.indexOf(','); + data.weatherEmoji = weatherStr.substring(0, firstCommaIndex).trim(); + data.weatherForecast = weatherStr.substring(firstCommaIndex + 1).trim(); + } else { + // No clear separation - assume it's all forecast text + data.weatherEmoji = '🌤️'; // Default emoji + data.weatherForecast = weatherStr; + } - // console.log('[RPG Companion] → Checking weather conditions:', { - // line: line, - // hasColon: hasColon, - // notInfoBox: notInfoBox, - // notDivider: notDivider - // }); + parsedFields.weather = true; + } + } else { + // Check if it's a legacy weather line (emoji format) + // Only parse if we haven't already found weather in text format + if (!parsedFields.weather) { + // Since \p{Emoji} doesn't work reliably, use a simpler approach + const hasColon = line.includes(':'); + const notInfoBox = !line.includes('Info Box'); + const notDivider = !line.includes('---'); + const notCodeFence = !line.trim().startsWith('```'); - if (hasColon && notInfoBox && notDivider && notCodeFence && line.trim().length > 0) { - // Match format: [Weather Emoji]: [Forecast] - // Capture everything before colon as emoji, everything after as forecast - // console.log('[RPG Companion] → Testing WEATHER match for:', line); - const weatherMatch = line.match(/^\s*([^:]+):\s*(.+)$/); - if (weatherMatch) { - const potentialEmoji = weatherMatch[1].trim(); - const forecast = weatherMatch[2].trim(); + // console.log('[RPG Companion] → Checking weather conditions:', { + // line: line, + // hasColon: hasColon, + // notInfoBox: notInfoBox, + // notDivider: notDivider + // }); - // If the first part is short (likely emoji), treat as weather - if (potentialEmoji.length <= 5) { - data.weatherEmoji = potentialEmoji; - data.weatherForecast = forecast; - parsedFields.weather = true; - // console.log('[RPG Companion] ✓ Weather parsed:', data.weatherEmoji, data.weatherForecast); + if (hasColon && notInfoBox && notDivider && notCodeFence && line.trim().length > 0) { + // Match format: [Weather Emoji]: [Forecast] + // Capture everything before colon as emoji, everything after as forecast + // console.log('[RPG Companion] → Testing WEATHER match for:', line); + const weatherMatch = line.match(/^\s*([^:]+):\s*(.+)$/); + if (weatherMatch) { + const potentialEmoji = weatherMatch[1].trim(); + const forecast = weatherMatch[2].trim(); + + // If the first part is short (likely emoji), treat as weather + if (potentialEmoji.length <= 5) { + data.weatherEmoji = potentialEmoji; + data.weatherForecast = forecast; + parsedFields.weather = true; + // console.log('[RPG Companion] ✓ Weather parsed:', data.weatherEmoji, data.weatherForecast); + } else { + // console.log('[RPG Companion] ✗ First part too long for emoji:', potentialEmoji); + } } else { - // console.log('[RPG Companion] ✗ First part too long for emoji:', potentialEmoji); + // console.log('[RPG Companion] ✗ Weather regex did not match'); } } else { - // console.log('[RPG Companion] ✗ Weather regex did not match'); + // console.log('[RPG Companion] → No match for this line'); } - } else { - // console.log('[RPG Companion] → No match for this line'); } } } - } - // console.log('[RPG Companion] Parsed Info Box data:', { - // date: data.date, - // weatherEmoji: data.weatherEmoji, - // weatherForecast: data.weatherForecast, - // temperature: data.temperature, - // timeStart: data.timeStart, - // location: data.location - // }); + // console.log('[RPG Companion] Parsed Info Box data:', { + // date: data.date, + // weatherEmoji: data.weatherEmoji, + // weatherForecast: data.weatherForecast, + // temperature: data.temperature, + // timeStart: data.timeStart, + // location: data.location + // }); } // Get tracker configuration @@ -363,9 +363,9 @@ export function renderInfoBox() { row1Widgets.push(`
${dateLockIconHtml} -
${monthDisplay}
-
${weekdayDisplay}
-
${yearDisplay}
+
${monthDisplay}
+
${weekdayDisplay}
+
${yearDisplay}
`); } @@ -373,14 +373,14 @@ export function renderInfoBox() { // Weather widget - show if enabled if (config?.widgets?.weather?.enabled) { const weatherEmoji = data.weatherEmoji || '🌤️'; - const weatherForecast = data.weatherForecast || 'Weather'; + const weatherForecast = data.weatherForecast || i18n.getTranslation('infoBox.weatherFallback'); const weatherLockIconHtml = getLockIconHtml('infoBox', 'weather'); row1Widgets.push(`
${weatherLockIconHtml} -
${weatherEmoji}
-
${weatherForecast}
+
${weatherEmoji}
+
${weatherForecast}
`); } @@ -399,12 +399,12 @@ export function renderInfoBox() { if (preferredUnit === 'F' && isCelsius) { // Convert C to F - const fahrenheit = Math.round((tempValue * 9/5) + 32); + const fahrenheit = Math.round((tempValue * 9 / 5) + 32); tempDisplay = `${fahrenheit}°F`; tempValue = fahrenheit; } else if (preferredUnit === 'C' && isFahrenheit) { // Convert F to C - const celsius = Math.round((tempValue - 32) * 5/9); + const celsius = Math.round((tempValue - 32) * 5 / 9); tempDisplay = `${celsius}°C`; tempValue = celsius; } @@ -415,7 +415,7 @@ export function renderInfoBox() { } // Calculate thermometer display (convert to Celsius for consistent thresholds) - const tempInCelsius = preferredUnit === 'F' ? Math.round((tempValue - 32) * 5/9) : tempValue; + const tempInCelsius = preferredUnit === 'F' ? Math.round((tempValue - 32) * 5 / 9) : tempValue; const tempPercent = Math.min(100, Math.max(0, ((tempInCelsius + 20) / 60) * 100)); const tempColor = tempInCelsius < 10 ? '#4a90e2' : tempInCelsius < 25 ? '#67c23a' : '#e94560'; const tempLockIconHtml = getLockIconHtml('infoBox', 'temperature'); @@ -429,7 +429,7 @@ export function renderInfoBox() {
-
${tempDisplay}
+
${tempDisplay}
`); } @@ -464,9 +464,9 @@ export function renderInfoBox() {
-
${timeStartDisplay}
+
${timeStartDisplay}
-
${timeEndDisplay}
+
${timeEndDisplay}
`); @@ -481,7 +481,7 @@ export function renderInfoBox() { // Row 2: Location widget (full width) - show if enabled if (config?.widgets?.location?.enabled) { - const locationDisplay = data.location || 'Location'; + const locationDisplay = data.location || i18n.getTranslation('infoBox.locationFallback'); const locationLockIconHtml = getLockIconHtml('infoBox', 'location'); html += ` @@ -491,7 +491,7 @@ export function renderInfoBox() {
📍
-
${locationDisplay}
+
${locationDisplay}
`; @@ -550,7 +550,7 @@ export function renderInfoBox() { html += `
- ${validEvents[i]} + ${validEvents[i]}
`; } @@ -591,7 +591,7 @@ export function renderInfoBox() { } // Add event handlers for editable Info Box fields - $infoBoxContainer.find('.rpg-editable').on('blur', function() { + $infoBoxContainer.find('.rpg-editable').on('blur', function () { const $this = $(this); const field = $this.data('field'); const value = $this.text().trim(); @@ -624,12 +624,12 @@ export function renderInfoBox() { }); // Update location size on input as well (real-time) - $infoBoxContainer.find('[data-field="location"]').on('input', function() { + $infoBoxContainer.find('[data-field="location"]').on('input', function () { updateLocationTextSize($(this)); }); // For date fields, show full value on focus - $infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function() { + $infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function () { const fullValue = $(this).data('full-value'); if (fullValue) { $(this).text(fullValue); @@ -637,7 +637,7 @@ export function renderInfoBox() { }); // Add event handler for lock icons (support both click and touch) - $infoBoxContainer.find('.rpg-section-lock-icon').on('click touchend', function(e) { + $infoBoxContainer.find('.rpg-section-lock-icon').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); const $lockIcon = $(this); @@ -652,7 +652,7 @@ export function renderInfoBox() { // Update icon $lockIcon.text(newLockState ? '🔒' : '🔓'); - $lockIcon.attr('title', newLockState ? 'Locked - AI cannot change this' : 'Unlocked - AI can change this'); + $lockIcon.attr('title', newLockState ? i18n.getTranslation('infoBox.locked') : i18n.getTranslation('infoBox.unlocked')); $lockIcon.toggleClass('locked', newLockState); // Save settings to persist lock state diff --git a/src/systems/rendering/thoughts.js b/src/systems/rendering/thoughts.js index 5cc4a93..6a90349 100644 --- a/src/systems/rendering/thoughts.js +++ b/src/systems/rendering/thoughts.js @@ -14,6 +14,7 @@ import { FALLBACK_AVATAR_DATA_URI, addDebugLog } from '../../core/state.js'; +import { i18n } from '../../core/i18n.js'; import { saveChatData, saveSettings } from '../../core/persistence.js'; import { getSafeThumbnailUrl } from '../../utils/avatars.js'; import { isItemLocked, setItemLock } from '../generation/lockManager.js'; @@ -30,7 +31,7 @@ function getLockIconHtml(tracker, path) { const isLocked = isItemLocked(tracker, path); const lockIcon = isLocked ? '🔒' : '🔓'; - const lockTitle = isLocked ? 'Locked' : 'Unlocked'; + const lockTitle = isLocked ? i18n.getTranslation('thoughts.locked') : i18n.getTranslation('thoughts.unlocked'); const lockedClass = isLocked ? ' locked' : ''; return `${lockIcon}`; } @@ -300,88 +301,88 @@ export function renderThoughts({ preserveScroll = false } = {}) { debugLog('[RPG Thoughts] Split into lines count:', lines.length); debugLog('[RPG Thoughts] Lines:', lines); - // Parse new multi-line format: - // - [Name] - // Details: [Emoji] | [Field1] | [Field2] | ... - // Relationship: [Relationship] - // Stats: Stat1: X% | Stat2: X% | ... - // Thoughts: [Description] - let lineNumber = 0; - let currentCharacter = null; + // Parse new multi-line format: + // - [Name] + // Details: [Emoji] | [Field1] | [Field2] | ... + // Relationship: [Relationship] + // Stats: Stat1: X% | Stat2: X% | ... + // Thoughts: [Description] + let lineNumber = 0; + let currentCharacter = null; - for (const line of lines) { - lineNumber++; + for (const line of lines) { + lineNumber++; - // Skip empty lines, headers, dividers, and code fences - if (!line.trim() || - line.includes('Present Characters') || - line.includes('---') || - line.trim().startsWith('```') || - line.trim() === '- …' || - line.includes('(Repeat the format')) { - continue; - } - - debugLog(`[RPG Thoughts] Processing line ${lineNumber}:`, line); - - // Check if this is a character name line (starts with "- ") - if (line.trim().startsWith('- ')) { - const name = line.trim().substring(2).trim(); - - if (name && name.toLowerCase() !== 'unavailable') { - currentCharacter = { name }; - presentCharacters.push(currentCharacter); - debugLog(`[RPG Thoughts] ✓ Started new character: ${name}`); - } else { - currentCharacter = null; - debugLog(`[RPG Thoughts] ✗ Rejected character - name: "${name}" (unavailable or empty)`); - } - } - // Check if this is a Details line - else if (line.trim().startsWith('Details:') && currentCharacter) { - const detailsContent = line.substring(line.indexOf(':') + 1).trim(); - const parts = detailsContent.split('|').map(p => p.trim()); - - // First part is the emoji - if (parts.length > 0) { - currentCharacter.emoji = parts[0]; - debugLog(`[RPG Thoughts] Parsed emoji: ${parts[0]}`); + // Skip empty lines, headers, dividers, and code fences + if (!line.trim() || + line.includes('Present Characters') || + line.includes('---') || + line.trim().startsWith('```') || + line.trim() === '- …' || + line.includes('(Repeat the format')) { + continue; } - // Remaining parts are custom fields - for (let i = 0; i < enabledFields.length && i + 1 < parts.length; i++) { - const fieldName = enabledFields[i].name; - currentCharacter[fieldName] = parts[i + 1]; - debugLog(`[RPG Thoughts] Parsed field ${fieldName}: ${parts[i + 1]}`); - } - } - // Check if this is a Relationship line - else if (line.trim().startsWith('Relationship:') && currentCharacter) { - const relationship = line.substring(line.indexOf(':') + 1).trim(); - currentCharacter.Relationship = relationship; - debugLog(`[RPG Thoughts] Parsed relationship: ${relationship}`); - } - // Check if this is a Stats line - else if (line.trim().startsWith('Stats:') && currentCharacter && enabledCharStats.length > 0) { - const statsContent = line.substring(line.indexOf(':') + 1).trim(); - const statParts = statsContent.split('|').map(p => p.trim()); + debugLog(`[RPG Thoughts] Processing line ${lineNumber}:`, line); - for (const statPart of statParts) { - const statMatch = statPart.match(/^(.+?):\s*(\d+)%$/); - if (statMatch) { - const statName = statMatch[1].trim(); - const statValue = parseInt(statMatch[2]); - currentCharacter[statName] = statValue; - debugLog(`[RPG Thoughts] Parsed stat: ${statName} = ${statValue}%`); + // Check if this is a character name line (starts with "- ") + if (line.trim().startsWith('- ')) { + const name = line.trim().substring(2).trim(); + + if (name && name.toLowerCase() !== 'unavailable') { + currentCharacter = { name }; + presentCharacters.push(currentCharacter); + debugLog(`[RPG Thoughts] ✓ Started new character: ${name}`); + } else { + currentCharacter = null; + debugLog(`[RPG Thoughts] ✗ Rejected character - name: "${name}" (unavailable or empty)`); } } + // Check if this is a Details line + else if (line.trim().startsWith('Details:') && currentCharacter) { + const detailsContent = line.substring(line.indexOf(':') + 1).trim(); + const parts = detailsContent.split('|').map(p => p.trim()); + + // First part is the emoji + if (parts.length > 0) { + currentCharacter.emoji = parts[0]; + debugLog(`[RPG Thoughts] Parsed emoji: ${parts[0]}`); + } + + // Remaining parts are custom fields + for (let i = 0; i < enabledFields.length && i + 1 < parts.length; i++) { + const fieldName = enabledFields[i].name; + currentCharacter[fieldName] = parts[i + 1]; + debugLog(`[RPG Thoughts] Parsed field ${fieldName}: ${parts[i + 1]}`); + } + } + // Check if this is a Relationship line + else if (line.trim().startsWith('Relationship:') && currentCharacter) { + const relationship = line.substring(line.indexOf(':') + 1).trim(); + currentCharacter.Relationship = relationship; + debugLog(`[RPG Thoughts] Parsed relationship: ${relationship}`); + } + // Check if this is a Stats line + else if (line.trim().startsWith('Stats:') && currentCharacter && enabledCharStats.length > 0) { + const statsContent = line.substring(line.indexOf(':') + 1).trim(); + const statParts = statsContent.split('|').map(p => p.trim()); + + for (const statPart of statParts) { + const statMatch = statPart.match(/^(.+?):\s*(\d+)%$/); + if (statMatch) { + const statName = statMatch[1].trim(); + const statValue = parseInt(statMatch[2]); + currentCharacter[statName] = statValue; + debugLog(`[RPG Thoughts] Parsed stat: ${statName} = ${statValue}%`); + } + } + } + // Check if this is a Thoughts line (handled separately for thought bubbles) + else if (line.trim().match(/^[A-Z][a-z]+:/) && currentCharacter) { + // This could be Thoughts, Feelings, etc. - skip for now, handled in thought bubble rendering + debugLog(`[RPG Thoughts] Skipping thoughts/feelings line (handled in bubble rendering)`); + } } - // Check if this is a Thoughts line (handled separately for thought bubbles) - else if (line.trim().match(/^[A-Z][a-z]+:/) && currentCharacter) { - // This could be Thoughts, Feelings, etc. - skip for now, handled in thought bubble rendering - debugLog(`[RPG Thoughts] Skipping thoughts/feelings line (handled in bubble rendering)`); - } - } } // End of text format parsing // Get relationship emojis from config (with fallback defaults) @@ -502,14 +503,14 @@ export function renderThoughts({ preserveScroll = false } = {}) { html += `
-
+
${char.name} - ${hasRelationshipEnabled ? `
${relationshipBadge}
` : ''} + ${hasRelationshipEnabled ? `
${relationshipBadge}
` : ''}
- ${char.emoji} - ${char.name} - + ${char.emoji} + ${char.name} +
@@ -532,12 +533,12 @@ export function renderThoughts({ preserveScroll = false } = {}) { html += `
${lockIconHtml} - ${fieldValue} + ${fieldValue}
`; } else { html += ` -
${fieldValue}
+
${fieldValue}
`; } } @@ -563,7 +564,7 @@ export function renderThoughts({ preserveScroll = false } = {}) { ); html += `
- ${stat.name}: ${statValue}% + ${stat.name}: ${statValue}%
`; } @@ -589,8 +590,8 @@ export function renderThoughts({ preserveScroll = false } = {}) { // Add "Add Character" button if data exists (inside rpg-thoughts-content) if (presentCharacters.length > 0) { html += ` - `; } @@ -604,7 +605,7 @@ export function renderThoughts({ preserveScroll = false } = {}) { debugLog('[RPG Thoughts] ======================================================='); // Add event handlers for editable character fields - $thoughtsContainer.find('.rpg-editable').on('blur', function() { + $thoughtsContainer.find('.rpg-editable').on('blur', function () { const character = $(this).data('character'); const field = $(this).data('field'); const value = $(this).text().trim(); @@ -613,12 +614,12 @@ export function renderThoughts({ preserveScroll = false } = {}) { }); // Prevent click events on editable elements from bubbling to avatar upload handler - $thoughtsContainer.find('.rpg-editable').on('click mousedown', function(e) { + $thoughtsContainer.find('.rpg-editable').on('click mousedown', function (e) { e.stopPropagation(); }); // Add event listener for section lock icon clicks (support both click and touch) - $thoughtsContainer.find('.rpg-section-lock-icon').on('click touchend', function(e) { + $thoughtsContainer.find('.rpg-section-lock-icon').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); const $icon = $(this); @@ -643,7 +644,7 @@ export function renderThoughts({ preserveScroll = false } = {}) { }); // Add event listener for character remove button - $thoughtsContainer.find('.rpg-character-remove').on('click', function(e) { + $thoughtsContainer.find('.rpg-character-remove').on('click', function (e) { e.preventDefault(); e.stopPropagation(); @@ -652,7 +653,7 @@ export function renderThoughts({ preserveScroll = false } = {}) { }); // Add event listener for avatar upload clicks - $thoughtsContainer.find('.rpg-avatar-upload').on('click', function(e) { + $thoughtsContainer.find('.rpg-avatar-upload').on('click', function (e) { e.preventDefault(); e.stopPropagation(); @@ -661,13 +662,13 @@ export function renderThoughts({ preserveScroll = false } = {}) { // Create hidden file input const fileInput = $(''); - fileInput.on('change', function() { + fileInput.on('change', function () { const file = this.files[0]; if (!file) return; // Read file as data URL const reader = new FileReader(); - reader.onload = function(e) { + reader.onload = function (e) { const imageUrl = e.target.result; // Store in npcAvatars @@ -694,20 +695,20 @@ export function renderThoughts({ preserveScroll = false } = {}) { }); // Add event listener for "Add Character" button (support both click and touch for mobile) - $thoughtsContainer.find('.rpg-add-character-btn').on('click touchend', function(e) { + $thoughtsContainer.find('.rpg-add-character-btn').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); addNewCharacter(); }); // Handle empty field focus - remove placeholder styling on focus - $thoughtsContainer.find('.rpg-editable.rpg-empty-field').on('focus', function() { + $thoughtsContainer.find('.rpg-editable.rpg-empty-field').on('focus', function () { $(this).removeClass('rpg-empty-field'); $(this).removeAttr('data-placeholder'); }); // Restore placeholder if field becomes empty on blur (after the main blur handler) - $thoughtsContainer.find('.rpg-editable').on('blur', function() { + $thoughtsContainer.find('.rpg-editable').on('blur', function () { const $this = $(this); if (!$this.text().trim()) { const field = $this.data('field'); @@ -1494,59 +1495,59 @@ export function updateChatThoughts() { if (thoughtsArray.length === 0) { const lines = lastGeneratedData.characterThoughts.split('\n'); - // console.log('[RPG Companion] Parsing thoughts from lines:', lines); + // console.log('[RPG Companion] Parsing thoughts from lines:', lines); - // Parse new format to build character map and thoughts - let currentCharName = null; - let currentCharEmoji = null; + // Parse new format to build character map and thoughts + let currentCharName = null; + let currentCharEmoji = null; - for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim(); + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); - if (!line || - line.includes('Present Characters') || - line.includes('---') || - line.startsWith('```') || - line.trim() === '- …' || - line.includes('(Repeat the format')) { - continue; - } + if (!line || + line.includes('Present Characters') || + line.includes('---') || + line.startsWith('```') || + line.trim() === '- …' || + line.includes('(Repeat the format')) { + continue; + } - // Check if this is a character name line (starts with "- ") - if (line.startsWith('- ')) { - const name = line.substring(2).trim(); - if (name && name.toLowerCase() !== 'unavailable') { - currentCharName = name; - currentCharEmoji = null; // Reset emoji for new character - } else { - currentCharName = null; - currentCharEmoji = null; + // Check if this is a character name line (starts with "- ") + if (line.startsWith('- ')) { + const name = line.substring(2).trim(); + if (name && name.toLowerCase() !== 'unavailable') { + currentCharName = name; + currentCharEmoji = null; // Reset emoji for new character + } else { + currentCharName = null; + currentCharEmoji = null; + } + } + // Check if this is a Details line (contains the emoji) + else if (line.startsWith('Details:') && currentCharName) { + const detailsContent = line.substring(line.indexOf(':') + 1).trim(); + const parts = detailsContent.split('|').map(p => p.trim()); + + // First part is the emoji + if (parts.length > 0) { + currentCharEmoji = parts[0]; + } + } + // Check if this is a Thoughts line + else if (line.startsWith(thoughtsLabel + ':') && currentCharName && currentCharEmoji) { + const thoughtContent = line.substring(thoughtsLabel.length + 1).trim(); + + // The thought content is just the text (no emoji prefix in new format) + if (thoughtContent) { + thoughtsArray.push({ + name: currentCharName.toLowerCase(), + emoji: currentCharEmoji, + thought: thoughtContent + }); + } } } - // Check if this is a Details line (contains the emoji) - else if (line.startsWith('Details:') && currentCharName) { - const detailsContent = line.substring(line.indexOf(':') + 1).trim(); - const parts = detailsContent.split('|').map(p => p.trim()); - - // First part is the emoji - if (parts.length > 0) { - currentCharEmoji = parts[0]; - } - } - // Check if this is a Thoughts line - else if (line.startsWith(thoughtsLabel + ':') && currentCharName && currentCharEmoji) { - const thoughtContent = line.substring(thoughtsLabel.length + 1).trim(); - - // The thought content is just the text (no emoji prefix in new format) - if (thoughtContent) { - thoughtsArray.push({ - name: currentCharName.toLowerCase(), - emoji: currentCharEmoji, - thought: thoughtContent - }); - } - } - } } // End of text format parsing for thoughts bubbles debugLog('[RPG Thoughts] Parsed thoughts:', thoughtsArray); @@ -1628,7 +1629,7 @@ function attachDragHandlersToIcon($icon) { $icon.off('.thoughtIconDrag'); // Test: add a simple click handler to verify events work - $icon.on('click.thoughtIconDrag', function(e) { + $icon.on('click.thoughtIconDrag', function (e) { // Check global flag set immediately after drag completes if (justFinishedDragging) { // console.log('[Thought Icon] CLICK blocked - just finished dragging'); @@ -1641,7 +1642,7 @@ function attachDragHandlersToIcon($icon) { }); // Touch drag support - mobile only - $icon.on('touchstart.thoughtIconDrag', function(e) { + $icon.on('touchstart.thoughtIconDrag', function (e) { if (window.innerWidth > 1000) return; // console.log('[Thought Icon] touchstart'); @@ -1658,7 +1659,7 @@ function attachDragHandlersToIcon($icon) { isDragging = false; }); - $icon.on('touchmove.thoughtIconDrag', function(e) { + $icon.on('touchmove.thoughtIconDrag', function (e) { if (window.innerWidth > 1000) return; if (!touchMoved) { @@ -1701,7 +1702,7 @@ function attachDragHandlersToIcon($icon) { } }); - $icon.on('touchend.thoughtIconDrag', function(e) { + $icon.on('touchend.thoughtIconDrag', function (e) { // console.log('[Thought Icon] touchend - isDragging:', isDragging, 'touchMoved:', touchMoved); if (isDragging) { @@ -1756,7 +1757,7 @@ function attachDragHandlersToIcon($icon) { // Mouse drag support - mobile only let mouseDown = false; - $icon.on('mousedown.thoughtIconDrag', function(e) { + $icon.on('mousedown.thoughtIconDrag', function (e) { if (window.innerWidth > 1000) return; // console.log('[Thought Icon] mousedown'); @@ -1775,7 +1776,7 @@ function attachDragHandlersToIcon($icon) { isDragging = false; }); - $(document).on('mousemove.thoughtIconDrag', function(e) { + $(document).on('mousemove.thoughtIconDrag', function (e) { if (!mouseDown || window.innerWidth > 1000) return; if (!touchMoved) { @@ -1819,7 +1820,7 @@ function attachDragHandlersToIcon($icon) { } }); - $(document).on('mouseup.thoughtIconDrag', function(e) { + $(document).on('mouseup.thoughtIconDrag', function (e) { if (!mouseDown) return; // console.log('[Thought Icon] mouseup - isDragging:', isDragging, 'touchMoved:', touchMoved); @@ -2222,7 +2223,7 @@ export function createThoughtPanel($message, thoughtsArray) { }); // Close button functionality - support both click and touch - $thoughtPanel.find('.rpg-thought-close').on('click touchend', function(e) { + $thoughtPanel.find('.rpg-thought-close').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); @@ -2230,7 +2231,7 @@ export function createThoughtPanel($message, thoughtsArray) { if (isMobileView) { // Mobile: hide panel and show icon - $thoughtPanel.fadeOut(200, function() { + $thoughtPanel.fadeOut(200, function () { // Make sure icon is visible and clean state when panel closes (use selector, not variable) const $icon = $('#rpg-thought-icon'); $icon.removeClass('rpg-hidden dragging'); @@ -2252,14 +2253,14 @@ export function createThoughtPanel($message, thoughtsArray) { $icon.addClass('rpg-collapsed-desktop'); // Hide panel and show icon - $thoughtPanel.fadeOut(200, function() { + $thoughtPanel.fadeOut(200, function () { $icon.removeClass('rpg-hidden rpg-force-hide'); }); } }); // Icon click/tap to show panel - const handleThoughtIconTap = function(e) { + const handleThoughtIconTap = function (e) { const isMobileView = window.innerWidth <= 1000; const $icon = $('#rpg-thought-icon'); @@ -2303,7 +2304,7 @@ export function createThoughtPanel($message, thoughtsArray) { $thoughtIcon.on('click touchend', handleThoughtIconTap); // Add event handlers for editable thoughts in the bubble - $thoughtPanel.find('.rpg-editable').on('blur', function() { + $thoughtPanel.find('.rpg-editable').on('blur', function () { const character = $(this).data('character'); const field = $(this).data('field'); const value = $(this).text().trim(); @@ -2312,7 +2313,7 @@ export function createThoughtPanel($message, thoughtsArray) { }); // Add event listener for section lock icon clicks (support both click and touch) - $thoughtPanel.find('.rpg-section-lock-icon').on('click touchend', function(e) { + $thoughtPanel.find('.rpg-section-lock-icon').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); const $icon = $(this); @@ -2363,7 +2364,7 @@ export function createThoughtPanel($message, thoughtsArray) { // Position stays fixed at top-left // Remove panel when clicking outside (mobile only) - $(document).on('click.thoughtPanel', function(e) { + $(document).on('click.thoughtPanel', function (e) { // Only hide on click outside in mobile view if (window.innerWidth <= 1000) { if (!$(e.target).closest('#rpg-thought-panel, #rpg-thought-icon').length) { diff --git a/src/systems/rendering/userStats.js b/src/systems/rendering/userStats.js index 7706f7a..f44d9ac 100644 --- a/src/systems/rendering/userStats.js +++ b/src/systems/rendering/userStats.js @@ -12,6 +12,7 @@ import { $userStatsContainer, FALLBACK_AVATAR_DATA_URI } from '../../core/state.js'; +import { i18n } from '../../core/i18n.js'; import { saveSettings, saveChatData, @@ -273,7 +274,7 @@ export function renderUserStats() { // Check if stats bars section is locked const isStatsLocked = isItemLocked('userStats', 'stats'); const lockIcon = isStatsLocked ? '🔒' : '🔓'; - const lockTitle = isStatsLocked ? 'Locked - AI cannot change stats' : 'Unlocked - AI can change stats'; + const lockTitle = isStatsLocked ? i18n.getTranslation('userStats.statsLocked') : i18n.getTranslation('userStats.statsUnlocked'); const lockedClass = isStatsLocked ? ' locked' : ''; let html = '
'; @@ -286,8 +287,8 @@ export function renderUserStats() { ${userName} ${userName} ${showLevel ? `| - LVL - ${extensionSettings.level}` : ''} + ${i18n.getTranslation('userStats.level')} + ${extensionSettings.level}` : ''}
`; @@ -320,11 +321,11 @@ export function renderUserStats() { html += `
- ${stat.name}: + ${stat.name}:
- ${displayValue} + ${displayValue}
`; } @@ -334,7 +335,7 @@ export function renderUserStats() { if (config.statusSection.enabled) { const isMoodLocked = isItemLocked('userStats', 'status'); const moodLockIcon = isMoodLocked ? '🔒' : '🔓'; - const moodLockTitle = isMoodLocked ? 'Locked - AI cannot change mood' : 'Unlocked - AI can change mood'; + const moodLockTitle = isMoodLocked ? i18n.getTranslation('userStats.moodLocked') : i18n.getTranslation('userStats.moodUnlocked'); const moodLockedClass = isMoodLocked ? ' locked' : ''; html += '
'; if (showLockIcons) { @@ -342,7 +343,7 @@ export function renderUserStats() { } if (config.statusSection.showMoodEmoji) { - html += `
${stats.mood}
`; + html += `
${stats.mood}
`; } // Render custom status fields @@ -368,7 +369,7 @@ export function renderUserStats() { if (config.skillsSection.enabled) { const isSkillsLocked = isItemLocked('userStats', 'skills'); const skillsLockIcon = isSkillsLocked ? '🔒' : '🔓'; - const skillsLockTitle = isSkillsLocked ? 'Locked - AI cannot change skills' : 'Unlocked - AI can change skills'; + const skillsLockTitle = isSkillsLocked ? i18n.getTranslation('userStats.skillsLocked') : i18n.getTranslation('userStats.skillsUnlocked'); const skillsLockedClass = isSkillsLocked ? ' locked' : ''; let skillsValue = 'None'; // Handle JSON array format: [{name: "Art"}, {name: "Coding"}] @@ -385,7 +386,7 @@ export function renderUserStats() { } html += ` ${config.skillsSection.label}: -
${skillsValue}
+
${skillsValue}
`; } @@ -409,15 +410,15 @@ export function renderUserStats() { const enabledAttributes = rpgAttributes.filter(attr => attr && attr.enabled && attr.name && attr.id); if (enabledAttributes.length > 0) { - html += ` + html += `
`; - enabledAttributes.forEach(attr => { - const value = extensionSettings.classicStats[attr.id] !== undefined ? extensionSettings.classicStats[attr.id] : 10; - html += ` + enabledAttributes.forEach(attr => { + const value = extensionSettings.classicStats[attr.id] !== undefined ? extensionSettings.classicStats[attr.id] : 10; + html += `
${attr.name}
@@ -427,9 +428,9 @@ export function renderUserStats() {
`; - }); + }); - html += ` + html += `
@@ -448,7 +449,7 @@ export function renderUserStats() { // console.log('[RPG UserStats Render] ✓ HTML rendered to #rpg-user-stats container'); // Add event listeners for editable stat values - $('.rpg-editable-stat').on('blur', function() { + $('.rpg-editable-stat').on('blur', function () { const field = $(this).data('field'); const mode = $(this).data('mode'); const maxValue = parseInt($(this).data('max')) || 100; @@ -492,7 +493,7 @@ export function renderUserStats() { }); // Add event listeners for mood/conditions editing - $('.rpg-mood-emoji.rpg-editable').on('blur', function() { + $('.rpg-mood-emoji.rpg-editable').on('blur', function () { const value = $(this).text().trim(); extensionSettings.userStats.mood = value || '😐'; @@ -504,7 +505,7 @@ export function renderUserStats() { updateMessageSwipeData(); }); - $('.rpg-mood-conditions.rpg-editable').on('blur', function() { + $('.rpg-mood-conditions.rpg-editable').on('blur', function () { const value = $(this).text().trim(); const fieldKey = $(this).data('field'); extensionSettings.userStats[fieldKey] = value || 'None'; @@ -518,7 +519,7 @@ export function renderUserStats() { }); // Add event listener for skills editing - $('.rpg-skills-value.rpg-editable').on('blur', function() { + $('.rpg-skills-value.rpg-editable').on('blur', function () { const value = $(this).text().trim(); extensionSettings.userStats.skills = value || 'None'; @@ -531,7 +532,7 @@ export function renderUserStats() { }); // Add event listeners for stat name editing - $('.rpg-editable-stat-name').on('blur', function() { + $('.rpg-editable-stat-name').on('blur', function () { const field = $(this).data('field'); const value = $(this).text().trim().replace(':', ''); @@ -555,7 +556,7 @@ export function renderUserStats() { }); // Add event listener for level editing - $('.rpg-level-value.rpg-editable').on('blur', function() { + $('.rpg-level-value.rpg-editable').on('blur', function () { let value = parseInt($(this).text().trim()); if (isNaN(value) || value < 1) { value = 1; @@ -573,15 +574,15 @@ export function renderUserStats() { }); // Prevent line breaks in level field - $('.rpg-level-value.rpg-editable').on('keydown', function(e) { + $('.rpg-level-value.rpg-editable').on('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); $(this).blur(); } }); -// Add event listener for section lock icon clicks (support both click and touch) - $('.rpg-section-lock-icon').on('click touchend', function(e) { + // Add event listener for section lock icon clicks (support both click and touch) + $('.rpg-section-lock-icon').on('click touchend', function (e) { e.preventDefault(); e.stopPropagation(); const $icon = $(this); @@ -594,7 +595,7 @@ export function renderUserStats() { // Update icon const newIcon = !currentlyLocked ? '🔒' : '🔓'; - const newTitle = !currentlyLocked ? 'Locked - AI cannot change this section' : 'Unlocked - AI can change this section'; + const newTitle = !currentlyLocked ? i18n.getTranslation('infoBox.locked') : i18n.getTranslation('infoBox.unlocked'); $icon.text(newIcon); $icon.attr('title', newTitle); diff --git a/src/systems/ui/trackerEditor.js b/src/systems/ui/trackerEditor.js index 30b7cdc..90718ff 100644 --- a/src/systems/ui/trackerEditor.js +++ b/src/systems/ui/trackerEditor.js @@ -51,7 +51,7 @@ export function initTrackerEditor() { } // Tab switching - $(document).on('click', '.rpg-editor-tab', function() { + $(document).on('click', '.rpg-editor-tab', function () { $('.rpg-editor-tab').removeClass('active'); $(this).addClass('active'); @@ -61,51 +61,51 @@ export function initTrackerEditor() { }); // Save button - $(document).on('click', '#rpg-editor-save', function() { + $(document).on('click', '#rpg-editor-save', function () { applyTrackerConfig(); closeTrackerEditor(); }); // Cancel button - $(document).on('click', '#rpg-editor-cancel', function() { + $(document).on('click', '#rpg-editor-cancel', function () { closeTrackerEditor(); }); // Close X button - $(document).on('click', '#rpg-close-tracker-editor', function() { + $(document).on('click', '#rpg-close-tracker-editor', function () { closeTrackerEditor(); }); // Reset button - $(document).on('click', '#rpg-editor-reset', function() { + $(document).on('click', '#rpg-editor-reset', function () { resetToDefaults(); renderEditorUI(); }); // Close on background click - $(document).on('click', '#rpg-tracker-editor-popup', function(e) { + $(document).on('click', '#rpg-tracker-editor-popup', function (e) { if (e.target.id === 'rpg-tracker-editor-popup') { closeTrackerEditor(); } }); // Open button - $(document).on('click', '#rpg-open-tracker-editor', function() { + $(document).on('click', '#rpg-open-tracker-editor', function () { openTrackerEditor(); }); // Export button - $(document).on('click', '#rpg-editor-export', function() { + $(document).on('click', '#rpg-editor-export', function () { exportTrackerPreset(); }); // Import button - $(document).on('click', '#rpg-editor-import', function() { + $(document).on('click', '#rpg-editor-import', function () { importTrackerPreset(); }); // Preset select change - $(document).on('change', '#rpg-preset-select', function() { + $(document).on('change', '#rpg-preset-select', function () { const presetId = $(this).val(); if (presetId && presetId !== getActivePresetId()) { // Check if the current character had an association (either original or pending) @@ -139,7 +139,7 @@ export function initTrackerEditor() { }); // New preset button - $(document).on('click', '#rpg-preset-new', function() { + $(document).on('click', '#rpg-preset-new', function () { const name = prompt('Enter a name for the new preset:'); if (name && name.trim()) { const newId = createPreset(name.trim()); @@ -150,7 +150,7 @@ export function initTrackerEditor() { }); // Set as default preset button - $(document).on('click', '#rpg-preset-default', function() { + $(document).on('click', '#rpg-preset-default', function () { const currentPresetId = getActivePresetId(); if (currentPresetId) { setDefaultPreset(currentPresetId); @@ -161,7 +161,7 @@ export function initTrackerEditor() { }); // Delete preset button - $(document).on('click', '#rpg-preset-delete', function() { + $(document).on('click', '#rpg-preset-delete', function () { const currentPresetId = getActivePresetId(); const presets = getPresets(); if (Object.keys(presets).length <= 1) { @@ -180,7 +180,7 @@ export function initTrackerEditor() { }); // Associate preset checkbox - $(document).on('change', '#rpg-preset-associate', function() { + $(document).on('change', '#rpg-preset-associate', function () { const activePresetId = getActivePresetId(); const preset = getPreset(activePresetId); const entityName = getCurrentEntityName(); @@ -334,20 +334,20 @@ function resetToDefaults() { extensionSettings.trackerConfig = { userStats: { customStats: [ - { id: 'health', name: 'Health', enabled: true, persistInHistory: false }, - { id: 'satiety', name: 'Satiety', enabled: true, persistInHistory: false }, - { id: 'energy', name: 'Energy', enabled: true, persistInHistory: false }, - { id: 'hygiene', name: 'Hygiene', enabled: true, persistInHistory: false }, - { id: 'arousal', name: 'Arousal', enabled: true, persistInHistory: false } + { id: 'health', name: i18n.getTranslation('stats.health'), enabled: true, persistInHistory: false }, + { id: 'satiety', name: i18n.getTranslation('stats.satiety'), enabled: true, persistInHistory: false }, + { id: 'energy', name: i18n.getTranslation('stats.energy'), enabled: true, persistInHistory: false }, + { id: 'hygiene', name: i18n.getTranslation('stats.hygiene'), enabled: true, persistInHistory: false }, + { id: 'arousal', name: i18n.getTranslation('stats.arousal'), enabled: true, persistInHistory: false } ], showRPGAttributes: true, rpgAttributes: [ - { id: 'str', name: 'STR', enabled: true, persistInHistory: false }, - { id: 'dex', name: 'DEX', enabled: true, persistInHistory: false }, - { id: 'con', name: 'CON', enabled: true, persistInHistory: false }, - { id: 'int', name: 'INT', enabled: true, persistInHistory: false }, - { id: 'wis', name: 'WIS', enabled: true, persistInHistory: false }, - { id: 'cha', name: 'CHA', enabled: true, persistInHistory: false } + { id: 'str', name: i18n.getTranslation('stats.str'), enabled: true, persistInHistory: false }, + { id: 'dex', name: i18n.getTranslation('stats.dex'), enabled: true, persistInHistory: false }, + { id: 'con', name: i18n.getTranslation('stats.con'), enabled: true, persistInHistory: false }, + { id: 'int', name: i18n.getTranslation('stats.int'), enabled: true, persistInHistory: false }, + { id: 'wis', name: i18n.getTranslation('stats.wis'), enabled: true, persistInHistory: false }, + { id: 'cha', name: i18n.getTranslation('stats.cha'), enabled: true, persistInHistory: false } ], statusSection: { enabled: true, @@ -408,8 +408,8 @@ function resetToDefaults() { characterStats: { enabled: false, customStats: [ - { id: 'health', name: 'Health', enabled: true, colorLow: '#ff4444', colorHigh: '#44ff44' }, - { id: 'energy', name: 'Energy', enabled: true, colorLow: '#ffaa00', colorHigh: '#44ffff' } + { id: 'health', name: i18n.getTranslation('stats.health'), enabled: true, colorLow: '#ff4444', colorHigh: '#44ff44' }, + { id: 'energy', name: i18n.getTranslation('stats.energy'), enabled: true, colorLow: '#ffaa00', colorHigh: '#44ffff' } ] } } @@ -854,7 +854,7 @@ function renderUserStatsTab() { */ function setupUserStatsListeners() { // Add stat - $('#rpg-add-stat').off('click').on('click', function() { + $('#rpg-add-stat').off('click').on('click', function () { const newId = 'custom_' + Date.now(); extensionSettings.trackerConfig.userStats.customStats.push({ id: newId, @@ -870,39 +870,39 @@ function setupUserStatsListeners() { }); // Remove stat - $('.rpg-stat-remove').off('click').on('click', function() { + $('.rpg-stat-remove').off('click').on('click', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats.splice(index, 1); renderUserStatsTab(); }); // Toggle stat - $('.rpg-stat-toggle').off('change').on('change', function() { + $('.rpg-stat-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].enabled = $(this).is(':checked'); }); // Rename stat - $('.rpg-stat-name').off('blur').on('blur', function() { + $('.rpg-stat-name').off('blur').on('blur', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].name = $(this).val(); }); // Change stat max value - $('.rpg-stat-max').off('blur').on('blur', function() { + $('.rpg-stat-max').off('blur').on('blur', function () { const index = $(this).data('index'); const value = parseInt($(this).val()) || 100; extensionSettings.trackerConfig.userStats.customStats[index].maxValue = Math.max(1, value); }); // Stats display mode toggle - $('input[name="stats-display-mode"]').off('change').on('change', function() { + $('input[name="stats-display-mode"]').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.statsDisplayMode = $(this).val(); renderUserStatsTab(); // Re-render to show/hide max value fields }); // Add attribute - $('#rpg-add-attr').off('click').on('click', function() { + $('#rpg-add-attr').off('click').on('click', function () { // Ensure rpgAttributes array exists with defaults if needed if (!extensionSettings.trackerConfig.userStats.rpgAttributes || extensionSettings.trackerConfig.userStats.rpgAttributes.length === 0) { extensionSettings.trackerConfig.userStats.rpgAttributes = [ @@ -928,64 +928,64 @@ function setupUserStatsListeners() { }); // Remove attribute - $('.rpg-attr-remove').off('click').on('click', function() { + $('.rpg-attr-remove').off('click').on('click', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes.splice(index, 1); renderUserStatsTab(); }); // Toggle attribute - $('.rpg-attr-toggle').off('change').on('change', function() { + $('.rpg-attr-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes[index].enabled = $(this).is(':checked'); }); // Rename attribute - $('.rpg-attr-name').off('blur').on('blur', function() { + $('.rpg-attr-name').off('blur').on('blur', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes[index].name = $(this).val(); }); // Enable/disable RPG Attributes section toggle - $('#rpg-show-rpg-attrs').off('change').on('change', function() { + $('#rpg-show-rpg-attrs').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.showRPGAttributes = $(this).is(':checked'); }); // Show/hide level toggle - $('#rpg-show-level').off('change').on('change', function() { + $('#rpg-show-level').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.showLevel = $(this).is(':checked'); }); // Always send attributes toggle - $('#rpg-always-send-attrs').off('change').on('change', function() { + $('#rpg-always-send-attrs').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.alwaysSendAttributes = $(this).is(':checked'); }); // Status section toggles - $('#rpg-status-enabled').off('change').on('change', function() { + $('#rpg-status-enabled').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.statusSection.enabled = $(this).is(':checked'); }); - $('#rpg-mood-emoji').off('change').on('change', function() { + $('#rpg-mood-emoji').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.statusSection.showMoodEmoji = $(this).is(':checked'); }); - $('#rpg-status-fields').off('blur').on('blur', function() { + $('#rpg-status-fields').off('blur').on('blur', function () { const fields = $(this).val().split(',').map(f => f.trim()).filter(f => f); extensionSettings.trackerConfig.userStats.statusSection.customFields = fields; }); // Skills section toggles - $('#rpg-skills-enabled').off('change').on('change', function() { + $('#rpg-skills-enabled').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.skillsSection.enabled = $(this).is(':checked'); }); - $('#rpg-skills-label').off('blur').on('blur', function() { + $('#rpg-skills-label').off('blur').on('blur', function () { extensionSettings.trackerConfig.userStats.skillsSection.label = $(this).val(); saveSettings(); }); - $('#rpg-skills-fields').off('blur').on('blur', function() { + $('#rpg-skills-fields').off('blur').on('blur', function () { const fields = $(this).val().split(',').map(f => f.trim()).filter(f => f); extensionSettings.trackerConfig.userStats.skillsSection.customFields = fields; saveSettings(); @@ -1057,35 +1057,35 @@ function renderInfoBoxTab() { function setupInfoBoxListeners() { const widgets = extensionSettings.trackerConfig.infoBox.widgets; - $('#rpg-widget-date').off('change').on('change', function() { + $('#rpg-widget-date').off('change').on('change', function () { widgets.date.enabled = $(this).is(':checked'); }); - $('#rpg-date-format').off('change').on('change', function() { + $('#rpg-date-format').off('change').on('change', function () { widgets.date.format = $(this).val(); }); - $('#rpg-widget-weather').off('change').on('change', function() { + $('#rpg-widget-weather').off('change').on('change', function () { widgets.weather.enabled = $(this).is(':checked'); }); - $('#rpg-widget-temperature').off('change').on('change', function() { + $('#rpg-widget-temperature').off('change').on('change', function () { widgets.temperature.enabled = $(this).is(':checked'); }); - $('input[name="temp-unit"]').off('change').on('change', function() { + $('input[name="temp-unit"]').off('change').on('change', function () { widgets.temperature.unit = $(this).val(); }); - $('#rpg-widget-time').off('change').on('change', function() { + $('#rpg-widget-time').off('change').on('change', function () { widgets.time.enabled = $(this).is(':checked'); }); - $('#rpg-widget-location').off('change').on('change', function() { + $('#rpg-widget-location').off('change').on('change', function () { widgets.location.enabled = $(this).is(':checked'); }); - $('#rpg-widget-events').off('change').on('change', function() { + $('#rpg-widget-events').off('change').on('change', function () { widgets.recentEvents.enabled = $(this).is(':checked'); }); } @@ -1209,7 +1209,7 @@ function renderPresentCharactersTab() { */ function setupPresentCharactersListeners() { // Relationships enabled toggle - $('#rpg-relationships-enabled').off('change').on('change', function() { + $('#rpg-relationships-enabled').off('change').on('change', function () { if (!extensionSettings.trackerConfig.presentCharacters.relationships) { extensionSettings.trackerConfig.presentCharacters.relationships = { enabled: true, relationshipEmojis: {} }; } @@ -1217,7 +1217,7 @@ function setupPresentCharactersListeners() { }); // Add new relationship - $('#rpg-add-relationship').off('click').on('click', function() { + $('#rpg-add-relationship').off('click').on('click', function () { // Ensure relationships object exists if (!extensionSettings.trackerConfig.presentCharacters.relationships) { extensionSettings.trackerConfig.presentCharacters.relationships = { enabled: true, relationshipEmojis: {} }; @@ -1254,7 +1254,7 @@ function setupPresentCharactersListeners() { }); // Remove relationship - $('.rpg-remove-relationship').off('click').on('click', function() { + $('.rpg-remove-relationship').off('click').on('click', function () { const relationship = $(this).data('relationship'); // Remove from new structure @@ -1275,7 +1275,7 @@ function setupPresentCharactersListeners() { }); // Update relationship name - $('.rpg-relationship-name').off('blur').on('blur', function() { + $('.rpg-relationship-name').off('blur').on('blur', function () { const newName = $(this).val(); const $item = $(this).closest('.rpg-relationship-item'); const emoji = $item.find('.rpg-relationship-emoji').val(); @@ -1309,7 +1309,7 @@ function setupPresentCharactersListeners() { }); // Update relationship emoji - $('.rpg-relationship-emoji').off('blur').on('blur', function() { + $('.rpg-relationship-emoji').off('blur').on('blur', function () { const name = $(this).closest('.rpg-relationship-item').find('.rpg-relationship-name').val(); // Ensure structures exist @@ -1326,21 +1326,21 @@ function setupPresentCharactersListeners() { }); // Thoughts configuration - $('#rpg-thoughts-enabled').off('change').on('change', function() { + $('#rpg-thoughts-enabled').off('change').on('change', function () { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } extensionSettings.trackerConfig.presentCharacters.thoughts.enabled = $(this).is(':checked'); }); - $('#rpg-thoughts-name').off('blur').on('blur', function() { + $('#rpg-thoughts-name').off('blur').on('blur', function () { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } extensionSettings.trackerConfig.presentCharacters.thoughts.name = $(this).val(); }); - $('#rpg-thoughts-description').off('blur').on('blur', function() { + $('#rpg-thoughts-description').off('blur').on('blur', function () { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } @@ -1348,7 +1348,7 @@ function setupPresentCharactersListeners() { }); // Add field - $('#rpg-add-field').off('click').on('click', function() { + $('#rpg-add-field').off('click').on('click', function () { extensionSettings.trackerConfig.presentCharacters.customFields.push({ id: 'custom_' + Date.now(), name: 'New Field', @@ -1359,14 +1359,14 @@ function setupPresentCharactersListeners() { }); // Remove field - $('.rpg-field-remove').off('click').on('click', function() { + $('.rpg-field-remove').off('click').on('click', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields.splice(index, 1); renderPresentCharactersTab(); }); // Move field up - $('.rpg-field-move-up').off('click').on('click', function() { + $('.rpg-field-move-up').off('click').on('click', function () { const index = $(this).data('index'); if (index > 0) { const fields = extensionSettings.trackerConfig.presentCharacters.customFields; @@ -1376,7 +1376,7 @@ function setupPresentCharactersListeners() { }); // Move field down - $('.rpg-field-move-down').off('click').on('click', function() { + $('.rpg-field-move-down').off('click').on('click', function () { const index = $(this).data('index'); const fields = extensionSettings.trackerConfig.presentCharacters.customFields; if (index < fields.length - 1) { @@ -1386,25 +1386,25 @@ function setupPresentCharactersListeners() { }); // Toggle field - $('.rpg-field-toggle').off('change').on('change', function() { + $('.rpg-field-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].enabled = $(this).is(':checked'); }); // Rename field - $('.rpg-field-label').off('blur').on('blur', function() { + $('.rpg-field-label').off('blur').on('blur', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].name = $(this).val(); }); // Update description - $('.rpg-field-placeholder').off('blur').on('blur', function() { + $('.rpg-field-placeholder').off('blur').on('blur', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].description = $(this).val(); }); // Character stats toggle - $('#rpg-char-stats-enabled').off('change').on('change', function() { + $('#rpg-char-stats-enabled').off('change').on('change', function () { if (!extensionSettings.trackerConfig.presentCharacters.characterStats) { extensionSettings.trackerConfig.presentCharacters.characterStats = { enabled: false, customStats: [] }; } @@ -1412,7 +1412,7 @@ function setupPresentCharactersListeners() { }); // Add character stat - $('#rpg-add-char-stat').off('click').on('click', function() { + $('#rpg-add-char-stat').off('click').on('click', function () { if (!extensionSettings.trackerConfig.presentCharacters.characterStats) { extensionSettings.trackerConfig.presentCharacters.characterStats = { enabled: false, customStats: [] }; } @@ -1428,20 +1428,20 @@ function setupPresentCharactersListeners() { }); // Remove character stat - $('.rpg-char-stat-remove').off('click').on('click', function() { + $('.rpg-char-stat-remove').off('click').on('click', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats.splice(index, 1); renderPresentCharactersTab(); }); // Toggle character stat - $('.rpg-char-stat-toggle').off('change').on('change', function() { + $('.rpg-char-stat-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats[index].enabled = $(this).is(':checked'); }); // Rename character stat - $('.rpg-char-stat-label').off('blur').on('blur', function() { + $('.rpg-char-stat-label').off('blur').on('blur', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats[index].name = $(this).val(); }); @@ -1637,70 +1637,70 @@ function setupHistoryPersistenceListeners() { } // Main toggle - $('#rpg-history-persistence-enabled').off('change').on('change', function() { + $('#rpg-history-persistence-enabled').off('change').on('change', function () { extensionSettings.historyPersistence.enabled = $(this).is(':checked'); }); // Send All Enabled on Refresh toggle - $('#rpg-history-send-all-enabled').off('change').on('change', function() { + $('#rpg-history-send-all-enabled').off('change').on('change', function () { extensionSettings.historyPersistence.sendAllEnabledOnRefresh = $(this).is(':checked'); }); // Message count - $('#rpg-history-message-count').off('change').on('change', function() { + $('#rpg-history-message-count').off('change').on('change', function () { extensionSettings.historyPersistence.messageCount = parseInt($(this).val()) || 0; }); // Injection position - $('#rpg-history-injection-position').off('change').on('change', function() { + $('#rpg-history-injection-position').off('change').on('change', function () { extensionSettings.historyPersistence.injectionPosition = $(this).val(); }); // Context preamble - $('#rpg-history-context-preamble').off('blur').on('blur', function() { + $('#rpg-history-context-preamble').off('blur').on('blur', function () { extensionSettings.historyPersistence.contextPreamble = $(this).val(); }); // User Stats toggles - $('.rpg-history-stat-toggle').off('change').on('change', function() { + $('.rpg-history-stat-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].persistInHistory = $(this).is(':checked'); }); // Status section - $('#rpg-history-status').off('change').on('change', function() { + $('#rpg-history-status').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.statusSection.persistInHistory = $(this).is(':checked'); }); // Skills section - $('#rpg-history-skills').off('change').on('change', function() { + $('#rpg-history-skills').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.skillsSection.persistInHistory = $(this).is(':checked'); }); // Inventory - $('#rpg-history-inventory').off('change').on('change', function() { + $('#rpg-history-inventory').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.inventoryPersistInHistory = $(this).is(':checked'); }); // Quests - $('#rpg-history-quests').off('change').on('change', function() { + $('#rpg-history-quests').off('change').on('change', function () { extensionSettings.trackerConfig.userStats.questsPersistInHistory = $(this).is(':checked'); }); // Info Box widget toggles - $('.rpg-history-widget-toggle').off('change').on('change', function() { + $('.rpg-history-widget-toggle').off('change').on('change', function () { const widgetId = $(this).data('widget'); extensionSettings.trackerConfig.infoBox.widgets[widgetId].persistInHistory = $(this).is(':checked'); }); // Present Characters field toggles - $('.rpg-history-charfield-toggle').off('change').on('change', function() { + $('.rpg-history-charfield-toggle').off('change').on('change', function () { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].persistInHistory = $(this).is(':checked'); }); // Thoughts - $('#rpg-history-thoughts').off('change').on('change', function() { + $('#rpg-history-thoughts').off('change').on('change', function () { extensionSettings.trackerConfig.presentCharacters.thoughts.persistInHistory = $(this).is(':checked'); }); }