Commit Graph
62 Commits
Author SHA1 Message Date
rin.kagamine 8218234eb9 feat: Full audio player with controls and scrubber
Player.tsx completely rewritten with professional controls:
- Play/Pause: large circular button with play/pause icons
- Stop: square icon, resets audio to 0 and pauses
- Restart: circular arrow, jumps to beginning and plays
- Time display: elapsed / total (M:SS format, tabular-nums)
- Scrubber: clickable progress bar with drag support
  - Visual progress fill (lyria-accent)
  - White scrubber handle on hover
  - Mouse drag anywhere on page while holding
- Volume: mute toggle + range slider (0-1, step 0.01)
  - Three volume icons: full, half, muted
  - Custom styled range input with white thumb
- Track info: title or prompt (hidden on mobile)
- Close button: X icon
- All buttons have title tooltips
- Uses useRef for audio element, useState for UI state
- useCallback for seek handler, useEffect for drag events
- Responsive: track info hidden on small screens
2026-05-29 20:28:20 -04:00
rin.kagamine 4fab947d6c fix: Clean up SettingsModal layout
- Both danger zone rows now use flex items-start gap-4 for consistent
  text+button alignment with proper breathing room
- Added shrink-0 to buttons so they don't compress
- Added whitespace-nowrap to Delete All button so text doesn't wrap
- Shortened confirm button text to just 'Confirm' (was 'Confirm Delete')
- Moved confirmation warning inline under the description text instead
  of as a separate block below, keeping the row compact
- Shortened description text: 'Remove N clips. Cannot be undone.'
2026-05-29 20:23:33 -04:00
rin.kagamine e74c89e1b0 feat: Settings modal with danger zone admin operations
Backend:
- POST /api/admin/clear-waveforms — deletes all cached waveform SVGs
- POST /api/admin/delete-all-clips — nuclear option: deletes all clips,
  MP3s, artwork, and waveforms. Returns deleted count.
- database.py: delete_all_clips() for bulk DB wipe
- Clip deletion cascade-removes waveform SVGs

Frontend:
- SettingsModal.tsx: center-stage modal with backdrop blur (bg-black/60
  backdrop-blur-sm). Danger zone with red-styled border and header.
- Two-step confirmation for Delete All (click once to arm, click again
  to confirm). Shows clip count dynamically.
- Gear icon trigger in header bar
- App.tsx: handleClearWaveforms + handleDeleteAll handlers

Docs:
- Architecture KB updated with album art, waveform, settings modal sections
- Tasks KB updated with T-021 (waveform) and TASK-001 (album art) marked done
- T-026 (settings modal) added to backlog
2026-05-29 20:21:05 -04:00
rin.kagamine 744f0ae6e5 feat: Redesign waveform as amplitude-over-time visualization
Replaces 64-band FFT frequency spectrum with 200-bar amplitude envelope:
- RMS amplitude per time slice — shows actual track dynamics
- Quiet intros, loud drops, silence gaps all visible
- Per-track normalization — each track uses its full dynamic range
- Gentle compression (power 0.7) so quiet parts remain visible
- 3-point weighted smoothing preserves dynamics while looking polished
- Time-progressive HSL gradient: deep amber (20°) → bright orange (40°)
- Single centered bars with rounded caps — classic waveform look
- 160px height, 800px width, tight 1px gap for dense visualization
- Cleared cached waveforms so all clips regenerate with new algorithm
2026-05-29 20:15:50 -04:00
rin.kagamine 8f5c8a87d1 feat: Add themed FFT waveform visualization to detail panel
Backend:
- Install ffmpeg + numpy in Docker image
- New waveform.py: FFT-based SVG generator with 64 frequency bands
- GET /api/clips/{id}/waveform — generates on demand, caches to /data/waveforms/
- DELETE /api/clips/{id} now cascade-removes waveform SVG
- Warm amber-to-orange HSL gradient (25° to 35° hue)
- Mirrored bars: top full opacity, bottom 60% opacity

Frontend:
- DetailPanel shows waveform image beneath album art (80px height)
- Only shown for clips with status='ready'

Storage: /data/waveforms/ persisted via existing ./data volume mount
2026-05-29 20:11:01 -04:00
rin.kagamine 3c6a5d1a84 fix: Add onSelect click handler to ribbon view rows
Ribbon view was missing the onClick handler that opens the detail panel.
Tile view had it; ribbon view did not. Now both views open the slide-out
panel when clicking a clip row/card.
2026-05-29 19:57:07 -04:00
rin.kagamine 5868be5f93 fix: Move album art upload button out of ribbon thumbnail
Ribbon view artwork hover now shows only play button (larger, cleaner).
Upload art button moved to action row alongside rename/delete.
Order: rename → upload art → delete. All buttons 4x4, consistent spacing.
2026-05-29 19:55:06 -04:00
rin.kagamine 069170398f fix: Use proper Unicode treble clef (𝄞) for album art placeholders
Replaces broken SVG path with Unicode U+1D11E treble clef symbol
rendered via SVG text element. Applied to all three locations:
- Gallery ribbon: small treble clef (5x5)
- Gallery tile: medium treble clef (10x10)
- DetailPanel: large treble clef (14x14)
2026-05-29 19:41:49 -04:00
rin.kagamine c21a5f50e0 feat: Replace placeholder icons with treble clef SVG
Album art placeholders now show a treble clef (Rin's signature)
instead of generic icons. Applied to all three locations:
- Gallery ribbon: small treble clef (5x5)
- Gallery tile: medium treble clef (10x10)
- DetailPanel: large treble clef (14x14) with 'No album art' label
2026-05-29 18:58:34 -04:00
rin.kagamine 8ba7133269 feat: Add album artwork support
- Backend: artwork_filename column on clips table (auto-migration)
- Backend: PATCH /api/clips/{id}/artwork (upload image, 5MB limit)
- Backend: GET /api/clips/{id}/artwork (serve image with correct content-type)
- Backend: DELETE /api/clips/{id}/artwork (remove artwork file + null out DB)
- Backend: DELETE /api/clips/{id} now cascade-removes artwork file
- Frontend: Clip interface includes artwork_filename
- Frontend: Gallery ribbons show artwork thumbnail with hover overlay (play + upload)
- Frontend: Gallery tiles show artwork with hover overlay (play + upload)
- Frontend: DetailPanel shows full artwork with upload/change/remove controls
- Frontend: App.tsx handleUploadArtwork + handleRemoveArtwork handlers
- Storage: /data/artwork/ directory for image files
2026-05-29 18:27:28 -04:00
rin.kagamine 23672387e5 Fix Lyria API response parsing — audio in parts[1], not parts[0]
The Lyria API returns a candidates[].content.parts[] array where:
- parts[0] = text metadata (e.g. '<instrumental>')
- parts[1] = inlineData with the actual audio payload

Previous code only checked parts[0] for inlineData, which always failed
with 'No audio data in response'. Fixed by iterating all parts to find
the inlineData entry.

Successfully generated first real audio clip (lo-fi hip hop, 30s, 728KB MP3).

Also includes: .env.example, requirements updates, docker-compose fixes,
frontend vite config, logger setup.
2026-05-29 16:59:28 -04:00
rin.kagamine a2bc785b14 Initial scaffold: FastAPI backend + React frontend + Docker Compose
- Backend: FastAPI with SQLite, clip CRUD, health check
- Frontend: React + Vite + TypeScript + Tailwind, dark theme
- Components: Generator, Gallery, Player
- Docker: Compose setup for backend + frontend
- Architecture docs in Palace KB
2026-05-26 18:18:55 -04:00