Add design doc for tags, export/import, and changelog improvements
This commit is contained in:
166
docs/plans/2026-03-01-final-ux-improvements-design.md
Normal file
166
docs/plans/2026-03-01-final-ux-improvements-design.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# 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:**
|
||||||
|
|
||||||
|
```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<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)
|
||||||
Reference in New Issue
Block a user