@crouton-kit/capture 1.3.7 → 1.3.8
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 +2807 -1312
- package/package.json +4 -1
- package/src/capture.ts +10 -0
- package/src/cdp/args.ts +99 -10
- package/src/cdp/bridge/protocol.ts +99 -0
- package/src/cdp/bridge/server.ts +105 -43
- package/src/cdp/bridge/spawn.ts +62 -0
- package/src/cdp/commands/bridge-serve.ts +28 -3
- package/src/cdp/commands/cdp.ts +30 -2
- package/src/cdp/commands/measure/census.ts +31 -0
- package/src/cdp/commands/measure/check.ts +35 -0
- package/src/cdp/commands/measure/diff.ts +32 -0
- package/src/cdp/commands/measure/explain.ts +31 -0
- package/src/cdp/commands/measure/index.ts +114 -0
- package/src/cdp/commands/measure/map-focus.ts +24 -0
- package/src/cdp/commands/measure/map-layers.ts +25 -0
- package/src/cdp/commands/measure/map-scroll.ts +24 -0
- package/src/cdp/commands/measure/snap.ts +36 -0
- package/src/cdp/commands/measure/sweep.ts +30 -0
- package/src/cdp/commands/motion/index.ts +60 -0
- package/src/cdp/commands/motion/jank.ts +22 -0
- package/src/cdp/commands/motion/mask.ts +23 -0
- package/src/cdp/commands/motion/rec.ts +33 -0
- package/src/cdp/commands/motion/response.ts +26 -0
- package/src/cdp/commands/motion/timeline.ts +27 -0
- package/src/cdp/coordinates.ts +158 -0
- package/src/cdp/dispatch.ts +4 -0
- package/src/cdp/domains.ts +75 -0
- package/src/cdp/recorder-bridge.ts +521 -0
- package/src/cdp/source-map.ts +309 -0
- package/src/cdp/timing.ts +82 -0
- package/src/cdp/types.ts +66 -0
- package/src/cdp/usage.ts +2 -0
- package/src/output/artifact.ts +520 -0
- package/src/output/diff.ts +111 -0
- package/src/output/render.ts +593 -0
- package/src/output/selector.ts +185 -0
- package/src/session/artifacts.ts +229 -0
- package/src/session/commands.ts +119 -15
- package/src/session-context.ts +48 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure census [--snap <id>]... [--url <url>]... [--set-file <path>] --axis <axis>
|
|
5
|
+
|
|
6
|
+
Value distributions and token audits across one or more snapshots. Each
|
|
7
|
+
--snap/--url is repeatable to build a cross-page distribution; a --url
|
|
8
|
+
target creates a one-shot snap first.
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--snap <id> Existing snapshot id/path (repeatable)
|
|
12
|
+
--url <url> URL to snap first (repeatable)
|
|
13
|
+
--set-file <path> File listing additional snap/url targets, one per line
|
|
14
|
+
--viewport <WxH> Viewport to snap at (repeatable)
|
|
15
|
+
--axis <axis> color|font|spacing|radius|shadow|animation|geometry|media|queries
|
|
16
|
+
|
|
17
|
+
Not yet implemented.`;
|
|
18
|
+
|
|
19
|
+
export async function cmdMeasureCensus(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
20
|
+
if (parsed.help) {
|
|
21
|
+
console.log(USAGE);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
const result: RenderableResult = {
|
|
25
|
+
tag: 'error',
|
|
26
|
+
attrs: { command: 'measure census', status: 'not_implemented' },
|
|
27
|
+
summary: fact`\`measure census\` is not implemented yet.`,
|
|
28
|
+
};
|
|
29
|
+
emitResult(result, { json: parsed.json });
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure check [url|snap] [--for <checks>] [--viewport <WxH>]... [--gate]
|
|
5
|
+
|
|
6
|
+
Threshold/fact checks over a settled snapshot's substrate: overlap,
|
|
7
|
+
offscreen, overflow, tap-target size, contrast, hit-test receiver,
|
|
8
|
+
truncation, form visibility, media/replaced anomalies, and animation
|
|
9
|
+
facts. A URL target creates a one-shot snap first.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--for <checks> Comma-separated check names (e.g.
|
|
13
|
+
overlap,offscreen,overflow,tap-targets,contrast,
|
|
14
|
+
hit-test,truncation) or a category
|
|
15
|
+
(geometry|content|targetability|forms|animation|all).
|
|
16
|
+
Omit for every check.
|
|
17
|
+
--viewport <WxH> Viewport to check at (repeatable, for a discrete
|
|
18
|
+
multi-viewport sweep)
|
|
19
|
+
--gate Exit 2 if any check finds something (default: exit 0)
|
|
20
|
+
|
|
21
|
+
Not yet implemented.`;
|
|
22
|
+
|
|
23
|
+
export async function cmdMeasureCheck(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
24
|
+
if (parsed.help) {
|
|
25
|
+
console.log(USAGE);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
const result: RenderableResult = {
|
|
29
|
+
tag: 'error',
|
|
30
|
+
attrs: { command: 'measure check', status: 'not_implemented' },
|
|
31
|
+
summary: fact`\`measure check\` is not implemented yet.`,
|
|
32
|
+
};
|
|
33
|
+
emitResult(result, { json: parsed.json });
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure diff --before <snap> --after <snap> [--pixels] [--full] [--gate]
|
|
5
|
+
|
|
6
|
+
Structured before/after diff over two snapshots: style/geometry/text/form/
|
|
7
|
+
media deltas, cascade provenance, and reflow attribution. No positional
|
|
8
|
+
target — both snapshots must already exist.
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--before <snap> Earlier snapshot id/path (required)
|
|
12
|
+
--after <snap> Later snapshot id/path (required)
|
|
13
|
+
--pixels Also report raster regions not explained by geometry
|
|
14
|
+
--full Expand to state-matrix deltas and complete per-element
|
|
15
|
+
change records
|
|
16
|
+
--gate Exit 2 if any element changed (default: exit 0)
|
|
17
|
+
|
|
18
|
+
Not yet implemented.`;
|
|
19
|
+
|
|
20
|
+
export async function cmdMeasureDiff(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
21
|
+
if (parsed.help) {
|
|
22
|
+
console.log(USAGE);
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
const result: RenderableResult = {
|
|
26
|
+
tag: 'error',
|
|
27
|
+
attrs: { command: 'measure diff', status: 'not_implemented' },
|
|
28
|
+
summary: fact`\`measure diff\` is not implemented yet.`,
|
|
29
|
+
};
|
|
30
|
+
emitResult(result, { json: parsed.json });
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure explain <snap> --selector <sel> [--size] [--text] [--form]
|
|
5
|
+
|
|
6
|
+
Per-element cascade/stacking/clipping explanation over one snapshot, plus
|
|
7
|
+
optional detail sections.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--selector <sel> Element selector (required)
|
|
11
|
+
--size Include size/layout provenance (flex/grid/intrinsic)
|
|
12
|
+
--text Include text/line-box/wrap detail
|
|
13
|
+
--form Include form/caret/selection/autofill detail (redacted)
|
|
14
|
+
|
|
15
|
+
With no detail flag, returns the standard compact element explanation.
|
|
16
|
+
|
|
17
|
+
Not yet implemented.`;
|
|
18
|
+
|
|
19
|
+
export async function cmdMeasureExplain(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
20
|
+
if (parsed.help) {
|
|
21
|
+
console.log(USAGE);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
const result: RenderableResult = {
|
|
25
|
+
tag: 'error',
|
|
26
|
+
attrs: { command: 'measure explain', status: 'not_implemented' },
|
|
27
|
+
summary: fact`\`measure explain\` is not implemented yet.`,
|
|
28
|
+
};
|
|
29
|
+
emitResult(result, { json: parsed.json });
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `capture measure` branch router.
|
|
3
|
+
*
|
|
4
|
+
* Shifts the consumed leaf token(s) out of `parsed.positional` before
|
|
5
|
+
* handing off, so every leaf command still finds its own primary target
|
|
6
|
+
* (a URL, a snap id, ...) at `positional[0]` exactly as every other capture
|
|
7
|
+
* command does — leaf commands never need to know they're nested under a
|
|
8
|
+
* branch.
|
|
9
|
+
*/
|
|
10
|
+
import { type ParsedArgs } from '../../types.js';
|
|
11
|
+
import { cmdMeasureSnap } from './snap.js';
|
|
12
|
+
import { cmdMeasureCheck } from './check.js';
|
|
13
|
+
import { cmdMeasureDiff } from './diff.js';
|
|
14
|
+
import { cmdMeasureCensus } from './census.js';
|
|
15
|
+
import { cmdMeasureExplain } from './explain.js';
|
|
16
|
+
import { cmdMeasureSweep } from './sweep.js';
|
|
17
|
+
import { cmdMeasureMapFocus } from './map-focus.js';
|
|
18
|
+
import { cmdMeasureMapScroll } from './map-scroll.js';
|
|
19
|
+
import { cmdMeasureMapLayers } from './map-layers.js';
|
|
20
|
+
|
|
21
|
+
export const MEASURE_USAGE = `capture measure — enriched snapshot substrate + read-only queries over it.
|
|
22
|
+
|
|
23
|
+
\`snap\` drives the page (or a base snapshot) and writes one settled artifact
|
|
24
|
+
directory; every other leaf below is a cheap read over that artifact and
|
|
25
|
+
never re-drives the browser unless it explicitly accepts a URL target.
|
|
26
|
+
|
|
27
|
+
Leaves:
|
|
28
|
+
snap [url|snap] Drive + write a settled snapshot substrate
|
|
29
|
+
[--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled]
|
|
30
|
+
[--pixels] [--state <state[:selector]>]...
|
|
31
|
+
|
|
32
|
+
check [url|snap] [--for <checks>] [--viewport <WxH>]... [--gate]
|
|
33
|
+
Threshold/fact checks over a snapshot
|
|
34
|
+
|
|
35
|
+
diff --before <snap> --after <snap> [--pixels] [--full] [--gate]
|
|
36
|
+
Structured before/after snapshot diff
|
|
37
|
+
|
|
38
|
+
census [--snap <id>]... [--url <url>]... [--set-file <path>] --axis <axis>
|
|
39
|
+
Value distributions across one or more snapshots
|
|
40
|
+
(--snap/--url repeatable)
|
|
41
|
+
|
|
42
|
+
explain <snap> --selector <sel> [--size] [--text] [--form]
|
|
43
|
+
Per-element cascade/stacking/clipping/size/text/form explanation
|
|
44
|
+
|
|
45
|
+
sweep [url] --axis <axis> [--from <val>] [--to <val>] [--viewport-height <val>]
|
|
46
|
+
Responsive/environment sampling
|
|
47
|
+
|
|
48
|
+
map focus|scroll|layers [url|snap] Read one facet of a snapshot's substrate
|
|
49
|
+
(see \`capture measure map --help\`)
|
|
50
|
+
|
|
51
|
+
Every leaf defaults to rendered prose; --json mirrors the same result as JSON.
|
|
52
|
+
Exit codes: 0 by default — findings are a report, not a failure.
|
|
53
|
+
--gate exits 2 on findings/changes; only \`check\` and \`diff\` accept it.
|
|
54
|
+
|
|
55
|
+
capture measure <leaf> --help Per-leaf usage`;
|
|
56
|
+
|
|
57
|
+
export const MEASURE_MAP_USAGE = `capture measure map — read one facet of a snapshot's substrate (no browser re-drive).
|
|
58
|
+
|
|
59
|
+
Leaves:
|
|
60
|
+
focus [url|snap] Keyboard traversal order (focus.json)
|
|
61
|
+
scroll [url|snap] Scroll-container topology (scroll.json)
|
|
62
|
+
layers [url|snap] Paint/compositor layer map (layers.json)
|
|
63
|
+
|
|
64
|
+
A URL target first creates a snap; a snap target reads its existing artifact.
|
|
65
|
+
|
|
66
|
+
capture measure map <leaf> --help Per-leaf usage`;
|
|
67
|
+
|
|
68
|
+
export async function measureMain(parsed: ParsedArgs, args: string[]): Promise<void> {
|
|
69
|
+
const leaf = parsed.positional[0];
|
|
70
|
+
const rest: ParsedArgs = { ...parsed, positional: parsed.positional.slice(1) };
|
|
71
|
+
|
|
72
|
+
switch (leaf) {
|
|
73
|
+
case 'snap':
|
|
74
|
+
return cmdMeasureSnap(rest, args);
|
|
75
|
+
case 'check':
|
|
76
|
+
return cmdMeasureCheck(rest, args);
|
|
77
|
+
case 'diff':
|
|
78
|
+
return cmdMeasureDiff(rest, args);
|
|
79
|
+
case 'census':
|
|
80
|
+
return cmdMeasureCensus(rest, args);
|
|
81
|
+
case 'explain':
|
|
82
|
+
return cmdMeasureExplain(rest, args);
|
|
83
|
+
case 'sweep':
|
|
84
|
+
return cmdMeasureSweep(rest, args);
|
|
85
|
+
case 'map':
|
|
86
|
+
return measureMapMain(rest, args);
|
|
87
|
+
case undefined:
|
|
88
|
+
console.log(MEASURE_USAGE);
|
|
89
|
+
return;
|
|
90
|
+
default:
|
|
91
|
+
console.error(`Unknown measure leaf: ${leaf}\n\n${MEASURE_USAGE}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function measureMapMain(parsed: ParsedArgs, args: string[]): Promise<void> {
|
|
97
|
+
const sub = parsed.positional[0];
|
|
98
|
+
const rest: ParsedArgs = { ...parsed, positional: parsed.positional.slice(1) };
|
|
99
|
+
|
|
100
|
+
switch (sub) {
|
|
101
|
+
case 'focus':
|
|
102
|
+
return cmdMeasureMapFocus(rest, args);
|
|
103
|
+
case 'scroll':
|
|
104
|
+
return cmdMeasureMapScroll(rest, args);
|
|
105
|
+
case 'layers':
|
|
106
|
+
return cmdMeasureMapLayers(rest, args);
|
|
107
|
+
case undefined:
|
|
108
|
+
console.log(MEASURE_MAP_USAGE);
|
|
109
|
+
return;
|
|
110
|
+
default:
|
|
111
|
+
console.error(`Unknown measure map leaf: ${sub}\n\n${MEASURE_MAP_USAGE}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure map focus [url|snap]
|
|
5
|
+
|
|
6
|
+
Keyboard traversal order recorded in a snapshot's focus.json: forward and
|
|
7
|
+
reverse Tab sequence, rects, scroll jumps, focus-visible facts, and
|
|
8
|
+
unreached focusable elements. A URL target creates a snap first.
|
|
9
|
+
|
|
10
|
+
Not yet implemented.`;
|
|
11
|
+
|
|
12
|
+
export async function cmdMeasureMapFocus(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
13
|
+
if (parsed.help) {
|
|
14
|
+
console.log(USAGE);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
const result: RenderableResult = {
|
|
18
|
+
tag: 'error',
|
|
19
|
+
attrs: { command: 'measure map focus', status: 'not_implemented' },
|
|
20
|
+
summary: fact`\`measure map focus\` is not implemented yet.`,
|
|
21
|
+
};
|
|
22
|
+
emitResult(result, { json: parsed.json });
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure map layers [url|snap]
|
|
5
|
+
|
|
6
|
+
Paint/compositor layer map recorded in a snapshot's layers.json: layers,
|
|
7
|
+
bounds, compositing reasons, paint order, per-node membership, and source
|
|
8
|
+
provenance for layer-affecting declarations. A URL target creates a snap
|
|
9
|
+
first.
|
|
10
|
+
|
|
11
|
+
Not yet implemented.`;
|
|
12
|
+
|
|
13
|
+
export async function cmdMeasureMapLayers(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
14
|
+
if (parsed.help) {
|
|
15
|
+
console.log(USAGE);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
const result: RenderableResult = {
|
|
19
|
+
tag: 'error',
|
|
20
|
+
attrs: { command: 'measure map layers', status: 'not_implemented' },
|
|
21
|
+
summary: fact`\`measure map layers\` is not implemented yet.`,
|
|
22
|
+
};
|
|
23
|
+
emitResult(result, { json: parsed.json });
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure map scroll [url|snap]
|
|
5
|
+
|
|
6
|
+
Scroll-container topology recorded in a snapshot's scroll.json: containers,
|
|
7
|
+
ranges, current/max offsets, sticky/fixed occupancy, snap points, and
|
|
8
|
+
visual/layout viewport facts. A URL target creates a snap first.
|
|
9
|
+
|
|
10
|
+
Not yet implemented.`;
|
|
11
|
+
|
|
12
|
+
export async function cmdMeasureMapScroll(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
13
|
+
if (parsed.help) {
|
|
14
|
+
console.log(USAGE);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
const result: RenderableResult = {
|
|
18
|
+
tag: 'error',
|
|
19
|
+
attrs: { command: 'measure map scroll', status: 'not_implemented' },
|
|
20
|
+
summary: fact`\`measure map scroll\` is not implemented yet.`,
|
|
21
|
+
};
|
|
22
|
+
emitResult(result, { json: parsed.json });
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure snap [url|snap] [--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled] [--pixels] [--state <state[:selector]>]... [--viewport <WxH>]...
|
|
5
|
+
|
|
6
|
+
Drive the page (or re-capture states over a base snapshot) and write one
|
|
7
|
+
settled snapshot substrate directory: geometry, styles, hit-test, text,
|
|
8
|
+
forms, animation, ax, queries, focus, scroll, layers, and (with --pixels)
|
|
9
|
+
per-element crops. Every other measure/motion query leaf reads this
|
|
10
|
+
artifact instead of re-driving the browser.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--freeze-animations Pause CSS/WAAPI animation before capture
|
|
14
|
+
--settle-timeout <ms> Override the default 5000ms settle wait
|
|
15
|
+
--capture-unsettled Write full substrate despite non-settlement,
|
|
16
|
+
marking unstable regions
|
|
17
|
+
--pixels Also write per-element raster crops
|
|
18
|
+
--state <state[:selector]> Force a pseudo-state or real control state
|
|
19
|
+
(repeatable)
|
|
20
|
+
--viewport <WxH> Viewport to snap at (repeatable)
|
|
21
|
+
|
|
22
|
+
Not yet implemented.`;
|
|
23
|
+
|
|
24
|
+
export async function cmdMeasureSnap(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
25
|
+
if (parsed.help) {
|
|
26
|
+
console.log(USAGE);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
const result: RenderableResult = {
|
|
30
|
+
tag: 'error',
|
|
31
|
+
attrs: { command: 'measure snap', status: 'not_implemented' },
|
|
32
|
+
summary: fact`\`measure snap\` is not implemented yet.`,
|
|
33
|
+
};
|
|
34
|
+
emitResult(result, { json: parsed.json });
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture measure sweep [url] --axis <axis> [--from <val>] [--to <val>] [--viewport-height <val>]
|
|
5
|
+
|
|
6
|
+
Responsive/environment sampling: repeated snap captures across an axis
|
|
7
|
+
(width, dpr, zoom, color-scheme, reduced-motion, ...), binary-searched to
|
|
8
|
+
find transition points and piecewise-stable ranges.
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--axis <axis> The sampled axis (default: width)
|
|
12
|
+
--from <val> Range start (axis-dependent: width px, dpr, zoom, ...)
|
|
13
|
+
--to <val> Range end (axis-dependent)
|
|
14
|
+
--viewport-height <val> Fixed viewport height while sweeping width/dpr/zoom
|
|
15
|
+
|
|
16
|
+
Not yet implemented.`;
|
|
17
|
+
|
|
18
|
+
export async function cmdMeasureSweep(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
19
|
+
if (parsed.help) {
|
|
20
|
+
console.log(USAGE);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
const result: RenderableResult = {
|
|
24
|
+
tag: 'error',
|
|
25
|
+
attrs: { command: 'measure sweep', status: 'not_implemented' },
|
|
26
|
+
summary: fact`\`measure sweep\` is not implemented yet.`,
|
|
27
|
+
};
|
|
28
|
+
emitResult(result, { json: parsed.json });
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `capture motion` branch router.
|
|
3
|
+
*
|
|
4
|
+
* Shifts the consumed leaf token out of `parsed.positional` before handing
|
|
5
|
+
* off, so every leaf command still finds its own primary target (a URL, a
|
|
6
|
+
* recording id, ...) at `positional[0]` exactly as every other capture
|
|
7
|
+
* command does.
|
|
8
|
+
*/
|
|
9
|
+
import { type ParsedArgs } from '../../types.js';
|
|
10
|
+
import { cmdMotionRec } from './rec.js';
|
|
11
|
+
import { cmdMotionMask } from './mask.js';
|
|
12
|
+
import { cmdMotionTimeline } from './timeline.js';
|
|
13
|
+
import { cmdMotionJank } from './jank.js';
|
|
14
|
+
import { cmdMotionResponse } from './response.js';
|
|
15
|
+
|
|
16
|
+
export const MOTION_USAGE = `capture motion — recorder lifecycle + read-only queries over a finalized recording.
|
|
17
|
+
|
|
18
|
+
\`rec\` drives (and records) the browser, one-shot or composed across
|
|
19
|
+
intervening commands; every other leaf below is a cheap read over the
|
|
20
|
+
finalized recording artifact.
|
|
21
|
+
|
|
22
|
+
Leaves:
|
|
23
|
+
rec [url] --do <action> [--duration <ms>] One-shot: drive one action, record it
|
|
24
|
+
rec --start Composed: arm the recorder (needs an active session)
|
|
25
|
+
rec --stop Composed: finalize the recorder
|
|
26
|
+
|
|
27
|
+
mask <rec> Motion-diff composite image + per-region facts
|
|
28
|
+
timeline <rec> --element <sel> [--prop <prop>]
|
|
29
|
+
Per-frame geometry/scroll/property timeline
|
|
30
|
+
jank <rec> Dropped-frame/long-task/layout-shift facts
|
|
31
|
+
response <rec> [--action <action>] Input-to-settled response timeline
|
|
32
|
+
|
|
33
|
+
Every leaf defaults to rendered prose; --json mirrors the same result as JSON.
|
|
34
|
+
Exit codes: 0 always — findings are a report, not a failure. No leaf accepts --gate.
|
|
35
|
+
|
|
36
|
+
capture motion <leaf> --help Per-leaf usage`;
|
|
37
|
+
|
|
38
|
+
export async function motionMain(parsed: ParsedArgs, args: string[]): Promise<void> {
|
|
39
|
+
const leaf = parsed.positional[0];
|
|
40
|
+
const rest: ParsedArgs = { ...parsed, positional: parsed.positional.slice(1) };
|
|
41
|
+
|
|
42
|
+
switch (leaf) {
|
|
43
|
+
case 'rec':
|
|
44
|
+
return cmdMotionRec(rest, args);
|
|
45
|
+
case 'mask':
|
|
46
|
+
return cmdMotionMask(rest, args);
|
|
47
|
+
case 'timeline':
|
|
48
|
+
return cmdMotionTimeline(rest, args);
|
|
49
|
+
case 'jank':
|
|
50
|
+
return cmdMotionJank(rest, args);
|
|
51
|
+
case 'response':
|
|
52
|
+
return cmdMotionResponse(rest, args);
|
|
53
|
+
case undefined:
|
|
54
|
+
console.log(MOTION_USAGE);
|
|
55
|
+
return;
|
|
56
|
+
default:
|
|
57
|
+
console.error(`Unknown motion leaf: ${leaf}\n\n${MOTION_USAGE}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture motion jank <rec>
|
|
5
|
+
|
|
6
|
+
Dropped-frame/long-task/layout-shift facts over a finalized recording.
|
|
7
|
+
|
|
8
|
+
Not yet implemented.`;
|
|
9
|
+
|
|
10
|
+
export async function cmdMotionJank(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
11
|
+
if (parsed.help) {
|
|
12
|
+
console.log(USAGE);
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
const result: RenderableResult = {
|
|
16
|
+
tag: 'error',
|
|
17
|
+
attrs: { command: 'motion jank', status: 'not_implemented' },
|
|
18
|
+
summary: fact`\`motion jank\` is not implemented yet.`,
|
|
19
|
+
};
|
|
20
|
+
emitResult(result, { json: parsed.json });
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture motion mask <rec>
|
|
5
|
+
|
|
6
|
+
Motion-diff composite image over a finalized recording, plus per-region
|
|
7
|
+
area/distance/velocity facts and element attribution where resolvable.
|
|
8
|
+
|
|
9
|
+
Not yet implemented.`;
|
|
10
|
+
|
|
11
|
+
export async function cmdMotionMask(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
12
|
+
if (parsed.help) {
|
|
13
|
+
console.log(USAGE);
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
const result: RenderableResult = {
|
|
17
|
+
tag: 'error',
|
|
18
|
+
attrs: { command: 'motion mask', status: 'not_implemented' },
|
|
19
|
+
summary: fact`\`motion mask\` is not implemented yet.`,
|
|
20
|
+
};
|
|
21
|
+
emitResult(result, { json: parsed.json });
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture motion rec [url] --do <action> [--duration <ms>]
|
|
5
|
+
capture motion rec --start
|
|
6
|
+
capture motion rec --stop [--rec-id <id>]
|
|
7
|
+
|
|
8
|
+
One-shot (\`--do\`): drives one scripted action and records it end to end.
|
|
9
|
+
Composed (\`--start\` ... intervening commands ... \`--stop\`): records
|
|
10
|
+
whatever the active session does across multiple independent commands.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--do <action> One-shot action to drive and record (e.g. click:SEL)
|
|
14
|
+
--duration <ms> Recording duration override
|
|
15
|
+
--start Arm the composed recorder (requires an active session)
|
|
16
|
+
--stop Finalize the composed recorder
|
|
17
|
+
--rec-id <id> Explicit recording id (default: the session's active recording)
|
|
18
|
+
|
|
19
|
+
Not yet implemented.`;
|
|
20
|
+
|
|
21
|
+
export async function cmdMotionRec(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
22
|
+
if (parsed.help) {
|
|
23
|
+
console.log(USAGE);
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
const result: RenderableResult = {
|
|
27
|
+
tag: 'error',
|
|
28
|
+
attrs: { command: 'motion rec', status: 'not_implemented' },
|
|
29
|
+
summary: fact`\`motion rec\` is not implemented yet.`,
|
|
30
|
+
};
|
|
31
|
+
emitResult(result, { json: parsed.json });
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture motion response <rec> [--action <action>]
|
|
5
|
+
|
|
6
|
+
Input-to-settled response timeline over a finalized recording:
|
|
7
|
+
input -> mutation -> layout -> paint -> network -> settle.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--action <action> Narrow to one recorded action
|
|
11
|
+
|
|
12
|
+
Not yet implemented.`;
|
|
13
|
+
|
|
14
|
+
export async function cmdMotionResponse(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
15
|
+
if (parsed.help) {
|
|
16
|
+
console.log(USAGE);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
const result: RenderableResult = {
|
|
20
|
+
tag: 'error',
|
|
21
|
+
attrs: { command: 'motion response', status: 'not_implemented' },
|
|
22
|
+
summary: fact`\`motion response\` is not implemented yet.`,
|
|
23
|
+
};
|
|
24
|
+
emitResult(result, { json: parsed.json });
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage: capture motion timeline <rec> --element <sel> [--prop <prop>]
|
|
5
|
+
|
|
6
|
+
Per-frame geometry/scroll/property values for one element across a
|
|
7
|
+
finalized recording.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--element <sel> Element selector to track (required)
|
|
11
|
+
--prop <prop> Additional CSS/computed property to sample per frame
|
|
12
|
+
|
|
13
|
+
Not yet implemented.`;
|
|
14
|
+
|
|
15
|
+
export async function cmdMotionTimeline(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
16
|
+
if (parsed.help) {
|
|
17
|
+
console.log(USAGE);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
const result: RenderableResult = {
|
|
21
|
+
tag: 'error',
|
|
22
|
+
attrs: { command: 'motion timeline', status: 'not_implemented' },
|
|
23
|
+
summary: fact`\`motion timeline\` is not implemented yet.`,
|
|
24
|
+
};
|
|
25
|
+
emitResult(result, { json: parsed.json });
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|