Impeccable Live Mode 深度分析:把 AI 设计迭代接到真实页面上
AI 写前端最别扭的地方,往往不是“能不能改”,而是“怎么知道改得对不对”。传统流程里,你对 agent 说“这个 card 再活泼一点”,它改代码,你等 diff,刷新浏览器,再判断效果;不满意,就再描述一遍。每一轮都在代码和视觉之间来回切换。
Impeccable 的 /impeccable live 试图把这个循环压扁:直接在浏览器里点选真实元素,输入一句设计意图,拿到三版可切换的 HTML+CSS 变体,接受其中一版后写回源代码。
官方文档把它标成 Alpha,这个判断是合理的。Live Mode 的野心比普通 prompt 大很多:它不是让 AI “生成一段 UI”,而是把浏览器、开发服务器、AI agent 和项目源码串成一个实时设计迭代系统。

它解决的不是生成问题,而是迭代闭环问题
Impeccable 原本就是一套面向 AI 编码工具的设计语言:bolder、quieter、polish、typeset、colorize 等命令,负责把模糊的审美要求转成更明确的执行约束。Live Mode 做的是另一件事:把这些命令从“对源码发起请求”变成“对页面上的真实元素发起请求”。
它最适合三类场景:
| 场景 | Live Mode 的价值 |
|---|---|
| 对真实元素探索方向 | 在实际页面上下文里比较三种设计处理,而不是看脱离上下文的 mock |
| 打磨差一点的 UI | 你知道“不太对劲”,但很难用代码 diff 描述问题 |
| 快速做视觉 A/B | 生成几版、看一眼、可以接受,也可以全部丢弃 |
它不适合从零做新功能,也不适合整页重设计。官方文档也明确建议:新功能用 /impeccable craft,整页级别的改造用 /impeccable 或更具体的 refine 命令。Live Mode 的单位是“被选中的元素”,不是“整个产品”。
三段式体验:Pick、Generate、Accept
从用户视角看,Live Mode 的交互很简单:
- 运行
/impeccable live。 - 打开正在运行的 dev server 页面。
- 在浏览器里选择一个元素。
- 输入 freeform prompt,或选择
bolder、quieter、distill、polish、typeset、colorize、layout、adapt、animate、delight、overdrive等 action chip。 - 可选地在元素上加 comment pin 或 stroke。
- 点击 Go,得到三版变体。
- 用方向键或上下文栏切换,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.mjs、live-resume.mjs、live-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"、2、3 里,浏览器只负责切换当前可见版本。
接受变体时还有一个重要的“收尾”概念:carbonize。接受后,辅助脚本可能会先保留一段临时 inline CSS 和 data-impeccable-variant wrapper,保证页面立刻可见;随后 agent 需要把 CSS 移到项目真正的样式文件里,删除 marker、参数注释和未被接受的 scoped rules。这个步骤如果跳过,源码会堆积 live-mode 的临时痕迹。
生成三版变体:不是三种颜色,而是三条设计轴线
Live Mode 的设计生成规则比“给我三个方案”严格得多。官方 reference 明确要求:三版变体要有真正不同的设计方向,而不是同一个方案换三种颜色。
默认情况下,Live Mode 不是要换品牌,而是在已有 identity 内做变化。它会优先读取:
DESIGN.md:视觉系统、色板、字体、组件语言。- CSS custom properties:项目实际 token。
- 被选中元素和父级的 computed styles。
- 页面上相邻组件的视觉语言。
这里有一个很关键的优先级: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 需要创建配置:
| |
不同框架的注入点不同: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 定位元素,还会避开生成文件。但现代前端项目里,页面可能来自组件、模板、数据驱动渲染或静态生成产物。遇到生成文件时,它不能直接把接受结果写进 dist 或 public,否则下一次 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 的源码,而不是自动可信的最终设计。