4.8 KiB
4.8 KiB
Diseño: vista de grid estilo YouTube para la sección Vídeos
Fecha: 2026-08-22 Estado: aprobado en conversación
Problema
La sección Vídeos solo tiene visualización de tabla. Para navegar el catálogo visualmente (miniaturas grandes, explorar por canal/tema) falta una vista tipo YouTube: grid de tarjetas con miniatura 16:9, duración y metadatos compactos.
Objetivo
Añadir un conmutador tabla ⇄ grid en la sección Vídeos. El grid replica el look de YouTube (tarjetas 16:9) manteniendo toda la funcionalidad existente: filtros, orden, selección masiva ("Download .md" / "Audio") y paginación.
Decisiones tomadas en conversación
| Pregunta | Decisión |
|---|---|
| ¿Selección masiva en grid? | Sí, checkboxes como en la tabla; la barra masiva se comparte |
| ¿"Square" literal o estilo YouTube? | Miniatura 16:9 estilo YouTube (no recorte 1:1) |
| ¿Vista inicial y persistencia? | Tabla por defecto; la elección se recuerda en localStorage |
| ¿Estructura en index.html? | Dos bloques hermanos alternados con x-if; filtros/barra/paginación compartidos |
Cambios
1. Estado y persistencia (static/app.js)
- Nuevo campo en el store de
videos:view, valores'table' | 'grid', default'table'. - En
init(): leerlocalStorage["videos-view"]y aceptarlo solo si es'table'o'grid'(mismo patrón defensivo quevideos-size). - Método
setVideoView(v): asigna y guarda enlocalStorage. No tocasyncURL/hydrateURL: es preferencia de UI comovideos-size, no estado navegable.
2. Conmutador de vista (index.html)
- Control segmentado de dos botones icono (lista / grid) alineado a la derecha, en una fila fina inmediatamente encima del contenedor de resultados.
- Una sola instancia visible en ambas vistas: fuera del
<thead>de la tabla a propósito, porque el grid no tiene cabecera donde colgarlo. - Botón activo con
accent-grad btn-primary, el mismo tratamiento que usa el número de página activo en la paginación; aria-pressed en cada botón.
3. Tarjetas del grid (index.html)
- Contenedor:
grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-5 gap-4. - Tarjeta: bloque
glassredondeado, cursor-pointer, hover con borde rose (mismo lenguaje que las tarjetas de canales). Click →openVideo(id); claserow-selectedsi está seleccionada. - Miniatura:
/api/thumbnails/{id},aspect-video w-full object-cover, esquinas superiores redondeadas, mismoonerror="this.style.visibility='hidden'"que la tabla.- Badge de duración abajo-derecha sobre la miniatura:
ts(v.duration), font-mono text-xs, fondo negro semitransparente. - Checkbox de selección arriba-izquierda sobre la miniatura: aparece al
hover de la tarjeta o permanece visible si está seleccionada; click con
.stop→toggleSelect(id).
- Badge de duración abajo-derecha sobre la miniatura:
- Cuerpo:
- Título con clamp a 2 líneas (
line-clamp-2), text-zinc-100. - Línea de metadatos:
chanName(channel_id)·fmtNum(view_count)vistas ·videoDate(v)(con~si estimada; tooltipvideoDateTitle(v)). - Pills de estado y candado de bloqueo idénticos a los de la tabla
(
statusClass,isBlocked,blockLabel,blockTitle).
- Título con clamp a 2 líneas (
4. Comportamiento compartido (sin cambios)
- Filtros, barra masiva, paginación y select de orden: intactos, aplican a ambas vistas.
- Estados loading/vacío duplicados dentro del bloque del grid con los mismos mensajes ("loading…", "No videos match these filters.", "Clear filters").
- Las acciones por vídeo
.md/Process/Clipno se duplican en las tarjetas: quedan en la tabla y en la vista de detalle (look limpio tipo YouTube; un click abre el detalle que las tiene todas).
Errores cubiertos
| Caso | Comportamiento |
|---|---|
| Miniatura ausente | onerror oculta el <img>; la tarjeta conserva proporción por el contenedor aspect-video |
| Valor inválido/corrupto en localStorage | Se ignora y cae a 'table' |
| Selección activa + cambio de vista | videos.selected no se toca; la barra masiva sigue funcionando en ambas |
Fuera de alcance
- No se toca backend ni endpoints:
/api/videosya devuelve todos los campos que la tarjeta necesita. - Sin acciones por tarjeta, sin hover-preview de vídeo, sin infinite scroll.
- Sin tests automatizados nuevos: el repo no tiene infra de tests de frontend (pytest es backend sin red). Verificación manual.
Verificación manual
Con start-server.bat:
- Alternar tabla ⇄ grid: ambas renderizan los mismos vídeos del filtro activo.
- Marcar 3 tarjetas → barra masiva aparece → "Download .md" encola el job.
- Paginar en grid: sin repeticiones ni saltos (mismo desempate que tabla).
- Recargar la página: la vista elegida se conserva; primera visita → tabla.
- La tabla existente funciona exactamente igual que antes del cambio.