diff --git a/utils/__pycache__/presets_manager.cpython-312.pyc b/utils/__pycache__/presets_manager.cpython-312.pyc index e4f7197..9397162 100644 Binary files a/utils/__pycache__/presets_manager.cpython-312.pyc and b/utils/__pycache__/presets_manager.cpython-312.pyc differ diff --git a/utils/__pycache__/text_to_mic.cpython-312.pyc b/utils/__pycache__/text_to_mic.cpython-312.pyc index c75ce4a..097fc59 100644 Binary files a/utils/__pycache__/text_to_mic.cpython-312.pyc and b/utils/__pycache__/text_to_mic.cpython-312.pyc differ diff --git a/utils/__pycache__/tone_presets_manager.cpython-312.pyc b/utils/__pycache__/tone_presets_manager.cpython-312.pyc index 20403ef..bf32b75 100644 Binary files a/utils/__pycache__/tone_presets_manager.cpython-312.pyc and b/utils/__pycache__/tone_presets_manager.cpython-312.pyc differ diff --git a/utils/ai_editor_manager.py b/utils/ai_editor_manager.py index 926b1ee..f80b8c1 100644 --- a/utils/ai_editor_manager.py +++ b/utils/ai_editor_manager.py @@ -62,7 +62,7 @@ class AIEditorManager: # Prompt entry renamed to "Copy Editing Rules" with a Text area ttk.Label(main_frame, text="Copy Editing Rules:").grid(row=4, column=0, sticky=tk.NW, pady=2) - prompt_entry = tk.Text(main_frame, height=8, width=50) + prompt_entry = tk.Text(main_frame, height=8, width=50, background="white", font=("Arial", 10)) # Default prompt example if none exists default_prompt = "Edit the text provided to ensure it has a clear, professional tone. Fix any grammatical errors, improve sentence structure, and maintain consistent formatting. Make the language concise and impactful while preserving the original meaning. Make sure to edit text only and do not reply to it." diff --git a/utils/presets_manager.py b/utils/presets_manager.py index 6101925..bc0e83b 100644 --- a/utils/presets_manager.py +++ b/utils/presets_manager.py @@ -44,6 +44,8 @@ class PresetsManager: # Bind to window resize for responsive layout self.parent.bind("", self.on_window_resize) + # This resize timer helps batch updates during resizing + self.resize_timer = None def create_presets_section(self): """Create the presets section UI with accordion behavior.""" @@ -146,6 +148,9 @@ class PresetsManager: # Configure the scroll region to update when the frame changes self.presets_scrollable_frame.bind("", lambda e: self.presets_canvas.configure(scrollregion=self.presets_canvas.bbox("all"))) + + # Add a binding for the canvas size changes + self.presets_canvas.bind("", self.on_canvas_resize) # Populate tabs and presets self.populate_tabs() # Refresh tabs to show selection @@ -159,10 +164,9 @@ class PresetsManager: """Handler for window resize events to adjust the presets layout.""" # Only proceed if event is from the main window and presets are visible if event and event.widget == self.parent and not self.presets_collapsed: - # Schedule a refresh after a short delay to prevent excessive updates during resize - if hasattr(self, 'resize_timer') and self.resize_timer: - self.parent.after_cancel(self.resize_timer) - self.resize_timer = self.parent.after(100, self.refresh_presets_display) + # We don't need to immediately refresh here, as on_canvas_resize will handle it + # This is because the window resize will trigger canvas resize events + pass def _adjust_row_weights(self): """Adjust row weights to prioritize presets area expansion.""" @@ -313,14 +317,17 @@ class PresetsManager: canvas_width = self.presets_canvas.winfo_width() # Ensure we have a minimum width to calculate with if canvas_width < 50: # If the canvas is too narrow or not yet realized - canvas_width = self.parent.winfo_width() - 30 # Estimate canvas width - + canvas_width = self.parent.winfo_width() - 40 # Estimate canvas width with more margin for scrollbar + # Calculate number of columns (minimum 1, maximum 20) min_card_width = 140 # Minimum width for each card num_columns = max(1, min(20, canvas_width // min_card_width)) + # Log for debugging - can be removed in production + print(f"Canvas width: {canvas_width}, Columns: {num_columns}") + # Dynamically adjust card width based on available space - preset_width = max(min_card_width, canvas_width // num_columns - 8) + preset_width = max(min_card_width, (canvas_width // num_columns) - 10) # Slightly more padding preset_height = 100 # Configure columns to fill available space @@ -715,4 +722,15 @@ class PresetsManager: # Save and refresh self.debounced_save() - self.refresh_presets_display() \ No newline at end of file + self.refresh_presets_display() + + def on_canvas_resize(self, event=None): + """Handle resize events specifically for the presets canvas area.""" + # Only process if presets are visible + if not self.presets_collapsed: + # Cancel any previous refresh timer to avoid multiple refreshes + if hasattr(self, 'resize_timer') and self.resize_timer: + self.parent.after_cancel(self.resize_timer) + + # Schedule a refresh with a short delay to avoid excessive refreshes during drag + self.resize_timer = self.parent.after(150, self.refresh_presets_display) \ No newline at end of file diff --git a/utils/settings_manager.py b/utils/settings_manager.py index d6718ec..375f3c9 100644 --- a/utils/settings_manager.py +++ b/utils/settings_manager.py @@ -87,7 +87,7 @@ class SettingsManager: settings_file = cls.get_settings_file_path() with open(settings_file, "w") as f: - json.dump(settings, f) + json.dump(settings, f, indent=4) @classmethod def update_settings(cls, partial_settings): diff --git a/utils/text_to_mic.py b/utils/text_to_mic.py index 67a9639..1d403f1 100644 --- a/utils/text_to_mic.py +++ b/utils/text_to_mic.py @@ -40,7 +40,7 @@ class TextToMic(tk.Tk): def __init__(self): super().__init__() - self.version = "1.3.5" + self.version = "1.3.0" self.title(f"Text to Mic by Scorchsoft.com - v{self.version}") @@ -57,7 +57,7 @@ class TextToMic(tk.Tk): # Fixed window dimensions for all states - DEFINED ONCE as class constants # These are the ONLY values that should be used throughout the application - self.BASE_WIDTH = 590 + self.BASE_WIDTH = 600 self.BASE_HEIGHT_WITH_BANNER = 860 self.BASE_HEIGHT_NO_BANNER = 700 self.COLLAPSED_HEIGHT_WITH_BANNER = 630 @@ -188,45 +188,59 @@ class TextToMic(tk.Tk): self.menubar = Menu(self) self.config(menu=self.menubar) + # Get current hotkey settings + settings = self.load_settings() + hotkey_manager = self.hotkey_manager if hasattr(self, 'hotkey_manager') else None + + # Format hotkeys for display in menus + if hotkey_manager: + replay_shortcut = hotkey_manager.format_shortcut(settings["hotkeys"]["play_last_audio"]) + record_shortcut = hotkey_manager.format_shortcut(settings["hotkeys"]["record_start_stop"]) + stop_shortcut = hotkey_manager.format_shortcut(settings["hotkeys"]["stop_recording"]) + cancel_shortcut = hotkey_manager.format_shortcut(settings["hotkeys"]["cancel_operation"]) + else: + # Default values if hotkey_manager isn't available + replay_shortcut = "Ctrl+Shift+8" + record_shortcut = "Ctrl+Shift+0" + stop_shortcut = "Ctrl+Shift+9" + cancel_shortcut = "Ctrl+Shift+1" + # File or settings menu settings_menu = Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Settings", menu=settings_menu) - settings_menu.add_command(label="Change API Key", command=self.change_api_key) - settings_menu.add_command(label="AI Copy Editing", command=self.show_ai_editor_settings) - settings_menu.add_command(label="Hotkey Settings", command=self.show_hotkey_settings) - settings_menu.add_command(label="Manage Tone Presets", command=self.show_tone_presets_manager) + settings_menu.add_command(label="API Key", command=self.change_api_key) + settings_menu.add_command(label="AI Copyediting", command=self.show_ai_editor_settings) + settings_menu.add_command(label="Keyboard Shortcuts", command=self.show_hotkey_settings) + settings_menu.add_command(label="Manage Tones", command=self.show_tone_presets_manager) + settings_menu.add_separator() settings_menu.add_checkbutton(label="Auto Check for Updates", variable=self.auto_check_version, command=self.toggle_auto_version_check) + settings_menu.add_checkbutton(label="Hide Scorchsoft Banner", variable=self.banner_var, command=self.toggle_banner) # Playback menu playback_menu = Menu(self.menubar, tearoff=0) - self.menubar.add_cascade(label="Playback", menu=playback_menu) - playback_menu.add_command(label="Play Last Audio", command=self.play_last_audio) - - #apply_ai - input_menu = Menu(self.menubar, tearoff=0) - self.menubar.add_cascade(label="Input", menu=input_menu) - input_menu.add_command(label="Apply AI Manipulation to Input Text", command=self.apply_ai_to_input) - + self.menubar.add_cascade(label="Actions", menu=playback_menu) + + # Add keyboard shortcuts to menu items + playback_menu.add_command(label=f"Replay [{replay_shortcut}]", command=self.play_last_audio) + playback_menu.add_command(label="Apply AI Copyedit", command=self.apply_ai_to_input) + playback_menu.add_separator() + playback_menu.add_command(label=f"Start/Stop Recording [{record_shortcut}]", command=self.handle_record_button_click) + playback_menu.add_command(label=f"Stop Recording [{stop_shortcut}]", command=lambda: self.stop_recording(auto_play=False)) + playback_menu.add_command(label=f"Cancel Operation [{cancel_shortcut}]", command=self.stop_playback) # Help menu help_menu = Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Help", menu=help_menu) help_menu.add_command(label="Check Version", command=self.check_version) - help_menu.add_command(label="How to Use", command=self.show_instructions) help_menu.add_command(label="Terms of Use and Licence", command=self.show_terms_of_use) - help_menu.add_command(label="Hotkey Instructions", command=self.show_hotkey_instructions) - # Add toggle for banner visibility - use the existing banner_var from __init__ - help_menu.add_checkbutton(label="Hide Banner", variable=self.banner_var, command=self.toggle_banner) + def show_hotkey_settings(self): """Show the hotkey settings dialog.""" HotkeyManager.hotkey_settings_dialog(self) - def show_hotkey_instructions(self): - """Show hotkey instructions.""" - HotkeyManager.show_hotkey_instructions(self) def change_api_key(self): """Change the API key using APIKeyManager.""" @@ -401,12 +415,12 @@ class TextToMic(tk.Tk): self.text_input = tk.Text(main_frame, height=5, width=68) # Use white background for text input instead of the system background color text_color = self.style.lookup('TLabel', 'foreground') - self.text_input.configure(bg="white", fg=text_color, insertbackground=text_color, wrap=tk.WORD) + self.text_input.configure(bg="white", fg=text_color, insertbackground=text_color, wrap=tk.WORD, font=("Arial", 10)) self.text_input.grid(column=0, row=5, columnspan=2, pady=(0, 20), sticky="nsew") # Proper spacing # Add a status frame at the bottom of the text input with white background status_frame = ttk.Frame(main_frame, style='White.TFrame') - status_frame.grid(column=0, row=5, columnspan=2, sticky=(tk.S, tk.E), pady=(0, 25)) + status_frame.grid(column=0, row=5, columnspan=2, sticky=(tk.S, tk.E), pady=(0, 25), padx=(0, 5)) # Add right padding to shift the frame inward # Create a custom style for the white frame self.style.configure('White.TFrame', background='white') @@ -1029,6 +1043,7 @@ class TextToMic(tk.Tk): record_shortcut = "+".join(filter(None, settings["hotkeys"]["record_start_stop"])) play_shortcut = "+".join(filter(None, settings["hotkeys"]["play_last_audio"])) stop_shortcut = "+".join(filter(None, settings["hotkeys"]["stop_recording"])) + cancel_shortcut = "+".join(filter(None, settings["hotkeys"]["cancel_operation"])) # Update CTkButton for recording state, keeping shortcuts visible self.record_button.configure(text=f"Stop and Insert", fg_color="#d32f2f") diff --git a/utils/tone_presets_manager.py b/utils/tone_presets_manager.py index 1b992bf..ea0c429 100644 --- a/utils/tone_presets_manager.py +++ b/utils/tone_presets_manager.py @@ -103,7 +103,7 @@ class TonePresetsManager: # Listbox for tones with scrollbar self.tone_list = tk.Listbox(select_frame, height=8, selectmode=tk.BROWSE, - bg="#f0f0f0", fg="#333333", + bg="#ffffff", fg="#333333", selectbackground="#0078d7", selectforeground="#ffffff", font=("Arial", 10)) tone_scrollbar = ttk.Scrollbar(select_frame, orient=tk.VERTICAL, command=self.tone_list.yview) @@ -168,7 +168,7 @@ class TonePresetsManager: # Create the text widget with word wrap and vertical scrollbar self.content_text = tk.Text(text_frame, wrap=tk.WORD, yscrollcommand=v_scrollbar.set, - bg="#f0f0f0", fg="#333333", + bg="#ffffff", fg="#333333", font=("Arial", 10), relief="solid", borderwidth=1) self.content_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) diff --git a/utils/version_checker.py b/utils/version_checker.py index 12ec011..f6ec522 100644 --- a/utils/version_checker.py +++ b/utils/version_checker.py @@ -68,22 +68,28 @@ class VersionChecker: f"Could not check for updates. Server returned status code: {response.status_code}" )) except requests.RequestException as e: + error_message = f"Could not connect to update server: {str(e)}" + print(f"Version check error: {error_message}") if show_result: - self.app.after(0, lambda: messagebox.showwarning( + self.app.after(0, lambda msg=error_message: messagebox.showwarning( "Version Check Failed", - f"Could not connect to update server: {str(e)}" + msg )) - except json.JSONDecodeError: + except json.JSONDecodeError as e: + error_message = "Invalid update information received." + print(f"Version check error: {error_message}") if show_result: - self.app.after(0, lambda: messagebox.showwarning( + self.app.after(0, lambda msg=error_message: messagebox.showwarning( "Version Check Failed", - "Invalid update information received." + msg )) except Exception as e: + error_message = f"Could not check for updates: {str(e)}" + print(f"Version check error: {error_message}") if show_result: - self.app.after(0, lambda: messagebox.showwarning( + self.app.after(0, lambda msg=error_message: messagebox.showwarning( "Version Check Failed", - f"Could not check for updates: {str(e)}" + msg )) def show_update_notification(self, latest_version, download_url, message):