diff --git a/src/cthulhu/cthulhu_gui_prefs.py b/src/cthulhu/cthulhu_gui_prefs.py index 2771d10..078f50e 100644 --- a/src/cthulhu/cthulhu_gui_prefs.py +++ b/src/cthulhu/cthulhu_gui_prefs.py @@ -2563,22 +2563,20 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): self._updateAIControlsState(enabled) def _updateAIControlsState(self, enabled): - """Enable or disable AI controls based on AI enabled state.""" - self.aiProviderCombo.set_sensitive(enabled) - self.aiApiKeyEntry.set_sensitive(enabled) - self.aiOllamaModelEntry.set_sensitive(enabled) - self.aiOllamaEndpointEntry.set_sensitive(enabled) - self.aiConfirmationCheckButton.set_sensitive(enabled) - self.aiScreenshotQualityCombo.set_sensitive(enabled) + """Refresh AI controls while keeping configuration fields editable.""" + _ = enabled # kept for signal/call compatibility + # Keep settings editable even when AI assistant is disabled so users + # can configure providers/keys before enabling it. + self.aiProviderCombo.set_sensitive(True) + self.aiConfirmationCheckButton.set_sensitive(True) + self.aiScreenshotQualityCombo.set_sensitive(True) try: - self.get_widget("aiGetClaudeKeyButton").set_sensitive(enabled) + self.get_widget("aiGetClaudeKeyButton").set_sensitive(True) except: pass # Button might not exist in older UI files - - # Update provider-specific controls if AI is enabled - if enabled: - current_provider = self.prefsDict.get("aiProvider", settings.aiProvider) - self._updateProviderControls(current_provider) + + current_provider = self.prefsDict.get("aiProvider", settings.aiProvider) + self._updateProviderControls(current_provider) def _initIndentationState(self): """Initialize Indentation widgets with current settings.""" diff --git a/src/cthulhu/plugins/AIAssistant/plugin.py b/src/cthulhu/plugins/AIAssistant/plugin.py index f471ca9..6a64018 100644 --- a/src/cthulhu/plugins/AIAssistant/plugin.py +++ b/src/cthulhu/plugins/AIAssistant/plugin.py @@ -325,7 +325,6 @@ class AIAssistant(Plugin): if not self._prefs_widgets: return - enabled = self._prefs_widgets["enable_check"].get_active() provider_values = self._prefs_widgets.get("provider_values", []) provider_index = self._prefs_widgets["provider_combo"].get_active() provider = provider_values[provider_index] if 0 <= provider_index < len(provider_values) else settings.aiProvider @@ -333,12 +332,14 @@ class AIAssistant(Plugin): is_gemini = provider == settings.AI_PROVIDER_GEMINI is_ollama = provider == settings.AI_PROVIDER_OLLAMA - self._prefs_widgets["provider_combo"].set_sensitive(enabled) - self._prefs_widgets["api_key_entry"].set_sensitive(enabled and is_gemini) - self._prefs_widgets["ollama_model_entry"].set_sensitive(enabled and is_ollama) - self._prefs_widgets["ollama_endpoint_entry"].set_sensitive(enabled and is_ollama) - self._prefs_widgets["confirmation_check"].set_sensitive(enabled) - self._prefs_widgets["quality_combo"].set_sensitive(enabled) + # Keep preferences editable even when the feature is disabled so users + # can prepare configuration before turning AI assistant on. + self._prefs_widgets["provider_combo"].set_sensitive(True) + self._prefs_widgets["api_key_entry"].set_sensitive(is_gemini) + self._prefs_widgets["ollama_model_entry"].set_sensitive(is_ollama) + self._prefs_widgets["ollama_endpoint_entry"].set_sensitive(is_ollama) + self._prefs_widgets["confirmation_check"].set_sensitive(True) + self._prefs_widgets["quality_combo"].set_sensitive(True) def refresh_settings(self): """Refresh plugin settings and reinitialize provider. Called when settings change.""" diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 16b6444..857711b 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -1967,6 +1967,15 @@ class Script(script.Script): debug.printMessage(debug.LEVEL_INFO, msg, True) return + # Some toolkits emit transient window:deactivate/window:activate pairs + # while the same window remains active. Treat those as noise so we do + # not clear state and force a full script/settings reactivation. + AXObject.clear_cache(event.source) + if AXUtilities.is_active(event.source): + msg = "DEFAULT: Ignoring event. Source window still active." + debug.printMessage(debug.LEVEL_INFO, msg, True) + return + if self.flatReviewPresenter.is_active(): self.flatReviewPresenter.quit() diff --git a/src/cthulhu/scripts/web/speech_generator.py b/src/cthulhu/scripts/web/speech_generator.py index 11b57f2..8048c4b 100644 --- a/src/cthulhu/scripts/web/speech_generator.py +++ b/src/cthulhu/scripts/web/speech_generator.py @@ -572,7 +572,9 @@ class SpeechGenerator(speech_generator.SpeechGenerator): and soundEnabled: roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role) if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \ - and soundEnabled and roleSoundIcon: + and soundEnabled: + # Stateful controls present their role via state sounds; suppress + # spoken role names here even if no dedicated role icon exists. if AXUtilities.is_check_box(obj) \ or AXUtilities.is_check_menu_item(obj) \ or AXUtilities.is_radio_button(obj) \ diff --git a/src/cthulhu/speech_generator.py b/src/cthulhu/speech_generator.py index 9b96d79..7885c7d 100644 --- a/src/cthulhu/speech_generator.py +++ b/src/cthulhu/speech_generator.py @@ -678,7 +678,9 @@ class SpeechGenerator(generator.Generator): and soundEnabled: roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role) if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \ - and soundEnabled and roleSoundIcon: + and soundEnabled: + # Stateful controls present their role via state sounds; suppress + # spoken role names here even if no dedicated role icon exists. if AXUtilities.is_check_box(obj) \ or AXUtilities.is_check_menu_item(obj) \ or AXUtilities.is_radio_button(obj) \