# Better Answer View 卡片布局加固与边界测试 版本:0.3.5 · 2026-10-29 ## 结论与修复目标 本次将 0.3.4 针对 `span/small.answer-note` 的局部修复扩展为一套可逆的组件布局保护。目标是:内置卡片与说明组件在常见标签误用、狭窄容器、长内容和动态修改下仍完整可读,同时保留隐藏、折叠、输入控件和图形的原有行为。 没有改写用户保存的 HTML、历史版本或交互状态;修复发生在隔离 iframe 的实际渲染中。 ## 方案选择 | 方案 | 优点 | 局限 | 本次决定 | |---|---|---|---| | 仅补充模型提示 | 成本低,减少误用 | 不能修复历史回答,也不能保证模型每次遵守 | 作为辅助措施 | | 全局强制 block、自动高度、统一 `!important` | 能消除部分表面重叠 | 容易破坏隐藏、表格、折叠、图表和作者的后续修改 | 不采用全局强制 | | 完全改成固定组件结构,由模型只填数据 | 组件边界最明确,可限制非法组合 | 需要迁移现有自由 HTML,并限制自定义图形和交互 | 保留为未来可选模式,不在此次强行迁移 | | 明确组件语义 + 可逆运行时修复 + 几何回归 | 历史内容受益,保留现有表达能力,可针对实际缺陷验证 | 仍需明确任意自定义 CSS 的支持边界 | 本次实施 | ## 已实现的保护 ### 1. 组件类型与行内占位 - `.card`、`.answer-block`、`.answer-metric`、`.answer-decision`、`.answer-note` 以及新 `.answer-inline-note`:普通、可见、非定位的行内组件在渲染时获得能够参与行高计算的盒子。 - 识别实际计算后的 `display`,不再只识别 span/small 两个标签。em、strong、a、code、output 等同样受到保护。 - 不将 `display:none`、`contents`、table-cell、table-row、list-item、grid、flex 统一改成 block。 - 普通说明框建议使用 `p.answer-note`;行内说明使用 `.answer-inline-note`;表格副文字使用 `.answer-cell-detail`。 - 历史表格里的行内 `.answer-note` 自动紧凑化;完整段落说明框保留原有内边距。 ### 2. 可逆修复,保留动态交互 记录修复前的内联值、优先级和本次写入值。每轮布局先还原仍由插件持有的声明,再依据当前作者样式判断。 因此组件可以在修复后继续被 class 隐藏、改成 flex/contents、重新显示;脚本后续写入的新样式不会被旧备份覆盖。删除节点时释放修复记录,重新插入可再次正常判断。 修复期间暂停自身 MutationObserver,并在 finally 中恢复,避免修改 style 又触发下一次无意义修复。已有 ResizeObserver 继续处理真实尺寸变化。 ### 3. 长内容和狭窄容器 - 固定 height/max-height 的普通阅读卡片,在正常流内容实际溢出时恢复自然高度。 - 对卡片内的受限中间层,从内向外解除造成溢出的高度约束,包括 100% 高度与多层固定高度。 - 只有实际发生浮动溢出的普通 block 容器才使用 flow-root,避免后续内容穿过卡片。 - 极窄单元格优先缩减装饰性左右内边距,保留可读字号;不以裁剪文字掩盖溢出。 - 数值在有空间时保持完整单行;确实放不下时允许换行,避免长金额被 iframe 裁掉。变宽后重新计算,而非永久保留窄屏修改。 ### 4. 保护范围 保留原生输入、textarea 内部滚动、焦点、选区和保存状态。跳过明确滚动区域、listbox/menu/tree/grid 等控件子树、SVG/foreignObject、隐藏辅助内容、受控折叠子树及有意定位的图形。图表柱长、锚点和坐标不因卡片修复而被改写。 ### 5. 大量组件的性能 布局修复按阶段批量读取,再批量修改;数字测量也按阶段执行。避免对每张卡片反复执行“写样式→读取尺寸→强制整页排版”。 同机独立接收器探针:7000 个短卡片、217000 字节,旧逐项修复约 22.6 秒;批处理后多次测得约 0.29–0.43 秒。真实 AnswerView 的专门压力测试另外规定必须在 8 秒内就绪,以低于桥接层的 10 秒超时。具体终验耗时见附带日志;该数字不是对所有设备的性能承诺。 ## 详尽测试设计 测试通过真实 AnswerView、真实浏览器和隔离 iframe 执行。主要断言是矩形边界、文字 Range、碰撞、滚动尺寸、交互结果和稳定性;不是仅检查某个 CSS 属性是否被设置。 | 维度 | 输入与边界 | 通过条件 | |---|---|---| | 标签与组件组合 | 5 类 × 14 标签 × 4 宽度 × 2 主题,共 560 个组合 | 卡片不覆盖前后文字;文字留在表面和可见区域内 | | 标签种类 | span/small/em/strong/a/i/mark/abbr/code/output/label/div/p/section | 标签不决定是否漏修 | | 语义结构 | td/th/tr、li、summary、grid/flex、contents | 表格列序、行位置、列表标记和原布局语义保留 | | 独立预览 | 非 embedded 的实际 AnswerView | 行内表面同样安全;保留预览本身的高度策略 | | 作者样式 | `display:inline!important`、压缩到 1 的行高 | 盒子参与排版,相邻文字不相交 | | 固定高度 | height/max-height、visible/hidden/clip、多层20/40px与100%中间层 | 正常流内容完整显示,后续内容不被覆盖 | | 文字 | 中日韩、阿拉伯文、泰文、emoji、组合字符、URL、长无空格字符串 | 可见文字不越出容器、不被根区域裁切 | | 字号 | 1/2/3 倍根字号与动态32px字体 | 保持可读且容器适应;新作者字体不被旧修复覆盖 | | 数字 | 80位数值、金额、百分比;窄→宽变化 | 放不下时完整换行;普通短值仍可保持完整 | | 极窄表格 | 桌面12列、390px6列、263px4/8列、160px2/4列 | 单元格与文字不相互侵入、不横向裁切 | | 嵌套与重排 | 多层卡片、固定grid/flex、多轮变窄变宽 | 无溢出,变宽后尺寸恢复 | | 动态内容 | 插入、替换、整段style重写、class变化、删除、重新插入 | 修复能重新判断;删除后iframe高度回落 | | 可见性 | hidden、CSS none、visibility、辅助隐藏、details、受控折叠 | 隐藏保持隐藏;开合后高度正确恢复 | | 控件与图形 | textarea、checkbox、number、select、button、滚动区、listbox、SVG foreignObject、绝对定位图形 | 操作有效、滚动与定位保持原意 | | 浮动 | 仅含float、float高于其他文字 | 外框包含内容,后续文字不穿插 | | 真实旧回答 | 原始估值矩阵与已保存的历史柱状图 | 矩阵无重叠;轨道等宽、比例与标签正确;滚轮仍滚动外层聊天 | 宽度列描述的是测试浏览器宽度,iframe 的有效内容宽度还受外层容器影响。实际 DSH 桌面矩阵另验证有效宽度 691px 与 263px。 ## 极限与稳定性测试 1. **7000 个密集短卡片**:约217KB,8秒内就绪;检查全部卡片的边界、文字和相邻关系,检查首/中/末项以及最终一行。 2. **恰好256KiB**:1100个混合标签组件加真实可见文字填满字节上限;验证上万像素长回答、变窄、变宽和末行。 3. **24层嵌套**:多轮窄宽切换,逐层检查子元素与最深文字均在父容器内。 4. **动态批次**:250→1000→125→1000→0;保留textarea节点身份、输入内容、焦点、选区、数值和宿主保存状态。 5. **6000px以上长文**:反复增长/缩短,内容删除后恢复作者原始180px高度。 每个稳定阶段之后额外等待250ms,再观察450ms。期间 style/DOM 修改和宿主高度写入须保持在有限余量内,高度漂移不超过1px。这样能识别肉眼截图不易发现的观察器循环和高度振荡。 ## 复现方式 在插件仓库执行: ```sh npm run build npm test npm run test:browser ``` 与本机 DSH conversation/chat bundle 相关的两项测试需提供对应环境变量,否则按测试本身的环境条件处理。本次终验提供了实际 bundle 路径,60项全部执行,没有跳过。 主要测试文件: - `test/note-layout-browser.mjs`:原始矩阵回归。 - `test/component-layout-browser.mjs`:34组组件、动态行为与语义保护测试。 - `test/component-stress-browser.mjs`:5组容量、性能和稳定性测试。 - `test/layout-browser.mjs`:统一集成入口及历史图表可选检查。 `COMPONENT_LAYOUT_CASE`、`COMPONENT_STRESS_CASE` 可按组名筛选调试;终验不设置筛选,运行完整套件。历史图表可通过 `LAYOUT_REGRESSION_HTML` 传入保存的HTML。 ## 支持边界与仍需谨慎的内容 - 本轮测试浏览器是本机 Chromium/Chrome;没有声称经过 Safari、Firefox 或所有系统字体组合验证。 - 保护针对内置组件与可判定的普通阅读布局。任意负边距、零行高、视觉变换、画布内容、不断重写样式的脚本,不能由这套规则证明永远正确。 - 很多列与巨大字号在有限宽度下存在物理限制。生成规则仍要求减少无意义列数或按条目展示;测试通过不等于支持无限列。 - 既有保存的HTML和数据不会被迁移或改写。自定义CSS仍可表达复杂意图,运行时避免为了“看起来正常”而任意移动图形或替换交互节点。 - 真正需要更强保证的业务可另建只接受结构化组件数据的严格模式;这属于更大的产品改造,不应以本次修复冒充已经实现。