@crouton-kit/capture 1.3.19 → 1.3.21
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/bin/capture +159 -45
- package/package.json +1 -1
- package/src/cdp/args.ts +8 -1
- package/src/cdp/color-scheme.ts +47 -0
- package/src/cdp/commands/measure/index.ts +1 -1
- package/src/cdp/commands/measure/snap.ts +13 -6
- package/src/cdp/commands/page/click.ts +32 -4
- package/src/cdp/commands/page/index.ts +1 -1
- package/src/cdp/commands/page/scroll.ts +6 -4
- package/src/cdp/commands/page/shot.ts +33 -9
- package/src/cdp/commands/page/type.ts +6 -4
- package/src/cdp/measure/collectors/styles.ts +67 -43
- package/src/cdp/measure/snapshot.ts +26 -13
- package/src/cdp/measure/types.ts +4 -0
- package/src/cdp/types.ts +2 -0
package/bin/capture
CHANGED
|
@@ -9154,6 +9154,9 @@ function validateCliInvocation(parsed) {
|
|
|
9154
9154
|
throw invalidInput(`Invalid --viewport: ${parsed.viewport}.`);
|
|
9155
9155
|
}
|
|
9156
9156
|
}
|
|
9157
|
+
if (leaf === "shot" && parsed.colorScheme !== void 0 && parsed.colorScheme !== "dark" && parsed.colorScheme !== "light") {
|
|
9158
|
+
throw invalidInput(`Invalid --color-scheme: ${parsed.colorScheme}; expected dark or light.`);
|
|
9159
|
+
}
|
|
9157
9160
|
return;
|
|
9158
9161
|
}
|
|
9159
9162
|
if (parsed.command === "tab") {
|
|
@@ -9167,6 +9170,9 @@ function validateCliInvocation(parsed) {
|
|
|
9167
9170
|
}
|
|
9168
9171
|
if (parsed.command === "measure") {
|
|
9169
9172
|
if (leaf === "snap" || leaf === "check" || leaf === "sweep") requireCount(values, 0, 1, `measure ${leaf}`);
|
|
9173
|
+
if (leaf === "snap" && parsed.colorScheme !== void 0 && parsed.colorScheme !== "dark" && parsed.colorScheme !== "light") {
|
|
9174
|
+
throw invalidInput(`Invalid --color-scheme: ${parsed.colorScheme}; expected dark or light.`);
|
|
9175
|
+
}
|
|
9170
9176
|
if (leaf === "diff" || leaf === "census") requireCount(values, 0, 0, `measure ${leaf}`);
|
|
9171
9177
|
if (leaf === "diff" && (!parsed.before || !parsed.after)) throw invalidInput("measure diff requires --before and --after.");
|
|
9172
9178
|
if (leaf === "census" && !["color", "font", "spacing", "radius", "shadow", "animation", "geometry", "media", "queries"].includes(parsed.axis ?? "")) {
|
|
@@ -9272,6 +9278,9 @@ function parseCliSyntax(argv) {
|
|
|
9272
9278
|
parsed.viewport = value;
|
|
9273
9279
|
(parsed.viewports ??= []).push(value);
|
|
9274
9280
|
i++;
|
|
9281
|
+
} else if (arg === "--color-scheme") {
|
|
9282
|
+
parsed.colorScheme = valueFor(arg, next);
|
|
9283
|
+
i++;
|
|
9275
9284
|
} else if (arg === "--full-page") parsed.fullPage = true;
|
|
9276
9285
|
else if (arg === "--all") parsed.all = true;
|
|
9277
9286
|
else if (arg === "--session") {
|
|
@@ -9431,6 +9440,7 @@ var init_args = __esm({
|
|
|
9431
9440
|
"--url",
|
|
9432
9441
|
"--into",
|
|
9433
9442
|
"--viewport",
|
|
9443
|
+
"--color-scheme",
|
|
9434
9444
|
"--session",
|
|
9435
9445
|
"--filter",
|
|
9436
9446
|
"--name",
|
|
@@ -10221,7 +10231,7 @@ function validateHarAppendBatch(value, source) {
|
|
|
10221
10231
|
return { entries, incompleteLifecycles };
|
|
10222
10232
|
}
|
|
10223
10233
|
function captureVersion() {
|
|
10224
|
-
const version = "1.3.
|
|
10234
|
+
const version = "1.3.21";
|
|
10225
10235
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
10226
10236
|
}
|
|
10227
10237
|
function token() {
|
|
@@ -11980,7 +11990,7 @@ function wallTime(value) {
|
|
|
11980
11990
|
}
|
|
11981
11991
|
}
|
|
11982
11992
|
function captureVersion2() {
|
|
11983
|
-
const version = "1.3.
|
|
11993
|
+
const version = "1.3.21";
|
|
11984
11994
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
11985
11995
|
}
|
|
11986
11996
|
function deepFreeze(value) {
|
|
@@ -15933,6 +15943,21 @@ var init_interact = __esm({
|
|
|
15933
15943
|
function pageInputDeps() {
|
|
15934
15944
|
return deps2;
|
|
15935
15945
|
}
|
|
15946
|
+
async function capturePageInputScreenshot(client, action, label2, noScreenshot) {
|
|
15947
|
+
try {
|
|
15948
|
+
return {
|
|
15949
|
+
screenshot: await deps2.autoScreenshot(client, action, label2, noScreenshot),
|
|
15950
|
+
screenshotWarning: null
|
|
15951
|
+
};
|
|
15952
|
+
} catch (error) {
|
|
15953
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15954
|
+
if (!/^CDP request timeout \(\d+ms\): Page\.captureScreenshot$/.test(message)) throw error;
|
|
15955
|
+
return {
|
|
15956
|
+
screenshot: null,
|
|
15957
|
+
screenshotWarning: `automatic screenshot timed out after the page action completed; no screenshot was saved (${message})`
|
|
15958
|
+
};
|
|
15959
|
+
}
|
|
15960
|
+
}
|
|
15936
15961
|
function effectiveSettle(parsed, defaults) {
|
|
15937
15962
|
if (parsed.settle !== void 0) return parsed.settle;
|
|
15938
15963
|
return deps2.getActiveSession() !== null ? defaults.session : defaults.standalone;
|
|
@@ -16014,19 +16039,20 @@ async function cmdPageClick(parsed, _args) {
|
|
|
16014
16039
|
const resolved = await resolveLiveTarget(live, target);
|
|
16015
16040
|
if (!resolved.ok) return { failure: resolved };
|
|
16016
16041
|
const dispatch2 = await clickResolved(live, resolved);
|
|
16017
|
-
const
|
|
16018
|
-
return { dispatch: dispatch2,
|
|
16042
|
+
const screenshotResult = await capturePageInputScreenshot(client, "click", target, parsed.noScreenshot);
|
|
16043
|
+
return { dispatch: dispatch2, ...screenshotResult };
|
|
16019
16044
|
}
|
|
16020
16045
|
);
|
|
16021
16046
|
if ("failure" in outcome) {
|
|
16022
16047
|
return emitResolutionError(parsed, "page click", outcome.failure);
|
|
16023
16048
|
}
|
|
16024
|
-
const { dispatch, screenshot } = outcome;
|
|
16049
|
+
const { dispatch, screenshot, screenshotWarning } = outcome;
|
|
16025
16050
|
const rows = [
|
|
16026
16051
|
fact`clicked ${dispatch.role ?? "unknown"} "${dispatch.name ?? ""}" (backend:${dispatch.backendNodeId}) at x=${dispatch.x} y=${dispatch.y}`,
|
|
16027
16052
|
fact`settle: requested ${settleFacts.requestedMs}ms, waited ${settleFacts.waitedMs}ms`
|
|
16028
16053
|
];
|
|
16029
16054
|
if (screenshot) rows.push(fact`screenshot: ${screenshot}`);
|
|
16055
|
+
if (screenshotWarning) rows.push(fact`screenshot-warning: ${screenshotWarning}`);
|
|
16030
16056
|
emitResult(
|
|
16031
16057
|
{
|
|
16032
16058
|
tag: "clicked",
|
|
@@ -16056,7 +16082,7 @@ input:
|
|
|
16056
16082
|
--settle <ms> network-settle window applied after the click (default: 1000; 2500 with an active session; 0 disables)
|
|
16057
16083
|
--no-screenshot skip the auto-screenshot
|
|
16058
16084
|
output:
|
|
16059
|
-
<clicked backend-node-id=\u2026 role=\u2026 name=\u2026> \u2014 resolved identity, dispatched coordinates, the measured settle (requested/waited), screenshot artifact path; --json mirrors the same fields
|
|
16085
|
+
<clicked backend-node-id=\u2026 role=\u2026 name=\u2026> \u2014 resolved identity, dispatched coordinates, the measured settle (requested/waited), and either the screenshot artifact path or a warning when only that follow-up capture timed out; --json mirrors the same fields
|
|
16060
16086
|
effects:
|
|
16061
16087
|
scrolls the target into view, then dispatches a real mouse press/release at its center; writes one screenshot into the active session's shots/ sequence unless --no-screenshot`;
|
|
16062
16088
|
}
|
|
@@ -16091,19 +16117,20 @@ async function cmdPageType(parsed, _args) {
|
|
|
16091
16117
|
} else {
|
|
16092
16118
|
await typeText(live, textArg);
|
|
16093
16119
|
}
|
|
16094
|
-
const
|
|
16095
|
-
return { dispatch: dispatch2,
|
|
16120
|
+
const screenshotResult = await capturePageInputScreenshot(client, "type", parsed.into ?? "focused element", parsed.noScreenshot);
|
|
16121
|
+
return { dispatch: dispatch2, ...screenshotResult };
|
|
16096
16122
|
}
|
|
16097
16123
|
);
|
|
16098
16124
|
if ("failure" in outcome) {
|
|
16099
16125
|
return emitResolutionError(parsed, "page type", outcome.failure);
|
|
16100
16126
|
}
|
|
16101
|
-
const { dispatch, screenshot } = outcome;
|
|
16127
|
+
const { dispatch, screenshot, screenshotWarning } = outcome;
|
|
16102
16128
|
const rows = [
|
|
16103
16129
|
dispatch ? fact`typed "${textArg}" into ${dispatch.role ?? "unknown"} "${dispatch.name ?? ""}" (backend:${dispatch.backendNodeId}), focus click at x=${dispatch.x} y=${dispatch.y}` : fact`typed "${textArg}" into the focused element`,
|
|
16104
16130
|
fact`settle: requested ${settleFacts.requestedMs}ms, waited ${settleFacts.waitedMs}ms`
|
|
16105
16131
|
];
|
|
16106
16132
|
if (screenshot) rows.push(fact`screenshot: ${screenshot}`);
|
|
16133
|
+
if (screenshotWarning) rows.push(fact`screenshot-warning: ${screenshotWarning}`);
|
|
16107
16134
|
emitResult(
|
|
16108
16135
|
{
|
|
16109
16136
|
tag: "typed",
|
|
@@ -16131,7 +16158,7 @@ input:
|
|
|
16131
16158
|
--settle <ms> network-settle window applied after typing (default: 500; 1500 with an active session; 0 disables)
|
|
16132
16159
|
--no-screenshot skip the auto-screenshot
|
|
16133
16160
|
output:
|
|
16134
|
-
<typed> \u2014 the typed text, the resolved field identity (backend-node-id, role, name) and focus-click coordinates when --into was given, the measured settle (requested/waited), screenshot artifact path; --json mirrors the same fields
|
|
16161
|
+
<typed> \u2014 the typed text, the resolved field identity (backend-node-id, role, name) and focus-click coordinates when --into was given, the measured settle (requested/waited), and either the screenshot artifact path or a warning when only that follow-up capture timed out; --json mirrors the same fields
|
|
16135
16162
|
effects:
|
|
16136
16163
|
dispatches a real focus click (when --into) followed by real text insertion; writes one screenshot into the active session's shots/ sequence unless --no-screenshot`;
|
|
16137
16164
|
}
|
|
@@ -16176,19 +16203,20 @@ async function cmdPageScroll(parsed, _args) {
|
|
|
16176
16203
|
const resolved = await resolveLiveTarget(live, target);
|
|
16177
16204
|
if (!resolved.ok) return { failure: resolved };
|
|
16178
16205
|
const dispatch2 = await scrollResolved(live, resolved, to, { mark: `scroll:${target},to=${to}` });
|
|
16179
|
-
const
|
|
16180
|
-
return { dispatch: dispatch2,
|
|
16206
|
+
const screenshotResult = await capturePageInputScreenshot(client, "scroll", target, parsed.noScreenshot);
|
|
16207
|
+
return { dispatch: dispatch2, ...screenshotResult };
|
|
16181
16208
|
}
|
|
16182
16209
|
);
|
|
16183
16210
|
if ("failure" in outcome) {
|
|
16184
16211
|
return emitResolutionError(parsed, "page scroll", outcome.failure);
|
|
16185
16212
|
}
|
|
16186
|
-
const { dispatch, screenshot } = outcome;
|
|
16213
|
+
const { dispatch, screenshot, screenshotWarning } = outcome;
|
|
16187
16214
|
const rows = [
|
|
16188
16215
|
fact`scrolled ${dispatch.role ?? "unknown"} "${dispatch.name ?? ""}" (backend:${dispatch.backendNodeId}) to ${dispatch.to} — scrollTop now ${dispatch.scrollTop}`,
|
|
16189
16216
|
fact`settle: requested ${settleFacts.requestedMs}ms, waited ${settleFacts.waitedMs}ms`
|
|
16190
16217
|
];
|
|
16191
16218
|
if (screenshot) rows.push(fact`screenshot: ${screenshot}`);
|
|
16219
|
+
if (screenshotWarning) rows.push(fact`screenshot-warning: ${screenshotWarning}`);
|
|
16192
16220
|
emitResult(
|
|
16193
16221
|
{
|
|
16194
16222
|
tag: "scrolled",
|
|
@@ -16219,7 +16247,7 @@ input:
|
|
|
16219
16247
|
--settle <ms> network-settle window applied after the scroll (default: 1000; 2500 with an active session; 0 disables)
|
|
16220
16248
|
--no-screenshot skip the auto-screenshot
|
|
16221
16249
|
output:
|
|
16222
|
-
<scrolled backend-node-id=\u2026 role=\u2026 name=\u2026> \u2014 resolved identity, destination, the container's resulting scrollTop, the measured settle (requested/waited), screenshot artifact path; --json mirrors the same fields
|
|
16250
|
+
<scrolled backend-node-id=\u2026 role=\u2026 name=\u2026> \u2014 resolved identity, destination, the container's resulting scrollTop, the measured settle (requested/waited), and either the screenshot artifact path or a warning when only that follow-up capture timed out; --json mirrors the same fields
|
|
16223
16251
|
effects:
|
|
16224
16252
|
assigns the container's scrollTop in-page (may trigger lazy-load network); writes one screenshot into the active session's shots/ sequence unless --no-screenshot`;
|
|
16225
16253
|
}
|
|
@@ -227597,6 +227625,49 @@ var init_viewport = __esm({
|
|
|
227597
227625
|
}
|
|
227598
227626
|
});
|
|
227599
227627
|
|
|
227628
|
+
// src/cdp/color-scheme.ts
|
|
227629
|
+
function parseColorScheme(value) {
|
|
227630
|
+
if (value === void 0) return void 0;
|
|
227631
|
+
if (value === "dark" || value === "light") return value;
|
|
227632
|
+
throw new Error("--color-scheme must be dark or light");
|
|
227633
|
+
}
|
|
227634
|
+
async function withAppliedColorScheme(client, colorScheme, fn) {
|
|
227635
|
+
if (!colorScheme) return fn();
|
|
227636
|
+
let ownsMediaOverride = false;
|
|
227637
|
+
let primaryFailed = false;
|
|
227638
|
+
let primaryError;
|
|
227639
|
+
try {
|
|
227640
|
+
ownsMediaOverride = true;
|
|
227641
|
+
await client.send("Emulation.setEmulatedMedia", {
|
|
227642
|
+
features: [{ name: "prefers-color-scheme", value: colorScheme }]
|
|
227643
|
+
});
|
|
227644
|
+
return await fn();
|
|
227645
|
+
} catch (error) {
|
|
227646
|
+
primaryFailed = true;
|
|
227647
|
+
primaryError = error;
|
|
227648
|
+
throw error;
|
|
227649
|
+
} finally {
|
|
227650
|
+
if (ownsMediaOverride) {
|
|
227651
|
+
try {
|
|
227652
|
+
await client.send("Emulation.setEmulatedMedia", { features: [] });
|
|
227653
|
+
} catch (cleanupError) {
|
|
227654
|
+
if (primaryFailed) {
|
|
227655
|
+
throw new AggregateError(
|
|
227656
|
+
[primaryError, cleanupError],
|
|
227657
|
+
"Color-scheme capture failed and media-emulation cleanup also failed.",
|
|
227658
|
+
{ cause: primaryError }
|
|
227659
|
+
);
|
|
227660
|
+
}
|
|
227661
|
+
throw cleanupError;
|
|
227662
|
+
}
|
|
227663
|
+
}
|
|
227664
|
+
}
|
|
227665
|
+
}
|
|
227666
|
+
var init_color_scheme = __esm({
|
|
227667
|
+
"src/cdp/color-scheme.ts"() {
|
|
227668
|
+
}
|
|
227669
|
+
});
|
|
227670
|
+
|
|
227600
227671
|
// src/cdp/commands/page/shot.ts
|
|
227601
227672
|
function pngDimensions(png) {
|
|
227602
227673
|
if (png.length < 24) return null;
|
|
@@ -227625,7 +227696,8 @@ function buildScreenshotResult(f) {
|
|
|
227625
227696
|
const scale = f.dimensions && f.cssViewport ? { x: f.dimensions.width / f.cssViewport.width, y: f.dimensions.height / f.cssViewport.height } : void 0;
|
|
227626
227697
|
const sections = [
|
|
227627
227698
|
scale && f.cssViewport ? fact`CSS-to-image scale: ${scale.x.toFixed(6)} image px/CSS px horizontally and ${scale.y.toFixed(6)} image px/CSS px vertically (captured CSS region ${round6(f.cssViewport.width)}×${round6(f.cssViewport.height)} at page origin ${round6(f.cssViewport.x)},${round6(f.cssViewport.y)}).` : text`CSS-to-image scale unavailable: the browser did not return the CSS region this capture covers.`,
|
|
227628
|
-
f.emulation === "none" ? text`emulation: none — the browser's actual current viewport was captured
|
|
227699
|
+
f.emulation === "none" ? text`viewport emulation: none — the browser's actual current viewport was captured.` : f.emulation === "viewport" ? fact`emulation: transient ${f.viewport.width}x${f.viewport.height} device-metrics override applied for the capture and cleared after — two page-observable resizes.` : f.viewport ? fact`emulation: transient full-page device-metrics override (from ${f.viewport.width}x${f.viewport.height}) applied for the capture and cleared after — two page-observable resizes.` : text`emulation: transient full-page device-metrics override applied for the capture and cleared after — two page-observable resizes.`,
|
|
227700
|
+
f.colorScheme ? fact`color scheme: transient prefers-color-scheme=${f.colorScheme} override applied for the capture and cleared after.` : text`color scheme: browser default — no Emulation.setEmulatedMedia call was made.`
|
|
227629
227701
|
];
|
|
227630
227702
|
return {
|
|
227631
227703
|
tag: "screenshot",
|
|
@@ -227639,7 +227711,8 @@ function buildScreenshotResult(f) {
|
|
|
227639
227711
|
"css-y": f.cssViewport === void 0 ? void 0 : round6(f.cssViewport.y),
|
|
227640
227712
|
"css-to-image-x": scale?.x.toFixed(6),
|
|
227641
227713
|
"css-to-image-y": scale?.y.toFixed(6),
|
|
227642
|
-
emulation: f.emulation
|
|
227714
|
+
emulation: f.emulation,
|
|
227715
|
+
"color-scheme": f.colorScheme
|
|
227643
227716
|
},
|
|
227644
227717
|
summary: f.dimensions ? fact`saved ${f.path} — ${f.dimensions.width}x${f.dimensions.height}px, ${f.bytes} bytes.` : fact`saved ${f.path} — ${f.bytes} bytes.`,
|
|
227645
227718
|
sections
|
|
@@ -227655,7 +227728,7 @@ async function cmdPageShot(parsed, _args) {
|
|
|
227655
227728
|
{
|
|
227656
227729
|
tag: "error",
|
|
227657
227730
|
attrs: { command: "page shot", code: "invalid_input" },
|
|
227658
|
-
summary: fact`received: ${parsed.positional.length} positional arguments; expected none — \`capture page shot [--viewport <WxH>] [--full-page] [--out <path>]\`.`
|
|
227731
|
+
summary: fact`received: ${parsed.positional.length} positional arguments; expected none — \`capture page shot [--viewport <WxH>] [--full-page] [--color-scheme <dark|light>] [--out <path>]\`.`
|
|
227659
227732
|
},
|
|
227660
227733
|
{ json: parsed.json }
|
|
227661
227734
|
);
|
|
@@ -227679,18 +227752,33 @@ async function cmdPageShot(parsed, _args) {
|
|
|
227679
227752
|
return;
|
|
227680
227753
|
}
|
|
227681
227754
|
}
|
|
227755
|
+
let colorScheme;
|
|
227756
|
+
try {
|
|
227757
|
+
colorScheme = parseColorScheme(parsed.colorScheme);
|
|
227758
|
+
} catch {
|
|
227759
|
+
emitResult(
|
|
227760
|
+
{
|
|
227761
|
+
tag: "error",
|
|
227762
|
+
attrs: { command: "page shot", code: "invalid_color_scheme" },
|
|
227763
|
+
summary: fact`received: --color-scheme ${parsed.colorScheme}; expected dark or light.`
|
|
227764
|
+
},
|
|
227765
|
+
{ json: parsed.json }
|
|
227766
|
+
);
|
|
227767
|
+
process.exitCode = 1;
|
|
227768
|
+
return;
|
|
227769
|
+
}
|
|
227682
227770
|
const emulation = parsed.fullPage ? "full-page" : viewport2 ? "viewport" : "none";
|
|
227683
227771
|
const result = await deps4.withConnection(
|
|
227684
227772
|
{ ...parsed, command: "shot" },
|
|
227685
|
-
async (client) => {
|
|
227773
|
+
async (client) => withAppliedColorScheme(client, colorScheme, async () => {
|
|
227686
227774
|
const screenshot = await captureScreenshotWithCssViewport(client, viewport2, { fullPage: parsed.fullPage });
|
|
227687
227775
|
const outPath = await resolveOutPath(parsed);
|
|
227688
227776
|
writeScreenshot(outPath, screenshot.png);
|
|
227689
227777
|
return { path: outPath, bytes: screenshot.png.length, dimensions: pngDimensions(screenshot.png), cssViewport: screenshot.cssViewport };
|
|
227690
|
-
},
|
|
227778
|
+
}),
|
|
227691
227779
|
{ settle: 0 }
|
|
227692
227780
|
);
|
|
227693
|
-
emitResult(buildScreenshotResult({ ...result, emulation, viewport: viewport2 }), { json: parsed.json });
|
|
227781
|
+
emitResult(buildScreenshotResult({ ...result, emulation, viewport: viewport2, colorScheme }), { json: parsed.json });
|
|
227694
227782
|
}
|
|
227695
227783
|
var fs15, path17, USAGE6, deps4, PNG_SIGNATURE;
|
|
227696
227784
|
var init_shot = __esm({
|
|
@@ -227700,6 +227788,7 @@ var init_shot = __esm({
|
|
|
227700
227788
|
init_connection();
|
|
227701
227789
|
init_screenshot();
|
|
227702
227790
|
init_viewport();
|
|
227791
|
+
init_color_scheme();
|
|
227703
227792
|
init_session_context();
|
|
227704
227793
|
init_commands();
|
|
227705
227794
|
init_artifacts();
|
|
@@ -227709,13 +227798,14 @@ var init_shot = __esm({
|
|
|
227709
227798
|
input:
|
|
227710
227799
|
--viewport <WxH> transient device-metrics override for this capture; grammar: <positive-safe-int>x<positive-safe-int>, exact lowercase x with no whitespace \u2014 preset names are not accepted. Absent \u2192 no emulation, the browser's actual current viewport is captured
|
|
227711
227800
|
--full-page transient override to the full scrollable content height for this capture
|
|
227801
|
+
--color-scheme <dark|light> transient prefers-color-scheme override for this capture; cleared afterward
|
|
227712
227802
|
--out <path> destination file; default: the active session's shots/ sequence, or a fresh oneshot-*/page/ dir under the capture root when no session is active
|
|
227713
227803
|
--target <tabId> | --url <pattern> | --port <n> tab targeting; defaults to the active session tab
|
|
227714
227804
|
--json mirror the result as JSON
|
|
227715
227805
|
output:
|
|
227716
|
-
<screenshot path=\u2026 width=\u2026 height=\u2026 css-width=\u2026 css-height=\u2026 css-x=\u2026 css-y=\u2026 css-to-image-x=\u2026 css-to-image-y=\u2026 emulation=none|viewport|full-page> \u2014 saved path, PNG pixel dimensions, the CSS-pixel region the image covers (its page-coordinate origin and size) with the CSS-to-image scale derived from it, byte size, and
|
|
227806
|
+
<screenshot path=\u2026 width=\u2026 height=\u2026 css-width=\u2026 css-height=\u2026 css-x=\u2026 css-y=\u2026 css-to-image-x=\u2026 css-to-image-y=\u2026 emulation=none|viewport|full-page color-scheme=dark|light> \u2014 saved path, PNG pixel dimensions, the CSS-pixel region the image covers (its page-coordinate origin and size) with the CSS-to-image scale derived from it, byte size, and any requested transient emulation
|
|
227717
227807
|
effects:
|
|
227718
|
-
no flags: none \u2014 the capture reads the viewport as-is, with zero Emulation.* calls.
|
|
227808
|
+
no flags: none \u2014 the capture reads the viewport as-is, with zero Emulation.* calls. --viewport/--full-page applies a transient Emulation.setDeviceMetricsOverride (~150ms re-layout wait) and clears it after the capture \u2014 two page-observable resizes. --color-scheme applies a transient Emulation.setEmulatedMedia prefers-color-scheme override and clears it after the capture.`;
|
|
227719
227809
|
deps4 = { withConnection, nextStepPath, createOneshotSession };
|
|
227720
227810
|
PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
227721
227811
|
}
|
|
@@ -227856,7 +227946,7 @@ During a live composed recording every verb routes through the recorder.
|
|
|
227856
227946
|
<subcommand name="scroll" args="<target> --to <top|bottom|px> [--no-screenshot]" whenToUse="scroll one resolved container to a position"/>
|
|
227857
227947
|
<subcommand name="navigate" args="<url> [--settle <ms>]" whenToUse="navigate the tab and wait for load + settle"/>
|
|
227858
227948
|
<subcommand name="exec" args="<code> | --file <path>" whenToUse="run arbitrary JS in the tab (expressions, return, await)"/>
|
|
227859
|
-
<subcommand name="shot" args="[--viewport <WxH>] [--full-page] [--out <path>]" whenToUse="look at the page right now without acting"/>
|
|
227949
|
+
<subcommand name="shot" args="[--viewport <WxH>] [--full-page] [--color-scheme <dark|light>] [--out <path>]" whenToUse="look at the page right now without acting"/>
|
|
227860
227950
|
<subcommand name="elements" args="[--all] [--limit <n>]" whenToUse="list what can be acted on \u2014 role, name, backend:<id> per element"/>
|
|
227861
227951
|
|
|
227862
227952
|
capture page <leaf> -h Per-leaf usage`;
|
|
@@ -230854,17 +230944,23 @@ function normalizeComputed(raw) {
|
|
|
230854
230944
|
function resolvedIdentity3(backendNodeId2) {
|
|
230855
230945
|
return backendNodeId2 === void 0 ? { backendNodeId: null, identityUnresolved: true } : { backendNodeId: backendNodeId2 };
|
|
230856
230946
|
}
|
|
230857
|
-
async function mapWithConcurrency(values, limit, fn) {
|
|
230858
|
-
const result =
|
|
230947
|
+
async function mapWithConcurrency(values, limit, budgetMs, fn) {
|
|
230948
|
+
const result = Array(values.length).fill(void 0);
|
|
230949
|
+
const deadline = Date.now() + budgetMs;
|
|
230859
230950
|
let next = 0;
|
|
230951
|
+
let truncated = false;
|
|
230860
230952
|
const worker = async () => {
|
|
230861
230953
|
while (next < values.length) {
|
|
230954
|
+
if (Date.now() >= deadline) {
|
|
230955
|
+
truncated = true;
|
|
230956
|
+
return;
|
|
230957
|
+
}
|
|
230862
230958
|
const index = next++;
|
|
230863
230959
|
result[index] = await fn(values[index], index);
|
|
230864
230960
|
}
|
|
230865
230961
|
};
|
|
230866
230962
|
await Promise.all(Array.from({ length: Math.min(limit, values.length) }, worker));
|
|
230867
|
-
return result;
|
|
230963
|
+
return { values: result, truncated };
|
|
230868
230964
|
}
|
|
230869
230965
|
async function collectStylesInner(ctx, styleSheetUrls, styleSheetHeaders) {
|
|
230870
230966
|
const { client } = ctx;
|
|
@@ -230914,7 +231010,7 @@ async function collectStylesInner(ctx, styleSheetUrls, styleSheetHeaders) {
|
|
|
230914
231010
|
resolved = facts.map(() => void 0);
|
|
230915
231011
|
}
|
|
230916
231012
|
const sourceCache = /* @__PURE__ */ new Map();
|
|
230917
|
-
const
|
|
231013
|
+
const provenance = await mapWithConcurrency(facts, PROVENANCE_CONCURRENCY, PROVENANCE_BUDGET_MS, async (fact4, index) => {
|
|
230918
231014
|
const ref = resolved[index];
|
|
230919
231015
|
const computed2 = normalizeComputed(fact4.computed);
|
|
230920
231016
|
let winningDeclarations;
|
|
@@ -230949,6 +231045,7 @@ async function collectStylesInner(ctx, styleSheetUrls, styleSheetHeaders) {
|
|
|
230949
231045
|
provenanceUnavailable
|
|
230950
231046
|
};
|
|
230951
231047
|
});
|
|
231048
|
+
const elements = provenance.values.filter((element) => element !== void 0);
|
|
230952
231049
|
ctx.write.json("styles.json", {
|
|
230953
231050
|
elements,
|
|
230954
231051
|
coverage: {
|
|
@@ -230957,7 +231054,9 @@ async function collectStylesInner(ctx, styleSheetUrls, styleSheetHeaders) {
|
|
|
230957
231054
|
shadowRootsNotWalked: inventory.shadowRootsNotWalked,
|
|
230958
231055
|
totalCandidateElements: inventory.total,
|
|
230959
231056
|
keptElements: elements.length,
|
|
230960
|
-
elementsTruncated: inventory.total >
|
|
231057
|
+
elementsTruncated: inventory.total > facts.length,
|
|
231058
|
+
provenanceTruncated: provenance.truncated,
|
|
231059
|
+
...provenance.truncated ? { provenanceTruncationReason: "collector-time-budget" } : {}
|
|
230961
231060
|
},
|
|
230962
231061
|
styleSheetHeaders,
|
|
230963
231062
|
identity: identity3,
|
|
@@ -230965,13 +231064,14 @@ async function collectStylesInner(ctx, styleSheetUrls, styleSheetHeaders) {
|
|
|
230965
231064
|
...unavailableReason ? { unavailableReason } : {}
|
|
230966
231065
|
});
|
|
230967
231066
|
}
|
|
230968
|
-
var STYLES_MAX_ELEMENTS, PROVENANCE_CONCURRENCY, COMPUTED_PROPERTIES, PROVENANCE_PROPERTIES, EXCLUDED_TAGS, STYLES_SCRIPT, collectStyles;
|
|
231067
|
+
var STYLES_MAX_ELEMENTS, PROVENANCE_CONCURRENCY, PROVENANCE_BUDGET_MS, COMPUTED_PROPERTIES, PROVENANCE_PROPERTIES, EXCLUDED_TAGS, STYLES_SCRIPT, collectStyles;
|
|
230969
231068
|
var init_styles = __esm({
|
|
230970
231069
|
"src/cdp/measure/collectors/styles.ts"() {
|
|
230971
231070
|
init_redaction();
|
|
230972
231071
|
init_style_provenance();
|
|
230973
231072
|
STYLES_MAX_ELEMENTS = 150;
|
|
230974
231073
|
PROVENANCE_CONCURRENCY = 8;
|
|
231074
|
+
PROVENANCE_BUDGET_MS = 5e3;
|
|
230975
231075
|
COMPUTED_PROPERTIES = [
|
|
230976
231076
|
"display",
|
|
230977
231077
|
"position",
|
|
@@ -235864,22 +235964,24 @@ function boundedClient(client, phase, partialPath, onTimeout) {
|
|
|
235864
235964
|
const timeoutMs = Math.min(timeout, SNAPSHOT_REQUEST_TIMEOUT_MS);
|
|
235865
235965
|
let timer;
|
|
235866
235966
|
let timedOut = false;
|
|
235867
|
-
const markTimedOut = () => {
|
|
235967
|
+
const markTimedOut = (error) => {
|
|
235868
235968
|
if (timedOut) return;
|
|
235869
235969
|
timedOut = true;
|
|
235870
|
-
onTimeout?.();
|
|
235970
|
+
onTimeout?.(error);
|
|
235871
235971
|
};
|
|
235872
235972
|
const deadline = new Promise((_, reject) => {
|
|
235873
235973
|
timer = setTimeout(() => {
|
|
235874
|
-
|
|
235875
|
-
|
|
235974
|
+
const error = new SnapshotCaptureTimeout(phase, method, timeoutMs, partialPath);
|
|
235975
|
+
markTimedOut(error);
|
|
235976
|
+
reject(error);
|
|
235876
235977
|
}, timeoutMs);
|
|
235877
235978
|
});
|
|
235878
235979
|
const request = Promise.resolve().then(() => client.send(method, params, timeoutMs, sessionId)).catch((error) => {
|
|
235879
235980
|
if (error instanceof SnapshotCaptureTimeout) throw error;
|
|
235880
235981
|
if (error instanceof Error && error.message.startsWith("CDP request timeout")) {
|
|
235881
|
-
|
|
235882
|
-
|
|
235982
|
+
const timeoutError = new SnapshotCaptureTimeout(phase, method, timeoutMs, partialPath);
|
|
235983
|
+
markTimedOut(timeoutError);
|
|
235984
|
+
throw timeoutError;
|
|
235883
235985
|
}
|
|
235884
235986
|
throw error;
|
|
235885
235987
|
});
|
|
@@ -235901,6 +236003,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
235901
236003
|
const pixels = options.pixels ?? false;
|
|
235902
236004
|
const state = options.state ?? [];
|
|
235903
236005
|
const viewport2 = options.viewport ?? null;
|
|
236006
|
+
const colorScheme = options.colorScheme;
|
|
235904
236007
|
const url = options.url ? sanitizeString(options.url) : null;
|
|
235905
236008
|
const artifacts = [];
|
|
235906
236009
|
const phaseClient = (phase) => boundedClient(client, phase, dir);
|
|
@@ -235982,21 +236085,25 @@ async function captureSnapshotSubstrate(options) {
|
|
|
235982
236085
|
if (skipCollectors.has(collector.name)) return { name: collector.name, status: "skipped" };
|
|
235983
236086
|
const startedAt = Date.now();
|
|
235984
236087
|
let requestTimeouts = 0;
|
|
236088
|
+
let timeoutError;
|
|
235985
236089
|
const collectorWriter = makeCollectorWriter(ctx.write);
|
|
235986
236090
|
try {
|
|
235987
236091
|
await collector.fn({
|
|
235988
236092
|
...ctx,
|
|
235989
|
-
client: boundedClient(client, `collector:${collector.name}`, dir, () => {
|
|
236093
|
+
client: boundedClient(client, `collector:${collector.name}`, dir, (error) => {
|
|
235990
236094
|
requestTimeouts += 1;
|
|
236095
|
+
timeoutError ??= error;
|
|
235991
236096
|
}),
|
|
235992
236097
|
write: collectorWriter.writer
|
|
235993
236098
|
});
|
|
236099
|
+
if (collector.name === "styles" && timeoutError) throw timeoutError;
|
|
235994
236100
|
const ms = Date.now() - startedAt;
|
|
235995
236101
|
collectorWriter.commit();
|
|
235996
236102
|
return requestTimeouts > 0 ? { name: collector.name, status: "degraded", ms, requestTimeouts } : { name: collector.name, status: "ok", ms };
|
|
235997
|
-
} catch {
|
|
236103
|
+
} catch (error) {
|
|
235998
236104
|
const ms = Date.now() - startedAt;
|
|
235999
236105
|
collectorWriter.discard();
|
|
236106
|
+
if (collector.name === "styles" && error instanceof SnapshotCaptureTimeout) throw error;
|
|
236000
236107
|
return { name: collector.name, status: "error", ms, ...requestTimeouts > 0 ? { requestTimeouts } : {} };
|
|
236001
236108
|
}
|
|
236002
236109
|
};
|
|
@@ -236043,6 +236150,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
236043
236150
|
captureUnsettled,
|
|
236044
236151
|
pixels,
|
|
236045
236152
|
states: state.map((spec) => sanitizeString(spec)),
|
|
236153
|
+
...colorScheme ? { colorScheme } : {},
|
|
236046
236154
|
unstableRegionCount: unstableRegions.length,
|
|
236047
236155
|
...freezeAnimations ? { animationsRestored: animationsRestored ?? false } : {},
|
|
236048
236156
|
// #8: the browser-wide `Animation.setPlaybackRate({playbackRate:0})`
|
|
@@ -236568,7 +236676,7 @@ function settledNote(settled, captured, settleMs, settleTimeoutMs, unstableCount
|
|
|
236568
236676
|
}
|
|
236569
236677
|
return fact`Page did not settle within ${settleTimeoutMs}ms. No queryable substrate was written; evidence artifacts only (${unstableCount} unstable region(s)).`;
|
|
236570
236678
|
}
|
|
236571
|
-
async function captureMeasureSnap(parsed, targetRef = parsed.positional[0], viewportValue = parsed.viewport) {
|
|
236679
|
+
async function captureMeasureSnap(parsed, targetRef = parsed.positional[0], viewportValue = parsed.viewport, colorSchemeValue = parsed.colorScheme) {
|
|
236572
236680
|
if (parsed.positional.length > 1) {
|
|
236573
236681
|
throw invalidInput("measure snap accepts at most one positional URL or snapshot reference");
|
|
236574
236682
|
}
|
|
@@ -236581,6 +236689,7 @@ async function captureMeasureSnap(parsed, targetRef = parsed.positional[0], view
|
|
|
236581
236689
|
}
|
|
236582
236690
|
}
|
|
236583
236691
|
const viewport2 = viewportValue === void 0 ? void 0 : { label: viewportValue, ...parseViewport(viewportValue) };
|
|
236692
|
+
const colorScheme = parseColorScheme(colorSchemeValue);
|
|
236584
236693
|
let base;
|
|
236585
236694
|
let baseUrl = null;
|
|
236586
236695
|
if (targetRef && !isUrlRef(targetRef)) {
|
|
@@ -236595,7 +236704,7 @@ async function captureMeasureSnap(parsed, targetRef = parsed.positional[0], view
|
|
|
236595
236704
|
try {
|
|
236596
236705
|
let artifacts = [];
|
|
236597
236706
|
await withConnection(connectionArgs, async (client, tab) => {
|
|
236598
|
-
await withAppliedViewport(client, viewport2, async () => {
|
|
236707
|
+
await withAppliedColorScheme(client, colorScheme, async () => withAppliedViewport(client, viewport2, async () => {
|
|
236599
236708
|
const oneshot = active ? void 0 : createOneshotSession("measure");
|
|
236600
236709
|
const destination = active ? path22.join(active.dir, "measure", "snaps") : oneshot.artifactsDir;
|
|
236601
236710
|
snapDir = path22.join(destination, snapId);
|
|
@@ -236611,10 +236720,11 @@ async function captureMeasureSnap(parsed, targetRef = parsed.positional[0], view
|
|
|
236611
236720
|
pixels: parsed.pixels,
|
|
236612
236721
|
state: parsed.state ?? [],
|
|
236613
236722
|
viewport: viewport2?.label ?? null,
|
|
236723
|
+
colorScheme,
|
|
236614
236724
|
skipCollectors: parsed.skipCollector ?? []
|
|
236615
236725
|
});
|
|
236616
236726
|
artifacts = result.artifacts;
|
|
236617
|
-
});
|
|
236727
|
+
}));
|
|
236618
236728
|
}, { settle: 0 });
|
|
236619
236729
|
if (!snapDir) throw new Error("measure snap did not allocate an artifact directory");
|
|
236620
236730
|
return { id: snapId, dir: snapDir, artifacts, ...base ? { base } : {} };
|
|
@@ -236640,6 +236750,7 @@ function buildSnapshotResult(captured) {
|
|
|
236640
236750
|
attrs: {
|
|
236641
236751
|
url: meta.url ?? void 0,
|
|
236642
236752
|
viewport: meta.viewport ?? void 0,
|
|
236753
|
+
"color-scheme": meta.colorScheme,
|
|
236643
236754
|
elements,
|
|
236644
236755
|
settled: meta.settled,
|
|
236645
236756
|
"settle-ms": meta.settleMs,
|
|
@@ -236670,7 +236781,7 @@ function classifySnapError(err) {
|
|
|
236670
236781
|
if (message.startsWith("--viewport cannot ")) {
|
|
236671
236782
|
return { status: "viewport_unavailable", message };
|
|
236672
236783
|
}
|
|
236673
|
-
if (message.startsWith("measure snap accepts") || message.startsWith("--settle-timeout") || message.startsWith("--viewport")) {
|
|
236784
|
+
if (message.startsWith("measure snap accepts") || message.startsWith("--settle-timeout") || message.startsWith("--viewport") || message.startsWith("--color-scheme")) {
|
|
236674
236785
|
return { status: "invalid_input", message };
|
|
236675
236786
|
}
|
|
236676
236787
|
if (message.includes("No tab found") || message.includes("Use --target") || message.includes("Tab has no WebSocket debugger URL")) {
|
|
@@ -236715,9 +236826,10 @@ async function cmdMeasureSnap(parsed, _args) {
|
|
|
236715
236826
|
if (value === void 0) return void 0;
|
|
236716
236827
|
return { label: value, ...parseViewport(value) };
|
|
236717
236828
|
});
|
|
236829
|
+
const colorScheme = parseColorScheme(parsed.colorScheme);
|
|
236718
236830
|
const results = [];
|
|
236719
236831
|
for (const viewport2 of viewports) {
|
|
236720
|
-
const capture = await captureMeasureSnap(parsed, parsed.positional[0], viewport2?.label);
|
|
236832
|
+
const capture = await captureMeasureSnap(parsed, parsed.positional[0], viewport2?.label, colorScheme);
|
|
236721
236833
|
captures.push(capture);
|
|
236722
236834
|
results.push(buildSnapshotResult(capture));
|
|
236723
236835
|
}
|
|
@@ -236767,6 +236879,7 @@ var init_snap = __esm({
|
|
|
236767
236879
|
init_connection();
|
|
236768
236880
|
init_recorder_client();
|
|
236769
236881
|
init_viewport();
|
|
236882
|
+
init_color_scheme();
|
|
236770
236883
|
init_snapshot();
|
|
236771
236884
|
init_redaction();
|
|
236772
236885
|
init_settle();
|
|
@@ -236787,8 +236900,9 @@ input:
|
|
|
236787
236900
|
--pixels also write per-element raster crops
|
|
236788
236901
|
--state <state[:selector]> force a pseudo-state or real control state (repeatable)
|
|
236789
236902
|
--viewport <WxH> temporarily capture at a CSS-pixel viewport (repeatable); exact <positive-safe-int>x<positive-safe-int> grammar with lowercase x and no whitespace
|
|
236903
|
+
--color-scheme <dark|light> temporarily capture under this prefers-color-scheme value; cleared afterward
|
|
236790
236904
|
output: <snapshot id=\u2026 path=\u2026> \u2014 the settled artifact directory (geometry, styles, hit-test, text, forms, animation, ax, queries, focus, scroll, layers; per-element crops with --pixels) every other measure/motion query leaf reads instead of re-driving the browser; --json mirrors
|
|
236791
|
-
effects: drives the target page to capture; writes one snapshot artifact directory under the active (or a private one-shot) session
|
|
236905
|
+
effects: drives the target page to capture; writes one snapshot artifact directory under the active (or a private one-shot) session. --color-scheme transiently applies Emulation.setEmulatedMedia and clears it after capture.`;
|
|
236792
236906
|
}
|
|
236793
236907
|
});
|
|
236794
236908
|
|
|
@@ -240154,7 +240268,7 @@ Every leaf defaults to rendered prose; --json mirrors the same result.
|
|
|
240154
240268
|
Findings exit 0 \u2014 a report, not a failure.
|
|
240155
240269
|
\`--gate\` (exit 2 on findings/changes) is accepted only by check and diff.
|
|
240156
240270
|
|
|
240157
|
-
<subcommand name="snap" args="[url|snap] [--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled] [--pixels] [--state <state[:selector]>]... [--viewport <WxH>]..." whenToUse="drive + write the settled snapshot substrate every other leaf reads"/>
|
|
240271
|
+
<subcommand name="snap" args="[url|snap] [--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled] [--pixels] [--state <state[:selector]>]... [--viewport <WxH>]... [--color-scheme <dark|light>]" whenToUse="drive + write the settled snapshot substrate every other leaf reads"/>
|
|
240158
240272
|
<subcommand name="check" args="[url|snap] [--for <checks>] [--gate]" whenToUse="read threshold/fact measurements from one snapshot"/>
|
|
240159
240273
|
<subcommand name="diff" args="--before <snap> --after <snap> [--pixels] [--full] [--gate]" whenToUse="structured before/after delta between two snapshots"/>
|
|
240160
240274
|
<subcommand name="census" args="[--snap <id>]... [--url <url>]... [--set-file <path>] --axis <axis>" whenToUse="value distributions across one or more snapshots"/>
|
|
@@ -242098,7 +242212,7 @@ CDP_PORT / CDP_TARGET pin the browser + tab for orchestrators \u2014 precedence:
|
|
|
242098
242212
|
explicit flag > active session > env.`;
|
|
242099
242213
|
}
|
|
242100
242214
|
function printVersion() {
|
|
242101
|
-
const declared = "1.3.
|
|
242215
|
+
const declared = "1.3.21";
|
|
242102
242216
|
if (declared) {
|
|
242103
242217
|
console.log(declared);
|
|
242104
242218
|
return;
|
package/package.json
CHANGED
package/src/cdp/args.ts
CHANGED
|
@@ -15,7 +15,7 @@ export function expandEqualsFlags(argv: string[]): string[] {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const VALUE_FLAGS = new Set([
|
|
18
|
-
'--port', '--out', '--duration', '--settle', '--file', '--target', '--url', '--into', '--viewport', '--session',
|
|
18
|
+
'--port', '--out', '--duration', '--settle', '--file', '--target', '--url', '--into', '--viewport', '--color-scheme', '--session',
|
|
19
19
|
'--filter', '--name', '--filter-url', '--filter-status', '--filter-method', '--limit', '--params', '--wait-event',
|
|
20
20
|
'--timeout', '--socket', '--settle-timeout', '--state', '--for', '--before', '--after', '--snap', '--set-file',
|
|
21
21
|
'--axis', '--from', '--to', '--viewport-height', '--rec-id', '--selector', '--do', '--element', '--prop', '--action', '--occurrence',
|
|
@@ -126,6 +126,9 @@ export function validateCliInvocation(parsed: ParsedArgs): void {
|
|
|
126
126
|
throw invalidInput(`Invalid --viewport: ${parsed.viewport}.`);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
if (leaf === 'shot' && parsed.colorScheme !== undefined && parsed.colorScheme !== 'dark' && parsed.colorScheme !== 'light') {
|
|
130
|
+
throw invalidInput(`Invalid --color-scheme: ${parsed.colorScheme}; expected dark or light.`);
|
|
131
|
+
}
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -143,6 +146,9 @@ export function validateCliInvocation(parsed: ParsedArgs): void {
|
|
|
143
146
|
|
|
144
147
|
if (parsed.command === 'measure') {
|
|
145
148
|
if (leaf === 'snap' || leaf === 'check' || leaf === 'sweep') requireCount(values, 0, 1, `measure ${leaf}`);
|
|
149
|
+
if (leaf === 'snap' && parsed.colorScheme !== undefined && parsed.colorScheme !== 'dark' && parsed.colorScheme !== 'light') {
|
|
150
|
+
throw invalidInput(`Invalid --color-scheme: ${parsed.colorScheme}; expected dark or light.`);
|
|
151
|
+
}
|
|
146
152
|
if (leaf === 'diff' || leaf === 'census') requireCount(values, 0, 0, `measure ${leaf}`);
|
|
147
153
|
if (leaf === 'diff' && (!parsed.before || !parsed.after)) throw invalidInput('measure diff requires --before and --after.');
|
|
148
154
|
if (leaf === 'census' && !['color', 'font', 'spacing', 'radius', 'shadow', 'animation', 'geometry', 'media', 'queries'].includes(parsed.axis ?? '')) {
|
|
@@ -233,6 +239,7 @@ export function parseCliSyntax(argv: string[]): ParsedArgs {
|
|
|
233
239
|
else if (arg === '--into') { parsed.into = valueFor(arg, next); i++; }
|
|
234
240
|
else if (arg === '--no-screenshot') parsed.noScreenshot = true;
|
|
235
241
|
else if (arg === '--viewport') { const value = valueFor(arg, next); parsed.viewport = value; (parsed.viewports ??= []).push(value); i++; }
|
|
242
|
+
else if (arg === '--color-scheme') { parsed.colorScheme = valueFor(arg, next); i++; }
|
|
236
243
|
else if (arg === '--full-page') parsed.fullPage = true;
|
|
237
244
|
else if (arg === '--all') parsed.all = true;
|
|
238
245
|
else if (arg === '--session') { parsed.session = valueFor(arg, next); i++; }
|