Impeccable Live Mode 深度分析:把 AI 设计迭代接到真实页面上

AI 写前端最别扭的地方,往往不是“能不能改”,而是“怎么知道改得对不对”。传统流程里,你对 agent 说“这个 card 再活泼一点”,它改代码,你等 diff,刷新浏览器,再判断效果;不满意,就再描述一遍。每一轮都在代码和视觉之间来回切换。

Impeccable 的 /impeccable live 试图把这个循环压扁:直接在浏览器里点选真实元素,输入一句设计意图,拿到三版可切换的 HTML+CSS 变体,接受其中一版后写回源代码

官方文档把它标成 Alpha,这个判断是合理的。Live Mode 的野心比普通 prompt 大很多:它不是让 AI “生成一段 UI”,而是把浏览器、开发服务器、AI agent 和项目源码串成一个实时设计迭代系统。

Impeccable Live Mode 官方页面中的交互示意

它解决的不是生成问题,而是迭代闭环问题

Impeccable 原本就是一套面向 AI 编码工具的设计语言:bolderquieterpolishtypesetcolorize 等命令,负责把模糊的审美要求转成更明确的执行约束。Live Mode 做的是另一件事:把这些命令从“对源码发起请求”变成“对页面上的真实元素发起请求”。

它最适合三类场景:

场景Live Mode 的价值
对真实元素探索方向在实际页面上下文里比较三种设计处理,而不是看脱离上下文的 mock
打磨差一点的 UI你知道“不太对劲”,但很难用代码 diff 描述问题
快速做视觉 A/B生成几版、看一眼、可以接受,也可以全部丢弃

它不适合从零做新功能,也不适合整页重设计。官方文档也明确建议:新功能用 /impeccable craft,整页级别的改造用 /impeccable 或更具体的 refine 命令。Live Mode 的单位是“被选中的元素”,不是“整个产品”。

三段式体验:Pick、Generate、Accept

从用户视角看,Live Mode 的交互很简单:

  1. 运行 /impeccable live
  2. 打开正在运行的 dev server 页面。
  3. 在浏览器里选择一个元素。
  4. 输入 freeform prompt,或选择 bolderquieterdistillpolishtypesetcolorizelayoutadaptanimatedelightoverdrive 等 action chip。
  5. 可选地在元素上加 comment pin 或 stroke。
  6. 点击 Go,得到三版变体。
  7. 用方向键或上下文栏切换,Accept 写回源码,Discard 还原原始版本。

官方 Live Mode 页面把这个过程概括成三步:Pick、Generate、Accept。真正有意思的是,这三步背后并不是简单的 DOM patch。

sequenceDiagram
    participant U as 用户浏览器
    participant S as Impeccable 本地 helper server
    participant A as AI Agent
    participant F as 项目源代码
    participant D as Dev Server / HMR

    U->>S: POST generate 事件
元素 HTML、样式、prompt、注释截图 A->>S: live-poll 长轮询获取事件 A->>F: live-wrap 定位元素并插入 variant wrapper A->>F: 一次性写入三版 HTML+CSS 变体 A->>S: reply done D->>U: HMR 将源码变化热替换到页面 U->>S: accept / discard A->>F: live-accept 清理 wrapper
保留被接受版本或还原原始版本

这套设计的关键选择是:变体写进源文件,而不是只塞进浏览器 DOM

这样做的代价是实现复杂很多,因为 agent 必须找到页面元素对应的真实源码位置;但收益也很直接:变体经过项目自身的渲染管线和 HMR 出现在页面上,接受结果也天然是可 diff、可审查、可提交的代码。

架构拆解:browser、server、agent 三方协议

源码里的 ADR 把 Live Mode 划成三方:浏览器、server、agent。它们各自承担的职责非常清晰。

graph TD
    B[浏览器 live-browser.js] -->|POST /events| S[本地 helper server]
    S -->|SSE /events| B
    A[AI Agent] -->|GET /poll 长轮询| S
    A -->|POST /poll reply| S
    A -->|live-wrap / live-accept| F[项目源文件]
    F -->|HMR| B
    S -->|GET /source fallback| B

浏览器层:选择器、上下文栏和变体切换器

浏览器脚本由本地 helper server 的 /live.js 提供,并被注入到项目页面中。它负责几件事:

  • 鼠标移动时高亮可选元素,点击后锁定目标。
  • 展示 action bar:action pill、freeform input、变体数量和 Go 按钮。
  • 支持 comment pin 与 stroke;有注释时,浏览器会生成元素截图并上传给 helper server。
  • EventSource 接收 server push,用 fetch 把 generate、accept、discard 等事件发回 server。
  • 通过 MutationObserver 观察变体 wrapper 是否已经经由 HMR 出现在 DOM 中。
  • localStorage 保存会话状态,让刷新、HMR 或短暂断连后仍能恢复到当前变体位置。

这个层面更像一个轻量版的设计工具 overlay。不同的是,它不拥有最终画布;最终画布仍然是你的真实 app。

Server 层:零依赖的本地消息中继

