Files
probably-rendering-one-more…/scripts/prepare-document-covers.mjs
T

17 lines
5.0 KiB
JavaScript

/** Author distinct document cover PNGs locally. Run with tsx from apps/waterfall-wiki. */
import {createRequire} from 'node:module';import {fileURLToPath} from 'node:url';import path from 'node:path';import {mkdir,writeFile} from 'node:fs/promises';import {createHash} from 'node:crypto';
const require=createRequire(import.meta.url),sharp=require('sharp'),root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
const {guidanceDocuments}=require('../apps/waterfall-wiki/src/content/skills/guidance.ts'),{pageId}=require('../apps/waterfall-wiki/src/content/page-links.ts'),{chapters}=require('../apps/waterfall-wiki/src/content/learning-skills/data.ts');
const out=process.env.PROMPT_COVER_OUTPUT||path.resolve(root,'../output/prompt-tags-upgrade');await mkdir(out,{recursive:true});
function stableId(name){const b=createHash('sha256').update('prompt:learning-series:'+name).digest().subarray(0,16);b[6]=(b[6]&15)|80;b[8]=(b[8]&63)|128;const h=b.toString('hex');return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`}
const notes=[...guidanceDocuments.filter(d=>d.path.includes('/')).map((d,i)=>({id:pageId('guidance:'+d.path),title:d.title,collection:d.collection,slug:'guide-'+d.path.replaceAll('/','-'),file:d.path.replaceAll('/','-')+'.png',theme:d.collection==='照片入皮影'?'chinese':'sticker',number:String(guidanceDocuments.filter(x=>x.collection===d.collection&&x.path.includes('/')).findIndex(x=>x.path===d.path)+1).padStart(2,'0')})),...chapters.map((c,i)=>({id:stableId(c.key),title:['复习资料整理','出题与教学资料','英语学习资料'][i],collection:['笔记复习','教学资料','英语学习'][i],file:c.slug+'.png',theme:['notes','teaching','english'][i],number:c.number})),{id:'9ebd8f78d54b4b9db741b4e8',title:'Studio 封面工具',collection:'同一母版 / 三种构图',file:'studio-tools.png',theme:'studio',number:'TOOL',rebrand:true}];
const xml=s=>s.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;');
for(const [i,n] of notes.entries()){
const palettes={chinese:['#f5ead7','#7e322e','#b4835a'],sticker:['#f7f1e7','#334c46','#a6b9a1'],notes:['#edf0e5','#354b3b','#a7b48c'],teaching:['#f3e7da','#6c483b','#c99773'],english:['#e9edf1','#35485b','#9db1c4'],studio:['#efeee5','#334d43','#9cac9f']},[paper,ink,accent]=palettes[n.theme];
const lines=n.theme==='studio'?['Studio','封面工具']:Array.from(n.title).reduce((arr,c,j)=>(j%9===0?arr.push(c):arr[arr.length-1]+=c,arr),[]);
const ornament=n.theme==='chinese'?`<circle cx="694" cy="660" r="155" fill="${accent}" opacity=".23"/><path d="M160 780Q320 510 610 620T790 520M156 808Q355 580 745 710" fill="none" stroke="${ink}" stroke-width="3"/><rect x="685" y="430" width="73" height="94" fill="none" stroke="${ink}" stroke-width="3"/><text x="721" y="469" text-anchor="middle" font-size="25">文</text><text x="721" y="503" text-anchor="middle" font-size="25">档</text>`:n.theme==='sticker'?`<g transform="rotate(${i%2?7:-7} 450 680)"><rect x="166" y="474" width="535" height="347" rx="28" fill="${accent}" stroke="white" stroke-width="24"/><text x="204" y="645" font-size="95" font-family="Georgia,serif">${['Make.','Share.','Check.'][i%3]}</text><path d="M215 714h340m-340 35h240" stroke="${ink}" stroke-width="5"/></g><circle cx="711" cy="493" r="38" fill="${paper}" stroke="${ink}" stroke-width="2"/>`:`<rect x="170" y="500" width="547" height="335" rx="4" fill="${accent}" opacity=".55"/><path d="M215 577h390m-390 65h390m-390 65h260m-260 65h325" stroke="${ink}" stroke-width="3"/><text x="649" y="543" text-anchor="end" font-size="46" font-family="Georgia,serif">${n.theme==='english'?'Aa':n.number}</text>`;
const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="900" height="1200" viewBox="0 0 900 1200"><rect width="900" height="1200" fill="${paper}"/><g fill="${ink}" font-family="Arial,'Noto Sans CJK SC','PingFang SC',sans-serif"><text x="70" y="84" font-size="24" letter-spacing="3">PROMPT / DOCUMENTS</text><path d="M70 120H830" stroke="${ink}" stroke-width="2"/><text x="70" y="177" font-size="24">${xml(n.collection)}</text>${lines.map((l,j)=>`<text x="66" y="${278+j*88}" font-size="76" font-weight="600">${xml(l)}</text>`).join('')}${ornament}<text x="70" y="973" font-size="23" letter-spacing="2">${n.theme==='chinese'?'阅读 · 理解 · 制作':'READ / MAKE / KEEP'}</text><path d="M70 1032H830" stroke="${ink}" stroke-width="1"/><text x="70" y="1093" font-size="22">独立文档封面 · 参考素材见正文</text><text x="830" y="1145" text-anchor="end" font-family="Georgia,serif" font-size="42">${n.number}</text></g></svg>`;
await sharp(Buffer.from(svg)).png().toFile(path.join(out,n.file));
}
await writeFile(path.join(out,'catalog.json'),JSON.stringify({notes,roots:['street-people','shadow-puppet'].map(p=>({id:pageId('guidance:'+p),topic:p==='street-people'?'贴纸':'中国风'})),entries:['8eb8c76500580f7a4b56ba3c','9ebd8f78d54b4b9db741b4e8'],references:['street-people-reference','street-people-original']},null,2));console.log('Prepared '+notes.length+' independent covers and an API publication catalogue.');