@vizzly-testing/cli 0.29.6 → 0.31.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.
Files changed (87) hide show
  1. package/README.md +34 -6
  2. package/dist/api/client.js +6 -3
  3. package/dist/api/endpoints.js +59 -0
  4. package/dist/api/index.js +1 -1
  5. package/dist/auth/core.js +1 -1
  6. package/dist/auth/index.js +2 -1
  7. package/dist/cli.js +206 -51
  8. package/dist/client/index.js +21 -29
  9. package/dist/commands/api.js +145 -90
  10. package/dist/commands/baselines.js +78 -47
  11. package/dist/commands/builds.js +9 -8
  12. package/dist/commands/comparisons.js +7 -8
  13. package/dist/commands/config-cmd.js +15 -7
  14. package/dist/commands/context.js +870 -0
  15. package/dist/commands/doctor.js +116 -81
  16. package/dist/commands/init.js +202 -201
  17. package/dist/commands/login.js +134 -65
  18. package/dist/commands/logout.js +18 -7
  19. package/dist/commands/preview.js +29 -57
  20. package/dist/commands/project.js +120 -0
  21. package/dist/commands/projects.js +9 -2
  22. package/dist/commands/review.js +236 -202
  23. package/dist/commands/run.js +75 -72
  24. package/dist/commands/status.js +228 -150
  25. package/dist/commands/tdd-daemon-process.js +98 -0
  26. package/dist/commands/tdd-daemon.js +318 -212
  27. package/dist/commands/tdd.js +23 -8
  28. package/dist/commands/upload.js +10 -9
  29. package/dist/commands/whoami.js +64 -26
  30. package/dist/config/core.js +37 -36
  31. package/dist/context/local-workspace-provider.js +532 -0
  32. package/dist/context/provider-resolver.js +36 -0
  33. package/dist/index.js +5 -4
  34. package/dist/plugin-loader.js +55 -49
  35. package/dist/project/core.js +2 -2
  36. package/dist/project/operations.js +6 -3
  37. package/dist/reporter/reporter-bundle.css +2 -1
  38. package/dist/reporter/reporter-bundle.iife.js +119 -119
  39. package/dist/reporter-ssr/ssr-entry.js +605 -509
  40. package/dist/sdk/index.js +162 -107
  41. package/dist/server/handlers/tdd-handler.js +30 -54
  42. package/dist/server/routers/assets.js +4 -0
  43. package/dist/server/routers/baseline.js +195 -178
  44. package/dist/server/routers/events.js +226 -148
  45. package/dist/server/routers/projects.js +14 -5
  46. package/dist/server/routers/screenshot.js +1 -31
  47. package/dist/services/build-manager.js +19 -97
  48. package/dist/services/config-service.js +26 -34
  49. package/dist/services/index.js +2 -4
  50. package/dist/services/static-report-generator.js +11 -12
  51. package/dist/services/test-runner.js +21 -27
  52. package/dist/tdd/core/hotspot-coverage.js +1 -1
  53. package/dist/tdd/index.js +1 -1
  54. package/dist/tdd/metadata/region-metadata.js +0 -37
  55. package/dist/tdd/runtime-context.js +78 -0
  56. package/dist/tdd/server-registry.js +38 -17
  57. package/dist/tdd/services/baseline-manager.js +23 -0
  58. package/dist/tdd/services/hotspot-service.js +1 -14
  59. package/dist/tdd/tdd-service.js +82 -113
  60. package/dist/types/client.d.ts +36 -1
  61. package/dist/types/index.d.ts +114 -22
  62. package/dist/types/sdk.d.ts +47 -7
  63. package/dist/uploader/core.js +5 -4
  64. package/dist/uploader/index.js +79 -2
  65. package/dist/uploader/operations.js +40 -5
  66. package/dist/utils/api-url.js +14 -0
  67. package/dist/utils/async-utils.js +26 -0
  68. package/dist/utils/ci-env.js +19 -12
  69. package/dist/utils/colors.js +46 -44
  70. package/dist/utils/config-loader.js +39 -65
  71. package/dist/utils/config-schema.js +19 -55
  72. package/dist/utils/fetch-utils.js +12 -5
  73. package/dist/utils/git.js +35 -72
  74. package/dist/utils/global-config.js +22 -3
  75. package/dist/utils/output.js +19 -7
  76. package/dist/utils/patterns.js +14 -0
  77. package/dist/utils/project-link-store.js +105 -0
  78. package/dist/utils/report-data.js +31 -0
  79. package/dist/utils/screenshot-options.js +28 -0
  80. package/dist/utils/secret-store.js +65 -0
  81. package/dist/utils/security.js +3 -2
  82. package/package.json +14 -11
  83. package/dist/services/screenshot-server.js +0 -65
  84. package/dist/services/server-manager.js +0 -81
  85. package/dist/services/uploader.js +0 -100
  86. package/dist/tdd/services/region-service.js +0 -75
  87. package/dist/utils/build-history.js +0 -103
@@ -1,86 +1,98 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
1
  import { renderToString } from "react-dom/server";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/utils/status-utils.js
3
4
  function isNewComparisonStatus(status) {
4
- return status === "new" || status === "baseline-created";
5
+ return status === "new" || status === "baseline-created";
5
6
  }
