feat(onboarding): real demo stills in place of gradient placeholders
Swap the fake gallery tiles' gradient stand-ins for 13 rights-clean generated WebP stills (512px, ~25 KB each). Search demo now returns thematically matched results — beach/dunes for the filename query, sunset and water for /s, landscapes for /t — so the show-don't-tell steps look like a real library. tileGradient retained for the abstract Explore and Timeline mini-previews.
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -1,24 +1,24 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { FakeTile } from "./fakes";
|
import { SEARCH_RESULTS } from "./fakes";
|
||||||
|
|
||||||
const DEMOS = [
|
const DEMOS = [
|
||||||
{
|
{
|
||||||
query: "beach-day_042.jpg",
|
query: "beach-day_042.jpg",
|
||||||
mode: "Filename",
|
mode: "Filename",
|
||||||
description: "Plain text matches file names — the default, instant search.",
|
description: "Plain text matches file names — the default, instant search.",
|
||||||
tiles: [2, 5, 0],
|
results: SEARCH_RESULTS.filename,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: "/s golden sunset over water",
|
query: "/s golden sunset over water",
|
||||||
mode: "Semantic",
|
mode: "Semantic",
|
||||||
description: "Describe what's in the picture. Visual embeddings find matches even when filenames say nothing.",
|
description: "Describe what's in the picture. Visual embeddings find matches even when filenames say nothing.",
|
||||||
tiles: [1, 7, 5],
|
results: SEARCH_RESULTS.semantic,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: "/t landscape",
|
query: "/t landscape",
|
||||||
mode: "Tags",
|
mode: "Tags",
|
||||||
description: "Search by AI or manual tags. Autocomplete suggests tags as you type.",
|
description: "Search by AI or manual tags. Autocomplete suggests tags as you type.",
|
||||||
tiles: [4, 2, 6],
|
results: SEARCH_RESULTS.tags,
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -69,9 +69,11 @@ export function StepSearchDemo() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Fake results */}
|
{/* Fake results */}
|
||||||
<div className={`mt-3 grid grid-cols-6 gap-1.5 transition-opacity duration-300 ${fullyTyped ? "opacity-100" : "opacity-30"}`}>
|
<div className={`mt-3 grid grid-cols-3 gap-1.5 transition-opacity duration-300 ${fullyTyped ? "opacity-100" : "opacity-30"}`}>
|
||||||
{demo.tiles.map((tileIndex, i) => (
|
{demo.results.map((src, i) => (
|
||||||
<FakeTile key={`${demoIndex}-${i}`} index={tileIndex} />
|
<div key={`${demoIndex}-${i}`} className="aspect-square overflow-hidden rounded-xl bg-white/[0.04]">
|
||||||
|
<img src={src} alt="" className="h-full w-full object-cover" />
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,51 @@
|
|||||||
// Shared placeholder primitives for the onboarding tour. Everything here is
|
// Shared placeholder primitives for the onboarding tour. Everything here is
|
||||||
// deliberately fake — gradient stand-ins for media, looping demo progress —
|
// deliberately fake — rights-clean demo stills (generated, owned), looping
|
||||||
// so new users see the real UI's shapes before their own library exists.
|
// demo progress — so new users see the real UI's shapes before their own
|
||||||
|
// library exists.
|
||||||
|
import sunset from "../../assets/onboarding/sunset.webp";
|
||||||
|
import beach from "../../assets/onboarding/beach.webp";
|
||||||
|
import landscape1 from "../../assets/onboarding/landscape1.webp";
|
||||||
|
import landscape2 from "../../assets/onboarding/landscape2.webp";
|
||||||
|
import forest from "../../assets/onboarding/forest.webp";
|
||||||
|
import citynight from "../../assets/onboarding/citynight.webp";
|
||||||
|
import flower from "../../assets/onboarding/flower.webp";
|
||||||
|
import alpinelake from "../../assets/onboarding/alpinelake.webp";
|
||||||
|
import dunes from "../../assets/onboarding/dunes.webp";
|
||||||
|
import cozy from "../../assets/onboarding/cozy.webp";
|
||||||
|
import cat from "../../assets/onboarding/cat.webp";
|
||||||
|
import balloon from "../../assets/onboarding/balloon.webp";
|
||||||
|
import architecture from "../../assets/onboarding/architecture.webp";
|
||||||
|
|
||||||
|
// Ordered for grid variety: adjacent indices are visually distinct.
|
||||||
|
const FAKE_IMAGES = [
|
||||||
|
sunset,
|
||||||
|
cozy,
|
||||||
|
landscape1,
|
||||||
|
citynight,
|
||||||
|
flower,
|
||||||
|
dunes,
|
||||||
|
alpinelake,
|
||||||
|
cat,
|
||||||
|
forest,
|
||||||
|
balloon,
|
||||||
|
landscape2,
|
||||||
|
beach,
|
||||||
|
architecture,
|
||||||
|
];
|
||||||
|
|
||||||
|
export function fakeImage(index: number): string {
|
||||||
|
return FAKE_IMAGES[index % FAKE_IMAGES.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curated result sets so each search demo returns thematically right images.
|
||||||
|
export const SEARCH_RESULTS = {
|
||||||
|
filename: [beach, dunes, sunset], // "beach-day_042.jpg"
|
||||||
|
semantic: [sunset, alpinelake, beach], // "golden sunset over water"
|
||||||
|
tags: [landscape1, landscape2, forest], // "landscape"
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Abstract gradient blobs/ticks for the Explore & Timeline mini-previews,
|
||||||
|
// where small non-photographic shapes read more clearly than tiny stills.
|
||||||
const TILE_GRADIENTS = [
|
const TILE_GRADIENTS = [
|
||||||
"from-sky-900/70 via-slate-800 to-slate-900",
|
"from-sky-900/70 via-slate-800 to-slate-900",
|
||||||
"from-amber-900/60 via-stone-800 to-stone-900",
|
"from-amber-900/60 via-stone-800 to-stone-900",
|
||||||
@@ -35,7 +79,7 @@ export function FakeTile({
|
|||||||
return (
|
return (
|
||||||
<div className={`group relative aspect-square overflow-hidden rounded-xl bg-white/[0.04] ${className}`}>
|
<div className={`group relative aspect-square overflow-hidden rounded-xl bg-white/[0.04] ${className}`}>
|
||||||
{loaded ? (
|
{loaded ? (
|
||||||
<div className={`absolute inset-0 bg-gradient-to-br ${tileGradient(index)} transition-opacity duration-500`} />
|
<img src={fakeImage(index)} alt="" loading="lazy" className="absolute inset-0 h-full w-full object-cover" />
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute inset-0 animate-pulse bg-white/[0.04]" />
|
<div className="absolute inset-0 animate-pulse bg-white/[0.04]" />
|
||||||
)}
|
)}
|
||||||
|
|||||||