ui-snapshot
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSkill: ui-snapshot
Skill: ui-snapshot
Đúc kết từ phiên bonbon-ai 2026-09-19: 12 màn × desktop/mobile + 10 overlay của (Next.js 14,
Tailwind 3) gói trong 426 KB, Playwright 22 ảnh, 0 lỗi console.
fe/沉淀自 bonbon-ai 2026-09-19 会话:将 的 12 个界面 × desktop/mobile 与 10 个 overlay(Next.js 14、Tailwind 3)打包在 426 KB 内;Playwright 生成 22 张截图,0 个 console 错误。
fe/WHAT
WHAT(是什么)
Purpose và context
目的与背景
- Purpose: một file duy nhất mở bằng double-click (không server, không mạng, không login) tái hiện đúng giao diện app thật — layout, token màu, font, icon, trạng thái (loading/empty/error/overdue…), overlay (modal, sheet, toast, tour) — với dữ liệu placeholder cứng.
.html - Trigger (when to use):
- Cần đưa giao diện cho người không chạy được app (sếp, designer, khách, reviewer, tài liệu wiki).
- Cần bản mockup có dữ liệu mẫu để bàn UX/QC mà không đụng dữ liệu thật hay backend.
- User nói 'snapshot UI', 'gói giao diện vào 1 file html', 'nén UIUX dự án', hoặc .
/ui-snapshot
- Non-goals: không thiết kế lại (→ hallmark/redesign), không chụp ảnh tĩnh app đang chạy (→ playwright-verify), không nhét dữ liệu thật, không giữ logic nghiệp vụ/API (nút hành động chỉ đổi state trong trang).
- **目的:**生成唯一一个 文件,可通过双击打开(无需 server、网络或登录),并用硬编码的 placeholder 数据准确复现真实应用界面——包括 layout、颜色 token、字体、图标、状态(loading/empty/error/overdue……)以及 overlay(modal、sheet、toast、tour)。
.html - 触发条件(何时使用):
- 需要把界面交付给无法运行应用的人(老板、设计师、客户、reviewer、wiki 文档)。
- 需要带示例数据的 mockup,以便讨论 UX/QC,同时不接触真实数据或 backend。
- 用户说“snapshot UI”“把整个界面打包成 1 个 HTML 文件”“压缩项目 UI/UX”,或调用 。
/ui-snapshot
- **非目标:**不重新设计(→ hallmark/redesign),不对正在运行的应用进行静态截图(→ playwright-verify),不填入真实数据,也不保留业务逻辑/API(操作按钮仅在页面内切换 state)。
Mental model
思维模型
Vì sao PORT thay vì build/SSR app thật: app thật cần auth + backend + dữ liệu thật; snapshot bằng DOM thật sẽ kéo theo dữ liệu nhạy cảm và bundle lớn. Port sang vanilla JS với class chép nguyên văn + compile Tailwind bằng config thật cho ra đúng pixel mà không mang theo gì khác.
đọc HẾT JSX các trang → liệt kê màn + trạng thái + overlay → viết app.js (router hash, state, data cứng, 1 hàm/màn, nhánh desktop/mobile) → build.py (Tailwind config thật + font + icon subset + asset data URI) → shot.mjs verify → sửa → publish之所以选择 PORT,而不是构建/SSR 真实应用,是因为真实应用需要 auth + backend + 真实数据;使用真实 DOM 制作 snapshot 会引入敏感数据和庞大的 bundle。移植为 vanilla JS,并逐字保留 class + 使用真实 config 编译 Tailwind,既能实现像素级一致,又不会夹带其他内容。
通读所有页面的 JSX → 列出界面 + 状态 + overlay → 编写 app.js(hash router、state、硬编码数据、每个界面一个函数、desktop/mobile 分支)→ 编写 build.py(真实 Tailwind config + 字体 + 图标子集 + 使用 data URI 的 asset)→ 用 shot.mjs 验证 → 修正 → 发布Input và output contract
输入与输出约定
| Field | Required? | Ý nghĩa | |
|---|---|---|---|
| In | thư mục frontend ( | có | để dùng |
| In | danh sách trang/route | có (tự dò | phạm vi "toàn bộ UI" |
| In | ngưỡng dung lượng | không (5 MB) | |
| In | font chính / logo / banner | không | |
| Out | | có | file duy nhất, offline |
| Out | báo cáo: số màn, KB, ảnh verify, lệch so app thật | có | trung thực, liệt kê chỗ giả lập |
| 字段 | 是否必填 | 含义 | |
|---|---|---|---|
| In | frontend 目录( | 是 | 用于调用 |
| In | 页面/route 列表 | 是(自动探测 | 确定“全部 UI”的范围 |
| In | 体积阈值 | 否(5 MB) | |
| In | 主字体/logo/banner | 否 | |
| Out | | 是 | 唯一的离线文件 |
| Out | 报告:界面数量、KB、验证截图、与真实应用的差异 | 是 | 如实列出所有模拟项 |
Rules và capabilities
规则与能力
- RULE-01 (MUST): Đọc HẾT file JSX của từng trang trước khi port (kể cả nhánh mobile, empty/loading/error, modal, sheet). Port từ trí nhớ = lệch UI.
- RULE-02 (MUST): Class Tailwind chép NGUYÊN VĂN từ JSX, viết dạng chuỗi literal trong (JIT chỉ thấy literal; ghép động
app.jssẽ mất class).bg-${x} - RULE-03 (MUST): Dữ liệu placeholder cứng, không bao giờ dữ liệu thật (không gọi API, không copy từ network capture/DB). Ngày dùng offset tương đối () để trạng thái quá hạn ổn định theo thời gian.
D(-9) - RULE-04 (MUST): Tên icon viết dạng chuỗi có nháy () để
"search"subset font; icon động thì đặt tên đầy đủ trong map.build.py - RULE-05 (MUST): Cùng breakpoint với app (vd 768px) và render nhánh desktop/mobile bằng JS (không dựa khi có chế độ ép mobile trong khung điện thoại).
md: - RULE-06 (MUST): Asset ngoài (ảnh CDN, font Google) phải nhúng hoặc thay bằng nền/gradient — file phải chạy offline. Ghi rõ mọi chỗ thay thế trong báo cáo.
- RULE-07 (MUST): Verify bằng cả desktop + mobile, đọc ảnh,
shot.mjsmới báo xong; vượtNO ERRORS= FAIL.--max-mb - RULE-08 (SHOULD): Có panel demo (nút "UI") để nhảy tới mọi màn + bật trạng thái (loading/stale, lỗi, toast, tour, các state AI…), và chế độ Auto/Desktop/Mobile.
- Capabilities: đọc codebase; Python 3 + Node + Tailwind CLI của dự án; mạng lúc build (Google Fonts, codepoints); Playwright để verify.
- RULE-01 (MUST):移植前必须通读每个页面的所有 JSX 文件(包括 mobile 分支、empty/loading/error、modal、sheet)。凭记忆移植会导致 UI 偏差。
- RULE-02 (MUST):逐字复制 JSX 中的 Tailwind class,在 中写成 literal 字符串(JIT 只能识别 literal;动态拼接
app.js会导致 class 丢失)。bg-${x} - RULE-03 (MUST):数据必须是硬编码的 placeholder,绝不能使用真实数据(不调用 API,不从 network capture/DB 复制)。日期使用相对 offset(),使 overdue 状态随时间保持稳定。
D(-9) - RULE-04 (MUST):图标名称写成带引号的字符串(),以便
"search"对字体进行子集化;动态图标则必须在 map 中写全名。build.py - RULE-05 (MUST):使用与 app 相同的 breakpoint(例如 768px),并通过 JS 渲染 desktop/mobile 分支(在手机框中强制 mobile 模式时,不能依赖 )。
md: - RULE-06 (MUST):外部 asset(CDN 图片、Google 字体)必须嵌入或替换为背景/gradient——文件必须能够离线运行。所有替换点都要写入报告。
- RULE-07 (MUST):使用 验证 desktop + mobile,并查看截图;只有在输出
shot.mjs后才能报告完成。超过NO ERRORS即为 FAIL。--max-mb - RULE-08 (SHOULD):提供 demo panel(“UI”按钮),用于跳转到所有界面并切换状态(loading/stale、错误、toast、tour、各类 AI state……),同时支持 Auto/Desktop/Mobile 模式。
- 能力:读取 codebase;使用 Python 3 + Node + 项目自带的 Tailwind CLI;构建时需要网络访问 Google Fonts 和 codepoints;使用 Playwright 进行验证。
Failure boundaries
失败边界
- Dự án không dùng Tailwind → để CSS trống: chép CSS module/styled vào template tay, hoặc clarify với user.
build.py - Không có mạng lúc build → font/icon không nhúng được → blocked (hoặc chấp nhận fallback system font, ghi rõ).
- File vượt ngưỡng → giảm: bỏ weight font thừa, ảnh nhỏ hơn, bớt dữ liệu mẫu; không cắt màn hình.
sips -Z - Framework khác (Vue/Svelte/Angular template) → vẫn áp dụng, chỉ đổi nguồn đọc class.
- 项目不使用 Tailwind → 会生成空 CSS:手动将 CSS module/styled 复制到 template,或与用户确认。
build.py - 构建时无网络 → 无法嵌入 font/icon → blocked(或者接受 system font fallback,并清楚记录)。
- 文件超出阈值 → 减少体积:移除多余的 font weight、使用 缩小图片、减少示例数据;不得删减界面。
sips -Z - 其他 framework(Vue/Svelte/Angular template)→ 仍然适用,只需更改读取 class 的来源。
HOW
HOW(如何执行)
Main workflow
主工作流
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | deterministic | | Dò route + layout + component dùng chung; đọc | danh sách màn + token | không Tailwind → FB1 |
| W02 | judgment | JSX từng trang | Đọc HẾT từng trang (RULE-01), ghi lại mọi trạng thái/overlay/nhánh mobile | inventory màn × trạng thái | — |
| W03 | effect | inventory | Tạo | app.js | — |
| W04 | deterministic | app.js + template | | file HTML + KB | lỗi Tailwind → xem stderr; >max → FB3 |
| W05 | deterministic | file | | ảnh + | lỗi → sửa app.js, lặp W04 |
| W06 | effect | file | Đặt vào repo (vd | báo cáo | — |
| 步骤 | 类型 | 输入 | 操作 | 输出/退出条件 | 失败/下一步 |
|---|---|---|---|---|---|
| W01 | 确定性 | | 探测 route + layout + 公共 component;读取 | 界面列表 + token | 未使用 Tailwind → FB1 |
| W02 | 判断 | 各页面的 JSX | 通读每个页面(RULE-01),记录所有状态、overlay 和 mobile 分支 | 界面 × 状态清单 | — |
| W03 | 执行 | 界面 × 状态清单 | 基于 | app.js | — |
| W04 | 确定性 | app.js + template | | HTML 文件 + KB | Tailwind 错误 → 查看 stderr;超过 max → FB3 |
| W05 | 确定性 | file | | 截图 + | 出错 → 修正 app.js,重跑 W04 |
| W06 | 执行 | file | 放入 repo(例如 | 报告 | — |
Branches
分支
| ID | Kind | Guard | Hành vi | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | conditional | trang có dead-code UI (state không có đường vào) | vẫn port nhưng chỉ mở qua panel demo, ghi chú "legacy" | — | W03 |
| B02 | conditional | app có devtool page tối/khác design system | port bằng inline style như bản gốc | — | W03 |
| B03 | recovery | chế độ mobile trên màn rộng lệch layout | khung | — | W05 |
| ID | 类型 | 条件 | 行为 | 跳过/失败 | 汇合点 |
|---|---|---|---|---|---|
| B01 | 条件分支 | 页面包含无法进入的 dead-code UI(state 没有可到达路径) | 仍然移植,但只能通过 demo panel 打开,并注明“legacy” | — | W03 |
| B02 | 条件分支 | app 包含深色或其他 design system 的 devtool page | 使用 inline style 按原样移植 | — | W03 |
| B03 | 恢复 | mobile 模式在宽屏上出现布局偏差 | | — | W05 |
Validation và stopping
验证与停止条件
Tất định: exit 0 và dưới ngưỡng; in cho mọi màn desktop + mobile. Cần mắt: so ảnh với app thật/Figma (màu, khoảng cách, chữ tiếng Việt có dấu). Dừng khi mọi màn trong inventory có ảnh sạch và báo cáo đã liệt kê chỗ giả lập.
build.pyshot.mjsNO ERRORS确定性检查: exit 0 且文件低于阈值; 对每个界面的 desktop + mobile 输出 。人工检查:将截图与真实应用/Figma 对比(颜色、间距、带声调符号的越南语文本)。当清单中的每个界面都有无错误截图,且报告已列出所有模拟项时停止。
build.pyshot.mjsNO ERRORSExamples
示例
- Positive: bonbon-ai → login, dashboard overview/pending/overdue, filter theo phòng ban/loại HĐ, chi tiết phiếu 3 tab, VS Code overlay, AI tracker, sheet/modal/toast/tour → 426 KB, 79 icon, 22 ảnh verify, 0 lỗi.
fe/ - Boundary: dự án ảnh nền hero 3 MB từ CDN → thay gradient + ghi "khác app thật: ảnh nền login" thay vì nhúng làm file vượt ngưỡng.
- 正向: bonbon-ai → 登录、dashboard 概览/pending/overdue、按部门/合同类型筛选、带 3 个标签页的单据详情、VS Code overlay、AI tracker、sheet/modal/toast/tour → 426 KB、79 个图标、22 张验证截图、0 个错误。
fe/ - 边界: 项目的 hero 背景图从 CDN 加载且大小为 3 MB → 替换为 gradient,并记录“与真实应用不同:登录背景”,而不是嵌入后导致文件超出阈值。
Test tất định
确定性测试
python3 scripts/build.py --fe <fe> --src assets-copy --out /tmp/t.htmlassets/app-skeleton.jsassets/template.htmlnode scripts/shot.mjs /tmp/t.html /tmp/shots d-home=home m-home=homeNO ERRORS使用 (重命名为 app.js)和 执行 ,必须生成小于 1 MB 的文件;随后执行 并输出 。
assets/app-skeleton.jsassets/template.htmlpython3 scripts/build.py --fe <fe> --src assets-copy --out /tmp/t.htmlnode scripts/shot.mjs /tmp/t.html /tmp/shots d-home=home m-home=homeNO ERRORS