6
- const statusConfig = {
7
- failed: {
8
- label: "Changed",
9
- borderClass: "border-l-red-500",
10
- badgeClass: "bg-red-500/15 text-red-400 ring-red-500/30",
11
- dotClass: "bg-red-500",
12
- sectionTitle: "Visual Changes",
13
- sectionIcon: "◐"
14
- },
15
- new: {
16
- label: "New",
17
- borderClass: "border-l-blue-500",
18
- badgeClass: "bg-blue-500/15 text-blue-400 ring-blue-500/30",
19
- dotClass: "bg-blue-500",
20
- sectionTitle: "New Screenshots",
21
- sectionIcon: "+"
22
- },
23
- passed: {
24
- label: "Passed",
25
- borderClass: "border-l-emerald-500",
26
- badgeClass: "bg-emerald-500/15 text-emerald-400 ring-emerald-500/30",
27
- dotClass: "bg-emerald-500",
28
- sectionTitle: "Passed",
29
- sectionIcon: "✓"
30
- },
31
- error: {
32
- label: "Error",
33
- borderClass: "border-l-orange-500",
34
- badgeClass: "bg-orange-500/15 text-orange-400 ring-orange-500/30",
35
- dotClass: "bg-orange-500",
36
- sectionTitle: "Errors",
37
- sectionIcon: "!"
38
- }
7
+ //#endregion
8
+ //#region src/components/static-report-view.jsx
9
+ /**
10
+ * Static Report View
11
+ *
12
+ * A polished, hook-free component for SSR static report generation.
13
+ * Uses native HTML5 <details>/<summary> for expand/collapse without JavaScript.
14
+ * Designed for quick visual scanning with grouped status sections.
15
+ */
16
+ var statusConfig = {
17
+ failed: {
18
+ label: "Changed",
19
+ borderClass: "border-l-red-500",
20
+ badgeClass: "bg-red-500/15 text-red-400 ring-red-500/30",
21
+ dotClass: "bg-red-500",
22
+ sectionTitle: "Visual Changes",
23
+ sectionIcon: "◐"
24
+ },
25
+ new: {
26
+ label: "New",
27
+ borderClass: "border-l-blue-500",
28
+ badgeClass: "bg-blue-500/15 text-blue-400 ring-blue-500/30",
29
+ dotClass: "bg-blue-500",
30
+ sectionTitle: "New Screenshots",
31
+ sectionIcon: "+"
32
+ },
33
+ passed: {
34
+ label: "Passed",
35
+ borderClass: "border-l-emerald-500",
36
+ badgeClass: "bg-emerald-500/15 text-emerald-400 ring-emerald-500/30",
37
+ dotClass: "bg-emerald-500",
38
+ sectionTitle: "Passed",
39
+ sectionIcon: "✓"
40
+ },
41
+ error: {
42
+ label: "Error",
43
+ borderClass: "border-l-orange-500",
44
+ badgeClass: "bg-orange-500/15 text-orange-400 ring-orange-500/30",
45
+ dotClass: "bg-orange-500",
46
+ sectionTitle: "Errors",
47
+ sectionIcon: "!"
48
+ }
39
49
  };
40
50
  function StatusBadge({ status }) {
41
- let config = statusConfig[status] || statusConfig.error;
42
- return /* @__PURE__ */ jsxs(
43
- "span",
44
- {
45
- className: `inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium ring-1 ring-inset ${config.badgeClass}`,
46
- children: [
47
- /* @__PURE__ */ jsx("span", { className: `w-1.5 h-1.5 rounded-full ${config.dotClass}` }),
48
- config.label
49
- ]
50
- }
51
- );
51
+ let config = statusConfig[status] || statusConfig.error;
52
+ return /* @__PURE__ */ jsxs("span", {
53
+ className: `inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium ring-1 ring-inset ${config.badgeClass}`,
54
+ children: [/* @__PURE__ */ jsx("span", { className: `w-1.5 h-1.5 rounded-full ${config.dotClass}` }), config.label]
55
+ });
52
56
  }
53
57
  function DiffBadge({ percentage }) {
54
- if (percentage === void 0 || percentage === null || percentage === 0)
55
- return null;
56
- return /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-red-400/80 tabular-nums", children: [
57
- percentage.toFixed(2),
58
- "%"
59
- ] });
58
+ if (percentage === void 0 || percentage === null || percentage === 0) return null;
59
+ return /* @__PURE__ */ jsxs("span", {
60
+ className: "font-mono text-xs text-red-400/80 tabular-nums",
61
+ children: [percentage.toFixed(2), "%"]
62
+ });
60
63
  }
61
64
  function MetaInfo({ properties }) {
62
- if (!properties) return null;
63
- let { viewport_width, viewport_height, browser } = properties;
64
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500", children: [
65
- viewport_width && viewport_height && /* @__PURE__ */ jsxs("span", { className: "font-mono tabular-nums", children: [
66
- viewport_width,
67
- "×",
68
- viewport_height
69
- ] }),
70
- browser && /* @__PURE__ */ jsxs(Fragment, { children: [
71
- /* @__PURE__ */ jsx("span", { className: "text-slate-600", children: "·" }),
72
- /* @__PURE__ */ jsx("span", { className: "capitalize", children: browser })
73
- ] })
74
- ] });
65
+ if (!properties) return null;
66
+ let { viewport_width, viewport_height, browser } = properties;
67
+ return /* @__PURE__ */ jsxs("div", {
68
+ className: "flex items-center gap-2 text-xs text-slate-500",
69
+ children: [viewport_width && viewport_height && /* @__PURE__ */ jsxs("span", {
70
+ className: "font-mono tabular-nums",
71
+ children: [
72
+ viewport_width,
73
+ "×",
74
+ viewport_height
75
+ ]
76
+ }), browser && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
77
+ className: "text-slate-600",
78
+ children: "·"
79
+ }), /* @__PURE__ */ jsx("span", {
80
+ className: "capitalize",
81
+ children: browser
82
+ })] })]
83
+ });
75
84
  }
