diff --git a/README.md b/README.md
index 849022f..e3962d3 100644
--- a/README.md
+++ b/README.md
@@ -47,9 +47,11 @@ CYD_FIRMWARE_ROOT=C:\Users\dsx2016\Desktop\codes\cyd
After rebuilding a project, run its `firmware\export_from_build.ps1`, then click **Refresh**.
-## Extending a firmware (media / docs)
+## Extending a firmware (HTML UI preview / docs)
-Create `meta.json` beside the `.bin` files:
+Preferred card preview: **`media/preview.html`** — HTML recreation of the on-device LVGL UI (320×240). Auto-detected if present.
+
+Optional `meta.json` beside the `.bin` files:
```json
{
@@ -59,19 +61,18 @@ Create `meta.json` beside the `.bin` files:
"description": "Longer detail text",
"tags": ["pomodoro", "touch"],
"media": {
- "cover": "media/cover.webp",
- "animation": "media/preview.gif",
+ "preview": "media/preview.html",
"video": "media/demo.mp4",
"docs": "docs/README.md"
}
}
```
-- Relative media paths are served via `/api/firmware/.../media/...` (only if the file exists).
+- Media paths are served via `/api/firmware/.../media/...` (only if the file exists).
- `docs` may also be an `https://` URL.
- `boardId` values: `esp32-2432s028` (active), `esp32-2432s024`, `esp32-3248s035`, `esp32-s3-touch` (UI reserved).
-Without `meta.json`, the catalog still works from `VERSION.txt`.
+Without `meta.json`, the catalog still works from `VERSION.txt` + auto `media/preview.html`.
## Public deploy (Docker + Caddy)
diff --git a/components/firmware-card.tsx b/components/firmware-card.tsx
index 371559e..9e9d848 100644
--- a/components/firmware-card.tsx
+++ b/components/firmware-card.tsx
@@ -1,48 +1,10 @@
"use client";
-import { Film, FileText, ImageIcon } from "lucide-react";
+import { FileText, LayoutTemplate } from "lucide-react";
import type { FirmwareInfo } from "@/lib/firmware";
import { formatBytes, cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
-
-function MediaSlot({ fw, active }: { fw: FirmwareInfo; active: boolean }) {
- const src = fw.media.animation || fw.media.cover;
- if (src) {
- return (
- // Dynamic firmware media URLs — next/image needs remotePatterns per host
- // eslint-disable-next-line @next/next/no-img-element -- local API / arbitrary media
-
- );
- }
-
- 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 (
-