Live Mode 启动后会在本地起一个 Node.js helper server,默认从 127.0.0.1:8400 往后找可用端口。它不依赖 WebSocket,而是用 SSE 加 HTTP POST:

  • /live.js:返回浏览器脚本,并注入当前 session token。
  • /events:浏览器和 server 之间的 SSE / POST 通道。
  • /poll:agent 使用的长轮询接口。
  • /annotation:接收浏览器上传的 PNG 注释截图。
  • /source:无 HMR 场景下,浏览器直接读取源文件作为 fallback。
  • /status/health/stop:状态检查和清理。

官方 ADR 解释了为什么不用 WebSocket:SSE + fetch 可以做到零 npm 依赖,只靠 Node 内置模块运行。对一个要被安装进各种 AI coding harness 的 skill 来说,这个选择很务实。

安全边界也比较克制:server 只绑定 127.0.0.1,变更类请求带 session token,/source 会做路径穿越检查,浏览器 UI 也避免用 eval 或直接拼 innerHTML 构建自身界面。

Agent 层:真正改代码的一方

AI agent 不是只负责“想点好看的方案”。它在 Live Mode 中承担执行协议:

  • live.mjs:检查 .impeccable/live/config.json,启动或复用 helper server,注入浏览器脚本,读取 PRODUCT.md / DESIGN.md
  • live-poll.mjs:进入长轮询循环,等待浏览器事件。
  • live-wrap.mjs:根据元素 id、class、tag、text snippet 在源码中定位元素,并插入 variant wrapper。
  • live-accept.mjs:接受或丢弃某个变体,清理 wrapper。
  • live-status.mjslive-resume.mjslive-complete.mjs:处理中断恢复和 durable session journal。

这也是 Live Mode 和普通“浏览器插件式修改 UI”的根本区别:浏览器只表达选择和反馈,agent 才是代码变更的作者

为什么要写源文件,而不是直接 patch DOM

直接 patch DOM 看起来更快,但它有三个问题。

第一,DOM patch 很难保留框架语义。React、Vue、Svelte、Astro 等框架都有自己的组件边界、状态和编译产物。你在 DOM 里塞一个结果,看起来可能对,但未必对应真实代码。

第二,DOM patch 很难变成可维护代码。用户点击 Accept 后,系统还得反推出这个 DOM 对应哪个组件、哪个样式文件、哪些 token。这一步如果放到最后做,失败概率很高。

第三,DOM patch 会让“预览”和“最终代码”脱节。Live Mode 反过来做:先把变体写进源码,让 dev server / HMR 把它带回浏览器。接受动作因此更接近“删除未选中的候选项”,而不是“把临时 DOM 翻译回代码”。

源码里用 data-impeccable-variants 包一层 wrapper,并设置 display: contents,这样 wrapper 本身尽量不影响 flex/grid 布局。每个候选版本放在 data-impeccable-variant="1"23 里,浏览器只负责切换当前可见版本。

接受变体时还有一个重要的“收尾”概念:carbonize。接受后,辅助脚本可能会先保留一段临时 inline CSS 和 data-impeccable-variant wrapper,保证页面立刻可见;随后 agent 需要把 CSS 移到项目真正的样式文件里,删除 marker、参数注释和未被接受的 scoped rules。这个步骤如果跳过,源码会堆积 live-mode 的临时痕迹。

生成三版变体:不是三种颜色,而是三条设计轴线

Live Mode 的设计生成规则比“给我三个方案”严格得多。官方 reference 明确要求:三版变体要有真正不同的设计方向,而不是同一个方案换三种颜色。

默认情况下,Live Mode 不是要换品牌,而是在已有 identity 内做变化。它会优先读取:

  1. DESIGN.md:视觉系统、色板、字体、组件语言。
  2. CSS custom properties:项目实际 token。
  3. 被选中元素和父级的 computed styles。
  4. 页面上相邻组件的视觉语言。

这里有一个很关键的优先级:DESIGN.md 决定视觉,PRODUCT.md 决定产品语气和战略表达。如果没有这两个文件,Live Mode 仍然可以生成,但会更容易滑向通用默认审美。官方文档也建议在关心品牌一致性时,先跑 /impeccable teach/impeccable document

只有两种情况会进入“departure mode”:PRODUCT.md 的 anti-reference 明确指出当前页面就是要摆脱的失败样式,或者用户 prompt 明确要求完全换方向。否则,它应当保持同一品牌,只在层级、布局拓扑、字体系统、色彩策略、密度、结构拆解等轴线上变化。

这点很重要。很多 AI 设计工具会把“三个方案”理解成“三个品牌”。Live Mode 更像是在同一产品语境内做三次不同取舍。

参数面板:让用户微调,而不是重新生成

Live Mode 还支持给变体暴露 coarse knobs。它们不是重新调用模型,而是通过 CSS 变量或 data attribute 即时生效:

参数类型适合控制什么
range色彩用量、尺寸强度、动效强度这类连续值
steps密度、布局模式、色彩策略这类离散选项
toggle是否启用某个视觉特征

