import { useState } from "react"; import { proxyImageUrl } from "../utils/proxyImage.ts"; import { initialsFor, tintFor } from "../utils/thumbnailTint.ts"; interface ThumbnailPlaceholderProps { /** The dumped URL — seeds the fallback hue and the initial. */ url?: string; /** Explicit hue seed for dumps with no URL to hash (file dumps). */ seed?: string; /** The target page's declared brand color, if it had one. */ accentColor?: string; /** The target page's icon, drawn contained over the tint. */ faviconUrl?: string; /** Preferred source for the initial when there's no favicon. */ siteName?: string; /** Emoji shown instead of an initial — file dumps say more with 🎬 than "T". */ glyph?: string; className?: string; } /** * Stand-in artwork for pages that offer no preview image: the site's own color * washed over the theme surface, with its favicon centered on top — or its * initial when the favicon is missing or fails to load. * * Fills whatever box it's placed in, so the same component serves the 36px * compact thumbnail, the 128×72 feed preview and a 2×2 mosaic tile. */ export default function ThumbnailPlaceholder( { url, seed, accentColor, faviconUrl, siteName, glyph, className }: ThumbnailPlaceholderProps, ) { const [iconFailed, setIconFailed] = useState(false); // A different icon is a fresh thing to try — clear the prior failure during // render rather than in an effect (same shape as Avatar.tsx). const [prevIcon, setPrevIcon] = useState(faviconUrl); if (prevIcon !== faviconUrl) { setPrevIcon(faviconUrl); setIconFailed(false); } const style = { "--thumb-tint": tintFor({ accentColor, url, seed }) } as React.CSSProperties; return ( ); }