85
+ /**
86
+ * Comparison item for failed/changed screenshots
87
+ * Expandable with side-by-side comparison view
88
+ */
76
89
  function FailedComparison({ comparison, isEven }) {
77
- let { name, status, current, baseline, diff, diffPercentage, properties } = comparison;
78
- let config = statusConfig[status] || statusConfig.failed;
79
- return /* @__PURE__ */ jsxs("details", { className: "group", children: [
80
- /* @__PURE__ */ jsxs(
81
- "summary",
82
- {
83
- className: `
90
+ let { name, status, current, baseline, diff, diffPercentage, properties } = comparison;
91
+ let config = statusConfig[status] || statusConfig.failed;
92
+ return /* @__PURE__ */ jsxs("details", {
93
+ className: "group",
94
+ children: [/* @__PURE__ */ jsxs("summary", {
95
+ className: `
84
96
  flex items-center gap-4 p-4 cursor-pointer
85
97
  ${isEven ? "bg-slate-800/30" : "bg-slate-800/50"}
86
98
  hover:bg-slate-700/50
@@ -89,125 +101,131 @@ function FailedComparison({ comparison, isEven }) {
89
101
  list-none [&::-webkit-details-marker]:hidden
90
102
  focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900
91
103
  `,
92
- children: [
93
- /* @__PURE__ */ jsxs("div", { className: "relative w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900", children: [
94
- (current || baseline) && /* @__PURE__ */ jsx(
95
- "img",
96
- {
97
- src: current || baseline,
98
- alt: "",
99
- className: "w-full h-full object-cover object-top"
100
- }
101
- ),
102
- diff && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-red-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "w-2 h-2 bg-red-500 rounded-full animate-pulse" }) })
103
- ] }),
104
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
105
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
106
- /* @__PURE__ */ jsx("span", { className: "font-medium text-white truncate", children: name }),
107
- /* @__PURE__ */ jsx(DiffBadge, { percentage: diffPercentage })
108
- ] }),
109
- /* @__PURE__ */ jsx(MetaInfo, { properties })
110
- ] }),
111
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
112
- /* @__PURE__ */ jsx(StatusBadge, { status }),
113
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors", children: [
114
- /* @__PURE__ */ jsx("span", { className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity", children: "Details" }),
115
- /* @__PURE__ */ jsx(
116
- "svg",
117
- {
118
- className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
119
- fill: "none",
120
- viewBox: "0 0 24 24",
121
- stroke: "currentColor",
122
- "aria-hidden": "true",
123
- children: /* @__PURE__ */ jsx(
124
- "path",
125
- {
126
- strokeLinecap: "round",
127
- strokeLinejoin: "round",
128
- strokeWidth: 2,
129
- d: "M19 9l-7 7-7-7"
130
- }
131
- )
132
- }
133
- )
134
- ] })
135
- ] })
136
- ]
137
- }
138
- ),
139
- /* @__PURE__ */ jsxs("div", { className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]", children: [
140
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4", children: [
141
- baseline && /* @__PURE__ */ jsxs("div", { children: [
142
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
143
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider", children: "Baseline" }),
144
- /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "Expected" })
145
- ] }),
146
- /* @__PURE__ */ jsx(
147
- "a",
148
- {
149
- href: baseline,
150
- target: "_blank",
151
- rel: "noopener noreferrer",
152
- className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
153
- children: /* @__PURE__ */ jsx(
154
- "img",
155
- {
156
- src: baseline,
157
- alt: `${name} baseline`,
158
- className: "w-full"
159
- }
160
- )
161
- }
162
- )
163
- ] }),
164
- current && /* @__PURE__ */ jsxs("div", { children: [
165
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
166
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider", children: "Current" }),
167
- /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "Actual" })
168
- ] }),
169
- /* @__PURE__ */ jsx(
170
- "a",
171
- {
172
- href: current,
173
- target: "_blank",
174
- rel: "noopener noreferrer",
175
- className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
176
- children: /* @__PURE__ */ jsx("img", { src: current, alt: `${name} current`, className: "w-full" })
177
- }
178
- )
179
- ] })
180
- ] }),
181
- diff && /* @__PURE__ */ jsxs("div", { className: "mt-4 pt-4 border-t border-slate-700/30", children: [
182
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
183
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-red-400 uppercase tracking-wider", children: "Difference" }),
184
- diffPercentage > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-red-400/60 font-mono", children: [
185
- diffPercentage.toFixed(2),
186
- "% of pixels changed"
187
- ] })
188
- ] }),
189
- /* @__PURE__ */ jsx(
190
- "a",
191
- {
192
- href: diff,
193
- target: "_blank",
194
- rel: "noopener noreferrer",
195
- className: "block rounded-lg overflow-hidden border border-red-500/30 hover:border-red-500/50 transition-colors max-w-2xl",
196
- children: /* @__PURE__ */ jsx("img", { src: diff, alt: `${name} diff`, className: "w-full" })
197
- }
198
- )
199
- ] })
200
- ] })
201
- ] });
104
+ children: [
105
+ /* @__PURE__ */ jsxs("div", {
106
+ className: "relative w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900",
107
+ children: [(current || baseline) && /* @__PURE__ */ jsx("img", {
108
+ src: current || baseline,
109
+ alt: "",
110
+ className: "w-full h-full object-cover object-top"
111
+ }), diff && /* @__PURE__ */ jsx("div", {
112
+ className: "absolute inset-0 bg-red-500/20 flex items-center justify-center",
113
+ children: /* @__PURE__ */ jsx("span", { className: "w-2 h-2 bg-red-500 rounded-full animate-pulse" })
114
+ })]
115
+ }),
116
+ /* @__PURE__ */ jsxs("div", {
117
+ className: "flex-1 min-w-0",
118
+ children: [/* @__PURE__ */ jsxs("div", {
119
+ className: "flex items-center gap-2",
120
+ children: [/* @__PURE__ */ jsx("span", {
121
+ className: "font-medium text-white truncate",
122
+ children: name
123
+ }), /* @__PURE__ */ jsx(DiffBadge, { percentage: diffPercentage })]
124
+ }), /* @__PURE__ */ jsx(MetaInfo, { properties })]
125
+ }),
126
+ /* @__PURE__ */ jsxs("div", {
127
+ className: "flex items-center gap-3",
128
+ children: [/* @__PURE__ */ jsx(StatusBadge, { status }), /* @__PURE__ */ jsxs("div", {
129
+ className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors",
130
+ children: [/* @__PURE__ */ jsx("span", {
131
+ className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity",
132
+ children: "Details"
133
+ }), /* @__PURE__ */ jsx("svg", {
134
+ className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
135
+ fill: "none",
136
+ viewBox: "0 0 24 24",
137
+ stroke: "currentColor",
138
+ "aria-hidden": "true",
139
+ children: /* @__PURE__ */ jsx("path", {
140
+ strokeLinecap: "round",
141
+ strokeLinejoin: "round",
142
+ strokeWidth: 2,
143
+ d: "M19 9l-7 7-7-7"
144
+ })
145
+ })]
146
+ })]
147
+ })
148
+ ]
149
+ }), /* @__PURE__ */ jsxs("div", {
150
+ className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]",
151
+ children: [/* @__PURE__ */ jsxs("div", {
152
+ className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
153
+ children: [baseline && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
154
+ className: "flex items-center gap-2 mb-2",
155
+ children: [/* @__PURE__ */ jsx("span", {
156
+ className: "text-xs font-medium text-slate-400 uppercase tracking-wider",
157
+ children: "Baseline"
158
+ }), /* @__PURE__ */ jsx("span", {
159
+ className: "text-xs text-slate-600",
160
+ children: "Expected"
161
+ })]
162
+ }), /* @__PURE__ */ jsx("a", {
163
+ href: baseline,
164
+ target: "_blank",
165
+ rel: "noopener noreferrer",
166
+ className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
167
+ children: /* @__PURE__ */ jsx("img", {
168
+ src: baseline,
169
+ alt: `${name} baseline`,
170
+ className: "w-full"
171
+ })
172
+ })] }), current && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
173
+ className: "flex items-center gap-2 mb-2",
174
+ children: [/* @__PURE__ */ jsx("span", {
175
+ className: "text-xs font-medium text-slate-400 uppercase tracking-wider",
176
+ children: "Current"
177
+ }), /* @__PURE__ */ jsx("span", {
178
+ className: "text-xs text-slate-600",
179
+ children: "Actual"
180
+ })]
181
+ }), /* @__PURE__ */ jsx("a", {
182
+ href: current,
183
+ target: "_blank",
184
+ rel: "noopener noreferrer",
185
+ className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
186
+ children: /* @__PURE__ */ jsx("img", {
187
+ src: current,
188
+ alt: `${name} current`,
189
+ className: "w-full"
190
+ })
191
+ })] })]
192
+ }), diff && /* @__PURE__ */ jsxs("div", {
193
+ className: "mt-4 pt-4 border-t border-slate-700/30",
194
+ children: [/* @__PURE__ */ jsxs("div", {
195
+ className: "flex items-center gap-2 mb-2",
196
+ children: [/* @__PURE__ */ jsx("span", {
197
+ className: "text-xs font-medium text-red-400 uppercase tracking-wider",
198
+ children: "Difference"
199
+ }), diffPercentage > 0 && /* @__PURE__ */ jsxs("span", {
200
+ className: "text-xs text-red-400/60 font-mono",
201
+ children: [diffPercentage.toFixed(2), "% of pixels changed"]
202
+ })]
203
+ }), /* @__PURE__ */ jsx("a", {
204
+ href: diff,
205
+ target: "_blank",
206
+ rel: "noopener noreferrer",
207
+ className: "block rounded-lg overflow-hidden border border-red-500/30 hover:border-red-500/50 transition-colors max-w-2xl",
208
+ children: /* @__PURE__ */ jsx("img", {
209
+ src: diff,
210
+ alt: `${name} diff`,
211
+ className: "w-full"
212
+ })
213
+ })]
214
+ })]
215
+ })]
216
+ });
202
217
  }
218
+ /**
219
+ * Comparison item for new screenshots
220
+ * Expandable to show the new screenshot
221
+ */
203
222
  function NewComparison({ comparison, isEven }) {
204
- let { name, current, baseline, properties } = comparison;
205
- let imageSrc = current || baseline;
206
- return /* @__PURE__ */ jsxs("details", { className: "group", children: [
207
- /* @__PURE__ */ jsxs(
208
- "summary",
209
- {
210
- className: `
223
+ let { name, current, baseline, properties } = comparison;
224
+ let imageSrc = current || baseline;
225
+ return /* @__PURE__ */ jsxs("details", {
226
+ className: "group",
227
+ children: [/* @__PURE__ */ jsxs("summary", {
228
+ className: `
211
229
  flex items-center gap-4 p-4 cursor-pointer
212
230
  ${isEven ? "bg-slate-800/30" : "bg-slate-800/50"}
213
231
  hover:bg-slate-700/50
@@ -216,346 +234,424 @@ function NewComparison({ comparison, isEven }) {
216
234
  list-none [&::-webkit-details-marker]:hidden
217
235
  focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900
218
236
  `,
219
- children: [
220
- /* @__PURE__ */ jsx("div", { className: "w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900", children: imageSrc && /* @__PURE__ */ jsx(
221
- "img",
222
- {
223
- src: imageSrc,
224
- alt: "",
225
- className: "w-full h-full object-cover object-top"
226
- }
227
- ) }),
228
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
229
- /* @__PURE__ */ jsx("span", { className: "font-medium text-white truncate block", children: name }),
230
- /* @__PURE__ */ jsx(MetaInfo, { properties })
231
- ] }),
232
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
233
- /* @__PURE__ */ jsx(StatusBadge, { status: "new" }),
234
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors", children: [
235
- /* @__PURE__ */ jsx("span", { className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity", children: "Preview" }),
236
- /* @__PURE__ */ jsx(
237
- "svg",
238
- {
239
- className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
240
- fill: "none",
241
- viewBox: "0 0 24 24",
242
- stroke: "currentColor",
243
- "aria-hidden": "true",
244
- children: /* @__PURE__ */ jsx(
245
- "path",
246
- {
247
- strokeLinecap: "round",
248
- strokeLinejoin: "round",
249
- strokeWidth: 2,
250
- d: "M19 9l-7 7-7-7"
251
- }
252
- )
253
- }
254
- )
255
- ] })
256
- ] })
257
- ]
258
- }
259
- ),
260
- /* @__PURE__ */ jsxs("div", { className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]", children: [
261
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
262
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-blue-400 uppercase tracking-wider", children: "New Screenshot" }),
263
- /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "No baseline to compare" })
264
- ] }),
265
- imageSrc && /* @__PURE__ */ jsx(
266
- "a",
267
- {
268
- href: imageSrc,
269
- target: "_blank",
270
- rel: "noopener noreferrer",
271
- className: "block rounded-lg overflow-hidden border border-blue-500/30 hover:border-blue-500/50 transition-colors max-w-2xl",
272
- children: /* @__PURE__ */ jsx("img", { src: imageSrc, alt: name, className: "w-full" })
273
- }
274
- )
275
- ] })
276
- ] });
237
+ children: [
238
+ /* @__PURE__ */ jsx("div", {
239
+ className: "w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900",
240
+ children: imageSrc && /* @__PURE__ */ jsx("img", {
241
+ src: imageSrc,
242
+ alt: "",
243
+ className: "w-full h-full object-cover object-top"
244
+ })
245
+ }),
246
+ /* @__PURE__ */ jsxs("div", {
247
+ className: "flex-1 min-w-0",
248
+ children: [/* @__PURE__ */ jsx("span", {
249
+ className: "font-medium text-white truncate block",
250
+ children: name
251
+ }), /* @__PURE__ */ jsx(MetaInfo, { properties })]
252
+ }),
253
+ /* @__PURE__ */ jsxs("div", {
254
+ className: "flex items-center gap-3",
255
+ children: [/* @__PURE__ */ jsx(StatusBadge, { status: "new" }), /* @__PURE__ */ jsxs("div", {
256
+ className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors",
257
+ children: [/* @__PURE__ */ jsx("span", {
258
+ className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity",
259
+ children: "Preview"
260
+ }), /* @__PURE__ */ jsx("svg", {
261
+ className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
262
+ fill: "none",
263
+ viewBox: "0 0 24 24",
264
+ stroke: "currentColor",
265
+ "aria-hidden": "true",
266
+ children: /* @__PURE__ */ jsx("path", {
267
+ strokeLinecap: "round",
268
+ strokeLinejoin: "round",
269
+ strokeWidth: 2,
270
+ d: "M19 9l-7 7-7-7"
271
+ })
272
+ })]
273
+ })]
274
+ })
275
+ ]
276
+ }), /* @__PURE__ */ jsxs("div", {
277
+ className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]",
278
+ children: [/* @__PURE__ */ jsxs("div", {
279
+ className: "flex items-center gap-2 mb-2",
280
+ children: [/* @__PURE__ */ jsx("span", {
281
+ className: "text-xs font-medium text-blue-400 uppercase tracking-wider",
282
+ children: "New Screenshot"
283
+ }), /* @__PURE__ */ jsx("span", {
284
+ className: "text-xs text-slate-600",
285
+ children: "No baseline to compare"
286
+ })]
287
+ }), imageSrc && /* @__PURE__ */ jsx("a", {
288
+ href: imageSrc,
289
+ target: "_blank",
290
+ rel: "noopener noreferrer",
291
+ className: "block rounded-lg overflow-hidden border border-blue-500/30 hover:border-blue-500/50 transition-colors max-w-2xl",
292
+ children: /* @__PURE__ */ jsx("img", {
293
+ src: imageSrc,
294
+ alt: name,
295
+ className: "w-full"
296
+ })
297
+ })]
298
+ })]
299
+ });
277
300
  }
