# Final UX Improvements Design: Tags, Export/Import, Changelog ## Overview Three remaining UX improvements from the 30-item enhancement list: - **#12** App grouping with tags + grouped library sections - **#13** Unified export/import (shared core for CLI + GUI) with extended fields - **#14** Bridge changelog gap (GitHub release notes -> release_history) + updates view preview ## #12: Tags + Grouped Library Sections ### Current State - `tags: Option` field exists in `AppImageRecord` (database.rs:50) - `update_tags(id, tags)` DB method exists (database.rs:2069) - No UI for viewing, editing, or filtering by tags - Library view is flat (grid or list mode, no sections) ### Design **Detail view - tag editor:** - Add a "Tags" row in the detail view info section - Display existing tags as pill-shaped chips with "x" remove buttons - A "+" button at the end opens an inline text entry to add a new tag - Tags stored as comma-separated string in the existing `tags` column - On add/remove, call `db.update_tags()` immediately (no save button) **Library view - tag filter chips:** - Add a horizontal scrollable chip bar at the top of the library view (same pattern as catalog category chips) - "All" chip selected by default, plus one chip per unique tag found across all apps - Selecting a tag filters the view to only apps with that tag - Chip bar is only visible when at least one app has tags **Library view - grouped sections:** - When no search is active and no tag filter is selected, group apps by their first tag under collapsible section headers - Apps with no tags go under an "Uncategorized" section at the bottom - Sort dropdown still works within each group - In search mode or tag-filter mode, grouping is disabled (flat list) **Data model:** No schema changes. Comma-separated tags in existing column. ### Files Modified | File | Changes | |------|---------| | `src/ui/detail_view.rs` | Add tag editor chips row in info section | | `src/ui/library_view.rs` | Add tag filter chip bar, grouped section headers | | `src/core/database.rs` | Add `get_all_tags()` query to collect distinct tags | ## #13: Unified Export/Import (CLI + GUI) ### Current State - CLI-only export/import in `cli.rs` (lines 887-1000+) - JSON format v1 with limited fields: path, app_name, app_version, integrated, notes, categories - No GUI file picker dialogs for export/import - No shared module - logic is inline in CLI handlers ### Design **Shared core module: `src/core/backup.rs`** New module with two public functions: ``` pub fn export_app_list(db: &Database, path: &Path) -> Result pub fn import_app_list(db: &Database, path: &Path) -> Result ``` Where `ImportResult` contains: - `matched: usize` - apps found and metadata merged - `missing: Vec` - app names/paths not found on disk **JSON format v2:** ```json { "version": 2, "exported_at": "2026-03-01T10:00:00Z", "appimages": [ { "path": "/home/user/Applications/MyApp.AppImage", "app_name": "MyApp", "app_version": "1.0.0", "integrated": true, "notes": "user notes", "categories": "Graphics;Utility;", "tags": "work,design", "pinned": false, "launch_args": "--no-sandbox", "sandbox_mode": "permissive", "autostart": false } ] } ``` Graceful v1 handling: missing fields default to None (not overwritten on import). **Import merge semantics:** - Match apps by path (exact match) - For matched apps, merge each field: only overwrite if the import value is non-empty/non-null AND the existing value is empty/null (preserve user's current data) - Tags: merge (union of existing + imported tags, no duplicates) - For unmatched paths: collect into `missing` list **CLI (cli.rs):** - `cmd_export()` and `cmd_import()` become thin wrappers calling `backup::export_app_list()` / `backup::import_app_list()` - Same CLI flags and output messages **GUI (window.rs):** - Add "Export app list" and "Import app list" items to the hamburger menu - Export: `gtk::FileDialog` save picker, default filename `driftwood-apps.json`, calls `backup::export_app_list()`, toast on success - Import: `gtk::FileDialog` open picker with `.json` filter, calls `backup::import_app_list()`, toast showing "Imported N apps" + dialog listing missing apps if any - Refresh library view after import ### Files Modified | File | Changes | |------|---------| | `src/core/backup.rs` | New module: export/import logic, JSON v2 format | | `src/core/mod.rs` | Add `pub mod backup;` | | `src/cli.rs` | Refactor cmd_export/cmd_import to call backup module | | `src/window.rs` | Add menu items + file picker dialogs for export/import | ## #14: Bridge Changelog Gap + Updates View Preview ### Current State - `release_history: Option` field exists in AppImageRecord (JSON array) - Detail view already renders release history when data exists (detail_view.rs:760-820) - AppStream parsing populates release_history for local metainfo (rare) - GitHub enrichment fetches release info but does NOT write to release_history - Updates view shows update-available cards but no changelog preview ### Design **Enrichment bridge (github_enrichment.rs):** - After `enrich_app_release_info()` fetches the latest release, also fetch up to 10 recent releases from the GitHub releases API - Convert each release to `ReleaseInfo { version, date, description }` format - Serialize as JSON array and write to `release_history` column - Only update if the field is currently empty (don't overwrite AppStream data which may be more authoritative) - The existing detail view Release History section will then automatically display this data for all GitHub-enriched apps **New DB method:** - `update_release_history(id: i64, history_json: &str)` - simple UPDATE on the release_history column **Updates view changelog preview (updates_view.rs):** - Each update card gets an `adw::ExpanderRow` "What's new" section below the existing content - If `release_history` is populated, find the entry matching `latest_version` and show its description - If no matching entry or no release_history data: show "Release notes not available" in dim text - Collapsed by default to keep the view compact ### Files Modified | File | Changes | |------|---------| | `src/core/github_enrichment.rs` | Fetch recent releases, write to release_history | | `src/core/database.rs` | Add `update_release_history()` method | | `src/ui/updates_view.rs` | Add expandable "What's new" section to update cards | ## Implementation Order 1. **#14 first** - Smallest scope, self-contained enrichment + UI change 2. **#12 second** - Tags are self-contained, no dependency on other features 3. **#13 last** - Export should include tags (depends on #12 being functional)