(opts)
| 417 | }, |
| 418 | |
| 419 | async generateThumbnail(opts): Promise<Buffer | null> { |
| 420 | const browser = await getThumbnailBrowser(); |
| 421 | if (!browser) { |
| 422 | console.warn("[Studio] Thumbnail: no browser available — Chrome may not be installed"); |
| 423 | return null; |
| 424 | } |
| 425 | let page: import("puppeteer-core").Page | null = null; |
| 426 | try { |
| 427 | page = await browser.newPage(); |
| 428 | await page.setViewport({ width: opts.width || 1920, height: opts.height || 1080 }); |
| 429 | await page.goto(opts.previewUrl, { waitUntil: "domcontentloaded", timeout: 10000 }); |
| 430 | await page |
| 431 | .waitForFunction( |
| 432 | () => { |
| 433 | const w = window as Window & { |
| 434 | __timelines?: Record<string, unknown>; |
| 435 | }; |
| 436 | return !!(w.__timelines && Object.keys(w.__timelines).length > 0); |
| 437 | }, |
| 438 | { timeout: 5000 }, |
| 439 | ) |
| 440 | .catch(() => {}); |
| 441 | await page.evaluate((t: number) => { |
| 442 | const w = window as Window & { |
| 443 | __player?: { seek?: (time: number) => void }; |
| 444 | __timelines?: Record<string, { pause?: (time?: number) => void }>; |
| 445 | gsap?: { ticker?: { tick?: () => void } }; |
| 446 | }; |
| 447 | if (typeof w.__player?.seek === "function") { |
| 448 | w.__player.seek(t); |
| 449 | } else if (w.__timelines) { |
| 450 | for (const tl of Object.values(w.__timelines)) { |
| 451 | tl?.pause?.(t); |
| 452 | } |
| 453 | w.gsap?.ticker?.tick?.(); |
| 454 | } |
| 455 | }, opts.seekTime); |
| 456 | const manifestContent = readStudioManualEditManifestContent(opts.project.dir); |
| 457 | await applyStudioManualEditsToThumbnailPage(page, manifestContent, opts.compPath); |
| 458 | await page.evaluate(() => { |
| 459 | void document.fonts?.ready; |
| 460 | const body = document.body; |
| 461 | if (body && getComputedStyle(body).backgroundColor === "rgba(0, 0, 0, 0)") { |
| 462 | body.style.backgroundColor = "#1c2028"; |
| 463 | } |
| 464 | }); |
| 465 | await new Promise((r) => setTimeout(r, 200)); |
| 466 | await reapplyStudioManualEditsToThumbnailPage(page); |
| 467 | let clip: ScreenshotClip | undefined; |
| 468 | if (opts.selector) { |
| 469 | clip = await page.evaluate(getElementScreenshotClip, opts.selector, opts.selectorIndex); |
| 470 | } |
| 471 | const screenshot = (await page.screenshot( |
| 472 | opts.format === "png" |
| 473 | ? { |
| 474 | type: "png", |
| 475 | ...(clip ? { clip } : {}), |
| 476 | } |
nothing calls this directly
no test coverage detected