Files
driftwood/docs/plans/2026-03-01-final-ux-improvements-design.md

6.6 KiB

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<String> 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<usize, BackupError>
pub fn import_app_list(db: &Database, path: &Path) -> Result<ImportResult, BackupError>

Where ImportResult contains:

  • matched: usize - apps found and metadata merged
  • missing: Vec<String> - app names/paths not found on disk

JSON format v2:

{
  "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<String> 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)