"use client"; import { useEffect, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import type { FirmwareInfo } from "@/lib/firmware"; import { boardPreviewSize } from "@/lib/boards"; import { sensorMediaForProject } from "@/lib/sensor-media"; import { cn } from "@/lib/utils"; type Tab = "ui" | "sensor"; /** On-device HTML UI mock + optional hardware still, switchable via tabs. */ export function FirmwarePreview({ fw, className, title, }: { fw: FirmwareInfo; className?: string; title?: string; }) { const t = useTranslations("firmware"); const hostRef = useRef(null); const [scale, setScale] = useState(1); const { w: screenW, h: screenH } = boardPreviewSize(fw.boardId); const sensor = sensorMediaForProject(fw.project); const hasUi = Boolean(fw.media.preview); const [tab, setTab] = useState(hasUi ? "ui" : "sensor"); useEffect(() => { setTab(hasUi ? "ui" : sensor ? "sensor" : "ui"); }, [fw.id, hasUi, sensor]); useEffect(() => { const el = hostRef.current; if (!el || tab !== "ui") return; const update = () => { const { width, height } = el.getBoundingClientRect(); if (width <= 0 || height <= 0) return; setScale(Math.min(width / screenW, height / screenH)); }; update(); const ro = new ResizeObserver(update); ro.observe(el); return () => ro.disconnect(); }, [fw.media.preview, tab, screenW, screenH]); const showTabs = hasUi && Boolean(sensor); 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 (
{showTabs && (
e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} >
)}
{tab === "ui" && hasUi ? (