Skip to main content
Glama
screenshot.ts4.39 kB
/** * Screenshot utility using Playwright * * DESIGN PATTERNS: * - Utility function for browser automation * - Factory pattern for browser selection * - Options pattern for configuration * * CODING STANDARDS: * - Use async/await for browser operations * - Clean up resources (close browser) in finally block * - Support multiple browsers (chromium, firefox, webkit) * * AVOID: * - Leaving browser instances open * - Hardcoding viewport sizes */ import { chromium, firefox, webkit, type Browser, type BrowserType } from 'playwright'; import sharp from 'sharp'; import path from 'node:path'; export interface ScreenshotOptions { url: string; output: string; width?: number; height?: number; fullPage?: boolean; browser?: 'chromium' | 'firefox' | 'webkit'; waitTime?: number; darkMode?: boolean; mobile?: boolean; generateThumbnail?: boolean; thumbnailWidth?: number; thumbnailQuality?: number; base64?: boolean; } export interface ScreenshotResult { imagePath: string; thumbnailPath?: string; base64?: string; } const browsers: Record<string, BrowserType> = { chromium, firefox, webkit, }; /** * Browser launch configurations to try in order of preference. * Prefers system-installed Chrome before falling back to Playwright's bundled browsers. */ const browserLaunchConfigs = [ { browserType: chromium, channel: 'chrome', name: 'System Chrome' }, { browserType: chromium, channel: undefined, name: 'Playwright Chromium' }, { browserType: firefox, channel: undefined, name: 'Playwright Firefox' }, ] as const; /** * Attempts to launch a browser, trying multiple configurations in order of preference. * @returns Launched browser instance * @throws Error if no browser can be launched */ async function launchBrowserWithFallback(): Promise<Browser> { const errors: string[] = []; for (const config of browserLaunchConfigs) { try { const browser = await config.browserType.launch({ headless: true, channel: config.channel, }); console.log(`[screenshot] Using ${config.name}`); return browser; } catch (error) { const message = error instanceof Error ? error.message : String(error); errors.push(`${config.name}: ${message}`); } } throw new Error( `No browser available. Tried:\n${errors.join('\n')}\n\nPlease install Chrome, or run 'npx playwright install chromium'`, ); } export async function takeScreenshot(options: ScreenshotOptions): Promise<ScreenshotResult> { const { url, output, width = 1280, height = 800, fullPage = false, browser: browserName = 'chromium', waitTime = 1000, darkMode = false, mobile = false, generateThumbnail = false, thumbnailWidth = 400, thumbnailQuality = 80, base64 = false, } = options; let browser: Browser | null = null; try { // Use fallback browser detection for chromium (default) if (browserName === 'chromium') { browser = await launchBrowserWithFallback(); } else { const browserType = browsers[browserName]; if (!browserType) { throw new Error(`Unsupported browser: ${browserName}`); } browser = await browserType.launch({ headless: true }); } const context = await browser.newContext({ viewport: { width, height }, colorScheme: darkMode ? 'dark' : 'light', isMobile: mobile, }); const page = await context.newPage(); await page.goto(url, { waitUntil: 'networkidle' }); if (waitTime > 0) { await page.waitForTimeout(waitTime); } const screenshotBuffer = await page.screenshot({ path: output, fullPage, }); const result: ScreenshotResult = { imagePath: output, }; // Generate thumbnail if requested if (generateThumbnail) { const ext = path.extname(output); const thumbnailPath = output.replace(ext, `-thumb${ext}`); await sharp(screenshotBuffer) .resize(thumbnailWidth) .jpeg({ quality: thumbnailQuality }) .toFile(thumbnailPath.replace(ext, '.jpg')); result.thumbnailPath = thumbnailPath.replace(ext, '.jpg'); } // Generate base64 if requested if (base64) { result.base64 = screenshotBuffer.toString('base64'); } return result; } finally { if (browser) { await browser.close(); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AgiFlow/aicode-toolkit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server