added deploy watch, pull script. minor refactors

This commit is contained in:
Hunter
2026-10-03 14:31:43 -04:00
parent 6dbaa647dc
commit 30c8ad9293
30 changed files with 370 additions and 213 deletions

View File

@@ -3,17 +3,21 @@ import Image from "next/image";
import { useState, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
import '@/styles/gallery.css';
import { type GalleryImage, imageSrc, imageDescription } from "@/data/content";
type GallerySize = "small" | "medium" | "large";
type Props = {
images: string[];
images: GalleryImage[];
videos?: string[];
title: string;
gallerySize?: GallerySize;
};
export default function SimpleGallery({
images,
videos = [],
title
title,
gallerySize = "medium"
}: Props) {
const [active, setActive] = useState(0);
@@ -28,10 +32,24 @@ export default function SimpleGallery({
const totalMedia = images.length + videos.length;
const isVideo = active >= images.length;
const videoIndex = active - images.length;
const currentSrc = isVideo ? videos[videoIndex] : images[active];
const currentSrc = isVideo ? videos[videoIndex] : imageSrc(images[active]);
const currentDescription = isVideo ? undefined : imageDescription(images[active]);
if (totalMedia === 0) return null;
function getGallerySize() {
switch (gallerySize) {
case "small":
return { maxHeight: 150 };
case "medium":
return { maxHeight: 250 };
case "large":
return { maxHeight: 500 };
default:
return { maxHeight: 250 };
}
}
function handleFullscreenClose() {
setIsEnlarged(false);
}
@@ -94,8 +112,8 @@ export default function SimpleGallery({
const imageStyle: React.CSSProperties = {
width: "100%",
borderRadius: 4,
objectFit: "cover",
maxHeight: 250,
objectFit: "contain",
maxHeight: getGallerySize().maxHeight,
display: "block",
transition: "all 0.8s ease",
zIndex: 1,
@@ -104,7 +122,7 @@ export default function SimpleGallery({
const videoStyle: React.CSSProperties = {
width: "100%",
borderRadius: 4,
maxHeight: 250,
maxHeight: getGallerySize().maxHeight,
display: "block",
transition: "all 0.8s ease",
};
@@ -126,7 +144,8 @@ export default function SimpleGallery({
<Image
id="gallery-main-image"
src={`/${currentSrc}`}
alt={`${title} screenshot ${active + 1}`}
alt={currentDescription ?? `${title} screenshot ${active + 1}`}
title={currentDescription}
width={800}
height={600}
onClick={() => setIsEnlarged(true)}
@@ -216,11 +235,17 @@ export default function SimpleGallery({
) : (
<Image
src={`/${currentSrc}`}
alt={`${title} fullscreen ${active + 1}`}
alt={currentDescription ?? `${title} fullscreen ${active + 1}`}
title={currentDescription}
width={1600}
height={1200}
onClick={(e) => e.stopPropagation()}
style={{
// Auto sizing keeps the element's box tight to the visible image;
// otherwise the letterboxed area inside the box swallows taps
// meant for the darkened background.
width: "auto",
height: "auto",
maxWidth: "95vw",
maxHeight: "90vh",
borderRadius: 8,
@@ -245,27 +270,40 @@ export default function SimpleGallery({
marginTop: 8,
flexWrap: "wrap" }}>
{images.map((src, i) => (
<Image
key={`thumb-img-${i}`}
src={`/${src}`}
alt={`${title} thumbnail ${i + 1}`}
width={48}
height={36}
onClick={() => handleThumbnailClick(i)}
style={{
width: 48,
height: 36,
objectFit: "cover",
borderRadius: 3,
cursor: "pointer",
border: `2px solid ${i === active ? "var(--accent)" : "var(--border)"}`,
opacity: i === active ? 1 : 0.6,
transition: "all 0.15s ease",
pointerEvents: "auto",
}}
/>
))}
{images.map((image, i) => {
const description = imageDescription(image);
const isActive = i === active;
return (
<button
key={`thumb-img-${i}`}
className={`gallery-thumb${isActive ? " is-active" : ""}`}
onClick={() => handleThumbnailClick(i)}
title={description ?? `Show ${title} screenshot ${i + 1}`}
>
<Image
src={`/${imageSrc(image)}`}
alt={description ?? `${title} thumbnail ${i + 1}`}
width={48}
height={36}
style={{
width: 48,
height: 36,
objectFit: "cover",
borderRadius: 3,
border: `2px solid ${isActive ? "var(--accent)" : "var(--border)"}`,
opacity: isActive ? 1 : 0.6,
transition: "all 0.15s ease",
}}
/>
<span className="gallery-thumb-magnifier" aria-hidden="true">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg">
<circle cx="7" cy="7" r="4.5" strokeWidth="1.75" />
<path d="M10.5 10.5L14 14" strokeWidth="1.75" strokeLinecap="round" />
</svg>
</span>
</button>
);
})}
{videos.map((_, i) => {
const mediaIndex = images.length + i;
@@ -332,6 +370,8 @@ export default function SimpleGallery({
borderRadius: 7,
border: `1px solid ${mediaHovered ? "var(--border-strong)" : "transparent"}`,
transition: "border-color 0.15s ease",
backgroundColor: "var(--overlay, #1a1a1a)",
boxShadow: "var(--shadow-sm)"
}}
>
{inlineMedia()}