#!/usr/bin/env node import fs from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { buildFeedThumbnailSvg, extractCoverSignals, generateHtmlThumbnail, resolveCoverDataUri, } from '../mindspace-thumbnails.mjs'; const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); const sampleDir = path.join(root, 'public', 'thumbnail-demo-samples'); const outputDir = path.join(root, 'public', 'thumbnail-demo'); /** A/B pairs: bad = 缺 mindspace-cover,good = 方案 A 完整元数据 + hero 图 */ const pairs = [ { id: 'vietnam-guide', label: '越南旅游指南', bad: path.join(sampleDir, 'vietnam-guide-bad.html'), good: path.join(sampleDir, 'vietnam-guide-good.html'), }, ]; function galleryCard({ id, title, subtitle, variant }) { return `
${title} 预览图

${title}

${subtitle}

`; } async function renderVariant({ htmlPath, variant, id }) { const html = await fs.readFile(htmlPath, 'utf8'); const signals = extractCoverSignals(html); const coverDataUri = signals.image ? await resolveCoverDataUri({ storageRoot: outputDir, contentBaseDir: path.dirname(htmlPath), imageUrl: signals.image, }) : null; const feedSvg = buildFeedThumbnailSvg(signals, { coverDataUri }); const feedPath = path.join(outputDir, `${id}-${variant}-feed.svg`); await fs.writeFile(feedPath, feedSvg, 'utf8'); await generateHtmlThumbnail(outputDir, `${id}-${variant}-generated.svg`, html, { contentBaseDir: path.dirname(htmlPath), title: signals.title, subtitle: signals.subtitle, }); return { signals, hasPhoto: Boolean(coverDataUri) }; } async function main() { await fs.mkdir(outputDir, { recursive: true }); const sections = []; for (const pair of pairs) { const bad = await renderVariant({ htmlPath: pair.bad, variant: 'bad', id: pair.id }); const good = await renderVariant({ htmlPath: pair.good, variant: 'good', id: pair.id }); sections.push(`

${pair.label}

左侧:未写 mindspace-cover(退化为纯色渐变) · 右侧:方案 A(完整元数据 + hero 主图)

缺 cover ❌ 缺 mindspace-cover · ${bad.hasPhoto ? '有图' : '无图'} · tag=${bad.signals.tag}
方案 A ✅ 方案 A · ${good.hasPhoto ? '照片封面' : '场景/渐变'} · tag=${good.signals.tag}
${galleryCard({ id: pair.id, title: pair.label, subtitle: '缺 cover · 信息流效果', variant: 'bad' })} ${galleryCard({ id: pair.id, title: pair.label, subtitle: '方案 A · 信息流效果', variant: 'good' })}
`); } const html = ` MindSpace 缩略图 · 方案 A 对比

方案 A:mindspace-cover 对比 Demo

页面生成时在 <head> 写入 mindspace-cover 元数据并引用 hero 主图, 缩略图会从「纯色渐变」升级为「照片封面 + 标题」。 交付前可跑 npm run check:mindspace-cover 自检。

${sections.join('')}
`; await fs.writeFile(path.join(outputDir, 'index.html'), html, 'utf8'); console.log(`已生成对比页: ${path.join(outputDir, 'index.html')}`); console.log('本地查看: http://localhost:5173/thumbnail-demo/'); } await main();