# Driftwood Feature Roadmap - Design Document **Goal:** Add 26 features to make Driftwood the definitive AppImage manager for Linux newcomers coming from Windows, covering the full lifecycle from discovery to clean uninstall. **Architecture:** All features build on the existing GTK4/libadwaita/Rust stack. Every system modification (desktop files, icons, MIME associations, autostart entries) is tracked in a central `system_modifications` table and fully reversed on app removal. Features are ordered from simplest to most complex. **Tech Stack:** Rust, gtk4-rs, libadwaita-rs, rusqlite, gio, notify crate, pkexec/polkit for privileged operations, XDG specs (Desktop Entry, Autostart, MIME Applications), AppImage feed.json catalog. --- ## Core Architecture: System Modification Tracking Every feature that touches system files MUST use this tracking system. No exceptions. ### New Database Table ```sql CREATE TABLE IF NOT EXISTS system_modifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, appimage_id INTEGER REFERENCES appimages(id) ON DELETE CASCADE, mod_type TEXT NOT NULL, file_path TEXT NOT NULL, previous_value TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_system_mods_appimage ON system_modifications(appimage_id); ``` **mod_type values:** `desktop_file`, `icon`, `autostart`, `mime_default`, `mime_entry`, `system_desktop`, `system_icon`, `system_binary` ### Core Functions (src/core/integrator.rs) ```rust pub fn register_modification(db: &Database, appimage_id: i64, mod_type: &str, file_path: &str, previous_value: Option<&str>) -> Result<()> pub fn undo_modification(db: &Database, mod_id: i64) -> Result<()> pub fn undo_all_modifications(db: &Database, appimage_id: i64) -> Result<()> ``` ### Uninstall Flow 1. Query `system_modifications` for the appimage_id 2. For each modification (in reverse order): - `desktop_file` / `autostart` / `icon`: delete the file - `mime_default`: restore previous_value via `xdg-mime default {previous_value} {mime_type}` - `system_*`: delete via `pkexec rm` 3. Run `update-desktop-database ~/.local/share/applications/` 4. Run `gtk-update-icon-cache ~/.local/share/icons/hicolor/` (if icons were removed) 5. Optionally delete app data paths (with user confirmation) 6. Delete the AppImage file 7. Delete DB record (CASCADE handles system_modifications rows) ### CLI Purge Command `driftwood purge` - removes ALL system modifications for ALL managed apps, for when Driftwood itself is being removed from the system. --- ## Feature Specifications (Sorted: Easiest to Most Complex) --- ### F1. Auto-Set Executable Permission on Existing Files **Complexity:** Trivial (15 min) **Problem:** Files already in scan directories skip chmod during drag-and-drop. Non-executable AppImages found during scan aren't fixed. **Files:** `src/ui/drop_dialog.rs`, `src/core/discovery.rs` **Changes:** - `drop_dialog.rs`: In the `in_scan_dir` branch of `register_dropped_files()`, add permission check and fix: ```rust if !std::os::unix::fs::PermissionsExt::mode(&metadata.permissions()) & 0o111 != 0 { std::fs::set_permissions(&final_path, std::fs::Permissions::from_mode(0o755))?; } ``` - `discovery.rs`: After scan finds a non-executable AppImage, auto-fix permissions and log it --- ### F2. Move-or-Copy Option for Drag-and-Drop (+ Keep in Place) **Complexity:** Easy (30 min) **Problem:** Always copies file, wasting disk. No option to keep in place. **Files:** `src/ui/drop_dialog.rs` **Changes:** - Replace 3-button dialog with 4 response options: - `"cancel"` - Cancel - `"keep-in-place"` - Keep in place (register at current location, set executable, no copy) - `"copy-only"` - Copy to Applications (current "Just add" behavior) - `"copy-and-integrate"` - Copy & add to menu (current "Add to app menu", suggested/default) - `register_dropped_files()` receives a new `copy_mode` enum: `KeepInPlace`, `CopyOnly`, `CopyAndIntegrate` - `KeepInPlace`: set executable on original path, register in DB at original location, optionally integrate - Dialog text updated: "Where should this AppImage live?" --- ### F3. Version Rollback **Complexity:** Easy (1 hr) **Problem:** No way to go back if an update breaks things. **Files:** `src/core/updater.rs`, `src/ui/detail_view.rs`, `src/core/database.rs` **Changes:** - New DB column: `previous_version_path TEXT` on appimages - In `updater.rs` update flow: before replacing, rename old to `{path}.prev` and store path in `previous_version_path` - Detail view system tab: "Rollback to previous version" button (visible only when `previous_version_path` is set) - Rollback: swap current and .prev files, update DB fields, re-run analysis - Cleanup: delete .prev file when user explicitly confirms or after configurable retention --- ### F4. Source Tracking **Complexity:** Easy (1 hr) **Problem:** Users can't tell where an AppImage was downloaded from. **Files:** `src/ui/detail_view.rs`, `src/core/database.rs`, `src/ui/drop_dialog.rs` **Changes:** - New DB column: `source_url TEXT` on appimages - Auto-detect from `update_info` field (already stored): parse GitHub/GitLab URLs - Display "Source: github.com/obsidianmd/obsidian" on detail view overview tab - Drop dialog: optional "Where did you download this?" text field (pre-filled if detected) - Catalog installs (F26): automatically set source_url from catalog entry --- ### F5. Launch Statistics Dashboard **Complexity:** Easy (1-2 hrs) **Problem:** Launch data is tracked but never shown. **Files:** `src/ui/dashboard.rs`, `src/core/database.rs` **Changes:** - New DB queries: - `get_top_launched(limit: i32) -> Vec<(String, u64)>` - most launched apps - `get_launch_count_since(since: &str) -> u64` - total launches since date - `get_recent_launches(limit: i32) -> Vec<(String, String)>` - recent launch events with timestamps - Dashboard: new "Activity" section showing: - Top 5 most-launched apps with launch counts - "X launches this week" summary stat - "Last launched: AppName, 2 hours ago" --- ### F6. Batch Operations **Complexity:** Medium (2-3 hrs) **Problem:** Can't select multiple AppImages for bulk actions. **Files:** `src/ui/library_view.rs`, `src/ui/app_card.rs`, `src/window.rs` **Changes:** - Library view header: "Select" toggle button - When active: app cards show checkboxes, bottom action bar slides up - Action bar buttons: "Integrate" / "Remove Integration" / "Delete" / "Export" - Each action confirms with count: "Integrate 5 AppImages?" - Delete uses the full uninstall flow (F14/system_modifications cleanup) - Selection state stored in a `HashSet` of record IDs on the LibraryView --- ### F7. Automatic Desktop Integration on Scan **Complexity:** Easy (30 min) **Problem:** Users forget to integrate after scanning. **Files:** `src/window.rs`, `src/ui/preferences.rs` **Changes:** - GSettings key `auto-integrate` already exists (default false) - Wire it up: after scan completes in `window.rs`, if setting is true, iterate newly discovered apps and call `integrator::integrate()` for each - Register all created files via `register_modification()` - Preferences: add toggle in Behavior page (already may be there, verify) --- ### F8. Autostart Manager **Complexity:** Medium (2 hrs) **Problem:** No way to set AppImages to start at login. **Spec:** XDG Autostart - `.desktop` file in `~/.config/autostart/` **Files:** `src/core/integrator.rs`, `src/ui/detail_view.rs`, `src/core/database.rs` **Changes:** - New DB column: `autostart INTEGER NOT NULL DEFAULT 0` - New functions in `integrator.rs`: ```rust pub fn enable_autostart(db: &Database, record: &AppImageRecord) -> Result pub fn disable_autostart(db: &Database, record_id: i64) -> Result<()> ``` - `enable_autostart`: creates `~/.config/autostart/driftwood-{id}.desktop` with: ```ini [Desktop Entry] Type=Application Name={app_name} Exec={appimage_path} Icon={icon_path} X-GNOME-Autostart-enabled=true X-Driftwood-AppImage-ID={id} ``` - Registers modification with mod_type `autostart` - Detail view system tab: "Start at login" switch row - `disable_autostart`: deletes the file, removes from system_modifications - Uninstall flow: handled by `undo_all_modifications()` --- ### F9. System Notification Integration **Complexity:** Medium (2 hrs) **Problem:** Toasts vanish, important events get missed. **Files:** `src/core/notification.rs`, `src/window.rs` **Changes:** - Use `gio::Application::send_notification(id, notification)` for: - App crash on launch (high priority) - Updates available after background check (normal priority) - Security vulnerabilities found (high priority if critical/high severity) - Keep toasts for minor confirmations (copied, integrated, etc.) - Notification click opens Driftwood and navigates to relevant view - `notification.rs` already has the logic for CVE notifications - extend to use gio::Notification instead of/in addition to libnotify --- ### F10. Storage Dashboard per App **Complexity:** Medium (2 hrs) **Problem:** Users don't know total disk usage per app. **Files:** `src/ui/detail_view.rs`, `src/core/footprint.rs`, `src/core/database.rs` **Changes:** - `footprint.rs`: new function `get_total_footprint(db: &Database, record_id: i64) -> FootprintSummary` ```rust pub struct FootprintSummary { pub binary_size: u64, pub config_size: u64, pub cache_size: u64, pub data_size: u64, pub state_size: u64, pub total_size: u64, } ``` - Detail view storage tab: visual breakdown with labeled size bars - Each category shows path and size: "Config (~/.config/MyApp) - 12 MB" - "Clean cache" button per category (deletes cache paths only) - Library list view: optional "Total size" column --- ### F11. Background Update Checks **Complexity:** Medium (2-3 hrs) **Problem:** No automatic update awareness. **Files:** `src/window.rs`, `src/ui/dashboard.rs`, `src/ui/library_view.rs`, GSettings schema **Changes:** - New GSettings key: `update-check-interval-hours` type i, default 24, range 1-168 - New GSettings key: `last-update-check` type s, default '' - On startup: if `auto-check-updates` is true and enough time has passed, spawn background check - Background check: iterate all apps with update_info, call `check_appimage_for_update()` per app - Results: update `update_available` column in DB, send gio::Notification if updates found - Dashboard: show "X updates available" with timestamp "Last checked: 2h ago" - Library view: badge on apps with updates - Preferences: toggle + interval dropdown --- ### F12. One-Click Update All **Complexity:** Medium (3-4 hrs) **Problem:** Can only update one app at a time. **Files:** `src/ui/dashboard.rs`, new `src/ui/batch_update_dialog.rs`, `src/core/updater.rs` **Changes:** - Dashboard: "Update All (N)" button when updates are available - Opens batch update dialog showing list of apps to update with checkboxes - Progress: per-app progress bar, overall progress bar - Each update: download new version, save old as .prev (F3 rollback), replace, re-analyze - On completion: summary toast "Updated 5 apps successfully, 1 failed" - Failed updates: show error per app, keep old version - Cancel: stops remaining updates, already-updated apps stay updated --- ### F13. Full Uninstall with Data Cleanup **Complexity:** Medium (3 hrs) **Problem:** Deleting AppImage leaves config/cache/data behind. **Files:** `src/ui/detail_view.rs`, new confirmation dialog logic, `src/core/footprint.rs` **Changes:** - Delete button in detail view triggers new uninstall flow: 1. Show dialog with FootprintSummary (from F10): - "Delete MyApp?" with breakdown: - [x] AppImage file (245 MB) - [x] Configuration (~/.config/MyApp) - 12 MB - [x] Cache (~/.cache/MyApp) - 89 MB - [x] Data (~/.local/share/MyApp) - 1.2 GB - Total: 1.5 GB will be freed 2. All checked by default, user can uncheck to keep data 3. On confirm: - Call `undo_all_modifications()` (removes .desktop, icons, autostart, MIME defaults) - Delete selected data paths - Delete the AppImage file - Remove DB record - Batch delete (F6) uses same flow with aggregated summary --- ### F14. Theme/Icon Preview in Drop Dialog **Complexity:** Medium (2 hrs) **Problem:** Users don't see what the app looks like before integrating. **Files:** `src/ui/drop_dialog.rs`, `src/core/inspector.rs` **Changes:** - New function in inspector: `extract_icon_fast(path: &Path) -> Option` - Runs `unsquashfs -l` to find icon file, then extracts just that one file - Much faster than full inspection - Drop dialog: after registration, show preview card: - App icon (from fast extraction) - App name (from filename initially, updated after full analysis) - "This is how it will appear in your app menu" - If icon extraction fails, show default AppImage icon --- ### F15. FUSE Fix Wizard **Complexity:** Significant (4-5 hrs) **Problem:** #1 support issue - FUSE not installed, AppImages won't run. **Files:** `src/core/fuse.rs`, new `src/ui/fuse_wizard.rs`, `src/ui/dashboard.rs`, new `data/app.driftwood.Driftwood.policy` **Changes:** - New distro detection in `fuse.rs`: ```rust pub struct DistroInfo { pub id: String, // "ubuntu", "fedora", "arch", etc. pub id_like: Vec, pub version_id: String, } pub fn detect_distro() -> Option // parses /etc/os-release pub fn get_fuse_install_command(distro: &DistroInfo) -> Option ``` - Install commands by distro family: - Debian/Ubuntu: `apt install -y libfuse2t64` (24.04+) or `apt install -y libfuse2` (older) - Fedora/RHEL: `dnf install -y fuse-libs` - Arch/Manjaro: `pacman -S --noconfirm fuse2` - openSUSE: `zypper install -y libfuse2` - Polkit policy file (`data/app.driftwood.Driftwood.policy`): ```xml Install FUSE library for AppImage support Authentication is required to install the FUSE library auth_admin auth_admin auth_admin ``` - Wizard dialog (NavigationPage-style multi-step): 1. "FUSE is not installed" - explanation of what FUSE is and why it's needed 2. "We detected {distro}. Install with: `{command}`" - shows exact command 3. "Install now" button runs `pkexec sh -c "{command}"` and shows output 4. Re-check: calls `detect_system_fuse()` and shows success/failure - Dashboard: yellow banner when FUSE is missing with "Fix now" button --- ### F16. File Type Association Manager **Complexity:** Significant (4 hrs) **Problem:** AppImages don't register MIME types, so files don't open with them. **Spec:** XDG MIME Applications - `~/.local/share/applications/mimeapps.list` **Files:** `src/core/integrator.rs`, `src/ui/detail_view.rs` **Changes:** - `inspector.rs` already extracts desktop entry content including MimeType= field - New function: `parse_mime_types(desktop_entry: &str) -> Vec` - extracts MimeType= values - When integrating: include `MimeType=` in generated .desktop file - Detail view overview tab: "Supported file types" section listing MIME types - Per-type toggle: "Set as default for .png files" - Setting default: 1. Query current default: `xdg-mime query default {mime_type}` 2. Store in `system_modifications` with `previous_value` = current default 3. Set new: `xdg-mime default driftwood-{id}.desktop {mime_type}` - Removing: restore previous default from `previous_value` - Uninstall: handled by `undo_all_modifications()` --- ### F17. Taskbar/Panel Icon Fix (StartupWMClass) **Complexity:** Significant (3 hrs) **Problem:** Running AppImages show wrong/generic icon in taskbar. **Files:** `src/core/integrator.rs`, `src/core/wayland.rs`, `src/ui/detail_view.rs` **Changes:** - During inspection: extract `StartupWMClass=` from embedded .desktop entry - Store in new DB column: `startup_wm_class TEXT` - When generating .desktop file for integration, include StartupWMClass if available - After launch + Wayland analysis: read `/proc/{pid}/environ` for `GDK_BACKEND`, check window list via `xdotool` or `xprop` for WM_CLASS - If WM_CLASS doesn't match StartupWMClass: log warning, offer to patch .desktop file - Detail view system tab: show detected WM_CLASS, allow manual override --- ### F18. Download Verification Helper **Complexity:** Significant (4 hrs) **Problem:** No way to verify AppImage authenticity. **Files:** New `src/core/verification.rs`, `src/ui/detail_view.rs`, `src/ui/drop_dialog.rs` **Changes:** - AppImage signature spec: GPG signature embedded at offset stored in ELF section - New module `verification.rs`: ```rust pub enum VerificationStatus { SignedValid { signer: String }, SignedInvalid { reason: String }, Unsigned, ChecksumMatch, ChecksumMismatch, NotChecked, } pub fn check_embedded_signature(path: &Path) -> VerificationStatus pub fn verify_sha256(path: &Path, expected: &str) -> VerificationStatus ``` - `check_embedded_signature`: runs `unsquashfs -l` to check for `.sha256` or signature files, or uses the AppImage --appimage-signature flag - Drop dialog: after adding, show verification badge - Detail view: "Verification: Signed by ..." or "Unsigned - verify manually" - Manual verification: paste SHA256 hash, compare with computed hash - New DB column: `verification_status TEXT` on appimages --- ### F19. First-Run Permission Summary **Complexity:** Medium (3 hrs) **Problem:** Users don't know what an AppImage can access before running it. **Files:** New `src/ui/permission_dialog.rs`, `src/ui/detail_view.rs` **Changes:** - Before first launch of any AppImage (check `launch_events` count = 0): 1. Show permission dialog: - "MyApp will run with full access to your files and system" - Icon + app name - If `has_firejail()`: offer sandbox options - "Run without restrictions" (default for now) - "Run in Firejail sandbox (recommended)" - "Don't show this again" checkbox (stored per-app in DB) 2. If user chooses Firejail: set `sandbox_mode = 'firejail'` in DB - New DB column: `first_run_prompted INTEGER NOT NULL DEFAULT 0` - Skip dialog if `first_run_prompted = 1` - Preferences: global toggle to disable first-run prompts --- ### F20. Default App Selector **Complexity:** Medium (3 hrs) **Problem:** Can't set AppImage as default browser, mail client, etc. **Files:** `src/ui/detail_view.rs`, `src/core/integrator.rs` **Changes:** - Detect app capabilities from categories: - `WebBrowser` -> can be default web browser - `Email` -> can be default email client - `FileManager` -> can be default file manager - `TerminalEmulator` -> can be default terminal - Detail view overview tab: "Set as default" section (only shown for applicable categories) - Setting defaults: - Browser: `xdg-settings set default-web-browser driftwood-{id}.desktop` - Email: `xdg-mime default driftwood-{id}.desktop x-scheme-handler/mailto` - File manager: `xdg-mime default driftwood-{id}.desktop inode/directory` - Before setting: query current default, store in `system_modifications` with `previous_value` - Uninstall: restore previous defaults via `undo_all_modifications()` - Requirements: app must be integrated first (needs .desktop file) --- ### F21. Multi-User / System-Wide Install **Complexity:** Significant (4 hrs) **Problem:** Can't install for all users on a shared computer. **Files:** `src/core/integrator.rs`, `src/ui/detail_view.rs`, polkit policy **Changes:** - Reuses polkit policy from F15 (add new action `app.driftwood.Driftwood.system-install`) - "Install system-wide" option in detail view (requires app to be integrated first) - Flow: 1. `pkexec cp {appimage} /opt/driftwood-apps/{filename}` 2. `pkexec chmod 755 /opt/driftwood-apps/{filename}` 3. Generate system .desktop in `/usr/share/applications/driftwood-{id}.desktop` 4. `pkexec cp {desktop_file} /usr/share/applications/` 5. Copy icon to `/usr/share/icons/hicolor/` via pkexec 6. `pkexec update-desktop-database /usr/share/applications/` - Register all paths as `system_desktop`, `system_icon`, `system_binary` in system_modifications - Uninstall system-wide: `pkexec rm` for each tracked path - DB flag: `system_wide INTEGER NOT NULL DEFAULT 0` --- ### F22. CLI Enhancements **Complexity:** Medium (3 hrs) **Problem:** Missing install-from-URL and update-all commands. **Files:** `src/cli.rs` **New commands:** - `driftwood install ` - Download from URL, validate, move to ~/Applications, set executable, register, optionally integrate - `driftwood update --all` - Check all apps, download and apply available updates - `driftwood autostart --enable/--disable` - Toggle autostart for an AppImage - `driftwood purge` - Remove ALL system modifications for all managed apps (for Driftwood removal) - `driftwood verify ` - Check embedded signature or compare SHA256 --- ### F23. Portable Mode / USB Drive Support **Complexity:** Major (6-8 hrs) **Problem:** Can't manage AppImages on removable media. **Files:** New `src/core/portable.rs`, `src/ui/library_view.rs`, `src/window.rs`, `src/core/database.rs` **Changes:** - New GSettings key: `watch-removable-media` type b, default false - `portable.rs`: ```rust pub fn detect_removable_mounts() -> Vec pub fn scan_mount_for_appimages(mount: &MountInfo) -> Vec pub fn is_path_on_removable(path: &Path) -> bool ``` - Detection: parse `/proc/mounts` for removable media (type vfat, exfat, ntfs on /media/ or /run/media/) - Alternative: use `gio::VolumeMonitor` to watch for mount/unmount events - DB changes: - New column: `is_portable INTEGER DEFAULT 0` - New column: `mount_point TEXT` - Library view: "Portable" filter/section showing apps on removable media - When drive unmounts: grey out those apps, mark as unavailable - When drive mounts: re-scan and refresh - Portable apps skip the "copy to ~/Applications" step - they stay on the drive - Integration: .desktop files use the full path (may break when unmounted - show warning) --- ### F24. "Similar to..." Recommendations **Complexity:** Medium (3 hrs, depends on F26) **Problem:** No app discovery within the tool. **Files:** `src/ui/detail_view.rs`, `src/core/database.rs` **Changes:** - Requires catalog data from F26 (catalog_apps table populated) - New DB query: `find_similar_apps(categories: &[String], exclude_id: i64, limit: i32) -> Vec` - Matches on shared categories, weighted by specificity - Detail view overview tab: "You might also like" section at the bottom - Shows up to 5 catalog apps with icon, name, one-line description - Click opens catalog detail page (F26) - Falls back to nothing if catalog is empty --- ### F25. AppImageHub In-App Catalog Browser **Complexity:** Major (8-12 hrs) **Problem:** No way to discover and install new AppImages from within the app. **Data source:** `https://appimage.github.io/feed.json` (~1500 apps) **Files:** New `src/ui/catalog_view.rs`, new `src/ui/catalog_detail.rs`, `src/core/database.rs`, `src/window.rs` **Architecture:** #### Data Layer - Fetch `feed.json` and parse into `catalog_apps` table (already exists in DB schema) - Store in `catalog_sources` as source record - Fields mapped from feed.json: - `name` -> `name` - `description` -> `description` - `categories` -> `categories` (joined with ;) - `authors[0].name` -> `author` - `license` -> `license` - `links` (type=GitHub) -> `repository_url` - `links` (type=Download) -> `download_url` - `icons[0]` -> `icon_url` (prefix with `https://appimage.github.io/database/`) - `screenshots` -> `screenshots` (JSON array) - Refresh: on first open, then daily if enabled - New GSettings key: `catalog-last-refreshed` type s, default '' #### Catalog Browse View (NavigationPage) - Header: search bar + category filter chips - Categories from feed: AudioVideo, Development, Education, Game, Graphics, Network, Office, Science, System, Utility - Grid of catalog app cards (reuse app_card pattern): - Icon (fetched from URL, cached locally) - App name - Short description (first line) - Category badge - Pagination: load 50 at a time, "Load more" button - Search: filters by name and description (client-side, data is local) #### Catalog App Detail (NavigationPage pushed on click) - App icon (large) - Name, author, license - Full description - Screenshots carousel (if available) - "Install" button (suggested style) - Source link (opens GitHub/website in browser) #### Install Flow 1. User clicks "Install" 2. Resolve download URL: - If `download_url` points to GitHub releases page: fetch latest release via GitHub API (`https://api.github.com/repos/{owner}/{repo}/releases/latest`), find .AppImage asset - If direct link: use as-is 3. Download with progress bar (reqwest or gio file download) 4. Validate: check AppImage magic bytes 5. Move to ~/Applications, set executable 6. Register in DB with `source_url` set 7. Run full analysis pipeline 8. Optionally integrate (based on `auto-integrate` setting) 9. Navigate to the app's detail view in library #### Navigation - Main sidebar/navigation: add "Catalog" entry alongside Dashboard and Library - Or: floating action button on library view "Browse catalog" --- ## New GSettings Keys Summary | Key | Type | Default | Range/Choices | Feature | |-----|------|---------|---------------|---------| | `update-check-interval-hours` | i | 24 | 1-168 | F11 | | `last-update-check` | s | '' | - | F11 | | `catalog-last-refreshed` | s | '' | - | F25 | | `watch-removable-media` | b | false | - | F23 | | `show-first-run-prompt` | b | true | - | F19 | ## New Database Columns Summary | Table | Column | Type | Default | Feature | |-------|--------|------|---------|---------| | appimages | previous_version_path | TEXT | NULL | F3 | | appimages | source_url | TEXT | NULL | F4 | | appimages | autostart | INTEGER | 0 | F8 | | appimages | startup_wm_class | TEXT | NULL | F17 | | appimages | verification_status | TEXT | NULL | F18 | | appimages | first_run_prompted | INTEGER | 0 | F19 | | appimages | system_wide | INTEGER | 0 | F21 | | appimages | is_portable | INTEGER | 0 | F23 | | appimages | mount_point | TEXT | NULL | F23 | ## New Files Summary | File | Feature | Purpose | |------|---------|---------| | `src/ui/fuse_wizard.rs` | F15 | FUSE installation wizard dialog | | `src/ui/batch_update_dialog.rs` | F12 | Batch update progress dialog | | `src/ui/permission_dialog.rs` | F19 | First-run permission summary | | `src/ui/catalog_view.rs` | F25 | Catalog browse/search page | | `src/ui/catalog_detail.rs` | F25 | Catalog app detail page | | `src/core/verification.rs` | F18 | Signature and checksum verification | | `src/core/portable.rs` | F23 | Removable media detection and management | | `data/app.driftwood.Driftwood.policy` | F15, F21 | Polkit policy for privileged operations | ## Implementation Order The features are numbered F1-F25 in order of complexity. Implement sequentially - some later features depend on earlier ones: - F25 (Catalog) depends on nothing but is largest - F24 (Similar to) depends on F25 - F12 (Update All) depends on F11 (Background checks) - F13 (Full Uninstall) depends on system_modifications table (core architecture) - F20 (Default App) depends on F16 (MIME associations) **Critical path:** Core architecture (system_modifications) -> F1-F7 quick wins -> F8-F14 medium features -> F15-F22 significant features -> F23-F25 major features