@logicsync/pixelprobe 0.1.0
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/LICENSE +21 -0
- package/README.md +377 -0
- package/dist/bin/pixelprobe.d.ts +3 -0
- package/dist/bin/pixelprobe.d.ts.map +1 -0
- package/dist/bin/pixelprobe.js +218 -0
- package/dist/bin/pixelprobe.js.map +1 -0
- package/dist/browser/BrowserManager.d.ts +20 -0
- package/dist/browser/BrowserManager.d.ts.map +1 -0
- package/dist/browser/BrowserManager.js +64 -0
- package/dist/browser/BrowserManager.js.map +1 -0
- package/dist/browser/ViewportManager.d.ts +15 -0
- package/dist/browser/ViewportManager.d.ts.map +1 -0
- package/dist/browser/ViewportManager.js +65 -0
- package/dist/browser/ViewportManager.js.map +1 -0
- package/dist/cli/formatter.d.ts +4 -0
- package/dist/cli/formatter.d.ts.map +1 -0
- package/dist/cli/formatter.js +107 -0
- package/dist/cli/formatter.js.map +1 -0
- package/dist/comparator/DiffAggregator.d.ts +24 -0
- package/dist/comparator/DiffAggregator.d.ts.map +1 -0
- package/dist/comparator/DiffAggregator.js +94 -0
- package/dist/comparator/DiffAggregator.js.map +1 -0
- package/dist/comparator/LayoutComparator.d.ts +8 -0
- package/dist/comparator/LayoutComparator.d.ts.map +1 -0
- package/dist/comparator/LayoutComparator.js +68 -0
- package/dist/comparator/LayoutComparator.js.map +1 -0
- package/dist/comparator/PixelComparator.d.ts +9 -0
- package/dist/comparator/PixelComparator.d.ts.map +1 -0
- package/dist/comparator/PixelComparator.js +72 -0
- package/dist/comparator/PixelComparator.js.map +1 -0
- package/dist/comparator/StyleComparator.d.ts +8 -0
- package/dist/comparator/StyleComparator.d.ts.map +1 -0
- package/dist/comparator/StyleComparator.js +119 -0
- package/dist/comparator/StyleComparator.js.map +1 -0
- package/dist/core.d.ts +23 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +211 -0
- package/dist/core.js.map +1 -0
- package/dist/extractor/ScreenshotCapturer.d.ts +20 -0
- package/dist/extractor/ScreenshotCapturer.d.ts.map +1 -0
- package/dist/extractor/ScreenshotCapturer.js +56 -0
- package/dist/extractor/ScreenshotCapturer.js.map +1 -0
- package/dist/extractor/SectionExtractor.d.ts +26 -0
- package/dist/extractor/SectionExtractor.d.ts.map +1 -0
- package/dist/extractor/SectionExtractor.js +92 -0
- package/dist/extractor/SectionExtractor.js.map +1 -0
- package/dist/extractor/StyleExtractor.d.ts +23 -0
- package/dist/extractor/StyleExtractor.d.ts.map +1 -0
- package/dist/extractor/StyleExtractor.js +88 -0
- package/dist/extractor/StyleExtractor.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +203 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/reporter/HTMLReporter.d.ts +8 -0
- package/dist/reporter/HTMLReporter.d.ts.map +1 -0
- package/dist/reporter/HTMLReporter.js +219 -0
- package/dist/reporter/HTMLReporter.js.map +1 -0
- package/dist/reporter/JSONReporter.d.ts +17 -0
- package/dist/reporter/JSONReporter.d.ts.map +1 -0
- package/dist/reporter/JSONReporter.js +77 -0
- package/dist/reporter/JSONReporter.js.map +1 -0
- package/dist/types/comparison.d.ts +133 -0
- package/dist/types/comparison.d.ts.map +1 -0
- package/dist/types/comparison.js +3 -0
- package/dist/types/comparison.js.map +1 -0
- package/dist/types/config.d.ts +138 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +151 -0
- package/dist/types/config.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { STYLE_CATEGORIES } from "../types/config.js";
|
|
2
|
+
export class StyleExtractor {
|
|
3
|
+
/**
|
|
4
|
+
* Extract computed styles for specific categories from an element.
|
|
5
|
+
*/
|
|
6
|
+
static async extractStyles(locator, categories) {
|
|
7
|
+
const propertiesToExtract = StyleExtractor.getProperties(categories);
|
|
8
|
+
return await locator.evaluate((el, props) => {
|
|
9
|
+
const computed = window.getComputedStyle(el);
|
|
10
|
+
const styles = {};
|
|
11
|
+
for (const prop of props) {
|
|
12
|
+
const value = computed.getPropertyValue(prop);
|
|
13
|
+
if (value) {
|
|
14
|
+
styles[prop] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return styles;
|
|
18
|
+
}, propertiesToExtract);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extract layout metrics (box model) from an element.
|
|
22
|
+
*/
|
|
23
|
+
static async extractLayoutMetrics(locator) {
|
|
24
|
+
return await locator.evaluate((el) => {
|
|
25
|
+
const rect = el.getBoundingClientRect();
|
|
26
|
+
const computed = window.getComputedStyle(el);
|
|
27
|
+
const parseNum = (val) => parseFloat(val) || 0;
|
|
28
|
+
return {
|
|
29
|
+
boundingBox: {
|
|
30
|
+
x: Math.round(rect.x * 100) / 100,
|
|
31
|
+
y: Math.round(rect.y * 100) / 100,
|
|
32
|
+
width: Math.round(rect.width * 100) / 100,
|
|
33
|
+
height: Math.round(rect.height * 100) / 100,
|
|
34
|
+
},
|
|
35
|
+
margin: {
|
|
36
|
+
top: parseNum(computed.marginTop),
|
|
37
|
+
right: parseNum(computed.marginRight),
|
|
38
|
+
bottom: parseNum(computed.marginBottom),
|
|
39
|
+
left: parseNum(computed.marginLeft),
|
|
40
|
+
},
|
|
41
|
+
padding: {
|
|
42
|
+
top: parseNum(computed.paddingTop),
|
|
43
|
+
right: parseNum(computed.paddingRight),
|
|
44
|
+
bottom: parseNum(computed.paddingBottom),
|
|
45
|
+
left: parseNum(computed.paddingLeft),
|
|
46
|
+
},
|
|
47
|
+
border: {
|
|
48
|
+
top: parseNum(computed.borderTopWidth),
|
|
49
|
+
right: parseNum(computed.borderRightWidth),
|
|
50
|
+
bottom: parseNum(computed.borderBottomWidth),
|
|
51
|
+
left: parseNum(computed.borderLeftWidth),
|
|
52
|
+
},
|
|
53
|
+
scrollWidth: el.scrollWidth,
|
|
54
|
+
scrollHeight: el.scrollHeight,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract computed styles for ALL properties (not just categorized ones).
|
|
60
|
+
* Useful for deep analysis.
|
|
61
|
+
*/
|
|
62
|
+
static async extractAllStyles(locator) {
|
|
63
|
+
return await locator.evaluate((el) => {
|
|
64
|
+
const computed = window.getComputedStyle(el);
|
|
65
|
+
const styles = {};
|
|
66
|
+
for (let i = 0; i < computed.length; i++) {
|
|
67
|
+
const prop = computed[i];
|
|
68
|
+
styles[prop] = computed.getPropertyValue(prop);
|
|
69
|
+
}
|
|
70
|
+
return styles;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the flat list of CSS properties for given categories.
|
|
75
|
+
*/
|
|
76
|
+
static getProperties(categories) {
|
|
77
|
+
const cats = categories ?? Object.keys(STYLE_CATEGORIES);
|
|
78
|
+
const props = [];
|
|
79
|
+
for (const cat of cats) {
|
|
80
|
+
const catProps = STYLE_CATEGORIES[cat];
|
|
81
|
+
if (catProps) {
|
|
82
|
+
props.push(...catProps);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [...new Set(props)]; // deduplicate
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=StyleExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StyleExtractor.js","sourceRoot":"","sources":["../../src/extractor/StyleExtractor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { WebSectionComparator } from "./core.js";
|
|
2
|
+
export type { ComparisonConfig, ResolvedConfig, Breakpoint, PresetBreakpointName, StyleCategory, } from "./types/config.js";
|
|
3
|
+
export { ComparisonConfigSchema, PRESET_BREAKPOINTS, STYLE_CATEGORIES, } from "./types/config.js";
|
|
4
|
+
export type { ComparisonResult, ComparisonSummary, BreakpointResult, StyleDifference, StyleComparisonResult, PixelComparisonResult, LayoutDifference, LayoutComparisonResult, ElementInfo, ExtractedSection, LayoutMetrics, ProgressCallback, ProgressEvent, Severity, } from "./types/comparison.js";
|
|
5
|
+
export { JSONReporter } from "./reporter/JSONReporter.js";
|
|
6
|
+
export { HTMLReporter } from "./reporter/HTMLReporter.js";
|
|
7
|
+
export { ViewportManager } from "./browser/ViewportManager.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjD,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,QAAQ,GACT,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ── Main API ────────────────────────────────────────────────────────
|
|
2
|
+
export { WebSectionComparator } from "./core.js";
|
|
3
|
+
export { ComparisonConfigSchema, PRESET_BREAKPOINTS, STYLE_CATEGORIES, } from "./types/config.js";
|
|
4
|
+
// ── Reporters ───────────────────────────────────────────────────────
|
|
5
|
+
export { JSONReporter } from "./reporter/JSONReporter.js";
|
|
6
|
+
export { HTMLReporter } from "./reporter/HTMLReporter.js";
|
|
7
|
+
// ── Browser utilities ───────────────────────────────────────────────
|
|
8
|
+
export { ViewportManager } from "./browser/ViewportManager.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAWjD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAmB3B,uEAAuE;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,uEAAuE;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { WebSectionComparator } from "../core.js";
|
|
6
|
+
import { JSONReporter } from "../reporter/JSONReporter.js";
|
|
7
|
+
import { HTMLReporter } from "../reporter/HTMLReporter.js";
|
|
8
|
+
import { PRESET_BREAKPOINTS } from "../types/config.js";
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: "pixelprobe",
|
|
11
|
+
version: "0.1.0",
|
|
12
|
+
});
|
|
13
|
+
// ── Tool: compare_sections ──────────────────────────────────────────
|
|
14
|
+
server.tool("compare_sections", "Compare a web page section between source and target URLs across responsive breakpoints. " +
|
|
15
|
+
"Returns detailed style, layout, and visual differences at each breakpoint. " +
|
|
16
|
+
"Use this to verify that a rebuilt section matches the original design.", {
|
|
17
|
+
sourceUrl: z.string().url().describe("Source URL (the reference/original page)"),
|
|
18
|
+
targetUrl: z.string().url().describe("Target URL (the local build or new version)"),
|
|
19
|
+
selector: z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1)
|
|
22
|
+
.describe('CSS selector for the section on the source page (e.g. ".hero-section", "#pricing")'),
|
|
23
|
+
targetSelector: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1)
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('CSS selector for the section on the target page, if different from source. Falls back to selector.'),
|
|
28
|
+
elementIndex: z
|
|
29
|
+
.number()
|
|
30
|
+
.int()
|
|
31
|
+
.min(0)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Element index if selector matches multiple elements (default: 0)"),
|
|
34
|
+
breakpoints: z
|
|
35
|
+
.array(z.string())
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('Breakpoint names to test. Presets: mobile, tablet, desktop, desktop-lg. Or use WIDTHxHEIGHT format. Default: ["mobile", "tablet", "desktop", "desktop-lg"]'),
|
|
38
|
+
pixelThreshold: z
|
|
39
|
+
.number()
|
|
40
|
+
.min(0)
|
|
41
|
+
.max(1)
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Pixel comparison sensitivity (0-1, lower = stricter). Default: 0.1"),
|
|
44
|
+
includeVisual: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Include pixel-level visual comparison. Default: true"),
|
|
48
|
+
includeStyles: z
|
|
49
|
+
.boolean()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Include computed style comparison. Default: true"),
|
|
52
|
+
includeLayout: z
|
|
53
|
+
.boolean()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Include layout metrics comparison. Default: true"),
|
|
56
|
+
waitForTimeout: z
|
|
57
|
+
.number()
|
|
58
|
+
.int()
|
|
59
|
+
.min(0)
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Wait N ms after page load before capturing. Default: 0"),
|
|
62
|
+
outputDir: z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Directory to save report artifacts. Default: ./pixelprobe-output"),
|
|
66
|
+
}, async (args) => {
|
|
67
|
+
const comparator = new WebSectionComparator({
|
|
68
|
+
headless: true,
|
|
69
|
+
timeout: 30000,
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
const result = await comparator.compare({
|
|
73
|
+
sourceUrl: args.sourceUrl,
|
|
74
|
+
targetUrl: args.targetUrl,
|
|
75
|
+
selector: args.selector,
|
|
76
|
+
targetSelector: args.targetSelector,
|
|
77
|
+
elementIndex: args.elementIndex ?? 0,
|
|
78
|
+
breakpoints: args.breakpoints,
|
|
79
|
+
pixelThreshold: args.pixelThreshold ?? 0.1,
|
|
80
|
+
includeVisual: args.includeVisual ?? true,
|
|
81
|
+
includeStyles: args.includeStyles ?? true,
|
|
82
|
+
includeLayout: args.includeLayout ?? true,
|
|
83
|
+
waitForTimeout: args.waitForTimeout ?? 0,
|
|
84
|
+
});
|
|
85
|
+
// Save reports into a timestamped run directory
|
|
86
|
+
const baseDir = args.outputDir ?? "./pixelprobe-output";
|
|
87
|
+
const timestamp = new Date()
|
|
88
|
+
.toISOString()
|
|
89
|
+
.replace(/[:.]/g, "-")
|
|
90
|
+
.replace("T", "_")
|
|
91
|
+
.slice(0, 19);
|
|
92
|
+
const runDir = `${baseDir}/run_${timestamp}_${result.id}`;
|
|
93
|
+
const jsonPath = await JSONReporter.generate(result, runDir);
|
|
94
|
+
const htmlPath = await HTMLReporter.generate(result, runDir);
|
|
95
|
+
result.artifacts.jsonPath = jsonPath;
|
|
96
|
+
result.artifacts.htmlReportPath = htmlPath;
|
|
97
|
+
// Return compact summary for the agent
|
|
98
|
+
const summary = JSONReporter.toCompactSummary(result);
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: JSON.stringify(summary, null, 2),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: `\n---\nReports saved:\n JSON: ${jsonPath}\n HTML: ${htmlPath}`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: `Error during comparison: ${err.message}`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// ── Tool: capture_section ───────────────────────────────────────────
|
|
125
|
+
server.tool("capture_section", "Capture information about a section on a web page — its computed styles, layout metrics, " +
|
|
126
|
+
"and a screenshot. Useful for inspecting a single page before running a comparison.", {
|
|
127
|
+
url: z.string().url().describe("URL of the page to inspect"),
|
|
128
|
+
selector: z.string().min(1).describe("CSS selector for the section"),
|
|
129
|
+
elementIndex: z
|
|
130
|
+
.number()
|
|
131
|
+
.int()
|
|
132
|
+
.min(0)
|
|
133
|
+
.optional()
|
|
134
|
+
.describe("Element index if selector matches multiple (default: 0)"),
|
|
135
|
+
breakpoint: z
|
|
136
|
+
.string()
|
|
137
|
+
.optional()
|
|
138
|
+
.describe('Breakpoint to use. Default: "desktop"'),
|
|
139
|
+
}, async (args) => {
|
|
140
|
+
const comparator = new WebSectionComparator({ headless: true });
|
|
141
|
+
try {
|
|
142
|
+
// First enumerate to show what's available
|
|
143
|
+
const elements = await comparator.enumerate(args.url, args.selector, args.breakpoint);
|
|
144
|
+
if (elements.length === 0) {
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `No elements found matching "${args.selector}" on ${args.url}`,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const elementList = elements
|
|
156
|
+
.map((el) => ` [${el.index}] <${el.tagName}> ${el.boundingBox.width}×${el.boundingBox.height} at (${el.boundingBox.x},${el.boundingBox.y}) — "${el.textPreview}"`)
|
|
157
|
+
.join("\n");
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `Found ${elements.length} element(s) matching "${args.selector}":\n${elementList}\n\nUse elementIndex to select a specific one for comparison.`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `Error: ${err.message}`,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
isError: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// ── Tool: list_breakpoints ──────────────────────────────────────────
|
|
180
|
+
server.tool("list_breakpoints", "List all available responsive breakpoint presets with their dimensions.", {}, async () => {
|
|
181
|
+
const presets = Object.entries(PRESET_BREAKPOINTS)
|
|
182
|
+
.map(([name, bp]) => ` ${name.padEnd(14)} ${bp.width}×${bp.height} ${bp.label}`)
|
|
183
|
+
.join("\n");
|
|
184
|
+
return {
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: `Available breakpoint presets:\n${presets}\n\nYou can also use custom WIDTHxHEIGHT format (e.g. "1440x900").`,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
// ── Start Server ────────────────────────────────────────────────────
|
|
194
|
+
async function main() {
|
|
195
|
+
const transport = new StdioServerTransport();
|
|
196
|
+
await server.connect(transport);
|
|
197
|
+
console.error("pixelprobe MCP server running on stdio");
|
|
198
|
+
}
|
|
199
|
+
main().catch((err) => {
|
|
200
|
+
console.error("Fatal error:", err);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComparisonResult } from "../types/comparison.js";
|
|
2
|
+
export declare class HTMLReporter {
|
|
3
|
+
/**
|
|
4
|
+
* Generate a self-contained interactive HTML report.
|
|
5
|
+
*/
|
|
6
|
+
static generate(result: ComparisonResult, outputDir: string): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=HTMLReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HTMLReporter.d.ts","sourceRoot":"","sources":["../../src/reporter/HTMLReporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,wBAAwB,CAAC;AAEjF,qBAAa,YAAY;IACvB;;OAEG;WACU,QAAQ,CACnB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC;CA2BnB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export class HTMLReporter {
|
|
4
|
+
/**
|
|
5
|
+
* Generate a self-contained interactive HTML report.
|
|
6
|
+
*/
|
|
7
|
+
static async generate(result, outputDir) {
|
|
8
|
+
await mkdir(outputDir, { recursive: true });
|
|
9
|
+
// Save screenshots and diff images, collect base64 for embedding
|
|
10
|
+
const imageData = {};
|
|
11
|
+
for (const [bpName, bpResult] of Object.entries(result.breakpoints)) {
|
|
12
|
+
imageData[bpName] = {};
|
|
13
|
+
if (bpResult.visual?.diffImage) {
|
|
14
|
+
imageData[bpName].diff = bufferToDataUrl(bpResult.visual.diffImage);
|
|
15
|
+
// Also save to disk
|
|
16
|
+
const diffPath = join(outputDir, `diff-${bpName}.png`);
|
|
17
|
+
await writeFile(diffPath, bpResult.visual.diffImage);
|
|
18
|
+
result.artifacts.diffImages[bpName] = diffPath;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const html = buildHTML(result, imageData);
|
|
22
|
+
const htmlPath = join(outputDir, `report-${result.id}.html`);
|
|
23
|
+
await writeFile(htmlPath, html);
|
|
24
|
+
return htmlPath;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function bufferToDataUrl(buf) {
|
|
28
|
+
return `data:image/png;base64,${buf.toString("base64")}`;
|
|
29
|
+
}
|
|
30
|
+
function severityBadge(severity) {
|
|
31
|
+
const colors = {
|
|
32
|
+
critical: "#ef4444",
|
|
33
|
+
warning: "#f59e0b",
|
|
34
|
+
info: "#3b82f6",
|
|
35
|
+
pass: "#22c55e",
|
|
36
|
+
};
|
|
37
|
+
const color = colors[severity] || "#6b7280";
|
|
38
|
+
return `<span style="background:${color};color:white;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600;text-transform:uppercase">${severity}</span>`;
|
|
39
|
+
}
|
|
40
|
+
function buildHTML(result, imageData) {
|
|
41
|
+
const breakpointTabs = Object.entries(result.breakpoints)
|
|
42
|
+
.map(([name, bp], i) => `
|
|
43
|
+
<button class="tab ${i === 0 ? "active" : ""}" onclick="showTab('${name}', this)">
|
|
44
|
+
${name} (${bp.breakpoint.width}px)
|
|
45
|
+
${bp.error ? "⚠️" : bp.styles?.summary.critical ? "🔴" : bp.styles?.summary.warning ? "🟡" : "🟢"}
|
|
46
|
+
</button>`)
|
|
47
|
+
.join("");
|
|
48
|
+
const breakpointPanels = Object.entries(result.breakpoints)
|
|
49
|
+
.map(([name, bp], i) => `
|
|
50
|
+
<div class="panel ${i === 0 ? "active" : ""}" id="panel-${name}">
|
|
51
|
+
${bp.error ? `<div class="error">Error: ${escapeHtml(bp.error)}</div>` : buildBreakpointPanel(name, bp, imageData[name])}
|
|
52
|
+
</div>`)
|
|
53
|
+
.join("");
|
|
54
|
+
return `<!DOCTYPE html>
|
|
55
|
+
<html lang="en">
|
|
56
|
+
<head>
|
|
57
|
+
<meta charset="UTF-8">
|
|
58
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
59
|
+
<title>Selection Comparison Report — ${result.id}</title>
|
|
60
|
+
<style>
|
|
61
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
62
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #e2e8f0; padding: 24px; }
|
|
63
|
+
.container { max-width: 1400px; margin: 0 auto; }
|
|
64
|
+
h1 { font-size: 24px; margin-bottom: 8px; }
|
|
65
|
+
.meta { color: #94a3b8; font-size: 14px; margin-bottom: 24px; }
|
|
66
|
+
.meta a { color: #60a5fa; }
|
|
67
|
+
.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
|
|
68
|
+
.summary-card { background: #1e293b; border-radius: 8px; padding: 16px; }
|
|
69
|
+
.summary-card .label { font-size: 12px; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
70
|
+
.summary-card .value { font-size: 28px; font-weight: 700; margin-top: 4px; }
|
|
71
|
+
.tabs { display: flex; gap: 4px; border-bottom: 2px solid #1e293b; margin-bottom: 24px; overflow-x: auto; overflow-y: hidden; }
|
|
72
|
+
.tab { background: none; border: none; color: #94a3b8; padding: 10px 16px; cursor: pointer; font-size: 14px; border-bottom: 2px solid transparent; margin-bottom: -2px; white-space: nowrap; }
|
|
73
|
+
.tab:hover { color: #e2e8f0; }
|
|
74
|
+
.tab.active { color: #60a5fa; border-bottom-color: #60a5fa; }
|
|
75
|
+
.panel { display: none; }
|
|
76
|
+
.panel.active { display: block; }
|
|
77
|
+
.section { background: #1e293b; border-radius: 8px; padding: 20px; margin-bottom: 16px; }
|
|
78
|
+
.section h3 { font-size: 16px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
|
|
79
|
+
.diff-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
80
|
+
.diff-table th { text-align: left; padding: 8px 12px; border-bottom: 1px solid #334155; color: #94a3b8; font-weight: 500; }
|
|
81
|
+
.diff-table td { padding: 8px 12px; border-bottom: 1px solid #1e293b; font-family: "SF Mono", Monaco, monospace; font-size: 12px; }
|
|
82
|
+
.diff-table tr:hover { background: #334155; }
|
|
83
|
+
.severity-critical { color: #ef4444; }
|
|
84
|
+
.severity-warning { color: #f59e0b; }
|
|
85
|
+
.severity-info { color: #3b82f6; }
|
|
86
|
+
.diff-img { max-width: 100%; border-radius: 4px; border: 1px solid #334155; }
|
|
87
|
+
.error { background: #451a1a; border: 1px solid #ef4444; border-radius: 8px; padding: 16px; color: #fca5a5; }
|
|
88
|
+
.pixel-stats { display: flex; gap: 24px; margin-bottom: 12px; font-size: 14px; }
|
|
89
|
+
.pixel-stats .stat { color: #94a3b8; }
|
|
90
|
+
.pixel-stats .stat strong { color: #e2e8f0; }
|
|
91
|
+
.category-tag { background: #334155; color: #94a3b8; padding: 2px 6px; border-radius: 3px; font-size: 11px; }
|
|
92
|
+
</style>
|
|
93
|
+
</head>
|
|
94
|
+
<body>
|
|
95
|
+
<div class="container">
|
|
96
|
+
<h1>Selection Comparison Report</h1>
|
|
97
|
+
<div class="meta">
|
|
98
|
+
<strong>Source:</strong> <a href="${escapeHtml(result.config.sourceUrl)}">${escapeHtml(result.config.sourceUrl)}</a> · <code>${escapeHtml(result.config.selector)}</code><br>
|
|
99
|
+
<strong>Target:</strong> <a href="${escapeHtml(result.config.targetUrl)}">${escapeHtml(result.config.targetUrl)}</a> · <code>${escapeHtml(result.config.targetSelector ?? result.config.selector)}</code><br>
|
|
100
|
+
<strong>Duration:</strong> ${(result.duration / 1000).toFixed(1)}s ·
|
|
101
|
+
<strong>Generated:</strong> ${result.timestamp}
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="summary-grid">
|
|
105
|
+
<div class="summary-card">
|
|
106
|
+
<div class="label">Overall</div>
|
|
107
|
+
<div class="value">${severityBadge(result.summary.overallSeverity)}</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="summary-card">
|
|
110
|
+
<div class="label">Breakpoints with Differences</div>
|
|
111
|
+
<div class="value">${result.summary.breakpointsWithDifferences}/${result.summary.totalBreakpoints}</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="summary-card">
|
|
114
|
+
<div class="label">Style Differences</div>
|
|
115
|
+
<div class="value">${result.summary.totalStyleDifferences}</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="summary-card">
|
|
118
|
+
<div class="label">Avg Pixel Diff</div>
|
|
119
|
+
<div class="value">${result.summary.averagePixelDiff}%</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="summary-card">
|
|
122
|
+
<div class="label">Worst Breakpoint</div>
|
|
123
|
+
<div class="value" style="font-size:18px">${result.summary.worstBreakpoint ?? "None"}</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div class="tabs">${breakpointTabs}</div>
|
|
128
|
+
${breakpointPanels}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<script>
|
|
132
|
+
function showTab(name, btn) {
|
|
133
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
134
|
+
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
|
135
|
+
btn.classList.add('active');
|
|
136
|
+
document.getElementById('panel-' + name).classList.add('active');
|
|
137
|
+
}
|
|
138
|
+
</script>
|
|
139
|
+
</body>
|
|
140
|
+
</html>`;
|
|
141
|
+
}
|
|
142
|
+
function buildBreakpointPanel(name, bp, images) {
|
|
143
|
+
let html = "";
|
|
144
|
+
// Visual diff
|
|
145
|
+
if (bp.visual) {
|
|
146
|
+
html += `
|
|
147
|
+
<div class="section">
|
|
148
|
+
<h3>Visual Comparison ${severityBadge(bp.visual.diffPercentage > 5 ? "critical" : bp.visual.diffPercentage > 1 ? "warning" : bp.visual.diffPercentage > 0 ? "info" : "pass")}</h3>
|
|
149
|
+
<div class="pixel-stats">
|
|
150
|
+
<div class="stat">Pixels changed: <strong>${bp.visual.diffPixelCount.toLocaleString()}</strong> / ${bp.visual.totalPixels.toLocaleString()}</div>
|
|
151
|
+
<div class="stat">Diff: <strong>${bp.visual.diffPercentage}%</strong></div>
|
|
152
|
+
<div class="stat">Source size: <strong>${bp.visual.sourceWidth}×${bp.visual.sourceHeight}</strong></div>
|
|
153
|
+
<div class="stat">Target size: <strong>${bp.visual.targetWidth}×${bp.visual.targetHeight}</strong></div>
|
|
154
|
+
${bp.visual.sizeMismatch ? '<div class="stat severity-warning">⚠ Size mismatch</div>' : ""}
|
|
155
|
+
</div>
|
|
156
|
+
${images?.diff ? `<img class="diff-img" src="${images.diff}" alt="Visual diff for ${name}">` : ""}
|
|
157
|
+
</div>`;
|
|
158
|
+
}
|
|
159
|
+
// Style diffs
|
|
160
|
+
if (bp.styles && bp.styles.differences.length > 0) {
|
|
161
|
+
html += `
|
|
162
|
+
<div class="section">
|
|
163
|
+
<h3>Style Differences (${bp.styles.summary.total}) ${severityBadge(bp.styles.summary.critical > 0 ? "critical" : bp.styles.summary.warning > 0 ? "warning" : "info")}</h3>
|
|
164
|
+
<table class="diff-table">
|
|
165
|
+
<thead><tr><th>Property</th><th>Category</th><th>Source</th><th>Target</th><th>Severity</th></tr></thead>
|
|
166
|
+
<tbody>
|
|
167
|
+
${bp.styles.differences
|
|
168
|
+
.map((d) => `
|
|
169
|
+
<tr>
|
|
170
|
+
<td><strong>${escapeHtml(d.property)}</strong></td>
|
|
171
|
+
<td><span class="category-tag">${escapeHtml(d.category)}</span></td>
|
|
172
|
+
<td>${escapeHtml(d.sourceValue)}</td>
|
|
173
|
+
<td>${escapeHtml(d.targetValue)}</td>
|
|
174
|
+
<td class="severity-${d.severity}">${d.severity}</td>
|
|
175
|
+
</tr>`)
|
|
176
|
+
.join("")}
|
|
177
|
+
</tbody>
|
|
178
|
+
</table>
|
|
179
|
+
</div>`;
|
|
180
|
+
}
|
|
181
|
+
else if (bp.styles) {
|
|
182
|
+
html += `<div class="section"><h3>Style Differences ${severityBadge("pass")}</h3><p style="color:#94a3b8">No style differences found.</p></div>`;
|
|
183
|
+
}
|
|
184
|
+
// Layout diffs
|
|
185
|
+
if (bp.layout && bp.layout.differences.length > 0) {
|
|
186
|
+
html += `
|
|
187
|
+
<div class="section">
|
|
188
|
+
<h3>Layout Differences (${bp.layout.summary.total}) ${severityBadge(bp.layout.summary.critical > 0 ? "critical" : bp.layout.summary.warning > 0 ? "warning" : "info")}</h3>
|
|
189
|
+
<table class="diff-table">
|
|
190
|
+
<thead><tr><th>Property</th><th>Source</th><th>Target</th><th>Diff (px)</th><th>Diff (%)</th><th>Severity</th></tr></thead>
|
|
191
|
+
<tbody>
|
|
192
|
+
${bp.layout.differences
|
|
193
|
+
.map((d) => `
|
|
194
|
+
<tr>
|
|
195
|
+
<td><strong>${escapeHtml(d.property)}</strong></td>
|
|
196
|
+
<td>${d.sourceValue}px</td>
|
|
197
|
+
<td>${d.targetValue}px</td>
|
|
198
|
+
<td>${d.absoluteDiff > 0 ? "+" : ""}${d.absoluteDiff}px</td>
|
|
199
|
+
<td>${d.percentageDiff > 0 ? "+" : ""}${d.percentageDiff}%</td>
|
|
200
|
+
<td class="severity-${d.severity}">${d.severity}</td>
|
|
201
|
+
</tr>`)
|
|
202
|
+
.join("")}
|
|
203
|
+
</tbody>
|
|
204
|
+
</table>
|
|
205
|
+
</div>`;
|
|
206
|
+
}
|
|
207
|
+
else if (bp.layout) {
|
|
208
|
+
html += `<div class="section"><h3>Layout Differences ${severityBadge("pass")}</h3><p style="color:#94a3b8">No layout differences found.</p></div>`;
|
|
209
|
+
}
|
|
210
|
+
return html;
|
|
211
|
+
}
|
|
212
|
+
function escapeHtml(str) {
|
|
213
|
+
return str
|
|
214
|
+
.replace(/&/g, "&")
|
|
215
|
+
.replace(/</g, "<")
|
|
216
|
+
.replace(/>/g, ">")
|
|
217
|
+
.replace(/"/g, """);
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=HTMLReporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HTMLReporter.js","sourceRoot":"","sources":["../../src/reporter/HTMLReporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComparisonResult } from "../types/comparison.js";
|
|
2
|
+
export declare class JSONReporter {
|
|
3
|
+
/**
|
|
4
|
+
* Generate a JSON report file.
|
|
5
|
+
* Strips binary data (screenshots, diff images) and replaces with file paths.
|
|
6
|
+
*/
|
|
7
|
+
static generate(result: ComparisonResult, outputDir: string): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Convert result to a JSON-safe object (no Buffers).
|
|
10
|
+
*/
|
|
11
|
+
static toSerializable(result: ComparisonResult): any;
|
|
12
|
+
/**
|
|
13
|
+
* Generate a compact JSON summary (no per-property details).
|
|
14
|
+
*/
|
|
15
|
+
static toCompactSummary(result: ComparisonResult): object;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=JSONReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JSONReporter.d.ts","sourceRoot":"","sources":["../../src/reporter/JSONReporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,qBAAa,YAAY;IACvB;;;OAGG;WACU,QAAQ,CACnB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC;IAwBlB;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,GAAG;IAuBpD;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM;CA2B1D"}
|