docs(17-02): complete track details save flow & cover art editing plan

- SUMMARY.md with 5 commits (1 feat + 4 fixes during verification)
- STATE.md updated: Phase 17 complete, decisions added
- ROADMAP.md updated: Phase 17 2/2 Complete
- REQUIREMENTS.md updated: EDIT-02, EDIT-03, EDIT-04 all complete
This commit is contained in:
2026-03-18 11:21:46 -04:00
parent 8cd4914842
commit b0976c9217
4 changed files with 184 additions and 20 deletions
+4 -4
View File
@@ -31,8 +31,8 @@ Requirements for v1.2 Tag Editing milestone. Each maps to roadmap phases.
### Single Track Edit
- [x] **EDIT-01**: User can open tag editor for a single track from context menu or detail view
- [ ] **EDIT-02**: Editor shows all 8 editable fields with current values pre-populated
- [ ] **EDIT-03**: Editor shows current cover art with option to replace from image file
- [x] **EDIT-02**: Editor shows all 8 editable fields with current values pre-populated
- [x] **EDIT-03**: Editor shows current cover art with option to replace from image file
- [x] **EDIT-04**: Saving writes tags to file, updates DB, updates FTS5, and refreshes all views immediately
### Batch Edit
@@ -104,8 +104,8 @@ Which phases cover which requirements. Updated during roadmap creation.
| SYNC-03 | Phase 16 | Complete |
| SYNC-04 | Phase 16 | Complete |
| EDIT-01 | Phase 17 | Complete |
| EDIT-02 | Phase 17 | Pending |
| EDIT-03 | Phase 17 | Pending |
| EDIT-02 | Phase 17 | Complete |
| EDIT-03 | Phase 17 | Complete |
| EDIT-04 | Phase 17 | Complete |
| BATCH-01 | Phase 18 | Pending |
| BATCH-02 | Phase 18 | Pending |
+3 -3
View File
@@ -42,7 +42,7 @@
- [x] **Phase 15: Schema Migration & Write Safety** — FTS5 contentless_delete migration and atomic file write utility (completed 2026-03-16)
- [x] **Phase 16: Tag Writing & Database Sync** — Format-specific tag writers (MP3, FLAC, cover art) with inline DB + FTS5 update pipeline (3 plans) (completed 2026-03-17)
- [ ] **Phase 17: Single Track Edit** — End-to-end single track editing: UI → file write → DB sync → view refresh
- [x] **Phase 17: Single Track Edit** — End-to-end single track editing: UI → file write → DB sync → view refresh (completed 2026-03-18)
- [ ] **Phase 18: Batch Edit** — Multi-select batch editing with three-state field model, progress, and batch cover art
- [ ] **Phase 19: OGG Vorbis Tag Writing** — Custom OGG page rewriter for Vorbis Comment tag writing (stretch)
@@ -87,7 +87,7 @@ Plans:
2. The editor displays all 8 editable fields (title, artist, album, genre, year, track number, disc number, composer) pre-populated with the track's current values — empty fields show as empty, not "Unknown"
3. The editor displays the track's current cover art (or a placeholder if none) with a button to select a replacement image file from disk
4. Clicking "Save" writes the changes to the audio file, updates the database and search index, and refreshes all visible views (track list, album view, artist view, genre view, queue, now-playing bar) — the user sees the new metadata everywhere without restarting or rescanning
**Plans:** 1/2 plans executed
**Plans:** 2/2 plans complete
Plans:
- [ ] 17-01-PLAN.md — Backend wiring (WriteTrackTagsByPath, ImageFilePicker) + library store event handler + context menu fix
- [ ] 17-02-PLAN.md — Track details dialog save flow, cover art editing, error handling, human verification
@@ -133,7 +133,7 @@ Plans:
| 14. Performance Optimization | v1.1 | 4/4 | Complete | 2026-03-15 |
| 15. Schema Migration & Write Safety | 2/2 | Complete | 2026-03-16 | - |
| 16. Tag Writing & Database Sync | 3/3 | Complete | 2026-03-17 | - |
| 17. Single Track Edit | 1/2 | In Progress| | - |
| 17. Single Track Edit | 2/2 | Complete | 2026-03-18 | - |
| 18. Batch Edit | v1.2 | 0/? | Not started | - |
| 19. OGG Vorbis Tag Writing | v1.2 | 0/? | Not started | - |
+17 -13
View File
@@ -3,12 +3,12 @@ gsd_state_version: 1.0
milestone: v1.2
milestone_name: Tag Editing
status: in-progress
last_updated: "2026-03-18T01:05:17Z"
last_updated: "2026-03-18T15:18:33Z"
progress:
total_phases: 5
completed_phases: 2
total_plans: 6
completed_plans: 6
completed_phases: 3
total_plans: 7
completed_plans: 7
---
# YellowJacket — Project State
@@ -22,10 +22,10 @@ See: .planning/PROJECT.md (updated 2026-03-16)
## Current Position
Phase: Phase 17 — Single Track Edit (in progress)
Plan: 1 of 3 complete
Status: Completed 17-01 (backend bridge & frontend plumbing)
Last activity: 2026-03-18 — Completed 17-01 (WriteTrackTagsByPath, ImageFilePicker, TrackMetadataChanged handler, context menu fix)
Phase: Phase 17 — Single Track Edit (complete)
Plan: 2 of 2 complete
Status: Phase 17 complete — all single-track edit requirements fulfilled
Last activity: 2026-03-18 — Completed 17-02 (save flow, cover art editing, error handling, Wails deserialization fixes)
### Phase Overview
@@ -33,7 +33,7 @@ Last activity: 2026-03-18 — Completed 17-01 (WriteTrackTagsByPath, ImageFilePi
|-------|--------|
| 15. Schema Migration & Write Safety | Complete (2/2 plans) |
| 16. Tag Writing & Database Sync | Complete (3/3 plans) |
| 17. Single Track Edit | In Progress (1/3 plans) |
| 17. Single Track Edit | Complete (2/2 plans) |
| 18. Batch Edit | Not started |
| 19. OGG Vorbis Tag Writing | Not started |
@@ -63,6 +63,7 @@ Last activity: 2026-03-18 — Completed 17-01 (WriteTrackTagsByPath, ImageFilePi
| 16 | 02 | 20min | 2 | 6 |
| 16 | 03 | 9min | 2 | 7 |
| 17 | 01 | 11min | 2 | 11 |
| 17 | 02 | 25min | 2 | 8 |
## Accumulated Context
@@ -96,6 +97,9 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
| Global genre orphan cleanup via DELETE WHERE id NOT IN | Simpler than tracking old genre IDs; safe because genres only referenced via recording_genres |
| Manually added Wails TypeScript bindings for new Go methods | Wails binding generator runs at `wails dev`/`wails build` time, not via `go generate`; manual addition matches existing pattern exactly |
| Track Details opens for first selected track in multi-select | `filePaths[0]` is consistent across all 4 views; avoids blocking the menu item unnecessarily |
| ReadFile Go method on FrontendUtil for cover art bytes | Native file dialog returns path; frontend needs bytes for blob preview + save payload |
| asInt/asBytes helpers for Wails JSON deserialization | Wails sends JS numbers as float64 and []byte as base64; direct type assertions silently fail |
| Cover art DB sync with content-hash dedup + thumbnail generation | Saves to covers cache dir, upserts cover_art row, updates release_groups.cover_art_id |
### v1.2 Roadmap Decisions
@@ -136,9 +140,9 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
### Last Session
**Date:** 2026-03-18
**What happened:** Executed Phase 17 Plan 01Backend bridge methods (WriteTrackTagsByPath, ImageFilePicker), TrackMetadataChanged handler in LibraryStore, removed selection gate on Track Details context menu in all 4 views.
**Where we stopped:** Completed 17-01-PLAN.md
**Next action:** Execute 17-02 (track-details dialog edit mode wiring)
**What happened:** Completed Phase 17 Plan 02Save flow with diff-only TagChanges, cover art replace/remove via native file picker, inline error handling. Fixed 4 bugs during verification: stale dialog data, Wails float64 deserialization, cover art DB sync, cover art URL refresh.
**Where we stopped:** Completed 17-02-PLAN.md — Phase 17 complete
**Next action:** Plan or execute Phase 18 (Batch Edit)
---
*State initialized: 2026-02-27*
@@ -152,4 +156,4 @@ Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key
| 19 | fix phantom playlist tracks with multi-root path resolution | 2026-03-16 | 9144ded | [19-fix-phantom-playlist-tracks](./quick/19-fix-phantom-playlist-tracks/) |
Last activity: 2026-03-16 - Completed quick task 19: fix phantom playlist tracks with multi-root path resolution
*Last updated: 2026-03-18 — Completed 17-01 (backend bridge & frontend plumbing)*
*Last updated: 2026-03-18 — Completed 17-02 (save flow, cover art editing, Phase 17 complete)*
@@ -0,0 +1,160 @@
---
phase: 17-single-track-edit
plan: 02
subsystem: ui
tags: [tag-editing, cover-art, wails-bindings, lit-element, dialog, file-picker]
# Dependency graph
requires:
- phase: 17-single-track-edit
provides: WriteTrackTagsByPath, ImageFilePicker, TrackMetadataChanged handler, Track Details context menu
provides:
- Complete single-track tag editor with save flow, cover art editing, and error handling
- ReadFile Go method on FrontendUtil for reading cover art image bytes
- DB sync for cover art (save to cache, thumbnail generation, release_group update)
- Dialog data refresh after save (track + cover art URLs)
affects: [18-batch-edit]
# Tech tracking
tech-stack:
added: []
patterns:
- "Diff-only TagChanges map — only changed fields sent to backend, reducing unnecessary writes"
- "Blob URL preview for cover art — instant client-side preview without server round-trip"
- "Base64 decode for Go []byte return values — Wails serializes []byte as base64 JSON strings"
- "asInt/asBytes helpers for Wails JSON deserialization — JavaScript numbers arrive as float64, arrays as []interface{}"
key-files:
created: []
modified:
- frontend/src/components/track-details/track-details.ts
- backend/frontendutil/frontendutil.go
- backend/tagwriter/tagwriter.go
- backend/tagwriter/dbsync.go
- backend/tagwriter/mp3.go
- backend/tagwriter/flac.go
- frontend/wailsjs/go/frontendutil/FrontendUtil.js
- frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts
key-decisions:
- "ReadFile Go method on FrontendUtil to return file bytes to frontend — needed because Wails native file dialog returns path, but frontend needs bytes for preview + save"
- "asInt/asBytes type coercion helpers in tagwriter — Wails JSON deserialization sends all numbers as float64 and byte arrays as base64 strings"
- "Cover art DB sync saves to covers cache directory with content-hash dedup and thumbnail generation"
patterns-established:
- "Wails float64 coercion: always use asInt() helper for numeric TagChanges values, never direct .(int) assertion"
- "Wails []byte handling: Go []byte serializes as base64 JSON string; frontend must atob() decode before use"
requirements-completed: [EDIT-02, EDIT-03, EDIT-04]
# Metrics
duration: 25min
completed: 2026-03-18
---
# Phase 17 Plan 02: Track Details Save Flow & Cover Art Editing Summary
**Diff-only tag save with cover art replace/remove via native file picker, inline error handling, and automatic dialog + view refresh after write**
## Performance
- **Duration:** ~25 min (implementation) + verification session with bug fixes
- **Started:** 2026-03-18T01:08:53Z
- **Completed:** 2026-03-18T14:54:17Z
- **Tasks:** 2 (1 auto + 1 human-verify)
- **Files modified:** 8
## Accomplishments
- Complete save flow: `saveEdit()` builds diff-only TagChanges map and calls `WriteTrackTagsByPath` — unchanged fields are never sent
- Cover art editing: native file picker for JPEG/PNG with instant blob preview via object URL; remove button (×) clears embedded art
- Inline error handling: errors display in the dialog action bar, edit mode stays active for retry or cancel
- Saving state indicator: Save button shows "Saving…" and both buttons disabled during write
- Dialog data refresh: after save, track data and cover art URLs are re-fetched from the library store
- Cover art DB sync: image saved to covers cache with content-hash dedup + thumbnail generation, release_group updated
- Wails JSON deserialization fixes: asInt/asBytes helpers handle float64 numbers and base64 byte arrays
## Task Commits
Each task was committed atomically:
1. **Task 1: Implement saveEdit, cover art editing, error handling, and saving state** - `265a9ea` (feat)
2. **Task 2: Verify complete single-track edit flow** — human-verify checkpoint, APPROVED
**Bug fixes during verification (committed by orchestrator):**
3. **Fix: refresh track-details dialog data after save** - `ffcdc41` (fix)
4. **Fix: handle float64 numeric values from Wails JSON deserialization** - `900db2e` (fix)
5. **Fix: cover art replace and remove (asBytes, DB sync, base64 decode)** - `d7c2965` (fix)
6. **Fix: refresh cover art URLs after save** - `8cd4914` (fix)
## Files Created/Modified
- `frontend/src/components/track-details/track-details.ts` - Complete save flow, cover art editing UI, error handling, saving state, dialog refresh
- `backend/frontendutil/frontendutil.go` - Added ReadFile method for reading cover art bytes
- `backend/tagwriter/tagwriter.go` - Added asInt/asBytes helpers for Wails JSON deserialization
- `backend/tagwriter/dbsync.go` - Cover art DB sync (save image, update release_group, orphan cleanup)
- `backend/tagwriter/mp3.go` - Use asInt/asBytes helpers for type coercion
- `backend/tagwriter/flac.go` - Use asInt/asBytes helpers for type coercion
- `frontend/wailsjs/go/frontendutil/FrontendUtil.js` - Wails binding for ReadFile
- `frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts` - TypeScript declaration for ReadFile
## Decisions Made
- Added `ReadFile` Go method on FrontendUtil to bridge the gap between native file dialog (returns path) and frontend need for bytes (preview + save). Simplest approach that avoids additional Go-side image processing.
- Created `asInt()` and `asBytes()` type coercion helpers in tagwriter package — Wails JSON deserialization always sends JavaScript numbers as Go `float64` and `[]byte` as base64 strings. Direct `.(int)` assertions silently failed.
- Cover art DB sync saves the image to the covers cache directory using content-hash dedup with thumbnail generation, then updates `release_groups.cover_art_id`. Clear sets `cover_art_id` to NULL.
## Deviations from Plan
### Auto-fixed Issues (by orchestrator during verification)
**1. [Rule 1 - Bug] Dialog showed stale track data after save**
- **Found during:** Task 2 (human verification)
- **Issue:** After save, dialog returned to read-only mode but showed pre-edit values because `this.track` was the original snapshot passed via `show()`
- **Fix:** After successful `WriteTrackTagsByPath`, re-fetch tracks from library store and update `this.track` with fresh data
- **Files modified:** `frontend/src/components/track-details/track-details.ts`
- **Committed in:** `ffcdc41`
**2. [Rule 1 - Bug] Numeric fields silently ignored during save**
- **Found during:** Task 2 (human verification)
- **Issue:** Wails JSON deserialization sends all JavaScript numbers as Go `float64`. All `.(int)` type assertions on year, track_number, and disc_number silently failed (returned zero-value + false), meaning numeric edits were dropped
- **Fix:** Added `asInt()` helper that handles both `float64` and `int` types; replaced all direct `.(int)` assertions across tagwriter package
- **Files modified:** `backend/tagwriter/tagwriter.go`, `backend/tagwriter/dbsync.go`, `backend/tagwriter/mp3.go`, `backend/tagwriter/flac.go`
- **Committed in:** `900db2e`
**3. [Rule 1 - Bug] Cover art replace and remove did not work**
- **Found during:** Task 2 (human verification)
- **Issue:** Three related issues: (a) Cover art bytes from frontend arrived as `[]interface{}` of `float64` — same deserialization issue as numerics. (b) DB sync for cover art was a placeholder no-op — didn't save image to cache or update release_group. (c) Frontend `ReadFile` returns base64 string (Go `[]byte` JSON encoding), not `number[]` — preview blob was corrupted.
- **Fix:** Added `asBytes()` helper for `[]interface{}``[]byte` conversion. Implemented full cover art DB sync (save to covers cache with content-hash dedup + thumbnail generation, upsert cover_art row, update release_groups). Fixed frontend to decode base64 with `atob()` before creating `Uint8Array`.
- **Files modified:** `backend/tagwriter/tagwriter.go`, `backend/tagwriter/dbsync.go`, `backend/tagwriter/flac.go`, `backend/tagwriter/mp3.go`, `frontend/src/components/track-details/track-details.ts`
- **Committed in:** `d7c2965`
**4. [Rule 1 - Bug] Cover art image reverted to old after save**
- **Found during:** Task 2 (human verification)
- **Issue:** After save, dialog refreshed `this.track` but kept stale `this.coverArt` URLs pointing to old content-hash files. The image visually reverted until the dialog was closed and reopened.
- **Fix:** After save, re-fetch albums alongside tracks and re-resolve cover art URLs from updated album data
- **Files modified:** `frontend/src/components/track-details/track-details.ts`
- **Committed in:** `8cd4914`
---
**Total deviations:** 4 auto-fixed (all Rule 1 bugs discovered during human verification)
**Impact on plan:** All fixes were necessary for correct end-to-end functionality. The Wails JSON deserialization issues (float64 numbers, base64 bytes) were a systemic pattern not visible until real runtime testing. No scope creep — all fixes are within the plan's boundary.
## Issues Encountered
None beyond the deviations documented above. All issues were discovered and resolved during the human verification checkpoint.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 17 is now complete (2/2 plans done)
- Single-track editing works end-to-end: tag writes, cover art replacement/removal, error handling, view refresh
- Ready for Phase 18 (Batch Edit) which builds on this foundation
- The `asInt()`/`asBytes()` Wails deserialization helpers established in this plan will be essential for Phase 18
## Self-Check: PASSED
All 6 key files verified on disk. All 5 commits verified in git history.
---
*Phase: 17-single-track-edit*
*Completed: 2026-03-18*