#!/usr/bin/env node // Joins finished slides into one horizontal preview strip (webp), at most 2048 px on the long edge. // Usage: node scripts/slides-strip.mjs --output strip.webp --gap 16 slide-1.png slide-2.png ... // Options: --output (required), --gap (default 16), --height (upper limit; the strip is shrunk to fit 2048 px wide). let sharp try { sharp = (await import('sharp')).default } catch { console.error('This script needs the sharp package. Run `npm install sharp` in the Skill folder (Node 20.9 or newer), then try again.') process.exit(1) } function fail(message) { console.error(message); process.exit(1) } const a = process.argv.slice(2) const opt = { height: 640, gap: 16 } const files = [] let output for (let i = 0; i < a.length; i += 1) { const k = a[i] if (k === '--output' || k === '--height' || k === '--gap') { const v = a[i + 1] if (v === undefined || v.startsWith('--')) fail(`Flag ${k} needs a value.`) i += 1 if (k === '--output') output = v else { const n = Number(v) if (!Number.isInteger(n) || n < 1 || n > 4000) fail(`${k} must be a whole number of pixels (1 to 4000).`) opt[k.slice(2)] = n } } else if (k.startsWith('--')) fail(`Unknown flag ${k}. Known: --output, --height, --gap`) else files.push(k) } if (!files.length || !output) fail('Usage: node scripts/slides-strip.mjs --output strip.webp slide-1.png slide-2.png ...') const metas = await Promise.all(files.map((f) => sharp(f).metadata().catch(() => fail(`Cannot read image: ${f}`)))) const aspect = metas.reduce((n, m) => n + m.width / m.height, 0) const fit = Math.floor((2048 - opt.gap * (files.length - 1)) / aspect) - files.length const height = Math.max(1, Math.min(opt.height, fit)) const tiles = await Promise.all(files.map((f) => sharp(f).resize({ height }).toBuffer({ resolveWithObject: true }))) const width = tiles.reduce((n, t) => n + t.info.width, 0) + opt.gap * (tiles.length - 1) let left = 0 const composite = tiles.map((t) => { const c = { input: t.data, left, top: 0 }; left += t.info.width + opt.gap; return c }) await sharp({ create: { width, height, channels: 3, background: '#1a100a' } }).composite(composite).webp({ quality: 82 }).toFile(output) console.log(`${output} ${width}x${height}`)