MovieMapper/docs/rust-ui-plan.md
Jarian Cottingham 1a35eb346e chore: reorganize repo layout, remove dead files
- Move phase/plan docs into docs/
- Move legacy node:test files into tests/legacy/ with README
- Remove .backup file, test audit artifacts, and unused AI prompt/skill files
- Remove broken iOS GitHub workflows (reference missing MovieMapper-iOS/)
2026-08-20 20:11:57 +00:00

15 KiB

Rust UI Implementation Plan for MovieMapper

Executive Summary

This plan outlines the architecture and implementation strategy for replacing the current Electron-based UI with a native Rust UI using a performant GUI framework. The plan maintains full compatibility with the existing Rust backend while providing a modern, responsive desktop application.

Architecture Overview

Current Architecture

Electron App (Renderer + Main)
├── index.html (UI)
├── renderer.js (UI logic)
└── main.js (IPC handlers → Rust backend)

New Architecture

Native Desktop App
├── Rust UI Layer (iced/winit)
├── Rust Backend (existing)
└── IPC Bridge (optional: direct function calls)

GUI Framework Selection

Options Considered

Pros:

  • Modern, React-inspired API with Elm architecture
  • Excellent performance (native rendering)
  • Cross-platform (Windows, macOS, Linux)
  • Active community and good documentation
  • Async/await support built-in
  • Small binary size (~5MB runtime)
  • No WebKit dependencies

Cons:

  • Less mature than some alternatives
  • Smaller ecosystem

Why Chosen:

  • Perfect for data-heavy applications like MovieMapper
  • Similar state management to Electron/React
  • Excellent performance characteristics
  • Modern Rust ecosystem alignment

2. Dioxus

Pros:

  • React-inspired syntax (RSX)
  • Web, desktop, and mobile support
  • Strong community

Cons:

  • Heavier runtime (~50MB+)
  • Less mature for desktop apps
  • More complex build process

3. Tauri (Alternative)

Pros:

  • Use existing HTML/CSS/JS
  • Small binary size
  • Native performance

Cons:

  • Would keep web stack (defeats purpose)
  • Additional WebView overhead
  • Less "pure Rust" approach

4. Slint

Pros:

  • Declarative UI design
  • Good performance

Cons:

  • Learning curve for DSL
  • Smaller community
  • Less Rust-idiomatic

Final Choice: Iced

Project Structure

MovieMapper/
├── Cargo.toml                    # Workspace configuration
├── Cargo.lock
├── rust/
│   ├── Cargo.toml               # Backend crate
│   ├── src/
│   │   ├── lib.rs
│   │   ├── main.rs
│   │   ├── error.rs
│   │   ├── types.rs
│   │   ├── scanner.rs
│   │   ├── ffmpeg.rs
│   │   ├── tvdb.rs
│   │   ├── file_manager.rs
│   │   └── mapper.rs
│   └── tests/
│
├── ui/                           # NEW: Rust UI implementation
│   ├── Cargo.toml               # UI crate configuration
│   ├── src/
│   │   ├── main.rs              # Application entry point
│   │   ├── app.rs               # Main app structure
│   │   ├── theme.rs             # Styling and theming
│   │   ├── components/          # Reusable UI components
│   │   │   ├── mod.rs
│   │   │   ├── breadcrumb.rs    # Navigation breadcrumbs
│   │   │   ├── file_list.rs     # File listing component
│   │   │   ├── sidebar.rs       # Search and show details
│   │   │   ├── progress.rs      # Progress indicator
│   │   │   ├── tag_manager.rs   # Tagging UI
│   │   │   ├── episode_editor.rs # Episode range editing
│   │   │   └── floating_action.rs # FAB component
│   │   ├── state.rs             # Application state management
│   │   ├── messages.rs          # UI messages/events
│   │   ├── backend.rs           # Backend integration
│   │   ├── windows/
│   │   │   ├── mod.rs
│   │   │   ├── main.rs          # Main window
│   │   │   └── video_preview.rs # Video preview modal
│   │   └── utils/
│   │       ├── mod.rs
│   │       ├── path.rs          # Path utilities
│   │       ├── format.rs        # Formatting helpers
│   │       └── ffmpeg.rs        # FFmpeg helpers
│   └── assets/
│       ├── icons/
│       └── styles/
│
├── backend/                      # Backend integration layer
│   ├── Cargo.toml
│   └── src/
│       ├── lib.rs               # Re-exports from rust/
│       └── bridge.rs            # IPC/FFI bridge if needed
│
├── main.rs                       # Workspace entry point
├── index.html                    # Keep for reference/compatibility
├── renderer.js                   # Keep for reference
├── main.js                       # Keep for reference
└── package.json                  # Updated workspace config

Implementation Phases

Phase 1: Foundation (Week 1)

