关于 stitch::extract-static-html
该技能指导从 Web 应用提取独立静态 HTML,面向需要保存页面快照的开发者。它要求用户先选择方案,并提供 Puppeteer、浏览器交互和静态回退流程。
主要功能
- 01Puppeteer 快照并内联样式与图片
- 02浏览器交互后提取 DOM
- 03支持登录脚本与多路由页面
- 04可配置视口、等待时间和页面清理
- 05提供无法运行应用时的静态回退
适用场景
- 01将本地 Web 页面保存为独立 HTML
- 02登录后捕获受保护页面
- 03先操作页面再提取特定状态
- 04应用无法启动时手动构建页面快照
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ google-labs-code/ stitch-skills --skill stitch::extract-static-html安装后在 Claude Code 中输入/stitch::extract-static-html确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
scripts/post_process.tsextract_inline_html.tssnapshot.ts详细介绍
提取静态 HTML
从任意 Web 应用中提取独立的静态 HTML 文件。
选择使用哪种策略
继续之前,必须先询问用户选择使用哪种策略。清晰地列出选项,推荐策略 A作为默认首选,并简要总结每种选项的优缺点,帮助用户做出明智的决定。
| 策略 A(Puppeteer) | 策略 B(浏览器子 Agent) | |
|---|---|---|
| 适用场景 | 应用在本地运行,且没有身份验证限制 | 需要先与页面交互(点击、填写表单) |
| 保真度 | 最高 — 可解析计算后的样式 | 高 — 已渲染的 DOM |
| 设置 | 无需设置 — 不需要 mock | 无需设置 — 不需要 mock |
| 框架 | 任意 | 任意 |
| 输出 | 写入文件 — 无大小限制 | 可能在 Agent 上下文中被截断 |
警告
检查点 — 需要用户确认。 你必须询问用户偏好哪种策略,然后才能继续。 展示上方的对比表,推荐策略 A 作为默认选项,并 等待用户明确批准。不要自行做出决定,也不要在用户确认前继续操作。
策略 A:Puppeteer 快照(推荐)
启动无头 Chrome,捕获完全渲染后的 DOM,并生成独立的 HTML 文件,其中所有 CSS 均已内联,图片则以 base64 格式嵌入。适用于任意框架 — 无需 MockPage.jsx。
前提条件
- 应用在本地运行(例如,
npm run dev) - Node.js 中可使用
puppeteer(检查:node -e "require('puppeteer')")
工作流
-
启动应用并记下端口。
警告
检查点 — 需要用户确认。 启动本地服务器后,运行快照脚本或启动浏览器子 Agent 之前, 必须暂停并征求用户确认。向用户告知 URL 和端口,以便他们确认应用 正在运行且渲染正常。用户确认之前,不要继续执行快照 步骤。
-
运行快照脚本:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 \ --output .stitch/home.html \ --wait 2000 -
多个页面 — 每个路由运行一次:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173 --output .stitch/home.html --wait 2000 npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173/pricing --output .stitch/pricing.html --wait 2000 npx tsx <SKILL_DIR>/scripts/snapshot.ts \ --url http://localhost:5173/dashboard --output .stitch/dashboard.html --wait 2000 --html-class dark -
清理开发服务器: 如果专门为提取快照而启动了本地开发服务器,请务必在提取完成后停止服务器进程或终止后台任务。
脚本标志
| 标志 | 默认值 | 描述 |
|---|---|---|
--url | (必需) | 要捕获的 URL |
--output | (必需) | 输出文件路径 |
--wait | 1000 | 网络空闲后额外等待的时间(ms)。对于延迟加载的应用,请适当增加。 |
--viewport | 1280x800 | 视口大小,格式为 WIDTHxHEIGHT |
--html-class | — | <html> 元素的类(例如 dark) |
--remove-fixed | false | 移除固定/粘性元素(Cookie 横幅、聊天组件) |
--full-height | false | 将视口调整为完整的滚动高度 |
--title | — | 覆盖页面标题(设置为路由路径,例如 /dashboard 或 /settings/profile) |
--auth-script | — | JS/TS 模块的路径;该模块需导出默认的 async (page) => void 函数以进行身份验证 |
--inline-canvas | false | 将 <canvas> 元素(ECharts、Chart.js、D3)转换为 base64 <img> 标签 |
自动完成的操作
- 从
document.styleSheets捕获所有 CSSOM 规则(保留动态 Vite/Tailwind 开发样式和 CSS-in-JS) - 将所有
<link rel="stylesheet">内联为<style>块 - 将
<img>的src和srcset转换为 base64 数据 URI(跳过外部字体) - 将同源和相对路径的图标字体文件(
@font-face)内联为 base64 数据 URI,避免连字被渲染为 ASCII 文本 - 将
<source srcset>URL 内联为 base64 - 移除加载失败或失效的
srcset条目,让浏览器回退到内联的src - 移除
<script>标签、Vite HMR 开发样式块(createHotContext、import.meta.hot)和开发覆盖层 - 内联前解析相对 CSS
url()路径
框架说明
| 框架 | 说明 |
|---|---|
| React + Vite | 开箱即用。--wait 1000。 |
| Next.js | SSR 水合需要设置 --wait 3000。URL:http://localhost:3000。/_next/image 中的 <img srcset> 会自动内联为 base64。 |
| Angular (@angular/cli / v17+) | 使用 ng serve 开箱即用(默认 URL:http://localhost:4200)。对于 Angular Material / PrimeNG 动画水合和延迟加载路由,设置 --wait 2000。 |
| Vue / Nuxt | 开箱即用。 |
| Svelte / SvelteKit | 开箱即用。 |
| Storybook | 使用故事 URL:--url http://localhost:6006/?path=/story/... |
| SSR (Webpack) | 可能需要更长的 --wait。 |
故障排除
| 问题 | 解决方案 |
|---|---|
| 图片缺失 | 增加 --wait |
| 服务停止后图片显示为损坏 | 确认 srcset 已内联——检查日志中是否有“已内联 N 张图片”。如果 srcset URL 加载失败,会自动移除,以便使用内联的 src。 |
| 图标显示为文本 / 使用未设置样式的 Serif 字体 | 确保 snapshot.ts 从 document.styleSheets 捕获 CSSOM(步骤 0),并将同源图标字体(@font-face)内联为 base64 数据 URI。 |
Next.js /_next/image 未内联 | 确保运行快照时开发服务正在运行——脚本会从运行中的服务获取优化后的图片。 |
| 未应用深色模式 | --html-class dark |
| 输出中包含 Cookie 横幅 | --remove-fixed |
| 页面需要登录 | 使用 --auth-script ./auth.ts(见下方“受身份验证保护的页面”) |
| 图表显示为空白框 | 使用 --inline-canvas 将 <canvas> 序列化为 base64 <img> |
Cannot find module 'puppeteer' | npm install -g puppeteer |
受身份验证保护的页面
对于带有登录守卫的应用(Vue Router beforeEach、React ProtectedRoute 等),创建一个小型身份验证脚本,在 Puppeteer 会话中运行:
// auth-myapp.ts
import type { Page } from 'puppeteer';
export default async function authenticate(page: Page) {
// Example 1: Fill and submit a login form
await page.type('#username', 'admin');
await page.type('#password', 'password123');
await page.click('#login-button');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
// Example 2: Inject cookies/localStorage directly
// await page.evaluate(() => {
// localStorage.setItem('token', 'mock-jwt-token');
// });
// Example 3: Call the app's own login API via module injection (Vue/Vite)
// await page.evaluate(() => {
// return new Promise((resolve) => {
// const script = document.createElement('script');
// script.type = 'module';
// script.textContent = `
// import { useUserStore } from '/src/store/modules/user.ts';
// import { fetchLogin } from '/src/api/auth.ts';
// const res = await fetchLogin({ userName: 'Admin', password: '123456' });
// useUserStore().setToken(res.token, res.refreshToken);
// window.dispatchEvent(new CustomEvent('auth-done'));
// `;
// document.head.appendChild(script);
// window.addEventListener('auth-done', () => resolve(true), { once: true });
// });
// });
}
然后使用它:
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
--url http://localhost:5173/#/dashboard \
--output .stitch/dashboard.html \
--auth-script ./auth-myapp.ts \
--inline-canvas \
--wait 5000
脚本会先导航到 --url(此时可能会重定向到登录页),运行你的身份验证函数,然后使用已通过身份验证的会话重新导航到原始 --url。
策略 B:浏览器子 Agent 捕获
当你需要在捕获前与页面交互(点击按钮、填写表单、切换标签页)时使用。浏览器子 Agent 可让你完全控制页面,但大型页面的输出可能会被截断。
工作流
-
在本地启动应用。
-
使用浏览器子 Agent 导航。
-
按需交互(点击、滚动、填写表单)。
-
提取 DOM:
document.documentElement.outerHTML警告
大型页面的内容可能会被截断。处理方法:
- 提取前移除
<style>标签:document.querySelectorAll('style').forEach(el => el.remove()) - 静态地重新添加样式(Tailwind CDN 链接、源 CSS)
- 提取前移除
-
保存到文件。
附录:静态回退方案 (MockPage.jsx)
说明
当应用无法在本地运行时(依赖损坏、缺少后端、无法绕过的身份验证限制),可将此方法作为最后手段。它需要手动将 React 组件扁平化为单个 JSX 文件。只要条件允许,优先使用策略 A。
适用情况
- 应用完全无法在本地运行
- 页面需要身份验证,且没有模拟或绕过方案
- 你需要通过导航无法进入的特定 UI 状态(错误界面、空状态)
快速参考
npx tsx <SKILL_DIR>/scripts/extract_inline_html.ts \
--index-css src/css/App.css \
--extra-css index.html \
--outdir .stitch \
--page src/MockPage.jsx:Page.html:"Page Title"
关键标志:--no-tailwind(非 Tailwind 应用)、--html-class dark(深色模式)、--css-files(额外的 CSS 文件)。
自动检测:自动检测 Tailwind 配置。@apply 指令会自动使用 <style type="text/tailwindcss">。
MockPage.jsx 规则
- 包含完整布局——页眉、侧边栏、页脚(先阅读
App.js) - 展开所有条件判断——选择一种状态,移除所有三元表达式和
&&条件 - 将所有数据硬编码——将
{variable}替换为具体值,展开.map()循环 - 保留 Logo——使用带有本地路径的
<img>(后处理会将其内联) - 移除浮动元素——Cookie 横幅、聊天小组件、反馈按钮
后处理
内联本地图片:
npx tsx <SKILL_DIR>/scripts/post_process.ts \
.stitch/Page.html --base-dir <app-directory>