Files
yellowjacket/.planning/phases/17-single-track-edit/17-02-SUMMARY.md
T
yonlu b0976c9217 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
2026-03-18 11:21:46 -04:00

161 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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*