Files
yt-channel-scraper/docs/superpowers/specs/2026-08-22-videos-grid-view-design.md
T

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(): leer localStorage["videos-view"] y aceptarlo solo si es 'table' o 'grid' (mismo patrón defensivo que videos-size).
  • Método setVideoView(v): asigna y guarda en localStorage. No toca syncURL/hydrateURL: es preferencia de UI como videos-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 glass redondeado, cursor-pointer, hover con borde rose (mismo lenguaje que las tarjetas de canales). Click → openVideo(id); clase row-selected si está seleccionada.
  • Miniatura: /api/thumbnails/{id}, aspect-video w-full object-cover, esquinas superiores redondeadas, mismo onerror="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).
  • 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; tooltip videoDateTitle(v)).
    • Pills de estado y candado de bloqueo idénticos a los de la tabla (statusClass, isBlocked, blockLabel, blockTitle).

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/Clip no 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/videos ya 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:

  1. Alternar tabla ⇄ grid: ambas renderizan los mismos vídeos del filtro activo.
  2. Marcar 3 tarjetas → barra masiva aparece → "Download .md" encola el job.
  3. Paginar en grid: sin repeticiones ni saltos (mismo desempate que tabla).
  4. Recargar la página: la vista elegida se conserva; primera visita → tabla.
  5. La tabla existente funciona exactamente igual que antes del cambio.