官方 reference 给了一个预算规则:小元素可以没有参数,中等组合目标是 1 到 2 个,大型 hero 或复杂 section 目标是 2 到 3 个,硬上限通常是 4 个。这个设计很实用,因为用户常见反馈并不是“再生成三版”,而是“这个方案可以,但再紧一点、颜色再少一点”。

首次配置和框架适配

Live Mode 需要知道把 /live.js 注入到哪些页面入口里。首次运行时,如果 .impeccable/live/config.json 缺失,agent 需要创建配置:

1
2
3
4
5
6
7
{
  "files": ["index.html"],
  "exclude": [],
  "insertBefore": "</body>",
  "commentSyntax": "html",
  "cspChecked": true
}

不同框架的注入点不同:Vite / React 通常是 index.html,Next.js App Router 是 app/layout.tsx,SvelteKit 是 src/app.html,Nuxt 可以是 app.vue,Astro 需要找根 layout。多页面站点则更适合用 glob,比如 public/**/*.html

官方 reference 文档把前提写成“带 HMR 的 dev server,或浏览器中打开的静态 HTML 文件”,并点名 Vite、Next.js、Bun 等;Live Mode 演示页的 supported dev servers 还列出 SvelteKit、Astro、Nuxt 和 plain static HTML。它的基本前提是:最好有正在运行的 dev server 和 HMR;如果没有 HMR,Live Mode 会尝试通过 /source fallback 把源文件内容注入浏览器,但体验会弱一些。

还有一个现实问题是 CSP。若开发环境的 Content Security Policy 不允许加载 http://localhost:8400,Live Mode 的 picker 脚本和 SSE 连接都会被挡住。源码里的 setup 流程会检测常见 CSP 形态,并提示添加仅限 development 的 script-src / connect-src allowance。

边界和风险:Alpha 的复杂度在哪里

Live Mode 的方向很聪明,但它的复杂度也是真实的。

第一,源码定位不总是可靠。
live-wrap.mjs 会按 id、class、tag、text snippet 定位元素,还会避开生成文件。但现代前端项目里,页面可能来自组件、模板、数据驱动渲染或静态生成产物。遇到生成文件时,它不能直接把接受结果写进 distpublic,否则下一次 build 就会丢失。这时必须走 fallback:先在 served file 里临时预览,再由 agent 找到真正源头并写回。

第二,HMR 不是所有环境都一样。
Vite / Next.js 这类主流 dev server 通常更顺滑;Bun 的静态 HTML import 在源码里被列为已知限制,可能需要依赖 /source fallback。这个 fallback 能让预览继续,但不如框架原生 HMR 自然,也可能丢失部分运行时状态。

第三,接受不是终点,清理才是终点。
Accept 之后如果涉及 inline preview CSS,就需要 carbonize cleanup:移动 CSS、烘焙参数值、删除 live-mode wrapper 和 marker。这个过程依赖 agent 遵守协议。如果 agent 中途被打断,就要用 live-status / live-resume / live-complete 恢复。

第四,它一次只处理一个活跃生成会话。
这是合理限制。Live Mode 改的是同一份源码,如果多个元素同时生成、同时接受,source wrapper 和 HMR 状态会变得难以管理。

第五,输入质量决定输出质量。
官方文档特别提醒:不要在页面还充满 placeholder、默认样式、Lorem ipsum 时使用 Live Mode。它需要真实上下文;否则三版变体只会围绕一个不成熟的元素做表面变化。

我的判断:这是 AI 前端工具从“生成器”走向“操作台”的信号

Live Mode 最值得关注的地方,不是它能生成三个漂亮 card,而是它把 AI agent 放进了真实产品页面的反馈回路里。

传统 AI UI 生成更像一次性产出:prompt 进,代码出。Live Mode 更像一个操作台:开发者在页面上指哪改哪,agent 读取项目设计上下文,写入真实源码,浏览器用 HMR 回放结果,用户再决定接受还是丢弃。

这个思路比“让 AI 画更好看的界面”更接近专业前端工作流。设计迭代真正困难的地方,不是没有灵感,而是要在现有品牌、现有组件、现有页面语境、现有源码约束下快速比较取舍。Live Mode 正是在这个位置发力。

但也正因为它接入真实源码,Alpha 阶段的粗糙边缘不可避免。框架差异、CSP、生成文件、HMR、accept cleanup、agent 中断恢复,每一个点都可能让体验从“像设计工具一样顺滑”退回到“需要工程师理解内部机制”。

所以现阶段我会这样定位它:

/impeccable live 不是替代 Figma,也不是替代设计系统。它更像 AI 编码工具里的实时设计试验台,适合在真实页面上快速探索局部 UI 方向,并把可接受的结果落回生产代码。

如果你已经在用 Claude Code、Cursor、Codex 或 Gemini CLI 做前端迭代,Live Mode 值得试。但最好先准备好 PRODUCT.md / DESIGN.md,在 dev server 和 HMR 正常的项目里使用,并把 Accept 后的代码当作需要 review 的源码,而不是自动可信的最终设计。