Adds the new Rust-based UI (iced), backend service, shared Swift models, CI/CD workflows, build scripts, and project documentation.
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
1. Iced (RECOMMENDED) ⭐
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 withCargo.toml - Configure workspace in root
Cargo.toml - Add
iced = "0.12"dependency toui/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::Applicationtrait - 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_testor 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
taffyor 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
- Approve plan - Get stakeholder approval
- Setup repository - Create
ui/directory structure - Build MVP - Implement basic window with file list
- Weekly reviews - Demo progress each week
- Iterate - Add features incrementally
- Test - Comprehensive testing on all platforms
- 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.