120 lines
3.8 KiB
TypeScript
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>
|
|
);
|
|
}
|