从不误吵醒 Agent 的被动检测:lavish-axi iframe 布局审计实现深解
【免费下载链接】lavish-axiHTML is the new markdown. Lavish is the new editor for your HTML artifacts.项目地址: https://gitcode.com/gh_mirrors/la/lavish-axi
lavish-axi是一款面向人与 AI Agent 协作的本地优先HTML 编辑器:Agent 写好 HTML 产物,用户在本地浏览器里审阅,可以点选元素、选中文字,把反馈发回给 Agent。其中最精巧的设计是它的iframe 布局审计——一套被动检测机制,能发现文字被裁切、按钮被剪掉、内容跑出视口等严重布局故障,但永远不会因为"发现了问题"就去吵醒 Agent 🤫。
什么是"被动检测"?先看两种思路
Agent 生成的 HTML 经常"看着能跑、实际有伤":一段标题溢出了卡片、一个按钮被容器剪掉一半。传统思路是检测到问题就立刻通知 Agent 修复,但这会带来两个麻烦:
- 误报即骚扰:检测逻辑稍有噪音,Agent 就会被反复唤醒去"修"并不存在的问题;
- 浪费轮次:用户可能只是扫一眼,根本不想修。
lavish-axi 选择了另一条路:检测永远是被动发生的。审计在 iframe 里静默运行,发现的结果只落进顶部的Layout issues收件箱;只有当用户手动勾选问题并点击Queue selected fixes时,Agent 才会通过常规的长轮询通道收到一条layout-warnings提示。源码里把这个原则写得很直白:
检测从不返回
lavish-axi poll,从不唤醒 Agent;只有用户排队修复才会。
这个约定见 src/layout-warnings.js 文件头部的注释,也是整个模块的设计基石。
审计盯住哪 6 类严重布局故障?
审计只报告"有直接渲染证据"的严重失败,规则与用户看到的大白话描述在 src/layout-warnings.js 中一一对应:
| 规则 | 含义 | 用户看到的描述 |
|---|---|---|
page-horizontal-overflow | 页面整体横向溢出 | 页面比视口宽,内容跑到了屏幕外 |
clipped-text | 文字片段越过容器裁剪边界 | 文字被容器裁掉了 |
clipped-control | 必要控件被容器剪掉一部分 | 按钮的一部分无法使用 |
viewport-unreachable-control | 必要控件在视口之外 | 控件够不到 |
viewport-unreachable-content | 文字片段在视口之外 | 这段文字读不到 |
overlapping-text | 文字被不透明兄弟元素大面积遮挡 | 文字被盖住读不了 |
只有severity: "error"的发现才会入箱(见 src/layout-warnings.js),轻微抖动一律忽略——宁可漏报小瑕疵,不可误报。
审计如何等到"稳定时刻"再出手?
浏览器里做布局测量最大的坑是"测得太早":字体还没加载完、动画还没结束、响应式布局还没算完。审计入口 auditLayout() 之前有一整串"稳压器",全部位于注入到沙盒 iframe 的 SDK 中(src/artifact-sdk.js):
- 等字体:
document.fonts.ready——字体切换会显著改变文字几何; - 等尺寸稳定:用
ResizeObserver监听前 800 个元素,180ms 无变化才算稳定,最多等 2 秒; - 等有限动画结束:入场动画、过渡最多等 4 秒;无限动画(如持续旋转的装饰)则放行,但动画关联的元素会被整体排除在检测之外;
- 采样两到三轮:先测一遍,间隔 120ms 再测一遍,若 DOM 仍在"水合"(MutationObserver 观察到变化)则等其静止后再测第三轮,最后只发布两轮都稳定存在的发现(findStableLayoutFindings)。
审计会在load、resize、animationend、transitionend等事件后自动重排(startLayoutAudit),所以用户手动缩小窗口触发的响应式塌陷也会被捕获。
如何避免误报?一份精心设计的"豁免清单"
这是该模块最见功力的部分——大量看起来"越界"的布局其实是作者意图,审计会主动豁免(src/artifact-sdk.js):
- ✂️显式省略号 / line-clamp:作者想截断,就不算事故;
- 🙈标准 visually-hidden 无障碍文本(2px 裁剪的 sr-only 写法);
- 🎠有意的横向滚动容器(轮播、横向列表)及其后代;
- 🎭蒙版 / 圆角裁剪(mask、clip-path、带圆角的 overflow 容器)属于装饰手法;
- 🖼️装饰性重叠(海报式叠字)不产生文本遮挡发现;
- 🎬正在运动的元素:动画未结束时的中间态不算证据。
阈值也经过校准:文本要"越过裁剪边界 1px 以上且中心点在界外,或超过 20%"才算严重(classifySevereTextOverflow);页面级横向溢出必须同时满足"有真实内容逃出"且超出max(24px, 视口宽度 5%)(isMaterialPageOverflow)。遮挡检测则是对文字片段取 9 个采样点做elementFromPoint,不透明遮挡占比 ≥90% 且样本 ≥5才报(isNearTotalOcclusion)。
为什么告警不会"误消",也不会"重复刷屏"?
发现落库后,applyDiagnosticPass 按一套刻意保守的生命周期来维护记录:
- 稳定指纹:
规则 + 归一化目标 + 视口档位(layoutWarningFingerprint)生成 16 位哈希,同一个问题反复检测只会更新同一条记录,绝不虚增计数——溢出量甚至被刻意排除在指纹之外; - 视口隔离:
mobile / compact / desktop三档视口互不干扰,桌面端的通过永远不能清除手机端的告警; - 只有正向证据能解决:
Resolved要求"更新的产物加载 + 同一视口的一轮完整审计不再检出"。审计失败、页面没加载完、换了一个视口,统统只会把记录标成Unverified(未验证)而非清除; - 状态机:
Open → Queued for fix → Still present / Returned,外加Unverified / Dismissed / Obsolete / Resolved(完整枚举见 src/layout-warnings.js),每次状态迁移都写入有界的历史轨迹,方便回看"这条告警是怎么来的"。
这套规则有完整的单测覆盖:test/layout-warnings.test.js 里的用例名本身就是一份需求文档,例如"a different viewport class can never clear a warning"、"a newer complete matching-viewport pass without the finding resolves it"。
用户视角:从徽章到 Agent 修复的完整链路
- 审计完成后,顶栏出现Layout issues按钮与未解决计数;没有问题时按钮直接隐藏;
- 打开抽屉,每条问题展示严重程度、大白话解释、受影响视口、组件标识、最后出现时间与状态,并提供Reveal(在产物中高亮定位)与Dismiss(仅对当前产物版本生效);
- 勾选若干条(或Select all),点击Queue selected fixes——整批问题合并成一条普通排队提示,标签为
layout-warnings,随用户下次发送走常规反馈通道; - Agent 收到的是结构化载荷:每条带选择器、轴方向、溢出像素、视口档位(layoutWarningPromptPayload),并要求"一次修完、保存一次",让审阅页只刷新一次。
交互细节可见 test/layout-warning-inbox.browser.test.js。
打开时的布局闸门:只挡一瞬间
打开审阅页时,产物会先被遮罩盖住,只等第一轮真实的 iframe 审计完成就立刻揭开——无论查到什么问题。第一次完成的客户端检查即放行,绝不让网络往返或修复请求"绑架"审阅;超时有兜底超时自动放行,用户还能随时点Show anyway。用--no-gate可以跳过这层幕布(见 README.md 的Open-time layout gate一节)。
靠什么保证"不误报、不吵人"?真实浏览器回归测试
项目用真浏览器对一整组夹具页做回归(test/layout-audit-browser.test.js),夹具就在 test/fixtures/layout-audit/:
control-broken-*四类故意做坏的页面(溢出、裁切、可达性、遮挡)必须在桌面 + 移动视口下各报出预期条数;real-*-clean一组真实但健康的页面(产品计划、仪表盘、杂志排版、轮播、动画入场)必须保持沉默;- 断言里有一行点睛之笔:
detection alone must never wake an agent——检测到问题后执行 poll 仍然返回waiting; - 修复后的产物重新加载并通过完整审计,计数归零——全程 Agent 从未被唤醒。
小结
lavish-axi 的 iframe 布局审计把"检测"和"修复"彻底解耦:
- 对 Agent 安静:检测永不触发轮询返回,只有用户排队修复才产生提示;
- 对用户有用:每条告警都有大白话解释、可定位、可忽略、可批量修复;
- 对状态诚实:只有正向证据能清除告警,失败与不确定一律显式标记。
如果你也在做"AI 生成 HTML + 人工审阅"的产品,这套 src/layout-warnings.js + src/artifact-sdk.js 的"被动检测 + 保守生命周期"设计,值得完整读一遍源码。
【免费下载链接】lavish-axiHTML is the new markdown. Lavish is the new editor for your HTML artifacts.项目地址: https://gitcode.com/gh_mirrors/la/lavish-axi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考