# WebDrop Bridge - Professional Development Plan **Version**: 1.0 **Last Updated**: January 2026 **Status**: Pre-Release Development ## Executive Summary This document outlines the development roadmap for WebDrop Bridge, a professional Qt-based desktop application that converts web-based drag-and-drop text paths into native file operations for seamless integration with professional desktop software (InDesign, Word, Notepad++, etc.). ### Key Differences from PoC | Aspect | PoC | Production | |--------|-----|-----------| | **Structure** | Monolithic | Modular, scalable | | **Testing** | Ad-hoc | Comprehensive (unit/integration/e2e) | | **Documentation** | Minimal | Full API docs, user guides | | **Error Handling** | Basic | Robust with recovery | | **Logging** | Console only | File + structured logging | | **Security** | Basic validation | Enhanced, configurable | | **Distribution** | Source code | MSI (Windows), DMG (macOS) | | **CI/CD** | Manual | Automated GitHub Actions | | **Versioning** | Single version | Semantic versioning, auto-updates | --- ## Phase 1: Foundation (Weeks 1-4) ### 1.1 Core Architecture Refinement **Objectives:** - Refactor PoC code into production-quality modules - Implement proper logging and error handling - Create configuration management system **Tasks:** #### 1.1.1 Configuration System (`src/webdrop_bridge/config.py`) ```python from dataclasses import dataclass from pathlib import Path from typing import List import os from dotenv import load_dotenv @dataclass class Config: """Application configuration.""" app_name: str app_version: str log_level: str allowed_roots: List[Path] allowed_urls: List[str] # Empty = no restriction, non-empty = Kiosk-mode webapp_url: str window_width: int window_height: int enable_logging: bool @classmethod def from_env(cls): """Load configuration from environment.""" load_dotenv() allowed_roots_str = os.getenv("ALLOWED_ROOTS", "Z:/,C:/Users/Public") allowed_roots = [Path(p.strip()) for p in allowed_roots_str.split(",")] # URL whitelist (empty = no restriction) allowed_urls_str = os.getenv("ALLOWED_URLS", "") allowed_urls = [url.strip() for url in allowed_urls_str.split(",") if url.strip()] return cls( app_name=os.getenv("APP_NAME", "WebDrop Bridge"), app_version=os.getenv("APP_VERSION", "1.0.0"), log_level=os.getenv("LOG_LEVEL", "INFO"), allowed_roots=allowed_roots, allowed_urls=allowed_urls, webapp_url=os.getenv("WEBAPP_URL", "file:///./webapp/index.html"), window_width=int(os.getenv("WINDOW_WIDTH", "1024")), window_height=int(os.getenv("WINDOW_HEIGHT", "768")), enable_logging=os.getenv("ENABLE_LOGGING", "true").lower() == "true", ) ``` **Deliverables:** - [x] `src/webdrop_bridge/config.py` - Configuration management - [x] `.env.example` - Environment template - [x] Validation for all config parameters **Configuration Variables:** - `ALLOWED_ROOTS` - Comma-separated file paths (drag-drop whitelist) - `ALLOWED_URLS` - Comma-separated URL patterns for Kiosk-mode (empty = unrestricted) - Examples: `localhost,127.0.0.1,*.example.com` - Supports wildcards: `*.domain.com` - File URLs always allowed: `file://` **Acceptance Criteria:** - Config loads from `.env` file - All environment variables have sensible defaults - Invalid values raise `ConfigurationError` --- #### 1.1.2 Logging System (`src/webdrop_bridge/utils/logging.py`) ```python import logging from pathlib import Path from typing import Optional def setup_logging( level: str = "INFO", log_file: Optional[Path] = None, format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) -> logging.Logger: """Configure application-wide logging.""" logger = logging.getLogger("webdrop_bridge") logger.setLevel(getattr(logging, level)) # Console handler console = logging.StreamHandler() console.setFormatter(logging.Formatter(format)) logger.addHandler(console) # File handler (if enabled) if log_file: log_file.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(log_file) file_handler.setFormatter(logging.Formatter(format)) logger.addHandler(file_handler) return logger ``` **Deliverables:** - [ ] `src/webdrop_bridge/utils/logging.py` - Logging utilities - [ ] Logs directory with `.gitkeep` - [ ] Log rotation policy **Acceptance Criteria:** - Logs written to `logs/webdrop_bridge.log` - Console output matches log file - Log level configurable via environment --- ### 1.2 Drag Interceptor Component **Objectives:** - Implement robust drag-and-drop interception - Validate paths against whitelist - Create MimeData with proper file URLs **Tasks:** #### 1.2.1 Path Validator (`src/webdrop_bridge/core/validator.py`) ```python from pathlib import Path from typing import List class PathValidator: """Validates file paths against security policies.""" def __init__(self, allowed_roots: List[Path]): self.allowed_roots = [p.resolve() for p in allowed_roots] def is_allowed(self, path: Path) -> bool: """Check if path is within allowed roots.""" try: resolved = path.resolve() return any( self._is_relative_to(resolved, root) for root in self.allowed_roots ) except (ValueError, OSError): return False def is_valid_file(self, path: Path) -> bool: """Check if path is allowed and exists as file.""" return self.is_allowed(path) and path.exists() and path.is_file() @staticmethod def _is_relative_to(path: Path, other: Path) -> bool: """Backcompat for Path.is_relative_to (Python 3.9+).""" try: path.relative_to(other) return True except ValueError: return False ``` **Deliverables:** - [ ] `src/webdrop_bridge/core/validator.py` - Path validation - [ ] Unit tests for `PathValidator` - [ ] Security documentation **Acceptance Criteria:** - All paths resolved to absolute - Whitelist enforcement working - Symlink handling documented --- #### 1.2.2 Drag Interceptor Widget (`src/webdrop_bridge/core/drag_interceptor.py`) ```python from PySide6.QtCore import Qt, QUrl, QMimeData, pyqtSignal from PySide6.QtGui import QDrag from PySide6.QtWidgets import QWidget from pathlib import Path import logging logger = logging.getLogger(__name__) class DragInterceptor(QWidget): """Intercepts and converts text drags to file drags.""" file_dropped = pyqtSignal(Path) def __init__(self, validator, parent=None): super().__init__(parent) self.validator = validator self.setAcceptDrops(True) def dragEnterEvent(self, event): """Handle drag enter.""" if event.mimeData().hasText(): text = event.mimeData().text().strip() path = Path(text.replace("\\", "/")) if self.validator.is_valid_file(path): event.acceptProposedAction() logger.debug(f"Drag accepted: {path}") self._start_file_drag(path) else: event.ignore() logger.warning(f"Invalid path rejected: {text}") def _start_file_drag(self, path: Path): """Convert to native file drag.""" mime_data = QMimeData() url = QUrl.fromLocalFile(str(path)) mime_data.setUrls([url]) drag = QDrag(self) drag.setMimeData(mime_data) # Use move for typical file operations drag.exec(Qt.MoveAction | Qt.CopyAction) self.file_dropped.emit(path) logger.info(f"File dragged: {path}") ``` **Deliverables:** - [ ] `src/webdrop_bridge/core/drag_interceptor.py` - Drag handling - [ ] Unit tests with mocking - [ ] Platform-specific tests (Windows/macOS) **Acceptance Criteria:** - Drag events properly intercepted - File URLs created correctly - Signals emit appropriately - Cross-platform compatibility verified --- ### 1.3 Main Application Window **Tasks:** #### 1.3.1 Main Window (`src/webdrop_bridge/ui/main_window.py`) **Features:** - Qt QMainWindow with WebEngine integration - Navigation toolbar (Home, Back, Forward, Refresh) for Kiosk-mode - URL whitelist enforcement for restricted browsing - Drag-and-drop file path integration ```python from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QToolBar from PySide6.QtCore import QUrl, QSize, Qt import logging logger = logging.getLogger(__name__) class MainWindow(QMainWindow): """Application main window with restricted browsing.""" def __init__(self, config): super().__init__() self.config = config self.setWindowTitle(config.app_name) self.setGeometry(100, 100, config.window_width, config.window_height) # Create restricted web engine view (with URL whitelist) from webdrop_bridge.ui.restricted_web_view import RestrictedWebEngineView self.web_view = RestrictedWebEngineView(config.allowed_urls) self._create_navigation_toolbar() # Set as central widget self.setCentralWidget(self.web_view) logger.info(f"Loading webapp from: {config.webapp_url}") self.web_view.load(QUrl(config.webapp_url)) def _create_navigation_toolbar(self): """Create Kiosk-mode navigation toolbar.""" toolbar = QToolBar("Navigation") toolbar.setMovable(False) toolbar.setIconSize(QSize(24, 24)) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, toolbar) # Add Back, Forward, Home, Refresh buttons toolbar.addAction(self.web_view.pageAction(self.web_view.WebAction.Back)) toolbar.addAction(self.web_view.pageAction(self.web_view.WebAction.Forward)) toolbar.addSeparator() home_action = toolbar.addAction("Home") home_action.triggered.connect(self._navigate_home) toolbar.addAction(self.web_view.pageAction(self.web_view.WebAction.Reload)) def _navigate_home(self): """Navigate to home (startup) URL.""" self.web_view.load(QUrl(self.config.webapp_url)) ``` **URL Whitelist (Kiosk-Mode):** When `ALLOWED_URLS` is configured, the application enforces strict browsing: - ✅ Whitelisted URLs load in the WebView - ❌ Non-whitelisted URLs open in system default browser - ✅ File URLs (`file://`) always allowed - ✅ Empty whitelist = unrestricted (all URLs allowed) **Deliverables:** - [x] `src/webdrop_bridge/ui/main_window.py` - [x] `src/webdrop_bridge/ui/restricted_web_view.py` - URL whitelist enforcement - [x] UI tests **Acceptance Criteria:** - Window opens with correct title - WebEngine loads correctly - Toolbar displays with Home, Back, Forward, Refresh buttons - URL whitelist enforces restrictions - Non-whitelisted URLs open in system browser --- #### 1.3.2 Restricted Web View (`src/webdrop_bridge/ui/restricted_web_view.py`) **Purpose:** Enforce URL whitelist for Kiosk-mode browsing security. ```python from PySide6.QtWebEngineWidgets import QWebEngineView from PySide6.QtGui import QDesktopServices import fnmatch class RestrictedWebEngineView(QWebEngineView): """Web view with URL whitelist enforcement.""" def __init__(self, allowed_urls: List[str] = None): super().__init__() self.allowed_urls = allowed_urls or [] self.page().navigationRequested.connect(self._on_navigation_requested) def _on_navigation_requested(self, request): """Enforce URL whitelist on navigation.""" if not self.allowed_urls: # No restrictions return if not self._is_url_allowed(request.url): # Reject and open in system browser request.reject() QDesktopServices.openUrl(request.url) def _is_url_allowed(self, url): """Check if URL matches whitelist patterns.""" # file:// URLs always allowed if url.scheme() == "file": return True # Check wildcard patterns (*.example.com, localhost, etc.) for pattern in self.allowed_urls: if fnmatch.fnmatch(url.host(), pattern): return True return False ``` **Features:** - Wildcard pattern support: `*.example.com` - Exact domain matching: `example.com` - Port-aware: `localhost:8000` - File URL bypass: `file://` always allowed - Fallback to system browser for blocked URLs --- ### 1.4 Application Entry Point **Tasks:** #### 1.4.1 Main Entry (`src/webdrop_bridge/main.py`) ```python import sys import logging from pathlib import Path from PySide6.QtWidgets import QApplication from webdrop_bridge.config import Config from webdrop_bridge.utils.logging import setup_logging from webdrop_bridge.core.validator import PathValidator from webdrop_bridge.core.drag_interceptor import DragInterceptor from webdrop_bridge.ui.main_window import MainWindow logger = logging.getLogger(__name__) def main(): """Application entry point.""" # Load configuration config = Config.from_env() # Setup logging log_file = Path("logs") / "webdrop_bridge.log" if config.enable_logging else None setup_logging(config.log_level, log_file) logger.info(f"Starting {config.app_name} v{config.app_version}") # Create application app = QApplication(sys.argv) app.setApplicationName(config.app_name) # Create validator and interceptor validator = PathValidator(config.allowed_roots) interceptor = DragInterceptor(validator) # Create main window window = MainWindow(config) window.show() logger.info("Application started") sys.exit(app.exec()) if __name__ == "__main__": main() ``` **Deliverables:** - [x] `src/webdrop_bridge/main.py` - [x] Entry point tested **Acceptance Criteria:** - Application starts without errors - Configuration loaded correctly - Logging initialized --- ## Phase 1.5: Comprehensive Unit Testing ### 1.5.1 Unit Test Suite **Objective:** Achieve high code coverage with comprehensive unit tests **Test Files:** - [x] `tests/unit/test_config.py` - 12 tests (Config loading, validation, allowed_urls) - [x] `tests/unit/test_logging.py` - 9 tests (Console/file logging, rotation, format) - [x] `tests/unit/test_validator.py` - 16 tests (Path validation, symlinks, traversal attacks) - [x] `tests/unit/test_restricted_web_view.py` - 15 tests (URL whitelist, patterns, browser fallback) - [x] `tests/unit/test_project_structure.py` - 3 structural validation tests **Test Coverage Achieved:** - Config: 95% - Logging: 100% - Validator: 94% - RestrictedWebEngineView: 95% - **Total: 53 tests passing, 48%+ overall coverage** **Key Testing Features:** - Pytest with pytest-qt for Qt components - Fixtures for temp directories and environment isolation - Mock usage for external services (QDesktopServices) - Platform-specific tests (Windows symlinks, path handling) --- ## Phase 2: Testing & Quality (Weeks 5-6) ### 2.1 Extended Unit Tests **Files to create/extend:** - [x] `tests/unit/test_config.py` - Complete - [x] `tests/unit/test_validator.py` - Complete - [x] `tests/unit/test_drag_interceptor.py` - 25 tests (96% coverage) - [x] `tests/unit/test_main_window.py` - 38 tests (88% coverage) **Target Coverage**: 80%+ line coverage ✅ ACHIEVED (85% overall) **Test Suite Results:** - **Total Tests**: 99 passing - **Overall Coverage**: 85% - Config: 95% - DragInterceptor: 96% - Validator: 94% - MainWindow: 88% - RestrictedWebEngineView: 95% - Logging: 100% --- ### 2.2 Integration Tests **Files to create:** - [ ] `tests/integration/test_drag_workflow.py` - [ ] `tests/integration/test_webapp_loading.py` - [ ] `tests/integration/test_end_to_end.py` **Test Scenarios:** 1. Start app → Load webapp → Verify ready 2. Initiate drag → Validate → Drop file → Verify received 3. Invalid path → Reject → No file created --- ### 2.3 Code Quality **Checklist:** - [x] Black formatting: `tox -e format` - [x] Ruff linting: `tox -e lint` - [x] Type checking: `tox -e type` (with pragmatic type: ignore for Qt inheritance) - [x] Coverage report: `pytest --cov=src/webdrop_bridge` (85% achieved) - [ ] Security scan: `pip audit` (optional future) - [ ] Coverage report: `pytest --cov=src/webdrop_bridge` - [ ] Security scan: `pip audit` --- ## Phase 3: Build & Distribution (Weeks 7-8) ### 3.1 Windows Installer (Executable + MSI) **Build Script** (`build/scripts/build_windows.py`): - PyInstaller compilation with proper spec file - Standalone executable generation - Optional WiX MSI installer creation - Optional code signing support - Clean build management **Features:** - ✅ Automatic dependency bundling (PySide6, Qt, Chromium) - ✅ Resource embedding (webapp, icons, stylesheets) - ✅ Hidden imports configuration for Qt Web Engine - ✅ Output validation and size reporting - ✅ WiX support for professional MSI creation **PyInstaller Configuration** (`build/webdrop_bridge.spec`): - Bundles all dependencies (PySide6, Qt6 libraries) - Includes webapp files and resources - Sets up GUI mode (no console window) - Cross-platform compatible **Usage:** ```bash # Build executable only python build/scripts/build_windows.py # Build with MSI installer (requires WiX) python build/scripts/build_windows.py --msi # Build with code signing (requires certificate) python build/scripts/build_windows.py --sign ``` **Build Results:** - ✅ **Executable**: `WebDropBridge.exe` (195.66 MB) - ✅ **Output Directory**: `build/dist/windows/` - ✅ Contains all dependencies including Chromium engine **Acceptance Criteria:** - [x] Executable builds successfully - [x] Executable runs standalone (no Python required) - [x] All dependencies bundled correctly - [ ] MSI installer creation (requires WiX installation) - [ ] Code signing (requires certificate) --- ### 3.2 macOS DMG Package **Build Script** (`build/scripts/build_macos.sh`): - PyInstaller for .app bundle creation - DMG image generation - Professional DMG styling (optional via create-dmg) - Code signing and notarization support - Comprehensive error handling **Features:** - ✅ Creates proper macOS .app bundle - ✅ DMG image for distribution - ✅ Professional volume icon and layout - ✅ Code signing with signing identities - ✅ Apple notarization support - ✅ Checksum verification **Usage:** ```bash # Build .app bundle and DMG bash build/scripts/build_macos.sh # With code signing bash build/scripts/build_macos.sh --sign # With notarization bash build/scripts/build_macos.sh --notarize ``` **Configuration (Environment Variables):** ```bash # For code signing export APPLE_SIGNING_ID="Developer ID Application: Company Name" # For notarization export APPLE_ID="your@apple.id" export APPLE_PASSWORD="app-specific-password" export APPLE_TEAM_ID="XXXXXXXXXX" ``` **Acceptance Criteria:** - [ ] .app bundle builds successfully - [ ] DMG image creates without errors - [ ] DMG mounts and shows contents properly - [ ] Code signing works - [ ] Notarization passes --- ### 3.3 Forgejo Packages Distribution **Approach**: Instead of CI/CD runners, use Forgejo Packages for manual releases **Upload Scripts:** - Windows: `build/scripts/upload_to_packages.ps1` - Uploads WebDropBridge.exe to Packages - Uploads SHA256 checksum - Requires Forgejo personal access token - macOS: `build/scripts/upload_to_packages.sh` - Uploads WebDropBridge.dmg to Packages - Uploads SHA256 checksum - Requires Forgejo personal access token **Release Workflow:** ``` 1. Build locally on Windows: python build/scripts/build_windows.py 2. Build locally on macOS: bash build/scripts/build_macos.sh 3. Upload to Forgejo Packages: .\build\scripts\upload_to_packages.ps1 -Version 1.0.0 -ForgejoToken $token bash build/scripts/upload_to_packages.sh -v 1.0.0 -t $token 4. Tag release: git tag -a v1.0.0 -m "Release version 1.0.0" git push upstream v1.0.0 5. Users download from: https://git.him-tools.de/HIM-public/webdrop-bridge/packages ``` **Package Structure:** ``` https://git.him-tools.de/HIM-public/webdrop-bridge/packages/ ├─ webdrop-bridge/ │ ├─ 1.0.0/ │ │ ├─ WebDropBridge.exe │ │ ├─ WebDropBridge.exe.sha256 │ │ ├─ WebDropBridge.dmg │ │ └─ WebDropBridge.dmg.sha256 │ └─ 1.0.1/ │ └─ ... ``` **Setup Requirements:** - Forgejo personal access token with `write:package` scope - Build scripts for Windows and macOS - Local builds on each platform **Acceptance Criteria:** - [x] Upload scripts created and tested - [x] Documentation complete (FORGEJO_PACKAGES_SETUP.md) - [ ] Create GITEA_TOKEN with package permissions - [ ] Test upload: Build locally, upload to Packages - [ ] Verify files appear on Packages page - [ ] Verify checksums are correct - [ ] Test download and verify integrity **Advantages over CI/CD:** - Simpler: No runner installation needed - Flexible: Build on your schedule - Reliable: Forgejo handles storage - Secure: Token-based authentication - Integrated: Ready for UpdateManager (Phase 4.1) **Status**: ✅ Scripts created | ⏳ First test pending --- ## Phase 4: Professional Features & Auto-Update (Weeks 9-12) **Phase 4.1 Status**: ✅ **COMPLETE** (Jan 29, 2026) - Priority 1 (Core): 27 tests passing (100%) - UpdateManager fully implemented - Priority 2 (UI): 49 tests passing (100%) - Menu integration, dialogs, status bar - Total Coverage: 76 tests passing, 48% coverage - UpdateManager: 79% coverage - MainWindow: 64% coverage - Full workflow validated: startup check → dialog → download → install **Phase 4.2 Status**: ✅ **COMPLETE** (Jan 29, 2026) - Enhanced logging: 20 tests passing, 91% coverage - JSONFormatter for structured logging - PerformanceTracker for operation timing - Log archival with 30-day retention **Phase 4.3 Status**: ✅ **COMPLETE** (Jan 29, 2026) - Configuration validation: ConfigValidator class with comprehensive schema - Profile management: ConfigProfile for named profiles (work, personal, etc.) - Settings UI: SettingsDialog with 5 organized tabs - Import/Export: ConfigExporter for JSON serialization - Total: 43 tests passing across config_manager and settings_dialog **Phase 4 Overall**: ✅ **COMPLETE** - All 3 subphases complete - **Total Tests**: 139 tests (76 Phase 4.1 + 20 Phase 4.2 + 43 Phase 4.3) - **Coverage**: Professional-grade configuration, update, and logging systems - **Next Phase**: 4.4 User Documentation and Phase 5 Post-Release ### 4.1 Auto-Update System with Forgejo Integration **Forgejo Configuration:** ``` Host: https://git.him-tools.de Organization: HIM-public Repository: webdrop-bridge API Endpoint: https://git.him-tools.de/api/v1/repos/HIM-public/webdrop-bridge ``` **Tasks:** #### 4.1.1 Update Manager (`src/webdrop_bridge/core/updater.py`) ```python class UpdateManager: """Manages auto-updates via Forgejo releases.""" def __init__(self, config: Config): self.forgejo_url = "https://git.him-tools.de" self.repo = "HIM-public/webdrop-bridge" self.current_version = config.app_version async def check_for_updates(self) -> Optional[Release]: """Query Forgejo API for latest release. Returns: Release info if newer version available, None otherwise """ # GET https://git.him-tools.de/api/v1/repos/HIM-public/webdrop-bridge/releases/latest # Compare version semantic versioning async def download_update(self, release: Release) -> Path: """Download MSI/DMG from Forgejo release artifacts. Args: release: Release information from API Returns: Path to downloaded installer file """ # Download from release assets # Verify checksums def install_update(self, installer_path: Path) -> bool: """Launch installer and schedule restart. Args: installer_path: Path to MSI or DMG file Returns: True if installation scheduled, False otherwise """ ``` **Configuration** (`.env`): ```env # Auto-Update Settings FORGEJO_URL=https://git.him-tools.de FORGEJO_REPO=HIM-public/webdrop-bridge AUTO_UPDATE_CHECK=true AUTO_UPDATE_INTERVAL=86400 # 24 hours in seconds AUTO_UPDATE_NOTIFY=true ``` **Features:** - Check on startup (with 24h cache) - Manual "Check for Updates" menu option - Background download (non-blocking) - User notification with changelog - Automatic restart capability - Rollback to previous version (optional) - Security: HTTPS-only, checksum verification **Deliverables:** - [x] `src/webdrop_bridge/core/updater.py` - Update manager (COMPLETE) - [x] Unit tests for update checking and downloading (20 tests passing) - [x] Integration with Forgejo API (async queries working) - [x] Menu item for manual update check (COMPLETE - Priority 2) - [x] Update notification dialog (COMPLETE - Priority 2) **Acceptance Criteria:** - [x] Can query Forgejo releases API - [x] Detects new versions correctly - [x] Downloads and verifies checksums - [x] Gracefully handles network errors - [x] Version comparison uses semantic versioning - [x] Manual check works from menu (COMPLETE - Priority 2) - [x] Prompts user for restart (COMPLETE - Priority 2) --- #### 4.1.2 Update UI Components (`src/webdrop_bridge/ui/update_manager_ui.py`) **Menu Integration:** ``` Help Menu ├─ Check for Updates... (manual trigger) ├─ ───────────────────── └─ About WebDrop Bridge (show current version) ``` **Dialogs:** 1. **"Checking for Updates..." Dialog** - Animated spinner/progress - "Cancel" button - Message: "Checking for updates..." - Timeout: 10 seconds 2. **"Update Available" Dialog** - Current version: X.X.X - New version: Y.Y.Y - Changelog/release notes (scrollable) - Buttons: "Update Now", "Later", "Skip This Version" - Checkbox: "Show next update reminder" 3. **"Downloading Update..." Dialog** - Progress bar (download %) - File size info: "Downloading 195 MB..." - "Cancel Download" button - Cancel option reverts to "Later" 4. **"Install & Restart?" Dialog** - Message: "Update downloaded and ready to install" - Buttons: "Install Now", "Install on Next Restart" - Checkbox: "Save my work before installing" - Shows warning if unsaved changes exist 5. **"No Updates Available" Dialog** - Message: "You're running the latest version (X.X.X)" - Button: "OK" - Optional: "Check again" button 6. **"Update Failed" Dialog** - Error message with reason - Buttons: "Retry", "Download Manually", "OK" - Manual download link to Forgejo releases **Status Bar Integration:** ``` ┌─────────────────────────────────────┐ │ Ready 🔄 Checking for updates... │ (during check) │ Ready ✅ Update available (v1.1.0) │ (when found) │ Ready ⬇️ Downloading update (45%) │ (during download) └─────────────────────────────────────┘ ``` **Background Behavior:** - Startup: Check for updates automatically (no UI blocking) - If newer version found: Show notification badge on Help menu - Silent background download when user is idle - Notification when download complete - Prompt for restart when convenient **Implementation:** - Signal/slot architecture for async operations - Non-blocking UI (all operations async) - Graceful degradation if network unavailable - Thread pool for download operations - Cancel-safe download handling **Deliverables:** - [x] `src/webdrop_bridge/ui/update_manager_ui.py` - UI dialogs (COMPLETE) - [x] Status bar update indicator (COMPLETE - emoji + status text) - [x] Update menu item integration (COMPLETE - Priority 2) - [x] All dialogs with signal hookups (COMPLETE - Priority 2) - [x] Tests for UI interactions (COMPLETE - Priority 2) **Acceptance Criteria:** - [x] Status bar updates in real-time (DONE) - [x] No blocking operations on main thread (async/await) - [x] Network errors handled gracefully (try/except with logging) - [x] Menu item works and triggers check (COMPLETE - Priority 2) - [x] All dialogs display correctly (COMPLETE - Priority 2) - [x] Progress shown during download (COMPLETE - Priority 2) - [x] Restart options work (COMPLETE - Priority 2) - [x] Cancel operations work safely (COMPLETE - Priority 2) --- ### 4.2 Enhanced Logging & Monitoring **Status**: ✅ **COMPLETE** (Jan 29, 2026) - Structured JSON logging fully implemented - Log rotation and archival with retention policies - Performance metrics tracking with context managers - 20 comprehensive tests, 91% coverage **Deliverables:** - [x] Structured logging (JSON format option) - JSONFormatter class supports JSON output - [x] Log rotation/archival - _archive_old_logs() manages old logs with 30-day retention - [x] Performance metrics collection - PerformanceTracker context manager for timing operations - [x] Tests for enhanced logging - 20 tests covering all features **Features Implemented:** - `JSONFormatter` - Formats logs as JSON with timestamp, level, module, function, line number - `setup_logging()` - Now supports `json_format=True` parameter for structured logging - `_archive_old_logs()` - Automatically cleans up old log files based on retention period - `PerformanceTracker` - Context manager for tracking operation duration and logging performance ```python with PerformanceTracker("database_query") as tracker: # Your code pass # Automatically logs elapsed time ``` --- ### 4.3 Advanced Configuration **Status**: ✅ **COMPLETE** (Jan 29, 2026) - ConfigValidator with comprehensive schema validation (8 tests passing) - ConfigProfile for named profile management (7 tests passing) - ConfigExporter for JSON import/export (5 tests passing) - SettingsDialog Qt UI with 5 tabs (23 tests passing) - Total: 43 tests passing, 75% coverage on new modules **Deliverables:** - [x] Configuration validation schema - ConfigValidator class with 8-test suite - Validates all config fields with detailed error messages - Enforces type constraints, ranges, and allowed values - Used throughout to ensure config consistency - [x] UI settings dialog - SettingsDialog with 5 tabs (23 tests) - **Paths Tab**: Manage allowed root directories with add/remove buttons - **URLs Tab**: Manage allowed web URLs with wildcard support - **Logging Tab**: Select log level and choose log file location - **Window Tab**: Configure window width and height - **Profiles Tab**: Save/load/delete named profiles, export/import configs - [x] Profile support - ConfigProfile class (7 tests) - Save current config as named profile (work, personal, etc.) - Load saved profile to restore settings - List all available profiles - Delete profiles - Profiles stored in ~/.webdrop-bridge/profiles/ as JSON - [x] Export/import settings - ConfigExporter class (5 tests) - `export_to_json()` - Save configuration to JSON file - `import_from_json()` - Load and validate configuration from JSON - All imports validated with ConfigValidator - Handles file I/O errors gracefully **Key Features:** - Full configuration validation with helpful error messages - Named profiles for different work contexts - JSON export/import with validation - Professional Qt dialog with organized tabs - Profiles stored in standard ~/.webdrop-bridge/ directory - 43 unit tests covering all functionality (87% coverage on config_manager) **Test Results:** - `test_config_manager.py` - 20 tests, 87% coverage - `test_settings_dialog.py` - 23 tests, 75% coverage - Total Phase 4.3 - 43 tests passing --- ### 4.4 User Documentation **Deliverables:** - [ ] User manual (PDF, HTML) - [ ] Video tutorials - [ ] Troubleshooting guide - [ ] API documentation for developers --- ## Phase 5: Post-Release (Months 2-3) ### 5.1 Analytics & Monitoring **Requirements:** - App usage statistics - Error/crash reporting - Feature usage tracking --- ### 5.2 Community Support **Channels:** - GitHub Issues - Discussions forum - Community Slack - Email support --- ## Technical Specifications ### Supported Platforms ``` ┌─────────────────┬──────────┬────────┬────────────┐ │ Platform │ Version │ Arch │ Status │ ├─────────────────┼──────────┼────────┼────────────┤ │ Windows │ 10, 11 │ x64 │ Primary │ │ macOS │ 12-14 │ Intel │ Primary │ │ macOS │ 12-14 │ ARM64 │ Primary │ │ Linux │ Ubuntu │ x64 │ Experimental│ └─────────────────┴──────────┴────────┴────────────┘ ``` --- ### Dependencies **Core:** - PySide6 6.6.0+ - Python 3.10+ **Optional:** - PyInstaller (building) - Sphinx (documentation) - pytest (testing) - aiohttp (auto-update downloads) - packaging (version comparison) --- ### Directory Structure ``` webdrop-bridge/ │ ├── src/webdrop_bridge/ │ ├── __init__.py │ ├── main.py ← Entry point │ ├── config.py ← Configuration │ ├── core/ │ │ ├── __init__.py │ │ ├── validator.py ← Path validation │ │ ├── drag_interceptor.py ← Drag handling │ │ ├── updater.py ← Auto-update system (Phase 4) │ │ └── errors.py ← Custom exceptions │ ├── ui/ │ │ ├── __init__.py │ │ ├── main_window.py ← Main UI │ │ ├── widgets.py ← Reusable widgets │ │ └── styles.py ← UI styling │ └── utils/ │ ├── __init__.py │ ├── logging.py ← Logging setup │ ├── constants.py ← App constants │ └── helpers.py ← Utility functions │ ├── tests/ │ ├── __init__.py │ ├── conftest.py ← Pytest fixtures │ ├── unit/ ← Unit tests │ ├── integration/ ← Integration tests │ └── fixtures/ ← Test data │ ├── build/ │ ├── windows/ ← Windows build config │ ├── macos/ ← macOS build config │ └── scripts/ │ ├── build_windows.py │ └── build_macos.sh │ ├── webapp/ ← Embedded web app │ └── index.html │ ├── resources/ │ ├── icons/ ← App icons │ └── stylesheets/ ← Qt stylesheets │ ├── docs/ ← Documentation │ ├── architecture.md │ ├── api.md │ └── troubleshooting.md │ └── Configuration Files ├── pyproject.toml ← Modern Python packaging ├── setup.py ← Backwards compatibility ├── pytest.ini ← Test config ├── tox.ini ← Automation config └── .github/workflows/ ← CI/CD ``` --- ## Risk Analysis & Mitigation | Risk | Probability | Impact | Mitigation | |------|-------------|--------|-----------| | Qt/PySide6 API changes | Low | High | Lock versions, monitor releases | | macOS code signing | Medium | Medium | Use Apple Developer account, automate | | Drag performance issues | Low | Medium | Performance testing early, profiling | | Cross-platform bugs | Medium | Medium | Extensive testing on both platforms | | Security vulnerabilities | Low | High | Regular audits, dependency scanning | | User adoption | Medium | Medium | Clear documentation, community engagement | --- ## Success Metrics | Metric | Target | Timeline | |--------|--------|----------| | Code coverage | 80%+ | Week 6 | | Test pass rate | 100% | Continuous | | Build time | <2 min | Week 8 | | Application startup | <1 sec | Week 8 | | Installer size | <150 MB | Week 8 | | Documentation completeness | 100% | Week 12 | | Community contributions | 5+ | Month 3 | --- ## Milestones & Timeline ``` January 2026 ├── Week 1-2: Core Architecture (config, logging, validator) ├── Week 3-4: UI Components (main window, drag interceptor) ├── Week 5-6: Testing & Quality Assurance ├── Week 7-8: Build & Installer Creation ├── Week 9-10: Advanced Features & Polish ├── Week 11-12: Documentation & Release │ February 2026 └── Post-release: Auto-updates, Analytics, Community Support ``` --- ## Open Questions & Decisions ### Decision: Embedded Web App vs. External URL **Options:** 1. Embed static web app (current PoC approach) 2. Load from remote server 3. Hybrid: Support both **Decision**: **Hybrid approach** - Default: Load from `WEBAPP_URL` (local file) - Configurable: Allow remote URLs for advanced users - Security: Validate origins against whitelist --- ### Decision: Update Mechanism **Options:** 1. Manual downloads from website 2. Auto-update with staged rollout 3. Package manager (Chocolatey, Brew) **Decision**: **GitHub Releases + PyInstaller** - Phase 1: Manual downloads - Phase 5: Auto-update via custom launcher --- ### Decision: Telemetry **Options:** 1. No telemetry 2. Anonymous usage statistics 3. Detailed crash reporting **Decision**: **Opt-in telemetry** - No data collection by default - Users can enable for error reporting - Full transparency about data collected --- ## Next Steps 1. **Immediate** (This week): - [ ] Set up project directories ✅ - [ ] Create configuration system - [ ] Implement path validator - [ ] Set up CI/CD 2. **Near term** (Next 2 weeks): - [ ] Complete core components - [ ] Write comprehensive tests - [ ] Build installers 3. **Medium term** (Weeks 5-8): - [ ] Code review & QA - [ ] Performance optimization - [ ] Documentation 4. **Long term** (Months 2-3): - [ ] Advanced features - [ ] Community engagement - [ ] Auto-update system --- ## Document Control | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | Jan 28, 2026 | Team | Initial plan | --- ## Appendices ### A. Technology Stack Justification **PySide6 over PyQt5/6:** - Modern LGPL licensing - Excellent Windows & macOS support - Strong community and documentation - Built-in WebEngine **PyInstaller over Briefcase/Nuitka:** - Mature, stable, well-tested - Excellent one-file executable support - Cross-platform build scripts **pytest over unittest:** - Modern, expressive syntax - Powerful fixtures and plugins - Better integration with CI/CD --- ### B. Security Considerations **Path Validation:** - Only allow whitelisted directories - Resolve paths to prevent symlink attacks - Validate file existence before drag **Web Engine:** - Disable remote URL loading by default - Implement CSP headers - Regular security audits **User Data:** - No personally identifiable information stored - Configuration stored locally - Encrypted settings (future) --- ### C. Performance Targets ``` - Application startup: < 1 second - Drag interception: < 10ms - File drag initiation: < 50ms - Memory usage: < 200MB baseline - Memory/file size ratio: < 2MB per 100 files ``` --- **For updates or clarifications, see the main README.md or open an issue on GitHub.**