Files
Pulse/scripts/take-screenshots.js
2025-10-11 23:29:47 +00:00

232 lines
7.3 KiB
JavaScript

const puppeteer = require('puppeteer');
const fs = require('fs').promises;
const path = require('path');
const PULSE_URL = process.env.PULSE_URL || 'http://localhost:7655';
const OUTPUT_DIR = path.join(__dirname, '..', 'docs', 'images');
async function cleanExistingScreenshots() {
try {
const files = await fs.readdir(OUTPUT_DIR);
const screenshotFiles = files.filter(f =>
f.endsWith('.png') &&
(f.match(/^\d{2}-/) || f.includes('error-state'))
);
console.log(`Removing ${screenshotFiles.length} existing screenshots...`);
for (const file of screenshotFiles) {
await fs.unlink(path.join(OUTPUT_DIR, file));
}
} catch (error) {
console.error('Error cleaning screenshots:', error);
}
}
async function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
await cleanExistingScreenshots();
console.log('Starting simple Puppeteer screenshot capture...\n');
// Launch browser with M1 MacBook Air display settings
const browser = await puppeteer.launch({
headless: 'new', // Use new headless mode
defaultViewport: null, // Don't override viewport
args: [
'--window-size=1440,900', // M1 MacBook Air effective resolution (2560x1600 native / 2)
'--force-device-scale-factor=2', // Retina display (2x)
'--disable-dev-shm-usage', // Better memory handling
'--enable-font-antialiasing', // Smooth fonts
'--font-render-hinting=none' // Disable hinting for sharper text
]
});
const page = await browser.newPage();
// CRITICAL: Set viewport BEFORE navigating for sharp rendering!
// This must happen before page.goto() or text will be blurry
// M1 MacBook Air 13" display dimensions (native 2560x1600, effective 1440x900 at 2x)
await page.setViewport({
width: 1440,
height: 900,
deviceScaleFactor: 2 // Retina display
});
try {
console.log(`Navigating to ${PULSE_URL}...`);
await page.goto(PULSE_URL, {
waitUntil: 'networkidle0',
timeout: 30000
});
// Wait for fonts to fully load for crisp text
await page.evaluateHandle('document.fonts.ready');
// Wait for content to load
await wait(5000);
// Set dark theme
await page.evaluate(() => {
localStorage.setItem('pulse-theme', 'dark');
document.documentElement.classList.remove('light');
document.documentElement.classList.add('dark');
});
// Force crisp font rendering
await page.addStyleTag({
content: `
* {
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale !important;
text-rendering: optimizeLegibility !important;
}
`
});
await wait(2000);
// Take screenshots of each view
const views = [
{ name: 'Dashboard', file: '01-dashboard' },
{ name: 'Storage', file: '02-storage' },
{ name: 'Backups', file: '03-backups' },
{ name: 'Alerts', file: '04-alerts' },
{ name: 'Settings', file: '06-settings' }
];
for (const view of views) {
console.log(`Capturing ${view.name}...`);
if (view.name !== 'Dashboard') {
// Try to click on the tab
try {
await page.evaluate((viewName) => {
const buttons = Array.from(document.querySelectorAll('button, [role="tab"], a'));
const button = buttons.find(b => b.textContent.includes(viewName));
if (button) button.click();
}, view.name);
await wait(3000);
} catch (e) {
console.log(`Could not navigate to ${view.name}`);
}
}
// Take screenshot - MacBook Air resolution should fit content perfectly
await page.screenshot({
path: path.join(OUTPUT_DIR, `${view.file}.png`),
type: 'png'
});
console.log(`Saved ${view.file}.png`);
}
// Note: Active alerts are shown on the main Alerts tab (04-alerts.png)
// The tab has Config and History sub-tabs, no separate Active tab
// Take alert history screenshot - need to click on History sub-tab
console.log('Capturing alert history...');
// First make sure we're on the Alerts tab
await page.evaluate(() => {
const alertsTab = Array.from(document.querySelectorAll('button, [role="tab"], a'))
.find(b => b.textContent.includes('Alerts'));
if (alertsTab) alertsTab.click();
});
await wait(2000);
// Now click on the History sub-tab
await page.evaluate(() => {
const historyTab = Array.from(document.querySelectorAll('button, [role="tab"], a'))
.find(b => b.textContent.trim() === 'History' || b.textContent.includes('History'));
if (historyTab) historyTab.click();
});
// Wait for alert history to load with retry logic
console.log('Waiting for alert history to load...');
let retries = 0;
const maxRetries = 15; // 15 seconds max
while (retries < maxRetries) {
await wait(1000);
// Check if data has loaded by looking for alert rows in the DOM
const hasData = await page.evaluate(() => {
// Look for alert history rows or the "No alerts" message
const rows = document.querySelectorAll('tbody tr, [data-testid="alert-row"], .alert-history-row');
const noDataMsg = Array.from(document.querySelectorAll('*')).find(
el => el.textContent && el.textContent.includes('No alerts in selected time range')
);
return rows.length > 0 || noDataMsg;
});
if (hasData) {
console.log('Alert history loaded after', retries + 1, 'seconds');
break;
}
retries++;
}
// Extra wait for any animations
await wait(1000);
// Take the screenshot - MacBook Air resolution
await page.screenshot({
path: path.join(OUTPUT_DIR, '05-alert-history.png'),
type: 'png'
});
console.log('Saved 05-alert-history.png');
// Note: Dashboard is already in dark mode, no need for duplicate or light mode
// Mobile view
console.log('\nCapturing mobile view...');
// Set mobile viewport BEFORE reload for sharp rendering
// iPhone 12/13/14 dimensions for modern mobile testing
await page.setViewport({
width: 390, // iPhone 12/13/14 width
height: 844, // iPhone 12/13/14 height
isMobile: true,
deviceScaleFactor: 3 // 3x for Super Retina XDR
});
// Now reload with the new viewport already set
await page.reload({ waitUntil: 'networkidle0' });
// Wait for fonts to load again after reload
await page.evaluateHandle('document.fonts.ready');
await wait(3000);
// Set dark theme again
await page.evaluate(() => {
localStorage.setItem('pulse-theme', 'dark');
document.documentElement.classList.remove('light');
document.documentElement.classList.add('dark');
});
await wait(2000);
await page.screenshot({
path: path.join(OUTPUT_DIR, '08-mobile.png'),
type: 'png'
});
console.log('Saved 08-mobile.png');
console.log('\nScreenshot capture complete!');
} catch (error) {
console.error('Error during screenshot capture:', error);
} finally {
await browser.close();
}
}
main().catch(console.error);