// built-in skill: performance profiling import { launchReapedChrome } from '../../../../../scripts/lib/reap-browser' import type { RNXSkill } from '../types' interface OverlayProfile { syncCount?: number avgSyncMs?: number [key: string]: unknown } export const skill: RNXSkill = { name: 'perf-profile', description: 'Profile render performance of the app', type: 'performance', version: '1.0.0', triggers: ['performance', 'profile', 'render time', 'benchmark', 'perf'], tools: [ { name: 'profile_render', description: 'Profile render performance of the current screen', parameters: { duration: { type: 'number', description: 'Profile duration in seconds (default: 5)', }, interactions: { type: 'boolean', description: 'Include user interactions in profiling', }, }, async execute(params, context) { const { chromium } = await import('playwright') const path = await import('path') const fs = await import('fs') // sootsim renders CanvasKit — require GPU-backed Chrome (Metal). the // bundled swiftshader shell software-renders the canvas and pegs every // core; never use it for sootsim. const browser = await launchReapedChrome((options) => chromium.launch(options), { headless: true, }) const page = await browser.newPage({ viewport: { width: 500, height: 900 } }) try { await page.goto(context.url, { waitUntil: 'networkidle' }) await page.waitForFunction('window.__sootsimTest && window.__sootsimA11y', { timeout: 10000, }) await page.waitForTimeout(1000) // reset profiler await page.evaluate('window.__sootsimA11y.resetProfile()') // wait for profile duration const duration = (params.duration || 5) * 1000 await page.waitForTimeout(duration) // collect results const profile = (await page.evaluate( 'window.__sootsimA11y.profile()', )) as OverlayProfile const nodeCount = await page.evaluate('window.__sootsimTest.getNodeCount()') const syncCount = profile.syncCount ?? 0 const report = { timestamp: new Date().toISOString(), duration: params.duration || 5, nodeCount, ...profile, fps: syncCount > 0 ? (syncCount / (duration / 1000)).toFixed(1) : '0', } const reportPath = path.join(context.outputDir, 'perf-report.json') fs.mkdirSync(path.dirname(reportPath), { recursive: true }) fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)) return { success: true, message: `${report.fps} fps, ${profile.avgSyncMs?.toFixed(1)}ms avg sync, ${nodeCount} nodes`, artifacts: [{ type: 'report', name: 'perf-report', path: reportPath }], data: report, } } finally { await browser.close() } }, }, ], }