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
Files changed (4) hide show
  1. .gitignore +3 -1
  2. index.html +42 -7
  3. landing.html +1 -1
  4. 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-serif text-sm font-semibold text-foreground outline-none placeholder:font-normal placeholder:text-muted-foreground"
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-primary/50 px-5 py-4 sm:px-7">
2312
- <div className="flex items-center gap-2.5"><h1 className="font-display text-2xl text-foreground">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-primary/70 text-primary transition-colors hover:bg-accent"><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,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 ? photo.defaultCaption : ("frame " + String(index + 1).padStart(2, "0"))}
 
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/birdanimation.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">
 
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