Visor local de imágenes y videos optimizado para explorar carpetas del sistema directamente desde tu navegador web. Diseñado para ofrecer una experiencia fluida, rápida y ligera, ya sea de forma 100% cliente (local) mediante las APIs modernas del navegador o a través de un servidor local de Node.js.
El proyecto está diseñado bajo una filosofía zero-dependencies (sin librerías externas ni node_modules), lo que garantiza portabilidad y tiempos de carga inmediatos.
- HTML5: Estructura semántica avanzada utilizando etiquetas como
<main>,<section>,<figure>,<figcaption>y<aside>. - CSS3 (Vanilla):
- Variables CSS (Custom Properties): Gestión de colores y temas (
--bg,--accent, etc.), control reactivo del zoom (--zoom), paneo (--pan-x,--pan-y) y relaciones de aspecto dinámicas (--image-ratio). - Diseño Moderno: Uso exhaustivo de Grid, Flexbox y transiciones suaves (
ease) para interacciones fluidas. - Temas Dinámicos: Soporte para modo Claro, Oscuro y Auto (sincronizado con el sistema operativo) mediante el atributo
data-themey variables CSS adaptables. - Responsive Design: Consultas de medios (
@media) para adaptar la barra lateral, botones y visor en pantallas pequeñas y dispositivos móviles. - Modo Pantalla Completa: Estilos dinámicos usando el pseudo-selector
:fullscreen.
- Variables CSS (Custom Properties): Gestión de colores y temas (
- JavaScript (Vanilla ES Modules):
- Modularidad nativa (ES Modules) para estructurar el estado, UI, favoritos, visor y controles.
- File System Access API: Uso de
showDirectoryPickerpara obtener manejadores persistentes de carpetas locales y volver a abrirlas de forma directa. - IndexedDB: Almacenamiento persistente de favoritos (guardando los archivos como
Blob) y del historial de carpetas accedidas. - Object URLs (
URL.createObjectURL) para renderizar imágenes y reproducir videos pesados con alto rendimiento. - Pointer Events: Gestión robusta del arrastre (drag & pan) y selección de zoom rectangular usando
PointerCapture. - Fullscreen API & Keyboard Events nativos.
- Node.js: Construido exclusivamente sobre módulos nativos:
node:fsynode:fs/promisespara lectura asíncrona y recursiva del disco.node:httppara servir la aplicación y los recursos estáticos.node:pathynode:urlpara saneamiento y resolución de rutas.
- Seguridad Integrada: Validación de rutas mediante
resolveInsidepara prevenir ataques de Directory Traversal en el acceso de archivos.
La aplicación está dividida en submódulos JavaScript bajo el directorio src/, comunicados mediante un estado centralizado reactivo (src/state.js).
Centraliza los datos en ejecución, incluyendo:
- La lista de archivos cargados (
allMediaeimages). - El índice activo, nivel de zoom y compensación de paneo (
panX,panY). - El estado de la reproducción de diapositivas (
isPlaying,shuffleEnabled). - Elementos persistentes cargados de IndexedDB (favoritos y carpetas recientes).
Soporta dos flujos independientes:
- Modo Local (Client-Side Puro):
- File System Access API: Si el navegador lo soporta, solicita acceso a la carpeta usando
showDirectoryPicker(). Esto permite almacenar unFileSystemDirectoryHandlepersistente en IndexedDB para restaurar el acceso al reiniciar la app. - Fallback
webkitdirectory: En navegadores que no soportan la API moderna, utiliza el selector de archivos tradicional para leer la estructura completa. - Filtrado y Ordenación (
src/viewer.js): Soporta filtrado por subcarpetas (sidebar dinámico) y ordenación flexible por Nombre, Más recientes primero y Más antiguas primero basándose en la propiedadlastModifieddel archivo.
- File System Access API: Si el navegador lo soporta, solicita acceso a la carpeta usando
- Modo Servidor (Node.js API):
- El script
server.jssirve de endpoint y escanea recursivamente la carpeta proporcionada. - Hace streaming seguro de imágenes y videos directamente al cliente mediante
fs.createReadStream, optimizando el consumo de memoria en el backend.
- El script
Una de las funcionalidades avanzadas es la persistencia de favoritos:
- Base de Datos IndexedDB: Cuando agregas una imagen a favoritos, la app extrae su contenido binario (
Blob) y lo almacena localmente junto con sus metadatos (nombre, ruta original, tipo, fecha de modificación). - Acceso Sin Origen: Esto permite visualizar la galería de favoritos en cualquier momento, incluso si la carpeta de origen no está cargada actualmente en la sesión del navegador.
- Sincronización Inteligente: Al cargar una carpeta local, la app empareja y sincroniza los favoritos ya guardados con los archivos disponibles de esa sesión.
- Zoom Dinámico: Restringido entre 25% y 300%, aplicándose mediante variables CSS transformadas por GPU (
scale(var(--zoom))). - Drag & Pan: Al superar el 100% de zoom, la imagen se vuelve arrastrable. Los Pointer Events calculan el desplazamiento acumulado (
panXypanY) aplicandoPointerCapturepara evitar perder el foco del ratón fuera del visor. - Zoom por Selección Rectangular (Fullscreen):
- En pantalla completa o zoom inferior a 100%, arrastrar el ratón dibuja un rectángulo de selección.
- Al soltar el ratón, la app calcula la relación de aspecto e intersección del recuadro seleccionado respecto al viewport de la imagen original (
getRenderedImageRecteintersectRects), aplicando de forma instantánea el factor de escala y el paneo exacto para centrar y ampliar el área dibujada.
- Soporte Multimedia: Identifica dinámicamente si el archivo seleccionado es un video (
video/mp4,.mov, etc.) o imagen. Si es video, oculta el visor de imagen y activa un elemento<video>con soporte para reproducción automática (autoplay con fallback muteado) y avance de reproducción contiguo. - Slideshow / Presentación:
- Al presionar Play, se activa un temporizador de diapositivas (
SLIDESHOW_INTERVAL_MS = 4000). - Modo Aleatorio (Shuffle): Permite barajar dinámicamente el orden de reproducción sin alterar la ordenación visual de las carpetas.
- Avance Automático de Video: Si la presentación está activa y el archivo actual es un video, el temporizador de diapositivas se congela y la app espera a que el evento
endeddel video dispare el avance a la siguiente pista, garantizando ver el video completo.
- Al presionar Play, se activa un temporizador de diapositivas (
| Tecla | Acción |
|---|---|
ArrowRight / ArrowLeft |
Imagen/Video siguiente / anterior |
Space |
Reproducir / Pausar la presentación |
R / r |
Activar / Desactivar modo aleatorio (Shuffle) |
F / f |
Alternar pantalla completa |
+ / = |
Acercar zoom (+10%) |
- |
Alejar zoom (-10%) |
0 |
Restablecer zoom al 100% |
Esc |
Cerrar el visor / Salir de pantalla completa |
- Abre el archivo
index.htmlen tu navegador. - Haz clic en Importar una carpeta (o abre el menú lateral y haz clic en la misma opción).
- Concede permisos para leer el directorio local. La aplicación procesará y estructurará todo el contenido sin subir nada a internet.
- Levanta el servidor especificando la ruta de la carpeta que deseas indexar:
node server.js /ruta/a/tus/imagenes_o_videos
- Accede a
http://127.0.0.1:5173en tu navegador para interactuar con la galería.
- Imágenes:
.jpg,.jpeg,.png,.gif,.webp,.bmp,.svg,.avif - Videos:
.mov,.mp4,.m4v,.mpeg,.mpg