From 4f5a68b5724df0018f9f1cc5b9422d7ada3ddbd6 Mon Sep 17 00:00:00 2001 From: urieljareth Date: Sat, 22 Aug 2026 18:35:47 -0600 Subject: [PATCH] docs: spec de vista grid estilo YouTube para la seccion Videos --- .../2026-08-22-videos-grid-view-design.md | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-22-videos-grid-view-design.md diff --git a/docs/superpowers/specs/2026-08-22-videos-grid-view-design.md b/docs/superpowers/specs/2026-08-22-videos-grid-view-design.md new file mode 100644 index 0000000..1731a5f --- /dev/null +++ b/docs/superpowers/specs/2026-08-22-videos-grid-view-design.md @@ -0,0 +1,104 @@ +# 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 `` 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 ``; 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.