astro-better-declarative-screenshots 0.2.1 → 0.3.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/Screenshot.astro +349 -27
- package/package.json +1 -1
- package/src/config.mjs +12 -6
- package/src/highlight.mjs +7 -0
package/Screenshot.astro
CHANGED
|
@@ -38,10 +38,8 @@ const {
|
|
|
38
38
|
outputDir,
|
|
39
39
|
} = Astro.props;
|
|
40
40
|
|
|
41
|
-
// parse Highlight children from the rendered slot HTML
|
|
42
41
|
function parseHighlights(html: string): HighlightData[] {
|
|
43
42
|
const found: HighlightData[] = [];
|
|
44
|
-
// match <screenshot-highlight data-selector="..." ...></screenshot-highlight>
|
|
45
43
|
const tagRe = /<screenshot-highlight([^>]*)>/gi;
|
|
46
44
|
const attrRe = /data-([\w-]+)="([^"]*)"/g;
|
|
47
45
|
let tagMatch: RegExpExecArray | null;
|
|
@@ -66,29 +64,28 @@ function parseHighlights(html: string): HighlightData[] {
|
|
|
66
64
|
|
|
67
65
|
const slotHtml = Astro.slots.has('default') ? await Astro.slots.render('default') : '';
|
|
68
66
|
const highlights = parseHighlights(slotHtml);
|
|
69
|
-
|
|
70
67
|
const name = id ?? deriveName(url, highlights);
|
|
71
68
|
|
|
72
|
-
// resolve output directory: prop > env > screenshot.config.mjs > default
|
|
73
69
|
let resolvedOutputDir = outputDir ?? process.env.SCREENSHOTS_DIR;
|
|
70
|
+
let chrome = { style: 'safari-macos', renderIn: 'png', showUrl: true, theme: 'light',
|
|
71
|
+
shadowBlur: 40, shadowPadding: 48, baseUrl: undefined as string | undefined };
|
|
72
|
+
let configBaseUrl = '';
|
|
74
73
|
if (!resolvedOutputDir) {
|
|
75
74
|
try {
|
|
76
75
|
const cfg = await loadConfig(process.cwd());
|
|
77
76
|
resolvedOutputDir = cfg.outputDir;
|
|
77
|
+
chrome = cfg.chrome as typeof chrome;
|
|
78
|
+
configBaseUrl = cfg.baseUrl ?? '';
|
|
78
79
|
} catch {
|
|
79
80
|
resolvedOutputDir = 'src/assets/screenshots';
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
// resolve the public path the browser/img uses.
|
|
84
|
-
// files in public/ are served from the root, so strip 'public/' prefix.
|
|
85
|
-
// files in src/assets/ go through Astro's pipeline -- reference via root-relative path.
|
|
86
84
|
const servePath = resolvedOutputDir
|
|
87
85
|
.replace(/^\.\//, '')
|
|
88
86
|
.replace(/^public\//, '');
|
|
89
87
|
const imgPath = `/${servePath}/${name}.png`;
|
|
90
88
|
|
|
91
|
-
// check whether the file exists on disk (uses full resolvedOutputDir, not stripped)
|
|
92
89
|
const absolutePath = path.resolve(process.cwd(), resolvedOutputDir, `${name}.png`);
|
|
93
90
|
const fileExists = existsSync(absolutePath);
|
|
94
91
|
|
|
@@ -100,32 +97,357 @@ if (!fileExists && strict) {
|
|
|
100
97
|
);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
// data attributes encode the screenshot spec for the CLI scanner
|
|
104
|
-
// so `discover.mjs` can also pick these up from the built HTML if needed
|
|
105
100
|
const specData = JSON.stringify({ url, highlights, width, height, fullPage });
|
|
101
|
+
|
|
102
|
+
const cssChrome = chrome.renderIn === 'css' && chrome.style !== 'none';
|
|
103
|
+
let displayUrl = url;
|
|
104
|
+
if (chrome.baseUrl && configBaseUrl && displayUrl.startsWith(configBaseUrl)) {
|
|
105
|
+
displayUrl = chrome.baseUrl.replace(/\/$/, '') + displayUrl.slice(configBaseUrl.length);
|
|
106
|
+
}
|
|
107
|
+
if (displayUrl.length > 72) displayUrl = displayUrl.slice(0, 69) + '...';
|
|
106
108
|
---
|
|
109
|
+
|
|
107
110
|
<figure
|
|
108
111
|
class="screenshot-figure"
|
|
109
112
|
data-screenshot-name={name}
|
|
110
113
|
data-screenshot-spec={specData}
|
|
111
114
|
>
|
|
112
|
-
{
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
{cssChrome ? (
|
|
116
|
+
<div class="sc-window" data-chrome={chrome.style} data-theme={chrome.theme}>
|
|
117
|
+
<div class="sc-bar">
|
|
118
|
+
<div class="sc-lights">
|
|
119
|
+
<span class="sc-tl sc-close"></span>
|
|
120
|
+
<span class="sc-tl sc-min"></span>
|
|
121
|
+
<span class="sc-tl sc-zoom"></span>
|
|
122
|
+
</div>
|
|
123
|
+
{chrome.showUrl && (
|
|
124
|
+
<div class="sc-urlbar" aria-hidden="true">
|
|
125
|
+
<span class="sc-url">{displayUrl}</span>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
<div class="sc-content">
|
|
130
|
+
{fileExists ? (
|
|
131
|
+
<img
|
|
132
|
+
src={imgPath}
|
|
133
|
+
alt={alt ?? name.replace(/-/g, ' ')}
|
|
134
|
+
loading="lazy"
|
|
135
|
+
decoding="async"
|
|
136
|
+
/>
|
|
137
|
+
) : (
|
|
138
|
+
<div
|
|
139
|
+
class="sc-placeholder"
|
|
140
|
+
role="img"
|
|
141
|
+
aria-label={alt ?? `Placeholder for ${name}`}
|
|
142
|
+
>
|
|
143
|
+
Screenshot not yet generated: {name}.png —
|
|
144
|
+
run <code>take-screenshots</code> to generate it.
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
129
148
|
</div>
|
|
149
|
+
) : (
|
|
150
|
+
fileExists ? (
|
|
151
|
+
<img
|
|
152
|
+
src={imgPath}
|
|
153
|
+
alt={alt ?? name.replace(/-/g, ' ')}
|
|
154
|
+
loading="lazy"
|
|
155
|
+
decoding="async"
|
|
156
|
+
class="screenshot-img"
|
|
157
|
+
/>
|
|
158
|
+
) : (
|
|
159
|
+
<div
|
|
160
|
+
class="screenshot-placeholder"
|
|
161
|
+
role="img"
|
|
162
|
+
aria-label={alt ?? `Placeholder for ${name}`}
|
|
163
|
+
style={`width:${width ?? 1280}px;max-width:100%;aspect-ratio:${(width ?? 1280) / (height ?? 800)};background:#f0f0f0;display:flex;align-items:center;justify-content:center;font-family:sans-serif;color:#999;font-size:14px;border:1px dashed #ccc;`}
|
|
164
|
+
>
|
|
165
|
+
Screenshot not yet generated: {name}.png<br />
|
|
166
|
+
Run <code>take-screenshots</code> to generate it.
|
|
167
|
+
</div>
|
|
168
|
+
)
|
|
130
169
|
)}
|
|
131
170
|
</figure>
|
|
171
|
+
|
|
172
|
+
<style>
|
|
173
|
+
/* ── layout ── */
|
|
174
|
+
.sc-window {
|
|
175
|
+
display: flex;
|
|
176
|
+
flex-direction: column;
|
|
177
|
+
width: 100%;
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
border-radius: var(--sc-r);
|
|
180
|
+
box-shadow: var(--sc-shadow);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.sc-bar {
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
height: var(--sc-bar-h, 40px);
|
|
187
|
+
padding: 0 14px;
|
|
188
|
+
flex-shrink: 0;
|
|
189
|
+
background: var(--sc-bar-bg);
|
|
190
|
+
border-bottom: 1px solid var(--sc-bar-sep);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.sc-lights {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 8px;
|
|
197
|
+
flex-shrink: 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.sc-tl {
|
|
201
|
+
display: block;
|
|
202
|
+
width: 13px;
|
|
203
|
+
height: 13px;
|
|
204
|
+
border-radius: 50%;
|
|
205
|
+
flex-shrink: 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.sc-close { background: var(--sc-tl-close); }
|
|
209
|
+
.sc-min { background: var(--sc-tl-min); }
|
|
210
|
+
.sc-zoom { background: var(--sc-tl-zoom); }
|
|
211
|
+
|
|
212
|
+
.sc-urlbar {
|
|
213
|
+
flex: 1;
|
|
214
|
+
margin: 0 14px;
|
|
215
|
+
height: 26px;
|
|
216
|
+
border-radius: 5px;
|
|
217
|
+
display: flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
justify-content: center;
|
|
220
|
+
padding: 0 10px;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
background: var(--sc-url-bg);
|
|
223
|
+
border: 1px solid var(--sc-url-border);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.sc-url {
|
|
227
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
|
228
|
+
font-size: 12px;
|
|
229
|
+
color: var(--sc-url-text);
|
|
230
|
+
white-space: nowrap;
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
text-overflow: ellipsis;
|
|
233
|
+
max-width: 100%;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.sc-content {
|
|
237
|
+
line-height: 0;
|
|
238
|
+
overflow: hidden;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.sc-content img {
|
|
242
|
+
display: block;
|
|
243
|
+
width: 100%;
|
|
244
|
+
height: auto;
|
|
245
|
+
margin: 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.sc-placeholder {
|
|
249
|
+
background: #f0f0f0;
|
|
250
|
+
padding: 2rem;
|
|
251
|
+
font-family: sans-serif;
|
|
252
|
+
font-size: 14px;
|
|
253
|
+
color: #999;
|
|
254
|
+
text-align: center;
|
|
255
|
+
line-height: 1.5;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* ── safari-macos light ── */
|
|
259
|
+
.sc-window[data-chrome="safari-macos"] {
|
|
260
|
+
--sc-r: 10px;
|
|
261
|
+
--sc-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08),
|
|
262
|
+
0 6px 8px rgba(0, 0, 0, 0.05),
|
|
263
|
+
0 16px 16px rgba(0, 0, 0, 0.06),
|
|
264
|
+
0 36px 28px rgba(0, 0, 0, 0.05);
|
|
265
|
+
--sc-bar-bg: linear-gradient(to bottom, #e8e8e8, #dcdcdc);
|
|
266
|
+
--sc-bar-sep: #b8b8b8;
|
|
267
|
+
--sc-url-bg: #ffffff;
|
|
268
|
+
--sc-url-border: #cacaca;
|
|
269
|
+
--sc-url-text: #666666;
|
|
270
|
+
--sc-tl-close: #ff5f57;
|
|
271
|
+
--sc-tl-min: #febc2e;
|
|
272
|
+
--sc-tl-zoom: #28c840;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* ── safari-macos dark ── */
|
|
276
|
+
.sc-window[data-chrome="safari-macos"][data-theme="dark"],
|
|
277
|
+
:root[data-theme="dark"] .sc-window[data-chrome="safari-macos"],
|
|
278
|
+
:root.dark .sc-window[data-chrome="safari-macos"] {
|
|
279
|
+
--sc-bar-bg: linear-gradient(to bottom, #323232, #2c2c2c);
|
|
280
|
+
--sc-bar-sep: #1a1a1a;
|
|
281
|
+
--sc-url-bg: #454547;
|
|
282
|
+
--sc-url-border: #5a5a5c;
|
|
283
|
+
--sc-url-text: #f0f0f0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@media (prefers-color-scheme: dark) {
|
|
287
|
+
.sc-window[data-chrome="safari-macos"]:not([data-theme="light"]) {
|
|
288
|
+
--sc-bar-bg: linear-gradient(to bottom, #323232, #2c2c2c);
|
|
289
|
+
--sc-bar-sep: #1a1a1a;
|
|
290
|
+
--sc-url-bg: #454547;
|
|
291
|
+
--sc-url-border: #5a5a5c;
|
|
292
|
+
--sc-url-text: #f0f0f0;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ── linux/gnome light ── */
|
|
297
|
+
.sc-window[data-chrome="linux"] {
|
|
298
|
+
--sc-r: 8px;
|
|
299
|
+
--sc-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.14),
|
|
300
|
+
0 6px 8px rgba(0, 0, 0, 0.06),
|
|
301
|
+
0 18px 24px rgba(0, 0, 0, 0.08);
|
|
302
|
+
--sc-bar-bg: #edeceb;
|
|
303
|
+
--sc-bar-sep: rgba(0, 0, 0, 0.10);
|
|
304
|
+
--sc-url-bg: #ffffff;
|
|
305
|
+
--sc-url-border: rgba(0, 0, 0, 0.22);
|
|
306
|
+
--sc-url-text: #555555;
|
|
307
|
+
--sc-tl-close: #e05555;
|
|
308
|
+
--sc-tl-min: #b8b8b8;
|
|
309
|
+
--sc-tl-zoom: #b8b8b8;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* ── linux dark (solarized) ── */
|
|
313
|
+
.sc-window[data-chrome="linux"][data-theme="dark"],
|
|
314
|
+
:root[data-theme="dark"] .sc-window[data-chrome="linux"],
|
|
315
|
+
:root.dark .sc-window[data-chrome="linux"] {
|
|
316
|
+
--sc-bar-bg: #1a2d40;
|
|
317
|
+
--sc-bar-sep: #0f1c2c;
|
|
318
|
+
--sc-url-bg: #0f1c2c;
|
|
319
|
+
--sc-url-border: #2e4460;
|
|
320
|
+
--sc-url-text: #7a90a8;
|
|
321
|
+
--sc-tl-close: #dc322f;
|
|
322
|
+
--sc-tl-min: #2e4460;
|
|
323
|
+
--sc-tl-zoom: #2e4460;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@media (prefers-color-scheme: dark) {
|
|
327
|
+
.sc-window[data-chrome="linux"]:not([data-theme="light"]) {
|
|
328
|
+
--sc-bar-bg: #1a2d40;
|
|
329
|
+
--sc-bar-sep: #0f1c2c;
|
|
330
|
+
--sc-url-bg: #0f1c2c;
|
|
331
|
+
--sc-url-border: #2e4460;
|
|
332
|
+
--sc-url-text: #7a90a8;
|
|
333
|
+
--sc-tl-close: #dc322f;
|
|
334
|
+
--sc-tl-min: #2e4460;
|
|
335
|
+
--sc-tl-zoom: #2e4460;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* ── windows light ── */
|
|
340
|
+
.sc-window[data-chrome="windows"] {
|
|
341
|
+
--sc-r: 8px;
|
|
342
|
+
--sc-bar-h: 32px;
|
|
343
|
+
--sc-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08),
|
|
344
|
+
0 6px 8px rgba(0, 0, 0, 0.05),
|
|
345
|
+
0 18px 24px rgba(0, 0, 0, 0.07);
|
|
346
|
+
--sc-bar-bg: #f3f3f3;
|
|
347
|
+
--sc-bar-sep: rgba(0, 0, 0, 0.06);
|
|
348
|
+
--sc-url-bg: #ffffff;
|
|
349
|
+
--sc-url-border: rgba(0, 0, 0, 0.12);
|
|
350
|
+
--sc-url-text: #555555;
|
|
351
|
+
--sc-tl-close: transparent;
|
|
352
|
+
--sc-tl-min: transparent;
|
|
353
|
+
--sc-tl-zoom: transparent;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* strip right padding so controls sit flush against the edge */
|
|
357
|
+
.sc-window[data-chrome="windows"] .sc-bar { padding-right: 0; }
|
|
358
|
+
|
|
359
|
+
/* move url bar left, controls right */
|
|
360
|
+
.sc-window[data-chrome="windows"] .sc-urlbar { order: 1; margin-left: 0; }
|
|
361
|
+
.sc-window[data-chrome="windows"] .sc-lights { order: 2; gap: 0; margin-left: 14px; align-self: stretch; align-items: stretch; }
|
|
362
|
+
|
|
363
|
+
/* windows order: min − max □ close ✕ */
|
|
364
|
+
.sc-window[data-chrome="windows"] .sc-min { order: 1; }
|
|
365
|
+
.sc-window[data-chrome="windows"] .sc-zoom { order: 2; }
|
|
366
|
+
.sc-window[data-chrome="windows"] .sc-close { order: 3; }
|
|
367
|
+
|
|
368
|
+
/* rectangular flat buttons with symbols */
|
|
369
|
+
.sc-window[data-chrome="windows"] .sc-tl {
|
|
370
|
+
width: 46px;
|
|
371
|
+
height: auto;
|
|
372
|
+
border-radius: 0;
|
|
373
|
+
display: flex;
|
|
374
|
+
align-items: center;
|
|
375
|
+
justify-content: center;
|
|
376
|
+
font: 13px/1 'Segoe UI', system-ui, sans-serif;
|
|
377
|
+
color: #333;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.sc-window[data-chrome="windows"] .sc-min::after { content: '\2212'; }
|
|
381
|
+
.sc-window[data-chrome="windows"] .sc-zoom::after { content: '\25A1'; }
|
|
382
|
+
.sc-window[data-chrome="windows"] .sc-close::after { content: '\00D7'; font-size: 16px; }
|
|
383
|
+
|
|
384
|
+
/* ── windows dark ── */
|
|
385
|
+
.sc-window[data-chrome="windows"][data-theme="dark"],
|
|
386
|
+
:root[data-theme="dark"] .sc-window[data-chrome="windows"],
|
|
387
|
+
:root.dark .sc-window[data-chrome="windows"] {
|
|
388
|
+
--sc-bar-bg: #202020;
|
|
389
|
+
--sc-bar-sep: rgba(255, 255, 255, 0.06);
|
|
390
|
+
--sc-url-bg: #2d2d2d;
|
|
391
|
+
--sc-url-border: rgba(255, 255, 255, 0.10);
|
|
392
|
+
--sc-url-text: #c0c0c0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.sc-window[data-chrome="windows"][data-theme="dark"] .sc-tl,
|
|
396
|
+
:root[data-theme="dark"] .sc-window[data-chrome="windows"] .sc-tl,
|
|
397
|
+
:root.dark .sc-window[data-chrome="windows"] .sc-tl { color: #c0c0c0; }
|
|
398
|
+
|
|
399
|
+
@media (prefers-color-scheme: dark) {
|
|
400
|
+
.sc-window[data-chrome="windows"]:not([data-theme="light"]) {
|
|
401
|
+
--sc-bar-bg: #202020;
|
|
402
|
+
--sc-bar-sep: rgba(255, 255, 255, 0.06);
|
|
403
|
+
--sc-url-bg: #2d2d2d;
|
|
404
|
+
--sc-url-border: rgba(255, 255, 255, 0.10);
|
|
405
|
+
--sc-url-text: #c0c0c0;
|
|
406
|
+
}
|
|
407
|
+
.sc-window[data-chrome="windows"]:not([data-theme="light"]) .sc-tl { color: #c0c0c0; }
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* ── golden-gate light ── */
|
|
411
|
+
.sc-window[data-chrome="golden-gate"] {
|
|
412
|
+
--sc-r: 14px;
|
|
413
|
+
--sc-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.10),
|
|
414
|
+
0 8px 10px rgba(0, 0, 0, 0.06),
|
|
415
|
+
0 22px 22px rgba(0, 0, 0, 0.08),
|
|
416
|
+
0 48px 36px rgba(0, 0, 0, 0.06);
|
|
417
|
+
--sc-bar-bg: linear-gradient(to bottom, #efefef, #e8e8e8);
|
|
418
|
+
--sc-bar-sep: rgba(0, 0, 0, 0.12);
|
|
419
|
+
--sc-url-bg: rgba(255, 255, 255, 0.88);
|
|
420
|
+
--sc-url-border: rgba(0, 0, 0, 0.14);
|
|
421
|
+
--sc-url-text: #666666;
|
|
422
|
+
--sc-tl-close: #e8524a;
|
|
423
|
+
--sc-tl-min: #e6be46;
|
|
424
|
+
--sc-tl-zoom: #5cc84e;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* ── golden-gate dark ── */
|
|
428
|
+
.sc-window[data-chrome="golden-gate"][data-theme="dark"],
|
|
429
|
+
:root[data-theme="dark"] .sc-window[data-chrome="golden-gate"],
|
|
430
|
+
:root.dark .sc-window[data-chrome="golden-gate"] {
|
|
431
|
+
--sc-bar-bg: linear-gradient(to bottom, #2d2d2d, #272727);
|
|
432
|
+
--sc-bar-sep: rgba(255, 255, 255, 0.08);
|
|
433
|
+
--sc-url-bg: rgba(56, 56, 58, 0.9);
|
|
434
|
+
--sc-url-border: rgba(255, 255, 255, 0.1);
|
|
435
|
+
--sc-url-text: #e0e0e0;
|
|
436
|
+
--sc-tl-close: #c94847;
|
|
437
|
+
--sc-tl-min: #b89b38;
|
|
438
|
+
--sc-tl-zoom: #4ba043;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@media (prefers-color-scheme: dark) {
|
|
442
|
+
.sc-window[data-chrome="golden-gate"]:not([data-theme="light"]) {
|
|
443
|
+
--sc-bar-bg: linear-gradient(to bottom, #2d2d2d, #272727);
|
|
444
|
+
--sc-bar-sep: rgba(255, 255, 255, 0.08);
|
|
445
|
+
--sc-url-bg: rgba(56, 56, 58, 0.9);
|
|
446
|
+
--sc-url-border: rgba(255, 255, 255, 0.1);
|
|
447
|
+
--sc-url-text: #e0e0e0;
|
|
448
|
+
--sc-tl-close: #c94847;
|
|
449
|
+
--sc-tl-min: #b89b38;
|
|
450
|
+
--sc-tl-zoom: #4ba043;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
</style>
|
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -36,15 +36,21 @@ const DockerSchema = z.object({
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
const ChromeSchema = z.object({
|
|
39
|
-
// 'safari-macos'
|
|
40
|
-
style: z.enum(['safari-macos', 'none']).default('safari-macos'),
|
|
39
|
+
// chrome style: 'safari-macos' (classic pre-2026), 'golden-gate' (macOS 26 Liquid Glass), 'none'
|
|
40
|
+
style: z.enum(['safari-macos', 'golden-gate', 'linux', 'windows', 'none']).default('safari-macos'),
|
|
41
|
+
// 'css' renders the window frame in HTML/CSS in the Screenshot component (recommended);
|
|
42
|
+
// 'png' composites the chrome into the image at capture time (legacy)
|
|
43
|
+
renderIn: z.enum(['css', 'png']).default('png'),
|
|
41
44
|
// show the URL bar in the chrome
|
|
42
45
|
showUrl: z.boolean().default(true),
|
|
43
|
-
// dark chrome (title bar)
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
+
// light or dark chrome (title bar)
|
|
47
|
+
theme: z.enum(['light', 'dark']).default('light'),
|
|
48
|
+
// override the URL shown in the address bar (e.g. a public hostname instead of localhost)
|
|
49
|
+
// replaces the config-level baseUrl prefix in the displayed URL
|
|
50
|
+
baseUrl: z.string().url().optional(),
|
|
51
|
+
// png-only: drop shadow radius in pixels; 0 to disable
|
|
46
52
|
shadowBlur: z.number().int().min(0).default(40),
|
|
47
|
-
// extra padding around the window to make room for the shadow
|
|
53
|
+
// png-only: extra padding around the window to make room for the shadow
|
|
48
54
|
shadowPadding: z.number().int().min(0).default(48),
|
|
49
55
|
});
|
|
50
56
|
|
package/src/highlight.mjs
CHANGED
|
@@ -32,6 +32,13 @@ export async function injectHighlights(page, highlights) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const rect = target.getBoundingClientRect();
|
|
35
|
+
|
|
36
|
+
// skip hidden/zero-size elements (e.g. hidden inputs inside a tokenizer widget)
|
|
37
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
38
|
+
console.warn(`[screenshot] skipping zero-size element for selector: ${spec.selector}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
const scrollX = window.scrollX;
|
|
36
43
|
const scrollY = window.scrollY;
|
|
37
44
|
|