Files
cydweb/components/firmware-detail.tsx

120 lines
3.8 KiB
TypeScript

"use client";
import { useTranslations } from "next-intl";
import { ExternalLink, FileText } from "lucide-react";
import type { FirmwareInfo } from "@/lib/firmware";
import { formatBytes } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
import { EspWebInstall } from "@/components/esp-web-install";
import { FirmwarePreview } from "@/components/firmware-preview";
export function FirmwareDetail({
fw,
serialOk,
}: {
fw: FirmwareInfo | null;
serialOk: boolean;
}) {
const t = useTranslations();
if (!fw) {
return (
<div className="rounded-[14px] border border-dashed border-[var(--line)] bg-[rgba(0,0,0,0.2)] px-5 py-10 text-center text-sm text-[var(--muted)]">
{t("firmware.noneSelected")}
</div>
);
}
return (
<aside className="sticky top-4 flex flex-col overflow-hidden rounded-[14px] border border-[var(--line)] bg-[color-mix(in_srgb,var(--bg-elev)_92%,transparent)] backdrop-blur-sm">
<div className="border-b border-[var(--line)] px-4 py-3">
<p className="m-0 text-xs text-[var(--muted)]">
{t("firmware.selectedLabel")}
</p>
<h3 className="m-0 mt-0.5 truncate font-[family-name:var(--font-display)] text-lg font-bold">
{fw.name}
</h3>
</div>
<div
className={
fw.boardId === "esp32-3248s035"
? "aspect-[3/2] w-full border-b border-[var(--line)] bg-black/40"
: "aspect-[4/3] w-full border-b border-[var(--line)] bg-black/40"
}
>
{fw.media.preview ? (
<FirmwarePreview fw={fw} />
) : fw.media.video ? (
<video
key={fw.media.video}
className="h-full w-full object-cover"
controls
playsInline
preload="metadata"
>
<source src={fw.media.video} />
</video>
) : (
<div className="flex h-full flex-col items-center justify-center gap-1 px-4 text-center">
<p className="m-0 text-sm text-[var(--muted)]">
{t("firmware.mediaPlaceholder")}
</p>
<p className="m-0 text-xs text-[var(--muted)] opacity-70">
media/preview.html
</p>
</div>
)}
</div>
<div className="flex flex-col gap-4 p-4">
{(fw.description || fw.summary) && (
<p className="m-0 text-sm leading-relaxed text-[var(--muted)]">
{fw.description || fw.summary}
</p>
)}
<div className="flex flex-wrap gap-1.5">
{fw.tags.map((tag) => (
<Badge key={tag}>{tag}</Badge>
))}
<Badge variant="muted">{formatBytes(fw.size)}</Badge>
{fw.flash_size && <Badge variant="muted">{fw.flash_size}</Badge>}
{fw.git && <Badge variant="muted">git {fw.git}</Badge>}
{fw.date && <Badge variant="muted">{fw.date}</Badge>}
</div>
<p className="m-0 text-xs text-[var(--muted)]">
{fw.project}/{fw.variant} · {fw.board}
</p>
{fw.media.docs && (
<a
href={fw.media.docs}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-sm text-[var(--accent-hot)] underline-offset-2 hover:underline"
>
<FileText className="size-4" />
{t("firmware.docs")}
<ExternalLink className="size-3 opacity-70" />
</a>
)}
<EspWebInstall
manifest={fw.manifestUrl}
label={t("install.cta")}
disabled={!serialOk}
/>
<ol className="m-0 list-decimal space-y-1 pl-4 text-xs text-[var(--muted)]">
<li>{t("install.steps.1")}</li>
<li>{t("install.steps.2")}</li>
<li>{t("install.steps.3")}</li>
<li>{t("install.steps.4")}</li>
</ol>
</div>
</aside>
);
}