Implement creator studio and upload updates
This commit is contained in:
96
scripts/render-nova-card.cjs
Normal file
96
scripts/render-nova-card.cjs
Normal file
@@ -0,0 +1,96 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* render-nova-card.cjs
|
||||
*
|
||||
* Headless Playwright screenshot renderer for Nova Cards.
|
||||
* Visits the signed render-frame URL, waits for React + fonts to finish,
|
||||
* then screenshots the [data-card-canvas] element and writes a PNG to disk.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/render-nova-card.cjs \
|
||||
* --url=<signed-render-frame-url> \
|
||||
* --out=<absolute-path-to-output.png> \
|
||||
* [--width=1080] [--height=1080]
|
||||
*
|
||||
* Exit codes: 0 = success, 1 = error (message on stderr).
|
||||
* On success writes JSON to stdout: { "success": true, "out": "...", "width": N, "height": N }
|
||||
*/
|
||||
|
||||
const { chromium } = require('@playwright/test')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
// ── Parse CLI args ────────────────────────────────────────────────────────────
|
||||
const argv = Object.fromEntries(
|
||||
process.argv.slice(2)
|
||||
.filter((a) => a.startsWith('--'))
|
||||
.map((a) => {
|
||||
const eq = a.indexOf('=')
|
||||
return eq === -1 ? [a.slice(2), true] : [a.slice(2, eq), a.slice(eq + 1)]
|
||||
}),
|
||||
)
|
||||
|
||||
const { url, out } = argv
|
||||
const width = parseInt(argv.width || '1080', 10)
|
||||
const height = parseInt(argv.height || '1080', 10)
|
||||
|
||||
if (!url || !out) {
|
||||
process.stderr.write(
|
||||
'Usage: render-nova-card.cjs --url=<url> --out=<png-path> [--width=1080] [--height=1080]\n',
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// ── Render ────────────────────────────────────────────────────────────────────
|
||||
;(async () => {
|
||||
const browser = await chromium.launch({ headless: true })
|
||||
|
||||
try {
|
||||
const page = await browser.newPage({
|
||||
viewport: { width, height },
|
||||
deviceScaleFactor: 1,
|
||||
})
|
||||
|
||||
// Navigate and wait for network to settle (background images, fonts).
|
||||
await page.goto(url, { waitUntil: 'networkidle', timeout: 30_000 })
|
||||
|
||||
// Wait for React to finish painting the canvas element.
|
||||
const canvas = page.locator('[data-card-canvas]').first()
|
||||
await canvas.waitFor({ state: 'visible', timeout: 15_000 })
|
||||
|
||||
// Wait for all web fonts to finish loading before we capture layout.
|
||||
await page.evaluate(async () => {
|
||||
const requiredFonts = [
|
||||
'Anton',
|
||||
'Caveat',
|
||||
'Cormorant Garamond',
|
||||
'Inter',
|
||||
'Libre Franklin',
|
||||
'Playfair Display',
|
||||
]
|
||||
|
||||
await Promise.all(requiredFonts.map((family) => document.fonts?.load?.(`16px "${family}"`) || Promise.resolve()))
|
||||
|
||||
if (document.fonts?.ready) {
|
||||
await document.fonts.ready
|
||||
}
|
||||
})
|
||||
|
||||
// One extra frame so CSS transforms / container queries resolve fully.
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Screenshot only the canvas element; Playwright clips to the element bounding box.
|
||||
const png = await canvas.screenshot({ type: 'png' })
|
||||
|
||||
fs.mkdirSync(path.dirname(out), { recursive: true })
|
||||
fs.writeFileSync(out, png)
|
||||
|
||||
process.stdout.write(JSON.stringify({ success: true, out, width, height }) + '\n')
|
||||
} finally {
|
||||
await browser.close()
|
||||
}
|
||||
})().catch((err) => {
|
||||
process.stderr.write((err?.message ?? String(err)) + '\n')
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user