301
+ /**
302
+ * Compact passed item - no expand needed
303
+ */
278
304
  function PassedComparison({ comparison }) {
279
- let { name, properties } = comparison;
280
- return /* @__PURE__ */ jsxs(
281
- "div",
282
- {
283
- className: `
305
+ let { name, properties } = comparison;
306
+ return /* @__PURE__ */ jsxs("div", {
307
+ className: `
284
308
  flex items-center gap-3 px-4 py-2.5
285
309
  bg-slate-800/20 hover:bg-slate-800/30
286
310
  border-l-4 border-l-emerald-500/50
287
311
  rounded-r-lg transition-colors
288
312
  `,
289
- children: [
290
- /* @__PURE__ */ jsx("div", { className: "w-5 h-5 rounded-full bg-emerald-500/20 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
291
- "svg",
292
- {
293
- className: "w-3 h-3 text-emerald-400",
294
- fill: "currentColor",
295
- viewBox: "0 0 20 20",
296
- "aria-hidden": "true",
297
- children: /* @__PURE__ */ jsx(
298
- "path",
299
- {
300
- fillRule: "evenodd",
301
- d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
302
- clipRule: "evenodd"
303
- }
304
- )
305
- }
306
- ) }),
307
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-slate-300 truncate", children: name }),
308
- /* @__PURE__ */ jsx(MetaInfo, { properties })
309
- ]
310
- }
311
- );
313
+ children: [
314
+ /* @__PURE__ */ jsx("div", {
315
+ className: "w-5 h-5 rounded-full bg-emerald-500/20 flex items-center justify-center flex-shrink-0",
316
+ children: /* @__PURE__ */ jsx("svg", {
317
+ className: "w-3 h-3 text-emerald-400",
318
+ fill: "currentColor",
319
+ viewBox: "0 0 20 20",
320
+ "aria-hidden": "true",
321
+ children: /* @__PURE__ */ jsx("path", {
322
+ fillRule: "evenodd",
323
+ d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
324
+ clipRule: "evenodd"
325
+ })
326
+ })
327
+ }),
328
+ /* @__PURE__ */ jsx("span", {
329
+ className: "flex-1 text-sm text-slate-300 truncate",
330
+ children: name
331
+ }),
332
+ /* @__PURE__ */ jsx(MetaInfo, { properties })
333
+ ]
334
+ });
312
335
  }
336
+ /**
337
+ * Section header for grouping
338
+ */
313
339
  function SectionHeader({ title, count, icon, colorClass }) {
314
- if (count === 0) return null;
315
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-3", children: [
316
- /* @__PURE__ */ jsx("span", { className: `text-lg font-mono ${colorClass}`, children: icon }),
317
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-300 uppercase tracking-wider", children: title }),
318
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-500 font-mono", children: [
319
- "(",
320
- count,
321
- ")"
322
- ] })
323
- ] });
340
+ if (count === 0) return null;
341
+ return /* @__PURE__ */ jsxs("div", {
342
+ className: "flex items-center gap-3 mb-3",
343
+ children: [
344
+ /* @__PURE__ */ jsx("span", {
345
+ className: `text-lg font-mono ${colorClass}`,
346
+ children: icon
347
+ }),
348
+ /* @__PURE__ */ jsx("h3", {
349
+ className: "text-sm font-semibold text-slate-300 uppercase tracking-wider",
350
+ children: title
351
+ }),
352
+ /* @__PURE__ */ jsxs("span", {
353
+ className: "text-sm text-slate-500 font-mono",
354
+ children: [
355
+ "(",
356
+ count,
357
+ ")"
358
+ ]
359
+ })
360
+ ]
361
+ });
324
362
  }
363
+ /**
364
+ * Summary stats bar
365
+ */
325
366
  function SummaryBar({ stats }) {
326
- let hasIssues = stats.failed > 0 || stats.new > 0;
327
- return /* @__PURE__ */ jsxs(
328
- "div",
329
- {
330
- className: `
367
+ let hasIssues = stats.failed > 0 || stats.new > 0;
368
+ return /* @__PURE__ */ jsxs("div", {
369
+ className: `
331
370
  flex flex-wrap items-center gap-6 p-4 rounded-xl mb-8
332
371
  ${hasIssues ? "bg-slate-800/60 border border-slate-700/50" : "bg-emerald-500/10 border border-emerald-500/20"}
333
372
  `,
334
- children: [
335
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: hasIssues ? /* @__PURE__ */ jsxs(Fragment, { children: [
336
- /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-amber-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-amber-400 text-lg", children: "◐" }) }),
337
- /* @__PURE__ */ jsxs("div", { children: [
338
- /* @__PURE__ */ jsx("div", { className: "text-white font-semibold", children: "Review Required" }),
339
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-slate-400", children: [
340
- stats.failed + (stats.new || 0),
341
- " items need attention"
342
- ] })
343
- ] })
344
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
345
- /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-emerald-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
346
- "svg",
347
- {
348
- className: "w-5 h-5 text-emerald-400",
349
- fill: "currentColor",
350
- viewBox: "0 0 20 20",
351
- "aria-hidden": "true",
352
- children: /* @__PURE__ */ jsx(
353
- "path",
354
- {
355
- fillRule: "evenodd",
356
- d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
357
- clipRule: "evenodd"
358
- }
359
- )
360
- }
361
- ) }),
362
- /* @__PURE__ */ jsxs("div", { children: [
363
- /* @__PURE__ */ jsx("div", { className: "text-white font-semibold", children: "All Tests Passed" }),
364
- /* @__PURE__ */ jsx("div", { className: "text-sm text-slate-400", children: "No visual changes detected" })
365
- ] })
366
- ] }) }),
367
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6 ml-auto", children: [
368
- /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
369
- /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-white tabular-nums", children: stats.total }),
370
- /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Total" })
371
- ] }),
372
- /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
373
- /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-emerald-400 tabular-nums", children: stats.passed }),
374
- /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Passed" })
375
- ] }),
376
- stats.failed > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
377
- /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-red-400 tabular-nums", children: stats.failed }),
378
- /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Changed" })
379
- ] }),
380
- (stats.new || 0) > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
381
- /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-blue-400 tabular-nums", children: stats.new }),
382
- /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "New" })
383
- ] })
384
- ] })
385
- ]
386
- }
387
- );
373
+ children: [/* @__PURE__ */ jsx("div", {
374
+ className: "flex items-center gap-3",
375
+ children: hasIssues ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
376
+ className: "w-10 h-10 rounded-full bg-amber-500/20 flex items-center justify-center",
377
+ children: /* @__PURE__ */ jsx("span", {
378
+ className: "text-amber-400 text-lg",
379
+ children: "◐"
380
+ })
381
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
382
+ className: "text-white font-semibold",
383
+ children: "Review Required"
384
+ }), /* @__PURE__ */ jsxs("div", {
385
+ className: "text-sm text-slate-400",
386
+ children: [stats.failed + (stats.new || 0), " items need attention"]
387
+ })] })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
388
+ className: "w-10 h-10 rounded-full bg-emerald-500/20 flex items-center justify-center",
389
+ children: /* @__PURE__ */ jsx("svg", {
390
+ className: "w-5 h-5 text-emerald-400",
391
+ fill: "currentColor",
392
+ viewBox: "0 0 20 20",
393
+ "aria-hidden": "true",
394
+ children: /* @__PURE__ */ jsx("path", {
395
+ fillRule: "evenodd",
396
+ d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
397
+ clipRule: "evenodd"
398
+ })
399
+ })
400
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
401
+ className: "text-white font-semibold",
402
+ children: "All Tests Passed"
403
+ }), /* @__PURE__ */ jsx("div", {
404
+ className: "text-sm text-slate-400",
405
+ children: "No visual changes detected"
406
+ })] })] })
407
+ }), /* @__PURE__ */ jsxs("div", {
408
+ className: "flex items-center gap-6 ml-auto",
409
+ children: [
410
+ /* @__PURE__ */ jsxs("div", {
411
+ className: "text-center",
412
+ children: [/* @__PURE__ */ jsx("div", {
413
+ className: "text-2xl font-bold text-white tabular-nums",
414
+ children: stats.total
415
+ }), /* @__PURE__ */ jsx("div", {
416
+ className: "text-xs text-slate-500 uppercase tracking-wider",
417
+ children: "Total"
418
+ })]
419
+ }),
420
+ /* @__PURE__ */ jsxs("div", {
421
+ className: "text-center",
422
+ children: [/* @__PURE__ */ jsx("div", {
423
+ className: "text-2xl font-bold text-emerald-400 tabular-nums",
424
+ children: stats.passed
425
+ }), /* @__PURE__ */ jsx("div", {
426
+ className: "text-xs text-slate-500 uppercase tracking-wider",
427
+ children: "Passed"
428
+ })]
429
+ }),
430
+ stats.failed > 0 && /* @__PURE__ */ jsxs("div", {
431
+ className: "text-center",
432
+ children: [/* @__PURE__ */ jsx("div", {
433
+ className: "text-2xl font-bold text-red-400 tabular-nums",
434
+ children: stats.failed
435
+ }), /* @__PURE__ */ jsx("div", {
436
+ className: "text-xs text-slate-500 uppercase tracking-wider",
437
+ children: "Changed"
438
+ })]
439
+ }),
440
+ (stats.new || 0) > 0 && /* @__PURE__ */ jsxs("div", {
441
+ className: "text-center",
442
+ children: [/* @__PURE__ */ jsx("div", {
443
+ className: "text-2xl font-bold text-blue-400 tabular-nums",
444
+ children: stats.new
445
+ }), /* @__PURE__ */ jsx("div", {
446
+ className: "text-xs text-slate-500 uppercase tracking-wider",
447
+ children: "New"
448
+ })]
449
+ })
450
+ ]
451
+ })]
452
+ });
388
453
  }
