# 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

## Update Manager

The `UpdateManager` class checks for new releases using the Forgejo API. It caches results and only signals updates for newer versions. See `src/webdrop_bridge/core/updater.py` for implementation.

## Release Flow

- Checks for new releases on startup or user request
- Parses release notes and assets
- Notifies UI if update is available

## Integration Test Strategy

Integration tests verify workflows across modules. The update workflow is covered in [tests/integration/test_update_flow.py](../tests/integration/test_update_flow.py).

---

For more details, see [DEVELOPMENT_PLAN.md](DEVELOPMENT_PLAN.md)
