# 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.