389
454
  function formatTimestamp(timestamp) {
390
- if (!timestamp) return null;
391
- let date = new Date(timestamp);
392
- return date.toLocaleDateString(void 0, {
393
- year: "numeric",
394
- month: "short",
395
- day: "numeric",
396
- hour: "2-digit",
397
- minute: "2-digit"
398
- });
455
+ if (!timestamp) return null;
456
+ return new Date(timestamp).toLocaleDateString(void 0, {
457
+ year: "numeric",
458
+ month: "short",
459
+ day: "numeric",
460
+ hour: "2-digit",
461
+ minute: "2-digit"
462
+ });
399
463
  }
400
464
  function Header({ timestamp }) {
401
- return /* @__PURE__ */ jsx("header", { className: "bg-slate-950/80 backdrop-blur-sm border-b border-slate-800/60 sticky top-0 z-10", children: /* @__PURE__ */ jsxs("div", { className: "max-w-6xl mx-auto px-6 py-4 flex items-center justify-between", children: [
402
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
403
- /* @__PURE__ */ jsx("div", { className: "w-9 h-9 bg-gradient-to-br from-amber-400 to-amber-600 rounded-lg flex items-center justify-center shadow-lg shadow-amber-500/20", children: /* @__PURE__ */ jsxs(
404
- "svg",
405
- {
406
- className: "w-5 h-5 text-slate-900",
407
- viewBox: "0 0 24 24",
408
- fill: "currentColor",
409
- "aria-hidden": "true",
410
- children: [
411
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
412
- /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" })
413
- ]
414
- }
415
- ) }),
416
- /* @__PURE__ */ jsxs("div", { children: [
417
- /* @__PURE__ */ jsx("div", { className: "text-lg font-semibold text-white tracking-tight", children: "Vizzly" }),
418
- /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500", children: "Visual Test Report" })
419
- ] })
420
- ] }),
421
- timestamp && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 tabular-nums", children: formatTimestamp(timestamp) })
422
- ] }) });
465
+ return /* @__PURE__ */ jsx("header", {
466
+ className: "bg-slate-950/80 backdrop-blur-sm border-b border-slate-800/60 sticky top-0 z-10",
467
+ children: /* @__PURE__ */ jsxs("div", {
468
+ className: "max-w-6xl mx-auto px-6 py-4 flex items-center justify-between",
469
+ children: [/* @__PURE__ */ jsxs("div", {
470
+ className: "flex items-center gap-3",
471
+ children: [/* @__PURE__ */ jsx("div", {
472
+ className: "w-9 h-9 bg-gradient-to-br from-amber-400 to-amber-600 rounded-lg flex items-center justify-center shadow-lg shadow-amber-500/20",
473
+ children: /* @__PURE__ */ jsxs("svg", {
474
+ className: "w-5 h-5 text-slate-900",
475
+ viewBox: "0 0 24 24",
476
+ fill: "currentColor",
477
+ "aria-hidden": "true",
478
+ children: [/* @__PURE__ */ jsx("circle", {
479
+ cx: "12",
480
+ cy: "12",
481
+ r: "3"
482
+ }), /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" })]
483
+ })
484
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
485
+ className: "text-lg font-semibold text-white tracking-tight",
486
+ children: "Vizzly"
487
+ }), /* @__PURE__ */ jsx("div", {
488
+ className: "text-xs text-slate-500",
489
+ children: "Visual Test Report"
490
+ })] })]
491
+ }), timestamp && /* @__PURE__ */ jsx("div", {
492
+ className: "text-xs text-slate-500 tabular-nums",
493
+ children: formatTimestamp(timestamp)
494
+ })]
495
+ })
496
+ });
423
497
  }
