Scale the 320x240 iframe from the card container so covers stay centered and fully visible. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
2.3 KiB
TypeScript
90 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import type { FirmwareInfo } from "@/lib/firmware";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const SCREEN_W = 320;
|
|
const SCREEN_H = 240;
|
|
|
|
/** Renders HTML on-device UI mock (preferred) — never uses raster covers. */
|
|
export function FirmwarePreview({
|
|
fw,
|
|
className,
|
|
title,
|
|
}: {
|
|
fw: FirmwareInfo;
|
|
className?: string;
|
|
title?: string;
|
|
}) {
|
|
const hostRef = useRef<HTMLDivElement>(null);
|
|
const [scale, setScale] = useState(1);
|
|
|
|
useEffect(() => {
|
|
const el = hostRef.current;
|
|
if (!el) return;
|
|
|
|
const update = () => {
|
|
const { width, height } = el.getBoundingClientRect();
|
|
if (width <= 0 || height <= 0) return;
|
|
setScale(Math.min(width / SCREEN_W, height / SCREEN_H));
|
|
};
|
|
|
|
update();
|
|
const ro = new ResizeObserver(update);
|
|
ro.observe(el);
|
|
return () => ro.disconnect();
|
|
}, [fw.media.preview]);
|
|
|
|
if (fw.media.preview) {
|
|
return (
|
|
<div
|
|
ref={hostRef}
|
|
className={cn(
|
|
"relative h-full w-full overflow-hidden bg-[#0A1016]",
|
|
className
|
|
)}
|
|
>
|
|
<iframe
|
|
title={title || `${fw.name} UI preview`}
|
|
src={fw.media.preview}
|
|
width={SCREEN_W}
|
|
height={SCREEN_H}
|
|
loading="lazy"
|
|
sandbox="allow-scripts"
|
|
tabIndex={-1}
|
|
className="pointer-events-none absolute left-1/2 top-1/2 border-0 bg-[#0A1016]"
|
|
style={{
|
|
transform: `translate(-50%, -50%) scale(${scale})`,
|
|
transformOrigin: "center center",
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const initials = fw.name
|
|
.replace(/[^A-Za-z0-9\u4e00-\u9fff]+/g, " ")
|
|
.trim()
|
|
.split(/\s+/)
|
|
.slice(0, 2)
|
|
.map((w) => w[0]?.toUpperCase() ?? "")
|
|
.join("");
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex h-full w-full flex-col items-center justify-center gap-2 bg-[radial-gradient(ellipse_at_30%_20%,rgba(240,197,10,0.18),transparent_55%),linear-gradient(160deg,#1a1610,#0e0c09)]",
|
|
className
|
|
)}
|
|
>
|
|
<span className="font-[family-name:var(--font-display)] text-3xl font-extrabold tracking-tight text-[var(--accent-hot)]/80">
|
|
{initials || "FW"}
|
|
</span>
|
|
<span className="text-[0.65rem] uppercase tracking-[0.14em] text-[var(--muted)]">
|
|
HTML preview soon
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|