import { chromium } from 'playwright';
import { join } from 'path';
import { mkdirSync, existsSync, unlinkSync, readdirSync } from 'fs';
const SCREENSHOTS_DIR = './screenshots/cli-demo';
// Ensure directory exists
if (!existsSync(SCREENSHOTS_DIR)) {
mkdirSync(SCREENSHOTS_DIR, { recursive: true });
}
function generateTerminalHTML(prompt, output, status, showCursor) {
if (showCursor === undefined) showCursor = true;
const cursorHtml = showCursor ? '' : '';
return `
${prompt}${cursorHtml}
${output}
${status}
SkillHub
`;
}
// CLI Demo frames - 4 distinct frames
const FRAMES = [
// Frame 1: Empty prompt, ready to type
{
content: generateTerminalHTML('$ ', '', 'Ready'),
description: 'Empty prompt'
},
// Frame 2: Command typed
{
content: generateTerminalHTML('$ npx skillhub search pdf', '', 'Press Enter to search...'),
description: 'Command typed'
},
// Frame 3: Searching animation
{
content: generateTerminalHTML('$ npx skillhub search pdf', `
⠋ Searching skills...
`, 'Searching...', false),
description: 'Searching'
},
// Frame 4: Results displayed (final state)
{
content: generateTerminalHTML('$ npx skillhub search pdf', `
✔ Found 156 skills:
─────────────────────────────────────────────────────────────────
anthropics/skills/pdf ⭐ 30.2k ⬇ 1.2k
Read, extract and manipulate PDF files. Convert PDFs...
─────────────────────────────────────────────────────────────────
obra/superpowers/pdf-extraction ⭐ 8.1k ⬇ 421
Advanced PDF extraction with OCR support and table...
─────────────────────────────────────────────────────────────────
openai/skills/pdf-reader ⭐ 2.2k ⬇ 189
Parse PDF documents and extract structured text...
─────────────────────────────────────────────────────────────────
Install with: npx skillhub install <skill-id>
Showing 3 of 156. Use --limit to see more.
`, 'Done - 156 skills found', false),
description: 'Results'
},
];
async function main() {
console.log('=== CLI Demo Animation Generator ===\n');
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 800, height: 500 },
deviceScaleFactor: 2,
});
const page = await context.newPage();
// Clean up old frames
const existingFiles = readdirSync(SCREENSHOTS_DIR);
for (const file of existingFiles) {
if (file.endsWith('.png')) {
unlinkSync(join(SCREENSHOTS_DIR, file));
}
}
console.log('Generating 4 distinct frames...');
for (let i = 0; i < FRAMES.length; i++) {
const frame = FRAMES[i];
await page.setContent(frame.content);
await page.screenshot({
path: join(SCREENSHOTS_DIR, 'frame-' + String(i).padStart(3, '0') + '.png'),
});
console.log(' Frame ' + (i + 1) + '/' + FRAMES.length + ': ' + frame.description);
}
await browser.close();
console.log('\n=== Frames Generated ===');
console.log('\nFrames saved to: ' + SCREENSHOTS_DIR + '/');
console.log('\nTo create GIF:');
console.log(' ffmpeg -framerate 0.5 -i frame-%03d.png -vf "fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ../cli-demo.gif');
}
main().catch(console.error);