424
498
  function StaticReportView({ reportData }) {
425
- if (!reportData || !reportData.comparisons) {
426
- return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-900 text-white flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
427
- /* @__PURE__ */ jsx("div", { className: "text-xl font-medium mb-2", children: "No Report Data" }),
428
- /* @__PURE__ */ jsx("p", { className: "text-slate-400", children: "No comparison data available for this report." })
429
- ] }) });
430
- }
431
- let { comparisons } = reportData;
432
- let stats = {
433
- total: comparisons.length,
434
- passed: comparisons.filter((c) => c.status === "passed").length,
435
- failed: comparisons.filter((c) => c.status === "failed").length,
436
- new: comparisons.filter((c) => isNewComparisonStatus(c.status)).length,
437
- error: comparisons.filter((c) => c.status === "error").length
438
- };
439
- let failed = comparisons.filter((c) => c.status === "failed");
440
- let newItems = comparisons.filter((c) => isNewComparisonStatus(c.status));
441
- let passed = comparisons.filter((c) => c.status === "passed");
442
- let errors = comparisons.filter((c) => c.status === "error");
443
- return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-slate-900 text-white", children: [
444
- /* @__PURE__ */ jsx(Header, { timestamp: reportData.timestamp }),
445
- /* @__PURE__ */ jsxs("main", { className: "max-w-6xl mx-auto px-6 py-8", children: [
446
- /* @__PURE__ */ jsx(SummaryBar, { stats }),
447
- failed.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
448
- /* @__PURE__ */ jsx(
449
- SectionHeader,
450
- {
451
- title: "Visual Changes",
452
- count: failed.length,
453
- icon: "◐",
454
- colorClass: "text-red-400"
455
- }
456
- ),
457
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: failed.map((comparison, index) => /* @__PURE__ */ jsx(
458
- FailedComparison,
459
- {
460
- comparison,
461
- isEven: index % 2 === 0
462
- },
463
- comparison.id || comparison.signature || `failed-${index}`
464
- )) })
465
- ] }),
466
- newItems.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
467
- /* @__PURE__ */ jsx(
468
- SectionHeader,
469
- {
470
- title: "New Screenshots",
471
- count: newItems.length,
472
- icon: "+",
473
- colorClass: "text-blue-400"
474
- }
475
- ),
476
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: newItems.map((comparison, index) => /* @__PURE__ */ jsx(
477
- NewComparison,
478
- {
479
- comparison,
480
- isEven: index % 2 === 0
481
- },
482
- comparison.id || comparison.signature || `new-${index}`
483
- )) })
484
- ] }),
485
- errors.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
486
- /* @__PURE__ */ jsx(
487
- SectionHeader,
488
- {
489
- title: "Errors",
490
- count: errors.length,
491
- icon: "!",
492
- colorClass: "text-orange-400"
493
- }
494
- ),
495
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: errors.map((comparison, index) => /* @__PURE__ */ jsx(
496
- FailedComparison,
497
- {
498
- comparison,
499
- isEven: index % 2 === 0
500
- },
501
- comparison.id || comparison.signature || `error-${index}`
502
- )) })
503
- ] }),
504
- passed.length > 0 && /* @__PURE__ */ jsx("section", { className: "mb-8", children: /* @__PURE__ */ jsxs("details", { className: "group", children: [
505
- /* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none [&::-webkit-details-marker]:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-3", children: [
506
- /* @__PURE__ */ jsx("span", { className: "text-lg font-mono text-emerald-400", children: "✓" }),
507
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-300 uppercase tracking-wider", children: "Passed" }),
508
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-500 font-mono", children: [
509
- "(",
510
- passed.length,
511
- ")"
512
- ] }),
513
- /* @__PURE__ */ jsx(
514
- "svg",
515
- {
516
- className: "w-4 h-4 text-slate-500 transition-transform group-open:rotate-180 ml-auto",
517
- fill: "none",
518
- viewBox: "0 0 24 24",
519
- stroke: "currentColor",
520
- "aria-hidden": "true",
521
- children: /* @__PURE__ */ jsx(
522
- "path",
523
- {
524
- strokeLinecap: "round",
525
- strokeLinejoin: "round",
526
- strokeWidth: 2,
527
- d: "M19 9l-7 7-7-7"
528
- }
529
- )
530
- }
531
- )
532
- ] }) }),
533
- /* @__PURE__ */ jsx("div", { className: "space-y-1", children: passed.map((comparison, index) => /* @__PURE__ */ jsx(
534
- PassedComparison,
535
- {
536
- comparison
537
- },
538
- comparison.id || comparison.signature || `passed-${index}`
539
- )) })
540
- ] }) }),
541
- /* @__PURE__ */ jsx("footer", { className: "mt-12 pt-6 border-t border-slate-800/60 text-center", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500", children: [
542
- "Visual regression report generated by",
543
- " ",
544
- /* @__PURE__ */ jsx(
545
- "a",
546
- {
547
- href: "https://vizzly.dev",
548
- className: "text-amber-400 hover:text-amber-300 transition-colors",
549
- children: "Vizzly"
550
- }
551
- )
552
- ] }) })
553
- ] })
554
- ] });
499
+ if (!reportData?.comparisons) return /* @__PURE__ */ jsx("div", {
500
+ className: "min-h-screen bg-slate-900 text-white flex items-center justify-center",
501
+ children: /* @__PURE__ */ jsxs("div", {
502
+ className: "text-center",
503
+ children: [/* @__PURE__ */ jsx("div", {
504
+ className: "text-xl font-medium mb-2",
505
+ children: "No Report Data"
506
+ }), /* @__PURE__ */ jsx("p", {
507
+ className: "text-slate-400",
508
+ children: "No comparison data available for this report."
509
+ })]
510
+ })
511
+ });
512
+ let { comparisons } = reportData;
513
+ let stats = {
514
+ total: comparisons.length,
515
+ passed: comparisons.filter((c) => c.status === "passed").length,
516
+ failed: comparisons.filter((c) => c.status === "failed").length,
517
+ new: comparisons.filter((c) => isNewComparisonStatus(c.status)).length,
518
+ error: comparisons.filter((c) => c.status === "error").length
519
+ };
520
+ let failed = comparisons.filter((c) => c.status === "failed");
521
+ let newItems = comparisons.filter((c) => isNewComparisonStatus(c.status));
522
+ let passed = comparisons.filter((c) => c.status === "passed");
523
+ let errors = comparisons.filter((c) => c.status === "error");
524
+ return /* @__PURE__ */ jsxs("div", {
525
+ className: "min-h-screen bg-slate-900 text-white",
526
+ children: [/* @__PURE__ */ jsx(Header, { timestamp: reportData.timestamp }), /* @__PURE__ */ jsxs("main", {
527
+ className: "max-w-6xl mx-auto px-6 py-8",
528
+ children: [
529
+ /* @__PURE__ */ jsx(SummaryBar, { stats }),
530
+ failed.length > 0 && /* @__PURE__ */ jsxs("section", {
531
+ className: "mb-8",
532
+ children: [/* @__PURE__ */ jsx(SectionHeader, {
533
+ title: "Visual Changes",
534
+ count: failed.length,
535
+ icon: "◐",
536
+ colorClass: "text-red-400"
537
+ }), /* @__PURE__ */ jsx("div", {
538
+ className: "space-y-2",
539
+ children: failed.map((comparison, index) => /* @__PURE__ */ jsx(FailedComparison, {
540
+ comparison,
541
+ isEven: index % 2 === 0
542
+ }, comparison.id || comparison.signature || `failed-${index}`))
543
+ })]
544
+ }),
545
+ newItems.length > 0 && /* @__PURE__ */ jsxs("section", {
546
+ className: "mb-8",
547
+ children: [/* @__PURE__ */ jsx(SectionHeader, {
548
+ title: "New Screenshots",
549
+ count: newItems.length,
550
+ icon: "+",
551
+ colorClass: "text-blue-400"
552
+ }), /* @__PURE__ */ jsx("div", {
553
+ className: "space-y-2",
554
+ children: newItems.map((comparison, index) => /* @__PURE__ */ jsx(NewComparison, {
555
+ comparison,
556
+ isEven: index % 2 === 0
557
+ }, comparison.id || comparison.signature || `new-${index}`))
558
+ })]
559
+ }),
560
+ errors.length > 0 && /* @__PURE__ */ jsxs("section", {
561
+ className: "mb-8",
562
+ children: [/* @__PURE__ */ jsx(SectionHeader, {
563
+ title: "Errors",
564
+ count: errors.length,
565
+ icon: "!",
566
+ colorClass: "text-orange-400"
567
+ }), /* @__PURE__ */ jsx("div", {
568
+ className: "space-y-2",
569
+ children: errors.map((comparison, index) => /* @__PURE__ */ jsx(FailedComparison, {
570
+ comparison,
571
+ isEven: index % 2 === 0
572
+ }, comparison.id || comparison.signature || `error-${index}`))
573
+ })]
574
+ }),
575
+ passed.length > 0 && /* @__PURE__ */ jsx("section", {
576
+ className: "mb-8",
577
+ children: /* @__PURE__ */ jsxs("details", {
578
+ className: "group",
579
+ children: [/* @__PURE__ */ jsx("summary", {
580
+ className: "cursor-pointer list-none [&::-webkit-details-marker]:hidden",
581
+ children: /* @__PURE__ */ jsxs("div", {
582
+ className: "flex items-center gap-3 mb-3",
583
+ children: [
584
+ /* @__PURE__ */ jsx("span", {
585
+ className: "text-lg font-mono text-emerald-400",
586
+ children: "✓"
587
+ }),
588
+ /* @__PURE__ */ jsx("h3", {
589
+ className: "text-sm font-semibold text-slate-300 uppercase tracking-wider",
590
+ children: "Passed"
591
+ }),
592
+ /* @__PURE__ */ jsxs("span", {
593
+ className: "text-sm text-slate-500 font-mono",
594
+ children: [
595
+ "(",
596
+ passed.length,
597
+ ")"
598
+ ]
599
+ }),
600
+ /* @__PURE__ */ jsx("svg", {
601
+ className: "w-4 h-4 text-slate-500 transition-transform group-open:rotate-180 ml-auto",
602
+ fill: "none",
603
+ viewBox: "0 0 24 24",
604
+ stroke: "currentColor",
605
+ "aria-hidden": "true",
606
+ children: /* @__PURE__ */ jsx("path", {
607
+ strokeLinecap: "round",
608
+ strokeLinejoin: "round",
609
+ strokeWidth: 2,
610
+ d: "M19 9l-7 7-7-7"
611
+ })
612
+ })
613
+ ]
614
+ })
615
+ }), /* @__PURE__ */ jsx("div", {
616
+ className: "space-y-1",
617
+ children: passed.map((comparison, index) => /* @__PURE__ */ jsx(PassedComparison, { comparison }, comparison.id || comparison.signature || `passed-${index}`))
618
+ })]
619
+ })
620
+ }),
621
+ /* @__PURE__ */ jsx("footer", {
622
+ className: "mt-12 pt-6 border-t border-slate-800/60 text-center",
623
+ children: /* @__PURE__ */ jsxs("p", {
624
+ className: "text-sm text-slate-500",
625
+ children: [
626
+ "Visual regression report generated by",
627
+ " ",
628
+ /* @__PURE__ */ jsx("a", {
629
+ href: "https://vizzly.dev",
630
+ className: "text-amber-400 hover:text-amber-300 transition-colors",
631
+ children: "Vizzly"
632
+ })
633
+ ]
634
+ })
635
+ })
636
+ ]
637
+ })]
638
+ });
555
639
  }
640
+ //#endregion
641
+ //#region src/ssr-entry.jsx
642
+ /**
643
+ * SSR Entry Point
644
+ *
645
+ * This module exports a function to render the static report to HTML string.
646
+ * It's built by Vite as a Node-compatible module for use in the CLI.
647
+ */
648
+ /**
649
+ * Render the static report to an HTML string
650
+ * @param {Object} reportData - The report data from report-data.json
651
+ * @returns {string} The rendered HTML
652
+ */
556
653
  function renderStaticReport(reportData) {
557
- return renderToString(/* @__PURE__ */ jsx(StaticReportView, { reportData }));
654
+ return renderToString(/* @__PURE__ */ jsx(StaticReportView, { reportData }));
558
655
  }
559
- export {
560
- renderStaticReport
561
- };
656
+ //#endregion
657
+ export { renderStaticReport };