面向 AI 的布局检查#
FrameSeq 可以检查最终的浏览器布局,给出人或编码代理都能直接照做的诊断。
frameseq check slides.ts
检查器会构建幻灯片、在无头浏览器里按原生画布尺寸打开、等字体加载完成,然后逐页检查。目前能发现:
empty-slide—— 某一页没有可见内容,而且没有被显式标记为"有意留白"。canvas-overflow—— 渲染出的对象超出了页面画布。text-clipped—— 文字被裁切的文本框或祖先元素遮住。label-overflow——shape()的标签放不进它的轮廓;无论标签写了什么,形状都保持自己的大小。font-too-small—— 文字在它的尺寸下太小,读不清:在 1280 宽的幻灯片上小于 14px,标题小于 24px。这个下限随画布一起变大;在纸张上,它是纸张短边的同一比例,在 A0 海报上约为 21 pt,结果会同时给出两种尺寸。empty-region——at()创建的命名区域从未收到任何内容。similar-name—— 同一页上两个名字只差一次编辑,通常意味着某个at()路径或.as()名字拼错了。inert-modifier—— 布局修饰符落在了它不可能生效的地方,浏览器会直接忽略它。document-error—— 幻灯片文档在能测量任何东西之前就因错误停下了。
画布溢出、文字裁切和文档错误是错误;空白页、形状标签溢出、字号过小、空区域、近似重名、失效修饰符是警告。严格模式下任何警告都会失败。
布局修饰符只在一种上下文里有效:align()、justify()、gap()、wrap() 需要对象本身是 row()、column() 或网格;selfAlign()、centerSelf()、grow()、spacer() 需要容纳它的容器是其中之一。写在别处它们会被悄悄丢弃,所以检查器会同时指出是哪个修饰符、以及最接近的那个本该生效的写法:
WARNING Slide 2 "Result" [inert-modifier]
align() has no effect: this object is not a row(), column(), or grid().
Suggestion: Add row(), column(), or grid() to this object so it arranges its children.
Suggestion: To place this object inside its container, use selfAlign() or centerSelf(); to move the text inside it, use textAlign().
拼错的路径在运行时是看不出来的 —— at() 你给什么它就建什么 —— 所以上面这两条规则专门用来抓"本想回到 at("cell0/now"),却写成了 at("cell0/nwo")"这种情况:
WARNING Slide 1 "Roadmap" [similar-name]
Names "cell0/now" and "cell0/nwo" are one edit apart.
Suggestion: Rename one of them so the difference is deliberate.
由演示元数据自动生成的标题页算作可见内容。如果留白是有意的,就在源码里标明这个意图:
slide({ name: "Pause" }).allowEmpty();
用对象形式更顺手时,写 SlideOptions 里的 allowEmpty: true。在确认留白确实是有意的之前,不要急着消掉这条警告。
人类可读的输出#
ERROR Slide 4 "Architecture" [text-clipped]
Text is clipped by 38px on the bottom.
Object: text 3.1.2 "FrameSeq owns presentation structure..."
Suggestion: Increase the text box size, reduce the font size, or shorten the content.
对象路径标识的是渲染后的 FrameSeq 节点。对自动化编辑器来说,它比生成的 CSS 选择器更有用,而且不依赖浏览器生成的类名。
无法运行的文档#
当文档本身抛出错误时,比如 ref() 引用了没有任何对象叫这个名字,或者引用了参考文献里没有的条目,就什么都测量不了。检查器会立刻报告,定位到文档当时正在执行的语句,并给出出错的那一行:
talk.slides.ts:24:1 error ref() cannot find an object named "decodr"; named objects on this slide: encoder, decoder
23 | rect("Decoder").as("decoder");
> 24 | ref("decodr").fill("#dbeafe");
frameseq pdf、pptx 和 typst 也用同样的方式报告无法运行的文档。
给代理用的 JSON 输出#
当结果要被另一个程序或 AI 代理消费时,用 --json:
frameseq check slides.ts --json
{
"version": 1,
"file": "slides.ts",
"canvas": { "width": 1280, "height": 720 },
"summary": { "slides": 8, "errors": 1, "warnings": 0 },
"issues": [
{
"severity": "error",
"rule": "canvas-overflow",
"slide": { "index": 4, "label": "Architecture" },
"element": {
"type": "text",
"path": "3.1.2",
"text": "FrameSeq owns presentation structure...",
"source": { "line": 41, "column": 1 }
},
"message": "Text exceeds the slide canvas by 38px on the bottom.",
"details": { "left": 0, "right": 0, "top": 0, "bottom": 38 },
"suggestions": [
"Move the object inward or reduce its width, height, or font size."
]
}
]
}
JSON 模式只把报告写到标准输出,便于重定向或直接解析。
element.source 是写出该对象的那条命令所在的行和列,代理可以直接找到要改的代码。为纸张制作的演示会多出 canvas.paper,比如 "A0",它的 font-too-small 结果会多出 details.printedPoints。
无法运行的文档会产生同样结构的报告,其中只有一条 document-error,它的 source 就是停下的那条语句:
{
"severity": "error",
"rule": "document-error",
"message": "ref() cannot find an object named \"decodr\"; named objects on this slide: encoder, decoder",
"source": { "line": 24, "column": 1 }
}
退出码与严格模式#
默认情况下,发现错误时命令以非零状态退出;警告只报告,不影响退出码。
frameseq check slides.ts --strict
严格模式下警告同样返回非零状态,适合放在 CI 里或发布幻灯片之前。
生成的项目#
npm create frameseq 创建的项目会同时跑 TypeScript 和渲染布局检查:
npm run check
检查器刻意只从高置信度的几何规则开始。它目前不会否定有意的对象重叠,也不做主观的视觉评分。