Audit fixes: VIP self-grant, draft leak, onboarding flag, hero personalization, gated downloads
Deploy GitHub Pages / deploy (push) Canceled after 0s
Deploy GitHub Pages / deploy (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/* Cover generator — run: npm run covers
|
||||
Produces 12 JPGs (1600x900) in public/covers/ plus per-category defaults. */
|
||||
import sharp from 'sharp'
|
||||
import { mkdirSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const outDir = path.resolve(dirname, '../public/covers')
|
||||
mkdirSync(outDir, { recursive: true })
|
||||
|
||||
const W = 1600
|
||||
const H = 900
|
||||
|
||||
const POSTS = [
|
||||
// [file, title, category, kind: free|vip]
|
||||
['agents-free', 'بناء أول وكيل بالعربية', 'وكلاء'],
|
||||
['agents-vip', 'حلقة الوكيل: تخطيط وتنفيذ وتحقق', 'وكلاء'],
|
||||
['models-free', 'كيف تختار نموذجك', 'نماذج'],
|
||||
['models-vip', 'تقييم النماذج منهجياً', 'نماذج'],
|
||||
['retrieval-free', 'الاسترجاع بلا تعقيد', 'استرجاع'],
|
||||
['retrieval-vip', 'قواعد بيانات المتجهات عملياً', 'استرجاع'],
|
||||
['safety-free', 'حدود الوكيل الآمنة', 'سلامة'],
|
||||
['safety-vip', 'مراجعة مخرجات الوكلاء', 'سلامة'],
|
||||
['tools-free', 'أدوات لا غنى عنها', 'أدوات'],
|
||||
['tools-vip', 'أنابيب الأدوات والصلاحيات', 'أدوات'],
|
||||
['business-free', 'الذكاء الاصطناعي في عملك', 'أعمال'],
|
||||
['business-vip', 'من نموذج أولي إلى إنتاج', 'أعمال'],
|
||||
// fallback default cover for the agent API
|
||||
['cover-default', 'أنور بي آي', 'وكلاء'],
|
||||
// per-category default covers for the agent API
|
||||
['cover-agents', 'وكلاء', 'وكلاء'],
|
||||
['cover-models', 'نماذج', 'نماذج'],
|
||||
['cover-retrieval', 'استرجاع', 'استرجاع'],
|
||||
['cover-safety', 'سلامة', 'سلامة'],
|
||||
['cover-tools', 'أدوات', 'أدوات'],
|
||||
['cover-business', 'أعمال', 'أعمال'],
|
||||
['cover-default', 'أنور بي آي', 'وكلاء'],
|
||||
]
|
||||
|
||||
function svgCover(title, category) {
|
||||
const titleEsc = title.replace(/&/g, '&').replace(/</g, '<')
|
||||
const catEsc = category.replace(/&/g, '&').replace(/</g, '<')
|
||||
return Buffer.from(`
|
||||
<svg width="${W}" height="${H}" xmlns="http://www.w3.org/2000/svg" dir="rtl">
|
||||
<defs>
|
||||
<radialGradient id="g" cx="20%" cy="15%" r="90%">
|
||||
<stop offset="0%" stop-color="#16202a"/>
|
||||
<stop offset="55%" stop-color="#0c1016"/>
|
||||
<stop offset="100%" stop-color="#050708"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="beam" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#7fd8cc"/>
|
||||
<stop offset="100%" stop-color="#3a6a8a"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="${W}" height="${H}" fill="url(#g)"/>
|
||||
<g opacity="0.5" stroke="#31414f" stroke-width="1">
|
||||
${Array.from({ length: 9 }, (_, i) => `<line x1="0" y1="${(i + 1) * 90}" x2="${W}" y2="${(i + 1) * 90}"/>`).join('')}
|
||||
</g>
|
||||
<g transform="translate(${W - 140}, 110)">
|
||||
<rect x="-26" y="-26" width="52" height="52" transform="rotate(45)" fill="none" stroke="url(#beam)" stroke-width="5"/>
|
||||
<rect x="-9" y="-9" width="18" height="18" transform="rotate(45)" fill="#e8f6f3"/>
|
||||
</g>
|
||||
<text x="${W - 80}" y="480" text-anchor="end" fill="#f2f7f6"
|
||||
font-family="SF Arabic, Noto Sans Arabic, sans-serif" font-weight="700" font-size="72">${titleEsc}</text>
|
||||
<text x="${W - 80}" y="560" text-anchor="end" fill="#8fa3ad"
|
||||
font-family="SF Mono, monospace" font-size="30" letter-spacing="4">${catEsc} · أنور بي آي</text>
|
||||
<rect x="${W - 80 - 180}" y="620" width="180" height="3" fill="url(#beam)"/>
|
||||
</svg>`)
|
||||
}
|
||||
|
||||
for (const [name, title, category] of POSTS) {
|
||||
await sharp(svgCover(title, category))
|
||||
.jpeg({ quality: 82 })
|
||||
.toFile(path.join(outDir, `${name}.jpg`))
|
||||
console.log(`✓ ${name}.jpg`)
|
||||
}
|
||||
console.log(`Done: ${POSTS.length} covers → ${outDir}`)
|
||||
Reference in New Issue
Block a user