Skip to main content
Glama
mermaid-renderer.ts4.13 kB
import puppeteer from 'puppeteer'; export interface MermaidRenderOptions { theme?: 'default' | 'dark' | 'forest' | 'neutral'; backgroundColor?: string; width?: number; height?: number; } export class MermaidRenderer { async renderDiagram(mermaidCode: string, options: MermaidRenderOptions = {}): Promise<string> { const { theme = 'default', backgroundColor = 'white', width = 1200, height = 800 } = options; const browser = await puppeteer.launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu' ], headless: true }); try { const page = await browser.newPage(); // Set viewport for consistent rendering await page.setViewport({ width, height }); // Set up the page with Mermaid and professional styling await page.setContent(` <!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.0/dist/mermaid.min.js"></script> <script> mermaid.initialize({ startOnLoad: true, theme: '${theme}', securityLevel: 'loose', themeVariables: { primaryColor: '#0052CC', primaryTextColor: '#172B4D', primaryBorderColor: '#DFE1E6', lineColor: '#42526E', secondaryColor: '#F4F5F7', tertiaryColor: '#FFFFFF' }, flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'basis' }, sequence: { diagramMarginX: 50, diagramMarginY: 10, actorMargin: 50, width: 150, height: 65, boxMargin: 10, boxTextMargin: 5, noteMargin: 10, messageMargin: 35 } }); </script> <style> body { background: ${backgroundColor}; margin: 0; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .mermaid { display: flex; justify-content: center; align-items: center; min-height: 200px; padding: 20px; } .mermaid svg { max-width: 100%; height: auto; filter: drop-shadow(0 2px 8px rgba(0,0,0,0.1)); } </style> </head> <body> <div class="mermaid"> ${mermaidCode} </div> </body> </html> `); // Wait for Mermaid to render with timeout try { await page.waitForSelector('.mermaid svg', { timeout: 10000 }); } catch (error) { throw new Error(`Mermaid diagram failed to render: ${error}`); } // Get the SVG element const svgElement = await page.$('.mermaid svg'); if (!svgElement) { throw new Error('Failed to find rendered Mermaid diagram'); } // Take a high-quality screenshot of the SVG const imageBuffer = await svgElement.screenshot({ type: 'png', omitBackground: backgroundColor === 'transparent', clip: await svgElement.boundingBox() || undefined }); // Convert to base64 for embedding return `data:image/png;base64,${imageBuffer.toString('base64')}`; } finally { await browser.close(); } } async renderDiagramToFile(mermaidCode: string, outputPath: string, options: MermaidRenderOptions = {}): Promise<void> { const base64Data = await this.renderDiagram(mermaidCode, options); const buffer = Buffer.from(base64Data.split(',')[1], 'base64'); const fs = await import('fs'); fs.writeFileSync(outputPath, buffer); } }

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/manateeit/confluence-mcp'

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