Take a screenshot of a Windmill app using Puppeteer

Script windmill Verified

by hugo989 ยท 11/25/2025

The script

Submitted by hugo989 Bun
Verified 168 days ago
1
import puppeteer from 'puppeteer-core';
2
import dayjs from 'dayjs';
3
export async function main(
4
  app_path: string,
5
  startup_duration = 5,
6
  kind: 'pdf' | 'png' = 'pdf',
7
  customWidth: null | number,
8
  customHeight: null | number,
9
) {
10
  let browser = null
11
  try {
12
    browser = await puppeteer.launch({
13
      headless: true, executablePath: '/usr/bin/chromium', args: ['--no-sandbox',
14
        '--no-zygote',
15
        '--disable-setuid-sandbox',
16
        '--disable-dev-shm-usage',
17
        '--disable-gpu']
18
    });
19
    const page = await browser.newPage();
20
    await page.setCookie({
21
      "name": "token",
22
      "value": Bun.env["WM_TOKEN"],
23
      "domain": Bun.env["BASE_URL"]?.replace(/https?:\/\//, '')
24
  })
25
    page
26
      .on('console', msg =>
27
        console.log(dayjs().format("HH:mm:ss") + " " + msg.type().substr(0, 3).toUpperCase() + " " + msg.text()))
28
      .on('pageerror', ({ msg }) => console.log(dayjs().format("HH:mm:ss") + " " + msg));
29
    await page.setViewport({ width: 1200, height: 2000 });
30
    await page.goto(Bun.env["BASE_URL"] + '/apps/get/' + app_path + '?workspace=' + Bun.env["WM_WORKSPACE"] + "&hideRefreshBar=true&hideEditBtn=true");
31
    await page.waitForSelector("#app-content", { timeout: 20000 })
32

33
    const elem = await page.$('#app-content');
34
    let width: null | number = customWidth || 1200
35
    let height: null | number = customHeight || (await elem.boundingBox()).height
36
    await page.setViewport({ width, height });
37
    await new Promise((resolve, _) => {
38
      setTimeout(resolve, startup_duration * 1000)
39
    })
40
    try {
41
      await page.$eval("#sidebar", el => el.remove())
42
    } catch { }
43
    await page.$eval("#content", el => el.classList.remove("md:pl-12"))
44
    await page.$$eval(".app-component-refresh-btn", els => els.forEach(el => el.remove()))
45
    await page.$$eval(".app-table-footer-btn", els => els.forEach(el => el.remove()))
46

47
    await new Promise((resolve, _) => setTimeout(resolve, 500))
48

49
    const screenshot = kind === "pdf" ? await page.pdf({
50
      printBackground: true,
51
      width,
52
      height
53
    }) : await page.screenshot({
54
      fullPage: true,
55
      type: "png",
56
      captureBeyondViewport: false
57
    });
58
    await browser.close();
59
    return Buffer.from(screenshot).toString('base64');
60
  } catch (err) {
61
    if (browser) {
62
      await browser.close();
63
    }
64
    throw err;
65
  }
66
}