Design Prot Merge #1

Merged
NotS1mp merged 217 commits from design-prot into master 2026-09-21 15:48:16 +00:00
2 changed files with 233 additions and 0 deletions
Showing only changes of commit 9b866cb5f1 - Show all commits
+75
View File
@@ -0,0 +1,75 @@
# Handoff: Musify (App prototype)
## Overview
Musify is a music streaming app: no ads, upload your own files, import from other platforms, adaptive-bitrate HLS streaming, offline downloads, and user playlists. This bundle covers the **app prototype** only (`Musify.dc.html`) — the in-app experience (sidebar nav, home, search, library, upload, import, downloads, playlists, and a persistent bottom player). The marketing landing page is a separate deliverable and is not included here.
## About the Design File
`Musify.dc.html` is a **design reference built in HTML** — an interactive prototype showing the intended look, layout, and behavior. It is **not production code to copy directly**. The task is to **recreate this design in your real codebase** using your framework's component model and state management (stores, hooks, etc.) and your project's existing styling approach — not to embed the HTML as-is.
## Fidelity
**High-fidelity (hifi)**: colors, typography, spacing, and interactions are final-intent. Recreate pixel-close; adapt only where your framework's conventions differ (e.g. CSS scoping, component boundaries, routing).
## Design System
- **Background**: near-black `#08080a` throughout (not pure gray — avoid `#111`/`#222` grays; use this near-black plus translucent white overlays for elevation, e.g. `rgba(255,255,255,0.035)`–`rgba(255,255,255,0.09)`).
- **Accent color**: **dynamic** — derived from the currently-playing track's "hue" (`oklch(72% 0.17 <hue>)`), so the whole UI (nav highlight, progress bar, buttons, profile avatar) subtly recolors per song. There is no fixed brand accent in the app; drive this from real track metadata/album art in production (e.g. extracted dominant color), or fall back to a fixed brand purple if per-track theming isn't desired.
- **Typography**: headings in **Sora** (600–900 weight), body/UI text in **Manrope** (400–700 weight). Both loaded from Google Fonts.
- **Text color**: primary text `#f5f4f2`; secondary/subtitle text `rgba(245,244,242,0.7–0.85)` (kept fairly bright, not dimmed gray); tertiary/meta text down to `~0.4–0.55`.
- **Corners**: generous radii — 6–14px on small controls/rows, 100px (pill) on avatars/buttons/badges.
- **Cover art / track thumbnails**: gradient placeholders (`linear-gradient(135deg, oklch(70% 0.15 hue), oklch(36% 0.13 hue+28))`) with a diagonal repeating-stripe overlay — stand-ins for real album art (network image).
- **Icons**: all hand-built from divs (no icon font/SVG library) — simple geometric shapes (house, magnifying glass, bars, triangles, circles). Recreate as inline SVG or an icon library for maintainability.
- **Layout philosophy**: content areas fill the available width (search results, library list, playlist grids, "acceso rápido" grid all stretch to 100%/auto-fill rather than being capped at a narrow max-width) — avoid re-introducing narrow `max-width` caps when porting.
## Layout
CSS grid, `grid-template-columns: 264px 1fr`, `grid-template-rows: 1fr 92px`, `grid-template-areas: 'sidebar main' 'player player'`, fixed to viewport (`position:fixed; inset:0`).
### Sidebar (264px, left)
- Wordmark "Musify" (Sora 800, 20px) top-left — no icon/logo mark, text only.
- Nav list: Inicio (house icon), Buscar (magnifying-glass icon), Tu biblioteca (bars icon), Playlists (list icon); then a divider; then Subir música, Importar, Descargas. Active item: `rgba(255,255,255,0.08)` background pill, icon/text tinted with the current accent color; inactive: `rgba(245,244,242,0.5–0.62)`.
- Footer: profile row — circular avatar (gradient, offset hue from current track) + name "Alex Rivera" + "@alexrivera" handle, in a subtle rounded card, clickable (wire to a real account menu).
### Main content (right of sidebar, scrollable)
Seven views, toggled by sidebar nav (single active view at a time — replace with real routing):
1. **Inicio (Home)** — blurred color-wash hero (current track's gradient), greeting + headline "Tu música. Sin límites."; "Acceso rápido" grid (auto-fill, min 260px per card) of 6 track rows; "Recomendado para ti" horizontal scroll rail of 168×168 cards with a hidden-but-functional scrollbar.
2. **Buscar (Search)** — full-width search input filtering a track list live by title/artist; empty state message when no match. List and input both stretch to 100% width.
3. **Tu biblioteca (Library)** — full-width flat list of all tracks (built-in + uploaded), "Subida"/"Importada" source badges where applicable.
4. **Subir música (Upload)** — dashed drop-zone (click simulates an upload adding a fake track), list of uploaded tracks below.
5. **Importar (Import)** — 3 cards for external sources (cloud storage, external streaming account, other device), each with a "Conectar"/"Conectado" toggle pill.
6. **Descargas (Downloads)** — storage usage bar (used/2GB), list of tracks marked offline with per-track download progress (0–100%, simulated) and a circular toggle button.
7. **Playlists** — grid of playlist cards (2×2 color-mosaic thumbnail from track hues + name + track count) plus a "create playlist" input+button. Clicking a card opens **Playlist Detail**: editable title (inline text input), 2×2 mosaic art, track list with per-track remove (×), and an "Añadir canciones" list of tracks not yet in the playlist (click to add).
### Player bar (bottom, full width, 92px)
Same background treatment as sidebar. Fluid three-column layout (`minmax(200px,1fr) minmax(0,3fr) minmax(200px,1fr)`) so the transport/seek area grows on wide screens:
- Left: current track art (52px) + title/artist.
- Center: transport controls (prev/play-pause/next, hand-drawn triangles) and a seek bar — click-to-seek with a smooth animated fill (`transition: width 0.6s cubic-bezier(0.22,0.61,0.36,1)`), current/total time labels, now stretching up to 900px wide.
- Right: a thin custom volume slider styled to match the seek bar (not the default OS slider) — track is a gradient split at the volume %, small circular thumb via `::-webkit-slider-thumb`/`::-moz-range-thumb`.
## Interactions & Behavior
- **View switching**: clicking a sidebar item sets `state.view` and conditionally renders one of the 7 main-content sections; no real routing — replace with SvelteKit routes / React Router / etc.
- **Playback simulation**: a 300ms interval advances `progress` when `playing`; on reaching track duration it auto-advances to the next track and loops. Clicking a track toggles play/pause if it's already current, else switches to it and starts playing. Replace with a real `<audio>`/HLS.js player.
- **Seek**: click position on the progress bar computes a fraction of its width and sets `progress` to that fraction × duration; the fill animates smoothly rather than jumping.
- **Volume**: native `<input type="range">`, custom-styled; value drives a linear-gradient split on the track itself.
- **Offline downloads**: clicking the toggle starts a simulated download (progress increments ~14% every 300ms until 100%/"done"); clicking again while done removes it. Storage-used bar is derived from count of "done" downloads × ~0.18GB (placeholder math — replace with real file sizes).
- **Upload**: clicking the drop-zone appends a fake track to `state.uploads` (no real file picker/parsing) — wire up a real file input + upload pipeline.
- **Import**: toggling a service just flips a boolean per source — no real OAuth/integration.
- **Playlists**: create (name input + button) → adds a playlist and opens it; rename via inline text input on the detail header; delete button removes it and returns to the list; add/remove track buttons mutate `trackIds` on the playlist.
## State Management (what to model)
- `currentTrackId`, `playing`, `progress` (seconds), `volume` (0–100), `network` (currently cycled manually — in production drive from actual measured/adaptive HLS bitrate).
- `view` (active section) — replace with real routing.
- `tracks` (built-in + uploaded, merged) — replace with real catalog/uploaded-file data from your backend.
- `offline: Record<trackId, {status, percent}>` — replace simulated progress with real download-manager state.
- `playlists: {id, name, trackIds}[]`, `activePlaylistId`.
## Design Tokens
- **Colors**: background `#08080a`; text `#f5f4f2`; text-secondary `rgba(245,244,242,0.7–0.85)`; text-tertiary `rgba(245,244,242,0.4–0.55)`; surfaces `rgba(255,255,255,0.035–0.09)`. Accent computed at runtime: `oklch(72% 0.17 <trackHue>)`.
- **Typography**: Sora 600/700/800/900 for headings; Manrope 400/500/600/700 for body/UI.
- **Radii**: 6–14px (small UI), 100px (pills/avatars).
- **Spacing**: main content padding `44px 48px`; sidebar padding `26px 18px`.
## Assets
No external images — all art (track covers, avatar) is CSS-gradient placeholders. Replace with real album art / user avatar images before shipping. Icons are hand-built divs; consider swapping for an SVG icon set in your production build.
## Files
- `Musify.dc.html` — app prototype (self-contained; open directly in a browser).
- `support.js` — runtime helper used only by the HTML prototype format; **not needed** in your production port, it has no equivalent purpose in a real app.
+158
View File
@@ -0,0 +1,158 @@
# Musify — Guía de páginas
Documento vivo de qué debe contener cada página del frontend web. Sirve como mapa
para priorizar features. El sistema de diseño (tokens, recetas, componentes) vive en
el skill `musify-web`; aquí describimos **contenido y estructura**, no estilos.
Leyenda de estado: ✅ hecho · 🟡 parcial / maqueta · ⬜ pendiente.
> **Nota (rediseño app):** se está portando el prototipo `Musify.dc.html` (app: sidebar +
> vistas + player persistente con acento dinámico por pista). La landing de marketing se
> eliminó. Las secciones de abajo describen el diseño anterior y quedan pendientes de
> actualizar a las vistas de la app (Inicio, Buscar, Biblioteca, Subir, Importar, Descargas,
> Playlists). El **player** ya está hecho por componentes en `$lib/components/player/` con el
> tema en `$lib/theme/` (`theme.css` paleta + `color.ts` acento/gradiente).
## Inicio (app) `/` 🟡
- **Hero**: saludo + "Tu música. Sin límites." con color-wash del hue de la pista actual.
- **Acceso rápido = escuchado recientemente** (`player.recentlyPlayed`, hoy en cliente).
Estado vacío con icono + CTA «Explorar».
- **Tus playlists**: tarjetas mosaico de las playlists (hoy datos de muestra). Estado vacío.
### Backend pendiente (ASP.NET) para Inicio
- **Escuchado recientemente**: registrar reproducciones por usuario (tabla de historial:
`userId`, `trackId`, `playedAt`) y exponer `GET /me/recently-played?limit=` (o
`/users/{id}/recently-played`) devolviendo pistas ordenadas por `playedAt` desc, sin
duplicados. El front lo cargaría en un `load` y lo pasaría al store en vez de `recentlyPlayed`.
- **Playlists por última escucha**: añadir `lastPlayedAt` a la playlist (hoy el schema solo
tiene `createdAt`/`updatedAt`) y permitir ordenar `GET /playlists/users/{userId}` por ese
campo (p. ej. `?sort=lastPlayed`), para mostrar «tus últimas escuchadas».
- Las pistas necesitarán **artista** y un **color/hue** (o portada real de la que extraerlo)
para el arte y el acento dinámico; hoy `TrackApplicationResponse` no trae artista ni color.
## Estructura de rutas
Rutas agrupadas para separar lo público de lo autenticado (los grupos no cambian la URL):
```
src/routes/
(marketing)/ Público, sin sesión obligatoria
+page.svelte / Landing ✅
(app)/ Experiencia de la app
explore/ /explore Explorar / buscar ✅
upload/ /upload Subir música 🟡 maqueta
library/ /library Tu biblioteca ⬜
playlists/ /playlists Tus playlists ⬜
[id]/ /playlists/:id Detalle playlist ⬜
login logout auth/callback Auth (endpoints) ✅
api/tracks/[id]/stream Proxy de stream ✅
```
Componentes globales (en todas las páginas): **Navbar**, **Footer** y **PlayerBar**
(barra de reproducción fija, aparece al reproducir).
---
## 1. Landing `/` ✅ (marketing)
Página de entrada para no autenticados. Vender el producto y llevar a registro/explorar.
- **Hero**: badge de estado (beta), título, subtítulo, CTAs primario (Empezar gratis →
registro) y secundario (Explorar canciones).
- **Features**: 3 cards (escuchar, playlists, subir).
- **Pendiente sugerido**: sección de capturas/demo, prueba social, FAQ corta.
## 2. Login / Auth ✅ (endpoints)
No es una página visual propia: `/login` inicia el flujo OIDC de Zitadel y redirige.
`?mode=register` arranca en registro; `?returnTo=` vuelve a la ruta pedida tras entrar.
`/logout` cierra sesión. `/auth/callback` intercambia el código y crea la cookie
cifrada `mf_session`. La UI de credenciales la sirve Zitadel, no Musify.
## 3. Explorar / Buscar `/explore` ✅ (app)
Descubrimiento y búsqueda de canciones del catálogo.
- **Buscador**: input de texto (`?q=`) que filtra por nombre en el backend.
- **Contador de resultados** y **grid de tracks** (2→4 columnas): portada, título, fecha,
botón play/pausa en hover que encola y reproduce vía el player global.
- **Paginación** anterior/siguiente (`?page=`, 24 por página).
- **Estado vacío**: sin resultados / catálogo vacío.
- **Pendiente sugerido**: filtros (más recientes, por usuario), skeletons de carga,
portada real del track (hoy la API de listado no devuelve la key de imagen).
## 4. Subir música `/upload` 🟡 maqueta (app)
Alta de una canción nueva. Flujo real de backend (para cuando se cablee):
1. `POST /tracks/upload-urls` → URLs presignadas para portada y audio + `intentId`s.
2. `PUT` de cada archivo a su URL presignada (subida directa al almacenamiento).
3. `POST /tracks` con `title`, `pictureIntentId`, `audioIntentId`.
4. El backend procesa el audio (transcodifica a DASH) de forma asíncrona.
Contenido de la página:
- **Zona de audio**: drag & drop o selector; muestra nombre y tamaño del archivo, validación
de tipo (audio) y tamaño.
- **Portada**: selector de imagen con vista previa cuadrada.
- **Metadatos**: título (obligatorio). Espacio para futuros campos (descripción, etc.).
- **Pipeline visible**: pasos Subir → Procesar → Publicar con su estado.
- **Barra de progreso** de subida y estado de procesado.
- **Estados**: vacío, archivo seleccionado, subiendo, procesando, publicado, error.
- **Requiere sesión** (al cablear: redirigir a `/login?returnTo=/upload`).
## 5. Tu biblioteca `/library` ⬜ (app)
Panel personal del usuario autenticado: lo suyo en un sitio.
- **Tabs / secciones**: «Tus canciones» (`GET /tracks/users/{userId}`) y «Tus playlists»
(`GET /playlists/users/{userId}`).
- **Tus canciones**: lista con play, y acciones de gestión (borrar `DELETE /tracks/{id}`,
añadir a playlist).
- **Tus playlists**: grid de portadas + botón «Crear playlist».
- **Estado vacío** con CTA a subir / crear.
- **Requiere sesión**.
## 6. Playlists `/playlists` ⬜ (app)
Listado de las playlists del usuario.
- **Grid** de playlists (portada, nombre, nº de tracks) → enlaza al detalle.
- **Crear playlist**: modal/página con nombre, descripción y portada
(`POST /playlists`, portada vía `POST /playlists/upload-picture`).
- **Estado vacío** con CTA.
- **Requiere sesión**.
## 7. Detalle de playlist `/playlists/[id]` ⬜ (app)
- **Cabecera**: portada grande, nombre, descripción, nº de tracks, botón «Reproducir todo».
- **Lista de tracks** (`GET /playlists/{id}/tracks`, paginada): índice, título, fecha,
play, quitar de la playlist (`DELETE /playlists/{id}/tracks/{trackId}`).
- **Acciones del dueño**: editar (`PUT`), borrar (`DELETE`) la playlist, reordenar (futuro).
- **Añadir tracks**: desde explorar/biblioteca (`POST /playlists/{id}/tracks/{trackId}`).
## 8. Perfil de usuario `/users/[id]` ⬜ (futuro)
Vista pública de un usuario: avatar, nombre, sus canciones y playlists públicas
(`GET /users/{id}`, `GET /tracks/users/{id}`, `GET /playlists/users/{id}`).
---
## Player global (PlayerBar) ✅
Barra fija inferior visible al reproducir. Info de la pista, transporte
(anterior/play-pausa/siguiente), barra de progreso y control de volumen. Encola listas
desde cualquier grid (`player.playQueue`). Reproduce DASH vía el proxy
`/api/tracks/:id/stream`, que pide manifiesto + ticket firmado al backend.
## Backend disponible (resumen)
| Recurso | Endpoints |
| --- | --- |
| Tracks | listar/buscar, detalle, por usuario, stream, crear, borrar, upload-urls |
| Playlists | CRUD, tracks de la playlist, add/remove track, upload-picture |
| Users | listar/buscar, detalle, crear |
Cliente tipado en `$lib/server/api.ts` (`openapi-fetch`, estilo ErrorOr). Consumir en
`load`/actions server, nunca desde cliente. Tipos en `src/lib/api/schema.d.ts`.