关于 web-visual-assets
web-visual-assets 是为教学网站制作并集成视觉素材的技能,涵盖截图、生成式插图、SVG 图表和二维码,并提供回退与覆盖检查方法。
主要功能
- 01使用 Playwright 截取页面
- 02生成 AI 插图
- 03绘制 SVG 图表
- 04生成二维码
- 05将素材接入 SPA
适用场景
- 01为课程页面添加产品截图
- 02统一制作课程概念插图
- 03绘制带标签的流程图
- 04为课程链接生成二维码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ kevintsai1202/ teaching-site-skills --skill web-visual-assets安装后在 Claude Code 中输入/web-visual-assets确认已加载。
详细介绍
网页视觉素材
Schema authority:
Illustration基础结构({name, kind, alt, spec}和{kind: 'waived', reason})以及每个单元的illustrations[]覆盖下限,定义于_shared/domain-primitives.md§11。
此技能用于生成教学网站的视觉层。四种素材来源几乎可以满足所有需求,各有不同的取舍。
素材来源决策树
What do you need?
├── Screenshot of a real product/website → Source 1: Playwright scraping
├── A conceptual scene (workflow, metaphor) → Source 2: AI image generation
├── A simple diagram (boxes, arrows, labels) → Source 3: Hand-drawn SVG
└── A scannable code / functional artifact → Source 4: Code generator (QRCode, etc.)
来源 1:Playwright 抓取
适用于工具、网站、个人资料卡的截图。示例工作坊会抓取 6 个 AI 工具的主页和一个 YouTube 讲师频道。
标准无头流程
// scripts/scrape-tools.mjs
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: `data/tools/${id}.png`, fullPage: false });
await browser.close();
遇到反爬虫拦截时(CDP 模式)
有些网站(Cloudflare、现代 OpenAI 产品)会检测无头 Chromium 并进行拦截。解决方法:通过 CDP 连接到真实的 Chrome:
# scripts/start-cdp-chrome.ps1
Start-Process chrome.exe -ArgumentList '--remote-debugging-port=9222', '--user-data-dir=C:\tmp\cdp-profile'
const browser = await chromium.connectOverCDP('http://localhost:9222');
const page = (await browser.contexts()[0].pages())[0];
// ... drive an existing real-browser session
使用 --cdp --pause 标志运行,以便人工完成任何 CAPTCHA,然后按 Enter 继续。
子集合并模式(不要覆盖其他项)
重新抓取多项中的某一项时,应与现有数据合并,而不是覆盖:
async function writeMerged(jsonPath, updates) {
const existing = JSON.parse(await fs.readFile(jsonPath, 'utf8').catch(() => '{}'));
await fs.writeFile(jsonPath, JSON.stringify({ ...existing, ...updates }, null, 2));
}
CLI:node scrape-tools.mjs --ids codex,notebooklm 只会处理这两项。
YouTube 频道相关问题
- 频道页面是一个 SPA。URL 查询字符串(如
?sort=p&view=0)会被移除。获取最新的 N 个视频后,在客户端进行排序。 - 虚拟滚动会从视野外的
<img>中移除src。请根据videoId推导缩略图 URL:https://img.youtube.com/vi/{videoId}/hqdefault.jpg。不要依赖 DOMsrc。
来源 2:AI 图像生成
适用于场景插图、每日主视觉、概念场景。示例工作坊使用 Gemini 的图像生成功能;任何文生图 API 都可以。
提示词设计规则
- 为整个课程指定一致的视觉风格(例如:“扁平插画、柔和的粉彩色调、无文字、无人像”)。插画风格不一致会显得不专业。
- 避免人脸和品牌 Logo——AI 图像生成器难以处理两者,生成的效果也会显得不自然。
- 将提示词与图像一起保存:
assets/illustrations/
├── day1-token-prediction.png
├── day1-token-prediction.prompt.md ← regenerate-ready prompt
└── ...
这样以后可以重新生成更高质量的图像,或调整风格。
PNG + SVG 备用渲染
AI 生成偶尔会失败(速率限制、内容过滤器、模型问题)。为每张 AI PNG 配上一张手绘 SVG 备用图,并使用相同的路径主干:
function renderIllustration(name) {
const img = el('img', { src: `assets/illustrations/${name}.png`, alt: '' });
img.onerror = () => { img.src = `assets/illustrations/${name}.svg`; img.onerror = null; };
return img;
}
浏览器会先尝试 PNG;遇到 404 时则切换到 SVG。即使从未用到,备用图也会一并发布,以防链接失效。
来源 3:手绘 SVG
适用于需要精确控制的图表:带名称方框之间的箭头、逐步流程、带标注的 UI 模拟图、教室地图。
手写 SVG(或从 Figma/Excalidraw 导出)比 AI 更适合以下内容:
- 文本标签(AI 无法可靠地在图像中渲染中文)
- 箭头与方框流程(AI 容易弄错布局)
- 需要随课程详情变化而更新的内容
样式提示:即使 SVG 本身是线稿,也可以将其放入半透明容器,并添加 drop-shadow 滤镜,使其与网站的整体视觉风格一致。
来源 4:生成代码(QR)
import QRCode from 'qrcode';
await QRCode.toFile('assets/qr/workshop-url.png', 'https://your-workshop.example/', {
width: 512,
margin: 1,
color: { dark: '#000000', light: '#FFFFFF' }
});
对于深色主题的网站,应生成白底深色码,而不是深底白色码——大多数 QR 扫描器都要求浅色背景上的深色前景。
素材文件夹规范
assets/
├── tools/ ← Source 1: scraped tool/product screenshots
├── illustrations/ ← Source 2 (PNG) + Source 3 (SVG fallback)
├── scenarios/ ← Source 2: per-unit scene illustrations
├── cases/ ← Source 2: shared case visuals
├── characters/ ← Source 2: persona portraits
├── qr/ ← Source 4: QR codes
└── maps/ ← Source 1 (screenshot) or Source 3 (SVG)
将素材接入 SPA
在 course-data.js 中,每个单元都有一个 illustrations[] 数组(包含 1–3 个条目),其内容来自 Stage 2 编写的 圖片需求 区块:
{
id: 'u-3',
title: '...',
illustrations: [
{ name: 'day1-u3-hero.png', kind: 'hero', alt: '...', spec: '...' },
{ name: 'day1-u3-flow.svg', kind: 'diagram', alt: '...', spec: '...' },
{ name: 'day1-u3-example.png', kind: 'screenshot', alt: '...', spec: '...' } // optional 3rd
],
// ...
}
renderUnit 会遍历 unit.illustrations 并调用 renderIllustration(entry),后者会为每个条目处理优先使用 PNG / 备用使用 SVG 的逻辑。按此顺序渲染主视觉(首屏上方)、图表和截图。
旧版单个 illustration 字段:较旧的 course-data.js 文件使用 illustration: 'foo.png'(单个字符串)。请将其视为 illustrations: [{ name: 'foo.png', kind: 'hero' }],并在方便时迁移为数组形式。新网站不要依赖旧版结构。
覆盖下限(硬性规则——每个单元 1–3 张插图)
只配有封面图的教学网站看起来像未完成的草稿——Stage 5 必须达到最低覆盖要求,才能宣布网站功能完备:
-
course-data.js中的每个单元都满足illustrations.length >= 1(且<= 3)。 - 每个单元至少有一项对应
assets/下的实际文件(PNG 或 SVG)。所有内容都只是占位符(各处均为{ kind: 'placeholder' })的单元不合格。 - 确实不需要图片的单元必须明确豁免:
illustrations: [{ kind: 'waived', reason: '...' }]。未作说明——也就是使用空数组——不满足下限要求。 - 除各单元图片外,还必须有封面图(
assets/cover.png或同等文件)。封面图不计入任何单元的 1–3 张配额。
未达到下限要求时,不得宣布网站“功能完备”,也不得启动 Stages 5b / 6——编排器层面的强制检查见 teaching-site/SKILL.md 中的 Stage 5 Image Coverage Floor。
批量生成模式(推荐)
不要一次只为一个单元生成图片。应根据 course-data.js 构建清单并批量处理:
// scripts/generate-illustrations.mjs
const manifest = [];
for (const dayKey of ['day1', 'day2', 'day3', 'day4']) {
for (const unit of window.COURSE[dayKey].units) {
for (const ill of (unit.illustrations || [])) {
if (ill.kind === 'waived') continue;
manifest.push({ name: ill.name, prompt: ill.spec, kind: ill.kind });
}
}
}
// → feed manifest to AI image API in parallel batches of 4–6
批量处理的原因:保持风格一致。临时逐个生成单元的图片会导致风格漂移。使用相同风格前缀批量生成,才能形成统一的视觉体系。
验证
需要关注两项验证工作,分别使用不同的技能:
- “引用的资源是否存在于磁盘上?”(跨文件引用检查)→ 调用
web-content-audit。它的 Audit 1(Cross-Artifact Reference Resolution)和资源覆盖审计正好涵盖此项。 - “资源能否在浏览器中正常呈现且不出现 404?”(运行时检查)→ 调用
web-visual-verification。多视口验证脚本会收集console.error,自动检测失效的<img>请求。
每次部署前都要运行两项检查。审计会捕获对不存在文件的引用;验证则会捕获文件确实存在、但在渲染后的 HTML 中路径错误的资源。两者的失败方式不同,需要使用不同的脚本。
反模式
- 逐张生成图片,却不保持风格一致——只编写一次风格规范,并将其作为每个提示词的前缀。
- 在图片中生成 AI 文本——几乎总会出错;凡是需要准确文字的图片,都应使用 SVG。
- AI 生成的 PNG 没有 SVG 备用方案——PNG 链接失效(文件被删除或重命名错误)时,网站会显示破损图片图标;SVG 备用方案则能优雅降级。
- 保存截图却没有重新抓取脚本——六个月后有人想获取新截图时,却找不到当初生成原始截图的方法记录。
交接
告知用户:“视觉资源已就位。运行 node scripts/verify-assets.mjs,确认没有遗漏。网站现在功能完备。下一阶段(course-ebook-publishing)可以将相同内容转换为 PDF/DOCX 交付物——只有在网页版稳定后才调用该阶段。”