Loading...
Loading...
Use this skill when a content-complete website has missing/placeholder images and needs visual assets — scenario illustrations, tool screenshots, instructor cards, conceptual diagrams, classroom location maps, QR codes. Triggers on phrases like "插圖", "工具截圖", "QR", "講師卡", "地圖", "示意圖", "Playwright 爬蟲", "AI 生圖", "visual assets", "screenshots", "illustrations", "QR codes", "instructor cards". This skill covers the four asset sources (scraping, AI generation, hand-drawn SVG, generated codes), the PNG-first + SVG-fallback render pattern, and verification scripts. Usually invoked AFTER interactions are wired (so missing images are visible), but can be invoked earlier if assets are pre-planned.
npx skill4agent add kevintsai1202/teaching-site-skills web-visual-assetsSchema authority: theprimitive shape (Illustrationand{name, kind, alt, spec}) and the per-unit{kind: 'waived', reason}Coverage Floor are defined inillustrations[]§11._shared/domain-primitives.md
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.)// 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();# 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 --pauseasync function writeMerged(jsonPath, updates) {
const existing = JSON.parse(await fs.readFile(jsonPath, 'utf8').catch(() => '{}'));
await fs.writeFile(jsonPath, JSON.stringify({ ...existing, ...updates }, null, 2));
}node scrape-tools.mjs --ids codex,notebooklm?sort=p&view=0src<img>videoIdhttps://img.youtube.com/vi/{videoId}/hqdefault.jpgsrcassets/illustrations/
├── day1-token-prediction.png
├── day1-token-prediction.prompt.md ← regenerate-ready prompt
└── ...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;
}drop-shadowimport QRCode from 'qrcode';
await QRCode.toFile('assets/qr/workshop-url.png', 'https://your-workshop.example/', {
width: 512,
margin: 1,
color: { dark: '#000000', light: '#FFFFFF' }
});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)course-data.jsillustrations[]圖片需求{
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
],
// ...
}renderUnitunit.illustrationsrenderIllustration(entry)illustrationcourse-data.jsillustration: 'foo.png'illustrations: [{ name: 'foo.png', kind: 'hero' }]course-data.jsillustrations.length >= 1<= 3assets/{ kind: 'placeholder' }illustrations: [{ kind: 'waived', reason: '...' }]assets/cover.pngteaching-site/SKILL.mdcourse-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–6web-content-auditweb-visual-verificationconsole.error<img>node scripts/verify-assets.mjscourse-ebook-publishing