Spaces:
Paused
Paused
Shereen Lee commited on
Commit ·
b679ee8
1
Parent(s): 563750e
feat: shake reveals flux.2klein develop, intro_video bg, navy/cream Develop header, sync polaroid title, blank+scroll-in darkroom captions
Browse files- .gitignore +3 -1
- index.html +42 -7
- landing.html +1 -1
- public/intro_video.mp4 +3 -0
.gitignore
CHANGED
|
@@ -2,4 +2,6 @@
|
|
| 2 |
.vendor_cache
|
| 3 |
sozai_rooms.db
|
| 4 |
*.db-shm
|
| 5 |
-
*.db-wal
|
|
|
|
|
|
|
|
|
| 2 |
.vendor_cache
|
| 3 |
sozai_rooms.db
|
| 4 |
*.db-shm
|
| 5 |
+
*.db-wal
|
| 6 |
+
.DS_Store
|
| 7 |
+
**/.DS_Store
|
index.html
CHANGED
|
@@ -1631,7 +1631,7 @@
|
|
| 1631 |
onChange={(e) => onTitleChange(e.target.value)}
|
| 1632 |
placeholder="Add a title..."
|
| 1633 |
aria-label="Photo title"
|
| 1634 |
-
className="w-full bg-transparent font-
|
| 1635 |
/>
|
| 1636 |
<textarea
|
| 1637 |
value={caption}
|
|
@@ -2308,10 +2308,10 @@
|
|
| 2308 |
|
| 2309 |
return (
|
| 2310 |
<section className="w-full max-w-6xl rounded-xl border-2 border-primary bg-card shadow-lg">
|
| 2311 |
-
<header className="flex items-center justify-between gap-4 border-b-2 border-dashed border-
|
| 2312 |
-
<div className="flex items-center gap-2.5"><h1 className="font-display text-2xl text-
|
| 2313 |
<div className="flex items-center gap-2">
|
| 2314 |
-
<button type="button" onClick={() => onOpenSettings && onOpenSettings()} aria-label="Settings" className="flex size-9 items-center justify-center rounded-md border-2 border-
|
| 2315 |
</div>
|
| 2316 |
</header>
|
| 2317 |
<div className="grid gap-8 px-5 py-6 sm:px-7 lg:grid-cols-2">
|
|
@@ -6470,6 +6470,39 @@
|
|
| 6470 |
|
| 6471 |
useEffect(() => { if (revealed) onRevealed(photo.id); }, [revealed, onRevealed, photo.id]);
|
| 6472 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6473 |
const registerShake = useCallback(() => {
|
| 6474 |
const next = Math.min(1, progressRef.current + 1 / DR_SHAKES_TO_REVEAL);
|
| 6475 |
progressRef.current = next; setProgress(next);
|
|
@@ -6514,7 +6547,7 @@
|
|
| 6514 |
transition: dragging ? "none" : "transform 0.4s ease" }}
|
| 6515 |
onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={endDrag} onPointerCancel={endDrag}>
|
| 6516 |
<div className="relative aspect-[4/5] w-full overflow-hidden rounded-sm bg-[oklch(0.2_0.02_60)]">
|
| 6517 |
-
<img src={photo.src || "/assets/placeholder.svg"} alt={revealed ? photo.alt : ""} draggable={false}
|
| 6518 |
className="h-full w-full object-cover transition-[filter] duration-200"
|
| 6519 |
style={{ filter: "blur(" + blurPx + "px) grayscale(" + grayscale + "%) brightness(" + brightness + ") sepia(" + ((1 - progress) * 40) + "%)" }} />
|
| 6520 |
<div className="pointer-events-none absolute inset-0 transition-opacity duration-200"
|
|
@@ -6532,8 +6565,9 @@
|
|
| 6532 |
</div>
|
| 6533 |
)}
|
| 6534 |
</div>
|
| 6535 |
-
<figcaption className="absolute inset-x-0 bottom-2 line-clamp-2 px-2 text-center font-hand text-[12px] leading-tight text-[oklch(0.3_0.04_264)]"
|
| 6536 |
-
{revealed ?
|
|
|
|
| 6537 |
</figcaption>
|
| 6538 |
</figure>
|
| 6539 |
</li>
|
|
@@ -6560,6 +6594,7 @@
|
|
| 6560 |
"@keyframes dr-ring{from{transform:translate(-50%,-50%) scale(.2);opacity:.7}to{transform:translate(-50%,-50%) scale(1.7);opacity:0}}" +
|
| 6561 |
"@keyframes dr-sway-kf{0%,100%{transform:rotate(-1.5deg)}50%{transform:rotate(1.5deg)}}" +
|
| 6562 |
"@keyframes dr-fade{from{opacity:0}to{opacity:1}}" +
|
|
|
|
| 6563 |
".dr-flicker{animation:dr-safelight 5s ease-in-out infinite}" +
|
| 6564 |
".dr-sway{animation:dr-sway-kf 4s ease-in-out infinite;transform-origin:top center}" +
|
| 6565 |
"@media (prefers-reduced-motion: reduce){.dr-flicker,.dr-sway{animation:none}}"
|
|
|
|
| 1631 |
onChange={(e) => onTitleChange(e.target.value)}
|
| 1632 |
placeholder="Add a title..."
|
| 1633 |
aria-label="Photo title"
|
| 1634 |
+
className="w-full bg-transparent font-hand text-lg text-foreground outline-none placeholder:font-normal placeholder:text-muted-foreground"
|
| 1635 |
/>
|
| 1636 |
<textarea
|
| 1637 |
value={caption}
|
|
|
|
| 2308 |
|
| 2309 |
return (
|
| 2310 |
<section className="w-full max-w-6xl rounded-xl border-2 border-primary bg-card shadow-lg">
|
| 2311 |
+
<header className="flex items-center justify-between gap-4 border-b-2 border-dashed border-[#f5edd8]/30 bg-[#13294b] px-5 py-4 sm:px-7">
|
| 2312 |
+
<div className="flex items-center gap-2.5"><h1 className="font-display text-2xl text-[#f5edd8]">Develop Photos</h1></div>
|
| 2313 |
<div className="flex items-center gap-2">
|
| 2314 |
+
<button type="button" onClick={() => onOpenSettings && onOpenSettings()} aria-label="Settings" className="flex size-9 items-center justify-center rounded-md border-2 border-[#f5edd8]/50 text-[#f5edd8] transition-colors hover:bg-white/10"><Settings className="size-4" /></button>
|
| 2315 |
</div>
|
| 2316 |
</header>
|
| 2317 |
<div className="grid gap-8 px-5 py-6 sm:px-7 lg:grid-cols-2">
|
|
|
|
| 6470 |
|
| 6471 |
useEffect(() => { if (revealed) onRevealed(photo.id); }, [revealed, onRevealed, photo.id]);
|
| 6472 |
|
| 6473 |
+
// Kick off the watercolour "develop" (flux.2-klein img2img) the moment the
|
| 6474 |
+
// print is hung, so the result is ready by the time it's shaken in. The
|
| 6475 |
+
// shake then reveals the DEVELOPED image; if the endpoint is unavailable
|
| 6476 |
+
// or fails, we fall back to the original photo (no regression).
|
| 6477 |
+
const [developedSrc, setDevelopedSrc] = useState(null);
|
| 6478 |
+
const developRef = useRef(false);
|
| 6479 |
+
useEffect(() => {
|
| 6480 |
+
if (developRef.current || !photo.src) return;
|
| 6481 |
+
developRef.current = true;
|
| 6482 |
+
let alive = true;
|
| 6483 |
+
(async () => {
|
| 6484 |
+
try {
|
| 6485 |
+
let dataUrl = photo.src;
|
| 6486 |
+
if (!dataUrl.startsWith("data:")) {
|
| 6487 |
+
const blob = await (await fetch(photo.src)).blob();
|
| 6488 |
+
dataUrl = await new Promise((res, rej) => {
|
| 6489 |
+
const fr = new FileReader();
|
| 6490 |
+
fr.onload = () => res(fr.result); fr.onerror = rej;
|
| 6491 |
+
fr.readAsDataURL(blob);
|
| 6492 |
+
});
|
| 6493 |
+
}
|
| 6494 |
+
const r = await fetch("/api/img2img", {
|
| 6495 |
+
method: "POST", headers: { "Content-Type": "application/json" },
|
| 6496 |
+
body: JSON.stringify({ data_url: dataUrl, name: photo.alt || "photo" }),
|
| 6497 |
+
});
|
| 6498 |
+
if (!r.ok) return;
|
| 6499 |
+
const j = await r.json();
|
| 6500 |
+
if (alive && j && j.available && j.image) setDevelopedSrc(j.image);
|
| 6501 |
+
} catch (err) { /* keep the original photo on any failure */ }
|
| 6502 |
+
})();
|
| 6503 |
+
return () => { alive = false; };
|
| 6504 |
+
}, [photo.src, photo.alt]);
|
| 6505 |
+
|
| 6506 |
const registerShake = useCallback(() => {
|
| 6507 |
const next = Math.min(1, progressRef.current + 1 / DR_SHAKES_TO_REVEAL);
|
| 6508 |
progressRef.current = next; setProgress(next);
|
|
|
|
| 6547 |
transition: dragging ? "none" : "transform 0.4s ease" }}
|
| 6548 |
onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={endDrag} onPointerCancel={endDrag}>
|
| 6549 |
<div className="relative aspect-[4/5] w-full overflow-hidden rounded-sm bg-[oklch(0.2_0.02_60)]">
|
| 6550 |
+
<img src={(revealed && developedSrc) ? developedSrc : (photo.src || "/assets/placeholder.svg")} alt={revealed ? photo.alt : ""} draggable={false}
|
| 6551 |
className="h-full w-full object-cover transition-[filter] duration-200"
|
| 6552 |
style={{ filter: "blur(" + blurPx + "px) grayscale(" + grayscale + "%) brightness(" + brightness + ") sepia(" + ((1 - progress) * 40) + "%)" }} />
|
| 6553 |
<div className="pointer-events-none absolute inset-0 transition-opacity duration-200"
|
|
|
|
| 6565 |
</div>
|
| 6566 |
)}
|
| 6567 |
</div>
|
| 6568 |
+
<figcaption className="absolute inset-x-0 bottom-2 line-clamp-2 px-2 text-center font-hand text-[12px] leading-tight text-[oklch(0.3_0.04_264)]"
|
| 6569 |
+
style={revealed ? { animation: "dr-cap-in .6s ease-out both" } : undefined}>
|
| 6570 |
+
{revealed ? photo.defaultCaption : ""}
|
| 6571 |
</figcaption>
|
| 6572 |
</figure>
|
| 6573 |
</li>
|
|
|
|
| 6594 |
"@keyframes dr-ring{from{transform:translate(-50%,-50%) scale(.2);opacity:.7}to{transform:translate(-50%,-50%) scale(1.7);opacity:0}}" +
|
| 6595 |
"@keyframes dr-sway-kf{0%,100%{transform:rotate(-1.5deg)}50%{transform:rotate(1.5deg)}}" +
|
| 6596 |
"@keyframes dr-fade{from{opacity:0}to{opacity:1}}" +
|
| 6597 |
+
"@keyframes dr-cap-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}" +
|
| 6598 |
".dr-flicker{animation:dr-safelight 5s ease-in-out infinite}" +
|
| 6599 |
".dr-sway{animation:dr-sway-kf 4s ease-in-out infinite;transform-origin:top center}" +
|
| 6600 |
"@media (prefers-reduced-motion: reduce){.dr-flicker,.dr-sway{animation:none}}"
|
landing.html
CHANGED
|
@@ -180,7 +180,7 @@
|
|
| 180 |
<video autoPlay loop muted playsInline aria-hidden="true"
|
| 181 |
className="pointer-events-none absolute inset-0 z-0 h-full w-full object-cover"
|
| 182 |
style={{ mixBlendMode: "multiply", opacity: 0.32 }}>
|
| 183 |
-
<source src="/assets/
|
| 184 |
</video>
|
| 185 |
{/* top-left wordmark */}
|
| 186 |
<header className="absolute left-6 top-5 z-20 flex items-baseline gap-2 sm:left-10 sm:top-7">
|
|
|
|
| 180 |
<video autoPlay loop muted playsInline aria-hidden="true"
|
| 181 |
className="pointer-events-none absolute inset-0 z-0 h-full w-full object-cover"
|
| 182 |
style={{ mixBlendMode: "multiply", opacity: 0.32 }}>
|
| 183 |
+
<source src="/assets/intro_video.mp4" type="video/mp4" />
|
| 184 |
</video>
|
| 185 |
{/* top-left wordmark */}
|
| 186 |
<header className="absolute left-6 top-5 z-20 flex items-baseline gap-2 sm:left-10 sm:top-7">
|
public/intro_video.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1c5e2ca2deb00725e649e2e475a16cde5da21d4116bfb54476f2c3ecb047969a
|
| 3 |
+
size 3369460
|