From 44c21e68d8f1e788b7bdbbed96af6fad0e0cf2e4 Mon Sep 17 00:00:00 2001 From: jknapp Date: Tue, 6 Jan 2026 11:26:29 -0800 Subject: [PATCH] Release v1.0.0 with UI improvements and bug fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix window restore when clicking Show from system tray or double-clicking - Add Copy button for web interface URL - Fix image loading when app starts with Windows startup - Remove debug print statements - Bump version to 1.0.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- config.py | 2 +- gui/main_window.py | 50 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/config.py b/config.py index e9b851a..b73ab9e 100644 --- a/config.py +++ b/config.py @@ -1,6 +1,6 @@ # Configuration and constants for MacroPad Server -VERSION = "0.9.5" +VERSION = "1.0.0" DEFAULT_PORT = 40000 SETTINGS_FILE = "settings.json" diff --git a/gui/main_window.py b/gui/main_window.py index d75d33e..01b3d4a 100644 --- a/gui/main_window.py +++ b/gui/main_window.py @@ -40,7 +40,7 @@ class MacroButton(QPushButton): edit_requested = Signal(str) delete_requested = Signal(str) - def __init__(self, macro_id: str, macro: dict, parent=None): + def __init__(self, macro_id: str, macro: dict, app_dir: str, parent=None): super().__init__(parent) self.macro_id = macro_id self.macro = macro @@ -75,7 +75,9 @@ class MacroButton(QPushButton): image_label.setAlignment(Qt.AlignCenter) if macro.get("image_path"): - pixmap = QPixmap(macro["image_path"]) + # Resolve relative image path against app directory + image_path = os.path.join(app_dir, macro["image_path"]) + pixmap = QPixmap(image_path) if not pixmap.isNull(): pixmap = pixmap.scaled(48, 48, Qt.KeepAspectRatio, Qt.SmoothTransformation) image_label.setPixmap(pixmap) @@ -210,12 +212,28 @@ class MainWindow(QMainWindow): toolbar_layout.addStretch() - # IP address label + # IP address label with copy button self.ip_label = QLabel() self.ip_label.setStyleSheet(f"color: {THEME['fg_color']};") self.update_ip_label() toolbar_layout.addWidget(self.ip_label) + copy_btn = QPushButton("Copy") + copy_btn.setStyleSheet(f""" + QPushButton {{ + background-color: {THEME['button_bg']}; + color: white; + border: none; + padding: 8px 12px; + border-radius: 4px; + }} + QPushButton:hover {{ + background-color: {THEME['highlight_color']}; + }} + """) + copy_btn.clicked.connect(self.copy_url_to_clipboard) + toolbar_layout.addWidget(copy_btn) + qr_btn = QPushButton("QR Code") qr_btn.setStyleSheet(f""" QPushButton {{ @@ -358,7 +376,7 @@ class MainWindow(QMainWindow): # Tray menu tray_menu = QMenu() show_action = tray_menu.addAction("Show") - show_action.triggered.connect(self.show) + show_action.triggered.connect(self.restore_window) quit_action = tray_menu.addAction("Quit") quit_action.triggered.connect(self.close) @@ -369,8 +387,15 @@ class MainWindow(QMainWindow): def on_tray_activated(self, reason): """Handle tray icon activation.""" if reason == QSystemTrayIcon.ActivationReason.DoubleClick: - self.show() - self.activateWindow() + self.restore_window() + + def restore_window(self): + """Restore and bring window to front.""" + # Clear minimized state and show + self.setWindowState(Qt.WindowNoState) + self.show() + self.raise_() + self.activateWindow() def start_server(self): """Start the web server in a background thread.""" @@ -422,7 +447,6 @@ class MainWindow(QMainWindow): # Check if relay is connected and has a session ID relay_connected = self.relay_client and self.relay_client.is_connected() session_id = self.settings_manager.get_relay_session_id() - print(f"[DEBUG] update_ip_label: relay_connected={relay_connected}, session_id={session_id}") if relay_connected and session_id: relay_url = self.settings_manager.get_relay_url() @@ -430,7 +454,6 @@ class MainWindow(QMainWindow): base_url = relay_url.replace('wss://', 'https://').replace('ws://', 'http://') base_url = base_url.replace('/desktop', '').rstrip('/') full_url = f"{base_url}/{session_id}" - print(f"[DEBUG] Setting relay URL: {full_url}") self.ip_label.setText(full_url) return @@ -449,6 +472,13 @@ class MainWindow(QMainWindow): pass self.ip_label.setText(f"http://localhost:{DEFAULT_PORT}") + def copy_url_to_clipboard(self): + """Copy the web interface URL to clipboard.""" + url = self.ip_label.text() + clipboard = QApplication.clipboard() + clipboard.setText(url) + self.status_bar.showMessage("URL copied to clipboard", 2000) + def refresh_tabs(self): """Refresh the tab widget.""" self.tab_widget.blockSignals(True) @@ -499,7 +529,7 @@ class MainWindow(QMainWindow): # Add macro buttons cols = max(1, (self.width() - 40) // 130) for i, (macro_id, macro) in enumerate(filtered): - btn = MacroButton(macro_id, macro) + btn = MacroButton(macro_id, macro, self.app_dir) btn.clicked.connect(lambda checked, mid=macro_id: self.execute_macro(mid)) btn.edit_requested.connect(self.edit_macro) btn.delete_requested.connect(self.delete_macro) @@ -675,12 +705,10 @@ class MainWindow(QMainWindow): def on_relay_session_id(self, session_id: str): """Handle receiving session ID from relay (called from background thread).""" - print(f"[DEBUG] on_relay_session_id called with: {session_id}") self.relay_session_received.emit(session_id) def _handle_relay_session(self, session_id: str): """Handle relay session on main thread.""" - print(f"[DEBUG] _handle_relay_session on main thread: {session_id}") self.settings_manager.set_relay_session_id(session_id) self.update_ip_label()