1实现原理 · 为什么它能做到
横向翻页的实现是「一条 flex 轨道 + translateX(-idx*100vw)」:所有 slide 并排,翻页只是改 transform,配 700ms 过渡;比逐页 display 切换更顺滑,也让触摸拖动余量更大。
deck.style.transform = 'translateX(' + (-idx * 100) + 'vw)';
导航模型显式声明借用自同 repo 的 guizang-ppt 模板(同样的 flex track + translateX、同样的 key/wheel/touch handler),说明本模板是在既有实现上换皮而成。
* Navigation model is borrowed from skills/guizang-ppt — same flex
版式语言来自 kami 设计系统:注释写明采用 kami token(暖羊皮纸底、墨蓝 accent、单字重衬线、不用斜体),并把宏 token 相对印刷基线放大约 1.6×、字距缩小约 0.6×。
* tokens are scaled × ~1.6 vs. print baseline; letter-spacing scaled
与宿主有显式契约:注释指出 .slide.active 由 Open Design 的 host bridge(src/runtime/srcdoc.ts 的 findActiveByClass)读取来驱动页码,CSS 里没有任何规则引用它,并标注 load-bearing、不要删。
/* load-bearing: .slide.active is read by Open Design's host bridge
交互有节流锁:go(n) 在 lock 为真时直接返回,只有 wheel/key/touch 走 throttle,程序化同步(宿主消息、恢复初始页)绕过锁——避免快速输入在一次 700ms 过渡里连续加分页。
function go(n) {
页面种类用 data-slide-kind 标注(cover / chapter / content / stats / quote / cta / end),示例 9 页覆盖封面、章节幕、正文、数据大字报、金句、CTA 与末期页——正好兑现 SKILL.md 的布局清单。
<section class='slide s-stats' data-slide-kind='stats'>
「数据大字报页」与「金句页」都落在示例里:stats 页是一个巨数字 + 一句解释,quote 页走 Sunday-paper 风的引文排版。
<section class='slide s-quote' data-slide-kind='quote'>
运行时由宿主承担:模板目录无自带脚本文件,prompt 由宿主拼装后交本地 agent CLI;示例里的 JS 属产物内容。
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | Google Fonts(Source Serif 4 / Source Sans 3 / JetBrains Mono) |
| network | Google Fonts 静态域(preconnect 预连接) |
| network | 产物示例 CTA 页的 GitHub 外链(指引用户去项目仓库 Star / 提 issue) |
| network | 宿主共享指令声明的 Tailwind CDN(宿主侧要求,非本模板要求) |
4风险提醒 风险提醒:绿色 · 放心使用
- 宿主侧:预览 iframe 放宽沙箱 — template-picker.tsx 用 srcDoc + sandbox="allow-scripts allow-same-origin";本模板产物含真实 JS,预览时会执行(自身只操作 DOM),沙箱放宽属宿主实现风险,不判档。
- 宿主侧:会 spawn 本地 agent CLI — convert 路由经 invokeAgent 以本地已登录 CLI 执行 prompt。
- 产物示例含多处外链 — 字体 @import/preconnect、CTA 的 GitHub 链接与共享指令的 cdn.tailwindcss.com:渲染时会有外部请求(见 security.reason);离线场景需替换字体与 CTA 链接。
- 描述与示例有两处不符 — 「WebGL 流体背景」与「hash 同步」在示例中均未实现(见 second_pass);展示层若照抄描述会失真。
- 自带 JS 的注入面 — 示例脚本用模板字符串拼接 HTML 并写 innerHTML(nav 圆点、页码等),但拼入的是内部生成的数字与固定文案,不含用户文本;真正渲染用户内容的是模型随后生成的 HTML,注入面由宿主沙箱承担。
5第二遍独立确认
- [discrepancy] 「WebGL 流体背景」是否存在于示例 — 不存在。example.html 内 canvas / getContext / WebGL / shader 关键字计数为 0(文件里唯一 <script> 是翻页逻辑),封面是暖羊皮纸底 + 墨蓝章节页的静态排版。SKILL.md 的「WebGL 流体背景」在示例中未落地,属描述超出现有预览稿(宿主共享指令也禁止引用外部图片、优先 CSS/SVG 内联,实际生成时是否引入 WebGL 取决于模型与所加库)。
- [discrepancy] 「键盘 ← / → 切换; hash 同步」是否兑现 — 键盘兑现(方向键 / PageUp / PageDown / 空格 / Home / End 全套 + Escape 总览);但示例脚本内没有 location.hash 读写,「hash 同步」这条在示例中未实现(截图与核对范围内未发现 hashchange 监听)。
- [ok] 导航是否真有节流 — go(n) 在 lock 为真时直接 return;滚动 / 键盘 / 触摸走 throttle,程序化同步绕过锁(注释说明原因)。
- [ok] 与宿主的 .slide.active 契约是否存在 — 注释声明 host bridge(src/runtime/srcdoc.ts findActiveByClass)读取该 class 驱动页码,并标注 load-bearing;宿主 deck.ts 亦按 <section class="slide"> 约定解析。
- [ok] 目录内是否有脚本文件 / 凭证读取 / 写盘 — 目录仅 SKILL.md + example.html 两个文件,无独立脚本文件;示例 JS 不读取 env / 凭证、不写盘、无 fetch/XHR 请求(唯一外链是 <link> 字体与 CTA 的 <a href>)。
- [ok] pin commit 与路径 — HEAD = 553ed98c283f9c0f489902d035416a972d6a9699;skill.path = next/src/lib/templates/skills/deck-magazine-web,含 SKILL.md。
- [ok] README 收录一致性 — README deck 表列出 deck-magazine-web(Magazine-style web deck),与目录内容相符。
6结论
44c08bb9682d2bdc…553ed98c28