1.1 Setup Rust UI Project

  • Create ui/ directory with Cargo.toml
  • Configure workspace in root Cargo.toml
  • Add iced = "0.12" dependency to ui/Cargo.toml
  • Set up basic project structure
  • Configure build for cross-platform
  • Set up asset management (icons, styles)

1.2 Backend Integration

  • Create backend/ crate for Rust backend access
  • Implement direct function calls (no IPC overhead)
  • Handle async operations with tokio
  • Implement error propagation
  • Create backend state management

1.3 Core Application Structure

  • Implement iced::Application trait
  • Set up main window with Settings
  • Implement state management with Clone + Default
  • Create message enum for all UI events
  • Set up logging with tracing

Phase 2: UI Components (Week 2)

2.1 Basic Layout

  • Implement main layout (sidebar + content)
  • Create breadcrumb navigation component
  • Implement directory selector button
  • Add progress indicator component
  • Create file list container

2.2 File List Component

  • Implement file item rendering
  • Add drag-and-drop support (using iced_native::event::drag)
  • Create folder vs file visual distinction
  • Implement file metadata display (duration, quality, FPS)
  • Add episode number display with arrows

2.3 Sidebar Components

  • Search input with debouncing
  • Search results dropdown
  • Show details panel
  • Season selector with episode list
  • Episode hover highlighting

Phase 3: Feature Implementation (Week 3)

3.1 File Operations

  • Implement directory scanning UI
  • Add progress updates during scan
  • Create file rename functionality
  • Implement file tagging system
  • Add play button for individual files
  • Implement FAB for moving all tagged files

3.2 Episode Mapping

  • Create episode range editor component
  • Implement arrow buttons for range adjustment
  • Add visual feedback for episode matching
  • Create "Begin Mapping" button and flow
  • Implement mapping progress display

3.3 Show/Season/Episode Selection

  • Implement TVDB search
  • Display search results
  • Show details panel
  • Season selector with episode counts
  • Episode list with episode number matching

Phase 4: Advanced Features (Week 4)

4.1 Video Preview Modal

  • Create modal window component
  • Integrate with system video player
  • Display file metadata
  • Add close button and styling

4.2 Breadcrumb Navigation

  • Implement navigation stack
  • Add back button functionality
  • Display directory path history
  • Handle folder navigation

4.3 Audit Logging

  • Implement audit event logging
  • Display audit log in UI (optional)
  • Export audit logs (optional)

4.4 Performance Optimizations

  • Virtual scrolling for large file lists
  • Lazy loading for episode data
  • Caching for TVDB results
  • Background processing for large operations
  • Memory usage monitoring

Phase 5: Polish & Testing (Week 5)

5.1 Theming

  • Implement dark theme (match current design)
  • Create reusable component styles
  • Add hover states
  • Implement focus states
  • Support system theme detection (optional)

5.2 Testing

  • Unit tests for UI components
  • Integration tests for workflows
  • E2E tests with iced_test or similar
  • Manual testing on all platforms
  • Performance testing

5.3 Documentation

  • User documentation
  • API documentation
  • Architecture documentation
  • Contribution guidelines

Component Architecture

State Management

#[derive(Debug, Clone, Default)]
pub struct AppState {
    // Directory state
    current_directory: Option<PathBuf>,
    navigation_stack: Vec<PathBuf>,
    
    // File state
    files: Vec<FileMetadata>,
    tagged_files: HashMap<PathBuf, TagType>,
    
    // TVDB state
    search_query: String,
    search_results: Vec<Show>,
    selected_show: Option<Show>,
    selected_season: Option<Season>,
    episodes: Vec<Episode>,
    
    // Mapping state
    is_mapping: bool,
    mapping_progress: u32,
    
    // UI state
    progress_visible: bool,
    progress_message: String,
}

Messages (Events)

#[derive(Debug, Clone)]
pub enum Message {
    // Navigation
    SelectDirectory,
    OpenDirectory(PathBuf),
    NavigateBack,
    
    // File operations
    ScanDirectory(PathBuf),
    FileScanned(FileMetadata),
    ScanComplete(Vec<FileMetadata>),
    
    // Tagging
    TagFile(PathBuf, TagType),
    UntagFile(PathBuf, TagType),
    MoveTaggedFile(PathBuf, TagType),
    MoveAllTaggedFiles,
    
    // Episode editing
    UpdateEpisodeRange(usize, u32, u32),
    ShiftEpisodes(usize, i32),
    
    // TVDB
    SearchShows(String),
    ShowsLoaded(Vec<Show>),
    ShowSelected(Show),
    SeasonsLoaded(Vec<Season>),
    SeasonSelected(Season),
    EpisodesLoaded(Vec<Episode>),
    
    // Mapping
    BeginMapping,
    MappingComplete(Result<MappingResult, String>),
    
    // UI updates
    ProgressUpdate(u32, u32, String),
    ShowProgress,
    HideProgress,
    
    // System
    OpenVideoPreview(PathBuf),
    OpenFileInPlayer(PathBuf),
    LogAuditEvent(AuditEvent),
}

