mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
232 lines
7.3 KiB
JavaScript
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);
|