@vizejs/vite-plugin-musea 0.0.1-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +222 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1512 -0
- package/dist/index.js.map +1 -0
- package/dist/vrt-BfuTRv-J.d.ts +217 -0
- package/dist/vrt-BfuTRv-J.d.ts.map +1 -0
- package/dist/vrt-DRwtnkE5.js +773 -0
- package/dist/vrt-DRwtnkE5.js.map +1 -0
- package/dist/vrt.d.ts +2 -0
- package/dist/vrt.js +3 -0
- package/package.json +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrt-DRwtnkE5.js","names":["options: VrtOptions","artFiles: ArtFileInfo[]","baseUrl: string","results: VrtResult[]","art: ArtFileInfo","variantName: string","viewport: ViewportConfig","context: BrowserContext | null","page: Page | null","artPath: string","baselinePath: string","currentPath: string","diffPath: string","width","height","diff","summary: VrtSummary","ms: number","filepath: string","png: PNG","r1: number","g1: number","b1: number","a1: number","r2: number","g2: number","b2: number","a2: number","fg: number","bg: number","alpha: number","str: string"],"sources":["../src/vrt.ts"],"sourcesContent":["/**\n * Visual Regression Testing (VRT) module for Musea.\n * Uses Playwright for browser automation and pixel comparison.\n */\n\nimport type { Browser, BrowserContext, Page } from \"playwright\";\nimport type { ArtFileInfo, VrtOptions, ViewportConfig } from \"./types.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PNG } from \"pngjs\";\n\n/**\n * VRT test result for a single variant.\n */\nexport interface VrtResult {\n artPath: string;\n variantName: string;\n viewport: ViewportConfig;\n passed: boolean;\n snapshotPath: string;\n currentPath?: string;\n diffPath?: string;\n diffPercentage?: number;\n diffPixels?: number;\n totalPixels?: number;\n error?: string;\n isNew?: boolean;\n}\n\n/**\n * VRT summary for reporting.\n */\nexport interface VrtSummary {\n total: number;\n passed: number;\n failed: number;\n new: number;\n skipped: number;\n duration: number;\n}\n\n/**\n * Pixel comparison options.\n */\nexport interface PixelCompareOptions {\n /** Threshold for color difference (0-1). Default: 0.1 */\n threshold?: number;\n /** Include anti-aliasing in diff. Default: false */\n includeAA?: boolean;\n /** Alpha channel comparison. Default: true */\n alpha?: boolean;\n /** Diff highlight color */\n diffColor?: { r: number; g: number; b: number };\n}\n\n/**\n * VRT runner using Playwright.\n */\nexport class MuseaVrtRunner {\n private options: Required<VrtOptions>;\n private browser: Browser | null = null;\n private startTime: number = 0;\n\n constructor(options: VrtOptions = {}) {\n this.options = {\n snapshotDir: options.snapshotDir ?? \".vize/snapshots\",\n threshold: options.threshold ?? 0.1,\n viewports: options.viewports ?? [\n { width: 1280, height: 720, name: \"desktop\" },\n { width: 375, height: 667, name: \"mobile\" },\n ],\n };\n }\n\n /**\n * Initialize Playwright browser.\n */\n async init(): Promise<void> {\n const { chromium } = await import(\"playwright\");\n this.browser = await chromium.launch({ headless: true });\n this.startTime = Date.now();\n }\n\n /**\n * Close browser and cleanup.\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n\n /**\n * Run VRT tests for all Art files.\n */\n async runAllTests(artFiles: ArtFileInfo[], baseUrl: string): Promise<VrtResult[]> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const results: VrtResult[] = [];\n\n for (const art of artFiles) {\n for (const variant of art.variants) {\n if (variant.skipVrt) {\n continue;\n }\n\n for (const viewport of this.options.viewports) {\n const result = await this.captureAndCompare(art, variant.name, viewport, baseUrl);\n results.push(result);\n }\n }\n }\n\n return results;\n }\n\n /**\n * Capture screenshot and compare with baseline.\n */\n async captureAndCompare(\n art: ArtFileInfo,\n variantName: string,\n viewport: ViewportConfig,\n baseUrl: string,\n ): Promise<VrtResult> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const snapshotDir = this.options.snapshotDir;\n const artBaseName = path.basename(art.path, \".art.vue\");\n const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;\n const snapshotName = `${artBaseName}--${variantName}--${viewportName}.png`;\n const snapshotPath = path.join(snapshotDir, snapshotName);\n const currentPath = path.join(snapshotDir, \"current\", snapshotName);\n const diffPath = path.join(snapshotDir, \"diff\", snapshotName);\n\n // Ensure directories exist\n await fs.promises.mkdir(path.dirname(snapshotPath), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"current\"), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"diff\"), { recursive: true });\n\n let context: BrowserContext | null = null;\n let page: Page | null = null;\n\n try {\n context = await this.browser.newContext({\n viewport: {\n width: viewport.width,\n height: viewport.height,\n },\n deviceScaleFactor: viewport.deviceScaleFactor ?? 1,\n });\n page = await context.newPage();\n\n // Navigate to variant preview URL\n const variantUrl = this.buildVariantUrl(baseUrl, art.path, variantName);\n await page.goto(variantUrl, { waitUntil: \"networkidle\" });\n\n // Wait for content to render\n await page.waitForSelector(\".musea-variant\", { timeout: 10000 });\n\n // Additional wait for animations to settle\n await page.waitForTimeout(100);\n\n // Take screenshot\n await page.screenshot({\n path: currentPath,\n fullPage: false,\n });\n\n // Check if baseline exists\n const hasBaseline = await fileExists(snapshotPath);\n\n if (!hasBaseline) {\n // First run - save as baseline\n await fs.promises.copyFile(currentPath, snapshotPath);\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: true,\n snapshotPath,\n currentPath,\n isNew: true,\n };\n }\n\n // Compare images using pixel comparison\n const comparison = await this.compareImages(snapshotPath, currentPath, diffPath);\n\n const passed = comparison.diffPercentage <= this.options.threshold;\n\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed,\n snapshotPath,\n currentPath,\n diffPath: passed ? undefined : diffPath,\n diffPercentage: comparison.diffPercentage,\n diffPixels: comparison.diffPixels,\n totalPixels: comparison.totalPixels,\n };\n } catch (error) {\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: false,\n snapshotPath,\n error: error instanceof Error ? error.message : String(error),\n };\n } finally {\n if (page) await page.close();\n if (context) await context.close();\n }\n }\n\n /**\n * Update baseline snapshots with current screenshots.\n */\n async updateBaselines(results: VrtResult[]): Promise<number> {\n let updated = 0;\n const snapshotDir = this.options.snapshotDir;\n const currentDir = path.join(snapshotDir, \"current\");\n\n for (const result of results) {\n const currentPath = path.join(currentDir, path.basename(result.snapshotPath));\n\n if (await fileExists(currentPath)) {\n await fs.promises.copyFile(currentPath, result.snapshotPath);\n updated++;\n console.log(`[vrt] Updated: ${path.basename(result.snapshotPath)}`);\n }\n }\n\n return updated;\n }\n\n /**\n * Get VRT summary statistics.\n */\n getSummary(results: VrtResult[]): VrtSummary {\n return {\n total: results.length,\n passed: results.filter((r) => r.passed && !r.isNew).length,\n failed: results.filter((r) => !r.passed && !r.error).length,\n new: results.filter((r) => r.isNew).length,\n skipped: results.filter((r) => r.error).length,\n duration: Date.now() - this.startTime,\n };\n }\n\n /**\n * Build URL for variant preview.\n */\n private buildVariantUrl(baseUrl: string, artPath: string, variantName: string): string {\n const encodedPath = encodeURIComponent(artPath);\n const encodedVariant = encodeURIComponent(variantName);\n return `${baseUrl}/__musea__/preview?art=${encodedPath}&variant=${encodedVariant}`;\n }\n\n /**\n * Compare two PNG images and generate a diff image.\n * Returns pixel difference statistics.\n */\n private async compareImages(\n baselinePath: string,\n currentPath: string,\n diffPath: string,\n ): Promise<{ diffPixels: number; totalPixels: number; diffPercentage: number }> {\n const baseline = await readPng(baselinePath);\n const current = await readPng(currentPath);\n\n // Handle size mismatch\n if (baseline.width !== current.width || baseline.height !== current.height) {\n // Create a diff showing the size mismatch\n const width = Math.max(baseline.width, current.width);\n const height = Math.max(baseline.height, current.height);\n const diff = new PNG({ width, height });\n\n // Fill with red to indicate size mismatch\n for (let i = 0; i < diff.data.length; i += 4) {\n diff.data[i] = 255; // R\n diff.data[i + 1] = 0; // G\n diff.data[i + 2] = 0; // B\n diff.data[i + 3] = 255; // A\n }\n\n await writePng(diff, diffPath);\n\n return {\n diffPixels: width * height,\n totalPixels: width * height,\n diffPercentage: 100,\n };\n }\n\n const width = baseline.width;\n const height = baseline.height;\n const totalPixels = width * height;\n const diff = new PNG({ width, height });\n\n // Pixel comparison\n let diffPixels = 0;\n const threshold = 0.1; // Color difference threshold\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = (y * width + x) * 4;\n\n const r1 = baseline.data[idx];\n const g1 = baseline.data[idx + 1];\n const b1 = baseline.data[idx + 2];\n const a1 = baseline.data[idx + 3];\n\n const r2 = current.data[idx];\n const g2 = current.data[idx + 1];\n const b2 = current.data[idx + 2];\n const a2 = current.data[idx + 3];\n\n // Calculate color difference using YIQ color space (better for human perception)\n const delta = colorDelta(r1, g1, b1, a1, r2, g2, b2, a2);\n\n if (delta > threshold * 255 * 255) {\n // Mark as different\n diffPixels++;\n // Red highlight for diff\n diff.data[idx] = 255;\n diff.data[idx + 1] = 0;\n diff.data[idx + 2] = 0;\n diff.data[idx + 3] = 255;\n } else {\n // Grayscale for matching pixels\n const gray = Math.round((r2 + g2 + b2) / 3);\n diff.data[idx] = gray;\n diff.data[idx + 1] = gray;\n diff.data[idx + 2] = gray;\n diff.data[idx + 3] = 128; // Semi-transparent\n }\n }\n }\n\n // Only write diff if there are differences\n if (diffPixels > 0) {\n await writePng(diff, diffPath);\n }\n\n const diffPercentage = (diffPixels / totalPixels) * 100;\n\n return {\n diffPixels,\n totalPixels,\n diffPercentage,\n };\n }\n}\n\n/**\n * Generate VRT report in HTML format.\n * Uses Musea design language for consistency.\n */\nexport function generateVrtReport(results: VrtResult[], summary: VrtSummary): string {\n const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n if (minutes === 0) return `${seconds}s`;\n return `${minutes}m ${seconds % 60}s`;\n };\n\n const timestamp = new Date().toLocaleString(\"ja-JP\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>VRT Report - Musea</title>\n <style>\n :root {\n --musea-bg-primary: #0d0d0d;\n --musea-bg-secondary: #1a1815;\n --musea-bg-tertiary: #252220;\n --musea-accent: #a34828;\n --musea-accent-hover: #c45a32;\n --musea-text: #e6e9f0;\n --musea-text-muted: #7b8494;\n --musea-border: #3a3530;\n --musea-success: #4ade80;\n --musea-error: #f87171;\n --musea-info: #60a5fa;\n --musea-warning: #fbbf24;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: var(--musea-bg-primary);\n color: var(--musea-text);\n min-height: 100vh;\n line-height: 1.5;\n }\n\n /* Header */\n .header {\n background: var(--musea-bg-secondary);\n border-bottom: 1px solid var(--musea-border);\n padding: 1rem 2rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n .header-left {\n display: flex;\n align-items: center;\n gap: 1rem;\n }\n .logo {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--musea-accent);\n }\n .header-title {\n color: var(--musea-text-muted);\n font-size: 0.875rem;\n }\n .header-meta {\n display: flex;\n align-items: center;\n gap: 1.5rem;\n font-size: 0.8125rem;\n color: var(--musea-text-muted);\n }\n .header-meta span {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n }\n\n /* Main content */\n .main {\n max-width: 1400px;\n margin: 0 auto;\n padding: 2rem;\n }\n\n /* Summary cards */\n .summary {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 1rem;\n margin-bottom: 2rem;\n }\n .stat {\n background: var(--musea-bg-secondary);\n border: 1px solid var(--musea-border);\n border-radius: 8px;\n padding: 1.25rem;\n position: relative;\n overflow: hidden;\n }\n .stat::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n }\n .stat.passed::before { background: var(--musea-success); }\n .stat.failed::before { background: var(--musea-error); }\n .stat.new::before { background: var(--musea-info); }\n .stat.skipped::before { background: var(--musea-warning); }\n .stat-value {\n font-size: 2rem;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n margin-bottom: 0.25rem;\n }\n .stat.passed .stat-value { color: var(--musea-success); }\n .stat.failed .stat-value { color: var(--musea-error); }\n .stat.new .stat-value { color: var(--musea-info); }\n .stat.skipped .stat-value { color: var(--musea-warning); }\n .stat-label {\n color: var(--musea-text-muted);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 500;\n }\n\n /* Filters */\n .filters {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n flex-wrap: wrap;\n }\n .filter-btn {\n background: var(--musea-bg-secondary);\n border: 1px solid var(--musea-border);\n color: var(--musea-text);\n padding: 0.5rem 1rem;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.8125rem;\n font-weight: 500;\n transition: all 0.15s ease;\n }\n .filter-btn:hover {\n background: var(--musea-bg-tertiary);\n border-color: var(--musea-text-muted);\n }\n .filter-btn.active {\n background: var(--musea-accent);\n border-color: var(--musea-accent);\n color: #fff;\n }\n .filter-btn .count {\n opacity: 0.7;\n margin-left: 0.25rem;\n }\n\n /* Results */\n .results {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n }\n .result {\n background: var(--musea-bg-secondary);\n border: 1px solid var(--musea-border);\n border-radius: 8px;\n overflow: hidden;\n transition: border-color 0.15s ease;\n }\n .result:hover {\n border-color: var(--musea-text-muted);\n }\n .result-header {\n padding: 1rem 1.25rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n border-left: 3px solid transparent;\n background: var(--musea-bg-tertiary);\n }\n .result.passed .result-header { border-left-color: var(--musea-success); }\n .result.failed .result-header { border-left-color: var(--musea-error); }\n .result.new .result-header { border-left-color: var(--musea-info); }\n .result.error .result-header { border-left-color: var(--musea-warning); }\n\n .result-info {\n display: flex;\n align-items: center;\n gap: 1rem;\n }\n .result-name {\n font-weight: 600;\n font-size: 0.9375rem;\n }\n .result-meta {\n color: var(--musea-text-muted);\n font-size: 0.8125rem;\n padding: 0.125rem 0.5rem;\n background: var(--musea-bg-secondary);\n border-radius: 4px;\n }\n .result-badge {\n padding: 0.25rem 0.625rem;\n border-radius: 4px;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n .result.passed .result-badge { background: rgba(74, 222, 128, 0.15); color: var(--musea-success); }\n .result.failed .result-badge { background: rgba(248, 113, 113, 0.15); color: var(--musea-error); }\n .result.new .result-badge { background: rgba(96, 165, 250, 0.15); color: var(--musea-info); }\n .result.error .result-badge { background: rgba(251, 191, 36, 0.15); color: var(--musea-warning); }\n\n .result-body {\n border-top: 1px solid var(--musea-border);\n }\n .result-details {\n padding: 0.875rem 1.25rem;\n font-size: 0.8125rem;\n color: var(--musea-text-muted);\n font-family: 'SF Mono', 'Fira Code', monospace;\n background: var(--musea-bg-primary);\n }\n .result-details.error {\n color: var(--musea-error);\n }\n\n /* Image comparison */\n .result-images {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 1rem;\n padding: 1.25rem;\n background: var(--musea-bg-primary);\n }\n .image-container {\n background: var(--musea-bg-secondary);\n border: 1px solid var(--musea-border);\n border-radius: 6px;\n overflow: hidden;\n }\n .image-label {\n padding: 0.625rem 0.875rem;\n font-size: 0.6875rem;\n font-weight: 600;\n color: var(--musea-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n background: var(--musea-bg-tertiary);\n border-bottom: 1px solid var(--musea-border);\n }\n .image-wrapper {\n padding: 0.5rem;\n background: repeating-conic-gradient(\n var(--musea-bg-tertiary) 0% 25%,\n var(--musea-bg-secondary) 0% 50%\n ) 50% / 16px 16px;\n }\n .image-container img {\n width: 100%;\n height: auto;\n display: block;\n border-radius: 2px;\n }\n\n /* Empty state */\n .empty-state {\n text-align: center;\n padding: 4rem 2rem;\n color: var(--musea-text-muted);\n }\n .empty-state-icon {\n font-size: 3rem;\n margin-bottom: 1rem;\n opacity: 0.5;\n }\n\n /* Success state */\n .all-passed {\n background: rgba(74, 222, 128, 0.1);\n border: 1px solid rgba(74, 222, 128, 0.2);\n border-radius: 8px;\n padding: 1.5rem;\n text-align: center;\n margin-bottom: 1.5rem;\n }\n .all-passed-icon {\n font-size: 2.5rem;\n margin-bottom: 0.5rem;\n }\n .all-passed-text {\n color: var(--musea-success);\n font-weight: 600;\n }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div class=\"header-left\">\n <div class=\"logo\">Musea</div>\n <span class=\"header-title\">Visual Regression Report</span>\n </div>\n <div class=\"header-meta\">\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n ${formatDuration(summary.duration)}\n </span>\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/>\n </svg>\n ${timestamp}\n </span>\n </div>\n </header>\n\n <main class=\"main\">\n <div class=\"summary\">\n <div class=\"stat passed\">\n <div class=\"stat-value\">${summary.passed}</div>\n <div class=\"stat-label\">Passed</div>\n </div>\n <div class=\"stat failed\">\n <div class=\"stat-value\">${summary.failed}</div>\n <div class=\"stat-label\">Failed</div>\n </div>\n <div class=\"stat new\">\n <div class=\"stat-value\">${summary.new}</div>\n <div class=\"stat-label\">New</div>\n </div>\n <div class=\"stat skipped\">\n <div class=\"stat-value\">${summary.skipped}</div>\n <div class=\"stat-label\">Skipped</div>\n </div>\n </div>\n\n ${\n summary.failed === 0 && summary.skipped === 0 && summary.total > 0\n ? `<div class=\"all-passed\">\n <div class=\"all-passed-icon\">✓</div>\n <div class=\"all-passed-text\">All ${summary.total} visual tests passed</div>\n </div>`\n : \"\"\n }\n\n <div class=\"filters\">\n <button class=\"filter-btn active\" data-filter=\"all\">All<span class=\"count\">(${summary.total})</span></button>\n <button class=\"filter-btn\" data-filter=\"failed\">Failed<span class=\"count\">(${summary.failed})</span></button>\n <button class=\"filter-btn\" data-filter=\"passed\">Passed<span class=\"count\">(${summary.passed})</span></button>\n <button class=\"filter-btn\" data-filter=\"new\">New<span class=\"count\">(${summary.new})</span></button>\n </div>\n\n <div class=\"results\">\n ${\n results.length === 0\n ? `<div class=\"empty-state\">\n <div class=\"empty-state-icon\">📷</div>\n <p>No visual tests found</p>\n </div>`\n : results\n .map((r) => {\n const status = r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\";\n const badge = r.error ? \"Error\" : r.isNew ? \"New\" : r.passed ? \"Passed\" : \"Failed\";\n const artName = path.basename(r.artPath, \".art.vue\");\n const viewportName = r.viewport.name || `${r.viewport.width}×${r.viewport.height}`;\n\n let details = \"\";\n if (r.error) {\n details = `<div class=\"result-details error\">${escapeHtml(r.error)}</div>`;\n } else if (r.diffPercentage !== undefined) {\n const diffFormatted = r.diffPercentage.toFixed(3);\n const pixelsFormatted = r.diffPixels?.toLocaleString() ?? \"0\";\n const totalFormatted = r.totalPixels?.toLocaleString() ?? \"0\";\n details = `<div class=\"result-details\">diff: ${diffFormatted}% (${pixelsFormatted} / ${totalFormatted} pixels)</div>`;\n }\n\n let images = \"\";\n if (!r.error && !r.passed && r.diffPath) {\n images = `<div class=\"result-images\">\n ${\n r.snapshotPath\n ? `<div class=\"image-container\">\n <div class=\"image-label\">Baseline</div>\n <div class=\"image-wrapper\">\n <img src=\"file://${r.snapshotPath}\" alt=\"Baseline\" loading=\"lazy\" />\n </div>\n </div>`\n : \"\"\n }\n ${\n r.currentPath\n ? `<div class=\"image-container\">\n <div class=\"image-label\">Current</div>\n <div class=\"image-wrapper\">\n <img src=\"file://${r.currentPath}\" alt=\"Current\" loading=\"lazy\" />\n </div>\n </div>`\n : \"\"\n }\n ${\n r.diffPath\n ? `<div class=\"image-container\">\n <div class=\"image-label\">Diff</div>\n <div class=\"image-wrapper\">\n <img src=\"file://${r.diffPath}\" alt=\"Diff\" loading=\"lazy\" />\n </div>\n </div>`\n : \"\"\n }\n </div>`;\n }\n\n const hasBody = details || images;\n\n return `<div class=\"result ${status}\" data-status=\"${status}\">\n <div class=\"result-header\">\n <div class=\"result-info\">\n <div class=\"result-name\">${escapeHtml(artName)} / ${escapeHtml(r.variantName)}</div>\n <div class=\"result-meta\">${escapeHtml(viewportName)}</div>\n </div>\n <span class=\"result-badge\">${badge}</span>\n </div>\n ${hasBody ? `<div class=\"result-body\">${details}${images}</div>` : \"\"}\n </div>`;\n })\n .join(\"\")\n }\n </div>\n </main>\n\n <script>\n document.querySelectorAll('.filter-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n const filter = btn.dataset.filter;\n document.querySelectorAll('.result').forEach(result => {\n if (filter === 'all' || result.dataset.status === filter) {\n result.style.display = 'block';\n } else {\n result.style.display = 'none';\n }\n });\n });\n });\n </script>\n</body>\n</html>`;\n\n return html;\n}\n\n/**\n * Generate VRT JSON report for CI integration.\n */\nexport function generateVrtJsonReport(results: VrtResult[], summary: VrtSummary): string {\n return JSON.stringify(\n {\n timestamp: new Date().toISOString(),\n summary,\n results: results.map((r) => ({\n art: path.basename(r.artPath, \".art.vue\"),\n variant: r.variantName,\n viewport: r.viewport.name || `${r.viewport.width}x${r.viewport.height}`,\n status: r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\",\n diffPercentage: r.diffPercentage,\n error: r.error,\n })),\n },\n null,\n 2,\n );\n}\n\n// Utility functions\n\n/**\n * Read PNG file and return PNG object.\n */\nasync function readPng(filepath: string): Promise<PNG> {\n return new Promise((resolve, reject) => {\n fs.createReadStream(filepath)\n .pipe(new PNG())\n .on(\"parsed\", function (this: PNG) {\n resolve(this);\n })\n .on(\"error\", reject);\n });\n}\n\n/**\n * Write PNG object to file.\n */\nasync function writePng(png: PNG, filepath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n png.pack().pipe(fs.createWriteStream(filepath)).on(\"finish\", resolve).on(\"error\", reject);\n });\n}\n\n/**\n * Calculate color delta using YIQ color space.\n * This is more perceptually accurate than simple RGB difference.\n */\nfunction colorDelta(\n r1: number,\n g1: number,\n b1: number,\n a1: number,\n r2: number,\n g2: number,\n b2: number,\n a2: number,\n): number {\n // Blend with white if alpha is not fully opaque\n if (a1 !== 255) {\n r1 = blend(r1, 255, a1 / 255);\n g1 = blend(g1, 255, a1 / 255);\n b1 = blend(b1, 255, a1 / 255);\n }\n if (a2 !== 255) {\n r2 = blend(r2, 255, a2 / 255);\n g2 = blend(g2, 255, a2 / 255);\n b2 = blend(b2, 255, a2 / 255);\n }\n\n // Convert to YIQ color space\n const y1 = r1 * 0.29889531 + g1 * 0.58662247 + b1 * 0.11448223;\n const i1 = r1 * 0.59597799 - g1 * 0.2741761 - b1 * 0.32180189;\n const q1 = r1 * 0.21147017 - g1 * 0.52261711 + b1 * 0.31114694;\n\n const y2 = r2 * 0.29889531 + g2 * 0.58662247 + b2 * 0.11448223;\n const i2 = r2 * 0.59597799 - g2 * 0.2741761 - b2 * 0.32180189;\n const q2 = r2 * 0.21147017 - g2 * 0.52261711 + b2 * 0.31114694;\n\n // Calculate delta (weighted by human eye sensitivity)\n const dy = y1 - y2;\n const di = i1 - i2;\n const dq = q1 - q2;\n\n return dy * dy * 0.5053 + di * di * 0.299 + dq * dq * 0.1957;\n}\n\n/**\n * Blend foreground with background using alpha.\n */\nfunction blend(fg: number, bg: number, alpha: number): number {\n return bg + (fg - bg) * alpha;\n}\n\n/**\n * Check if file exists.\n */\nasync function fileExists(filepath: string): Promise<boolean> {\n try {\n await fs.promises.access(filepath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Escape HTML special characters.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nexport default MuseaVrtRunner;\n"],"mappings":"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"}
|
package/dist/vrt.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { MuseaVrtRunner$1 as MuseaVrtRunner, PixelCompareOptions, VrtResult, VrtSummary, generateVrtJsonReport$1 as generateVrtJsonReport, generateVrtReport$1 as generateVrtReport } from "./vrt-BfuTRv-J.js";
|
|
2
|
+
export { MuseaVrtRunner, PixelCompareOptions, VrtResult, VrtSummary, MuseaVrtRunner as default, generateVrtJsonReport, generateVrtReport };
|
package/dist/vrt.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vizejs/vite-plugin-musea",
|
|
3
|
+
"version": "0.0.1-alpha.11",
|
|
4
|
+
"description": "Vite plugin for Musea - Component gallery for Vue components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"musea-vrt": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./vrt": {
|
|
17
|
+
"import": "./dist/vrt.js",
|
|
18
|
+
"types": "./dist/vrt.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./cli": {
|
|
21
|
+
"import": "./dist/cli.js",
|
|
22
|
+
"types": "./dist/cli.d.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"bin"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"vite",
|
|
31
|
+
"vite-plugin",
|
|
32
|
+
"vue",
|
|
33
|
+
"storybook",
|
|
34
|
+
"component-gallery",
|
|
35
|
+
"musea"
|
|
36
|
+
],
|
|
37
|
+
"author": "ubugeeei",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/ubugeeei/vize.git",
|
|
42
|
+
"directory": "npm/vite-plugin-musea"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"pngjs": "^7.0.0",
|
|
46
|
+
"@vizejs/native": "0.0.1-alpha.33"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.14.0",
|
|
50
|
+
"@types/pngjs": "^6.0.5",
|
|
51
|
+
"tsdown": "^0.9.0",
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"vite": "^8.0.0-beta.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0",
|
|
57
|
+
"playwright": "^1.40.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"playwright": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"provenance": true,
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsdown",
|
|
70
|
+
"dev": "tsdown --watch",
|
|
71
|
+
"lint": "oxlint --deny-warnings --type-aware --tsconfig tsconfig.json",
|
|
72
|
+
"lint:fix": "oxlint --type-aware --tsconfig tsconfig.json --fix",
|
|
73
|
+
"fmt": "oxfmt --write src",
|
|
74
|
+
"fmt:check": "oxfmt src"
|
|
75
|
+
}
|
|
76
|
+
}
|