Component Structure

// ui/src/components/file_list.rs
pub struct FileList {
    files: Vec<FileMetadata>,
    tagged_files: HashMap<PathBuf, TagType>,
    on_file_click: Callback<PathBuf>,
    on_tag: Callback<(PathBuf, TagType)>,
    on_play: Callback<PathBuf>,
}

impl Component for FileList {
    type Message = Message;
    
    fn view(&self) -> Element<Message> {
        // Render file list with tags, play buttons, etc.
    }
}

// ui/src/components/sidebar.rs
pub struct Sidebar {
    search_query: String,
    search_results: Vec<Show>,
    selected_show: Option<Show>,
    on_search: Callback<String>,
    on_show_select: Callback<Show>,
}

impl Component for Sidebar {
    type Message = Message;
    
    fn view(&self) -> Element<Message> {
        // Render search and show details
    }
}

Backend Integration Strategy

Direct Function Calls (Preferred)

Instead of IPC, use direct Rust function calls:

// ui/src/backend.rs
use movie_mapper_rust::{scan_directory, rename_file, move_to_folder};

pub async fn scan_directory_ui(path: &str) -> Result<Vec<FileMetadata>, String> {
    scan_directory(path, Some(|current, total, filename| {
        // Send progress updates to UI
        Message::ProgressUpdate(current, total, filename.to_string())
    })).await.map_err(|e| e.to_string())
}

Benefits:

  • Zero IPC overhead
  • Type safety
  • Better error handling
  • Simpler code
  • Easier debugging

When IPC Might Be Needed:

  • Long-running operations that could block UI
  • When backend is in separate process for isolation
  • For plugin architecture

Performance Targets

  • Startup time: < 1 second
  • Directory scan (100 files): < 500ms
  • File rendering: Smooth 60fps
  • Memory usage: < 100MB for typical workflow
  • Binary size: < 15MB (with all dependencies)

Cross-Platform Considerations

macOS

  • Native look and feel
  • Touch Bar support (optional)
  • Spotlight integration (optional)

Windows

  • Taskbar integration
  • File association (optional)
  • Aero effects

Linux

  • AppImage support
  • Desktop file integration
  • Theme compatibility

Deployment

Build Commands

# Development
cargo build --package ui --features debug

# Release
cargo build --package ui --release

# Cross-platform
cargo build --package ui --release --target x86_64-apple-darwin
cargo build --package ui --release --target x86_64-pc-windows-msvc
cargo build --package ui --release --target x86_64-unknown-linux-gnu

Distribution

Option 1: Standalone Binary

  • Single executable for each platform
  • No runtime installation required
  • Include FFmpeg binaries if needed

Option 2: Installer

  • Platform-specific installers
  • Automatic updates (using taffy or similar)
  • Clean uninstall

Option 3: Package Managers

  • macOS: Homebrew
  • Windows: Scoop, MSI
  • Linux: AppImage, Flatpak, Snap

Risk Assessment

Technical Risks

Risk Impact Mitigation
Iced ecosystem maturity Medium Contribute back, use well-established features
Learning curve Low Team Rust expertise, documentation
Feature parity Low Phased implementation, testing

Schedule Risks

Risk Impact Mitigation
Feature complexity Medium Break into small PRs, daily demos
Platform differences Low Test early on all platforms
Backend integration Low Direct calls, type safety

Success Criteria

  • All existing features implemented
  • Performance matches or exceeds Electron version
  • UI looks native on all platforms
  • No memory leaks (verified with valgrind/ASAN)
  • All tests passing
  • Documentation complete
  • User testing successful

Next Steps

  1. Approve plan - Get stakeholder approval
  2. Setup repository - Create ui/ directory structure
  3. Build MVP - Implement basic window with file list
  4. Weekly reviews - Demo progress each week
  5. Iterate - Add features incrementally
  6. Test - Comprehensive testing on all platforms
  7. Release - Beta release with user feedback

Appendix: Alternative Approaches

Hybrid Approach (Tauri + Rust UI)

If Tauri is preferred:

  • Keep Tauri for window management
  • Use Rust UI components (Dioxus or Slint)
  • Keep some Electron features for compatibility

Web UI with Rust Backend

If web stack is preferred:

  • Keep HTML/CSS/JS for UI
  • Use Rust backend via WASM
  • Electron replaced with tao/wry

Native Desktop with Different Framework

Other options:

  • egui: Immediate mode GUI, very performant
  • slint: Declarative, good for form-like apps
  • gtk-rs: Mature, but heavier dependencies

Conclusion

The recommended approach using Iced provides the best balance of performance, maintainability, and developer experience for a Rust-based MovieMapper UI. It maintains the performance benefits of the Rust backend while providing a modern, responsive user interface that feels native on all platforms.

This plan provides a clear roadmap for implementation while allowing flexibility for adjustments based on team feedback and technical discoveries during development.