# Architecture Guide ## High-Level Design ``` ┌──────────────────────────────────────────────────────────┐ │ Application Layer (UI) │ │ ┌────────────────────────────────────────────────────────┤ │ │ QMainWindow (main_window.py) │ │ │ ├─ QWebEngineView (web app container) │ │ │ └─ DragInterceptor (drag handling) │ │ └────────────────────────────────────────────────────────┘ │ ↓ Events (dragEnterEvent, etc.) ├──────────────────────────────────────────────────────────┤ │ Business Logic Layer (Core) │ │ ┌────────────────────────────────────────────────────────┤ │ │ PathValidator (security & validation) │ │ │ DragInterceptor (drag/drop conversion) │ │ │ Config (configuration management) │ │ └────────────────────────────────────────────────────────┘ │ ↓ Platform APIs ├──────────────────────────────────────────────────────────┤ │ Platform Layer (OS Integration) │ │ ┌────────────────────────────────────────────────────────┤ │ │ QUrl (Windows: CF_HDROP, macOS: NSFilenamesPboardType)│ │ │ QMimeData (cross-platform mime data) │ │ │ QDrag (native drag-drop implementation) │ │ └────────────────────────────────────────────────────────┘ ``` ## Module Organization ### `src/webdrop_bridge/core/` **Purpose**: Core business logic, independent of UI **Key Components:** - `validator.py`: Path validation against whitelist - `drag_interceptor.py`: Drag event handling and conversion - `config.py`: Configuration management - `errors.py`: Custom exception classes **Dependencies**: None (only stdlib + pathlib) ### `src/webdrop_bridge/ui/` **Purpose**: User interface components using Qt/PySide6 **Key Components:** - `main_window.py`: Main application window - `widgets.py`: Reusable custom widgets - `styles.py`: UI styling and themes **Dependencies**: PySide6, core/ ### `src/webdrop_bridge/utils/` **Purpose**: Shared utilities and helpers **Key Components:** - `logging.py`: Logging configuration - `constants.py`: Application constants - `helpers.py`: General-purpose helper functions **Dependencies**: stdlib only ## Data Flow ### Drag-and-Drop Operation ``` User in Web App ↓ [dragstart event] → JavaScript sets dataTransfer.text = "Z:\path\file.txt" ↓ [dragend event] → Drag leaves WebEngine widget ↓ DragInterceptor.dragEnterEvent() triggered ↓ Extract text from QMimeData ↓ PathValidator.is_valid_file(path) ├─ is_allowed(path) → Check whitelist └─ path.exists() and path.is_file() → File system check ↓ If valid: → Create QUrl.fromLocalFile(path) → Create new QMimeData with URLs → QDrag.exec() → Native file drag ↓ If invalid: → event.ignore() → Log warning ↓ OS receives native file drag ↓ InDesign/Word receives file handle ``` ## Security Model ### Path Validation Strategy 1. **Whitelist-based**: Only allow configured root directories 2. **Absolute path resolution**: Prevent directory traversal attacks 3. **Symlink handling**: Resolve to real path before checking 4. **File existence check**: Don't drag non-existent files 5. **Type verification**: Only allow regular files ```python # Example allowed roots ALLOWED_ROOTS = [ Path("Z:/"), # Network share Path("C:/Users/Public"), # Windows public folder ] # Example validation path = Path(r"Z:\projects\file.psd") → Resolve: Z:\projects\file.psd → Check: starts with Z:\? YES → Check: file exists? YES → Check: is regular file? YES → Result: ALLOW ``` ### Web Engine Security - **LocalContentCanAccessFileUrls**: Enabled (required for drag) - **LocalContentCanAccessRemoteUrls**: Disabled (prevent phishing) - **Certificate validation**: Enforced for HTTPS ## Performance Considerations ### Drag Operation Timing ``` Event Time (ms) Critical Path ───────────────────────────────────── dragenter <1 ✓ Must be fast validation <10 ✓ Keep fast QUrl creation <1 QDrag.exec() <50 ✓ Start quickly ───────────────────────── TOTAL <50 ✓ Imperceptible ``` ### Memory Management - **Web engine**: ~100-150 MB baseline - **Drag interceptor**: Minimal overhead - **Logging**: Rotated to prevent growth - **File cache**: None (on-demand validation) ## Testing Strategy ### Unit Testing ``` validator.py ├─ test_is_allowed() ├─ test_is_valid_file() └─ test_symlink_handling() config.py ├─ test_load_from_env() ├─ test_default_values() └─ test_validation() drag_interceptor.py ├─ test_dragEnterEvent() ├─ test_invalid_path_rejected() └─ test_file_drag_created() ``` ### Integration Testing ``` end_to_end.py ├─ test_app_startup() ├─ test_webapp_loads() ├─ test_complete_drag_workflow() └─ test_invalid_path_handling() ``` ## Extension Points ### Adding New Handlers ```python # Future: Support for additional file types or sources class HandlerRegistry: def register(self, source_type: str, handler: Handler): self.handlers[source_type] = handler def handle(self, data: QMimeData) -> Optional[QMimeData]: for handler in self.handlers.values(): if handler.can_handle(data): return handler.handle(data) return None ``` ### Custom Validators ```python # Future: Pluggable validation rules class ValidatorPlugin(ABC): @abstractmethod def validate(self, path: Path) -> bool: pass class FileSizeValidator(ValidatorPlugin): def __init__(self, max_size: int): self.max_size = max_size def validate(self, path: Path) -> bool: return path.stat().st_size <= self.max_size ``` ## Deployment Architecture ### Single-File Executable ``` WebDropBridge.exe (built with PyInstaller) ├─ Python runtime ├─ PySide6 libraries ├─ Qt libraries ├─ Embedded web app (index.html) └─ Bundled resources (icons, etc.) Size: ~100-120 MB Startup: <1 second ``` ### Portable Deployment - Single executable: No installation required - No registry entries: Clean removal - Relative paths: Can run from any directory - Configuration: `.env` file per instance ## Platform Differences ### Windows - **Drag Format**: CF_HDROP (file paths) - **File URLs**: `file:///C:/path/to/file` - **Paths**: Backslash `\` (converted to forward `/`) - **Permissions**: Admin privileges not required ### macOS - **Drag Format**: NSFilenamesPboardType (same as Windows, different implementation) - **File URLs**: `file:///Users/username/path/to/file` - **Paths**: Forward slash `/` (native) - **Permissions**: May require accessibility permissions ## Monitoring & Debugging ### Debug Logging ```python # Enable debug logging LOG_LEVEL=DEBUG # Output 2026-01-28 14:32:15 - webdrop_bridge - DEBUG - DragInterceptor: dragEnterEvent triggered 2026-01-28 14:32:15 - webdrop_bridge - DEBUG - PathValidator: Checking Z:\file.psd 2026-01-28 14:32:15 - webdrop_bridge - INFO - File dragged: Z:\file.psd ``` ### Performance Profiling ```python import cProfile import pstats profiler = cProfile.Profile() profiler.enable() # ... drag operation ... profiler.disable() stats = pstats.Stats(profiler) stats.print_stats() ``` --- For more details, see [DEVELOPMENT_PLAN.md](DEVELOPMENT_PLAN.md)