versioncam 0.1.1
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/CHANGELOG.md +94 -0
- package/LICENSE.md +105 -0
- package/README.md +463 -0
- package/bin/versioncam.js +29 -0
- package/dist/.types-render/render-page/draw.d.ts +28 -0
- package/dist/.types-render/render-page/main.d.ts +24 -0
- package/dist/.types-render/render-page/theme.d.ts +37 -0
- package/dist/app-server.d.ts +59 -0
- package/dist/app-server.js +328 -0
- package/dist/app-server.js.map +1 -0
- package/dist/cli/app.d.ts +13 -0
- package/dist/cli/app.js +21 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/commands/check.d.ts +8 -0
- package/dist/cli/commands/check.js +51 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.js +130 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/dsl.d.ts +16 -0
- package/dist/cli/commands/dsl.js +22 -0
- package/dist/cli/commands/dsl.js.map +1 -0
- package/dist/cli/commands/frame.d.ts +8 -0
- package/dist/cli/commands/frame.js +72 -0
- package/dist/cli/commands/frame.js.map +1 -0
- package/dist/cli/commands/init.d.ts +23 -0
- package/dist/cli/commands/init.js +109 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +1 -0
- package/dist/cli/commands/inspect.js +32 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/install.d.ts +33 -0
- package/dist/cli/commands/install.js +66 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/login.d.ts +10 -0
- package/dist/cli/commands/login.js +49 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/measure.d.ts +1 -0
- package/dist/cli/commands/measure.js +36 -0
- package/dist/cli/commands/measure.js.map +1 -0
- package/dist/cli/commands/open-app.d.ts +14 -0
- package/dist/cli/commands/open-app.js +45 -0
- package/dist/cli/commands/open-app.js.map +1 -0
- package/dist/cli/commands/preview.d.ts +8 -0
- package/dist/cli/commands/preview.js +55 -0
- package/dist/cli/commands/preview.js.map +1 -0
- package/dist/cli/commands/record.d.ts +10 -0
- package/dist/cli/commands/record.js +86 -0
- package/dist/cli/commands/record.js.map +1 -0
- package/dist/cli/commands/render.d.ts +1 -0
- package/dist/cli/commands/render.js +93 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/review.d.ts +6 -0
- package/dist/cli/commands/review.js +89 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/sheet.d.ts +1 -0
- package/dist/cli/commands/sheet.js +48 -0
- package/dist/cli/commands/sheet.js.map +1 -0
- package/dist/cli/commands/stability.d.ts +14 -0
- package/dist/cli/commands/stability.js +110 -0
- package/dist/cli/commands/stability.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +69 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/usage.d.ts +10 -0
- package/dist/cli/usage.js +46 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/config.d.ts +250 -0
- package/dist/config.js +154 -0
- package/dist/config.js.map +1 -0
- package/dist/core/camera.d.ts +30 -0
- package/dist/core/camera.js +94 -0
- package/dist/core/camera.js.map +1 -0
- package/dist/core/compose.d.ts +38 -0
- package/dist/core/compose.js +81 -0
- package/dist/core/compose.js.map +1 -0
- package/dist/core/cursor.d.ts +38 -0
- package/dist/core/cursor.js +103 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/easing.d.ts +15 -0
- package/dist/core/easing.js +33 -0
- package/dist/core/easing.js.map +1 -0
- package/dist/core/loop.d.ts +30 -0
- package/dist/core/loop.js +96 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/core/motion-defaults.d.ts +61 -0
- package/dist/core/motion-defaults.js +62 -0
- package/dist/core/motion-defaults.js.map +1 -0
- package/dist/core/rng.d.ts +13 -0
- package/dist/core/rng.js +27 -0
- package/dist/core/rng.js.map +1 -0
- package/dist/core/sse.d.ts +15 -0
- package/dist/core/sse.js +16 -0
- package/dist/core/sse.js.map +1 -0
- package/dist/core/timeline.d.ts +146 -0
- package/dist/core/timeline.js +81 -0
- package/dist/core/timeline.js.map +1 -0
- package/dist/core/timing.d.ts +31 -0
- package/dist/core/timing.js +29 -0
- package/dist/core/timing.js.map +1 -0
- package/dist/core/typing.d.ts +12 -0
- package/dist/core/typing.js +35 -0
- package/dist/core/typing.js.map +1 -0
- package/dist/driver/clip.d.ts +71 -0
- package/dist/driver/clip.js +120 -0
- package/dist/driver/clip.js.map +1 -0
- package/dist/driver/compare.d.ts +34 -0
- package/dist/driver/compare.js +40 -0
- package/dist/driver/compare.js.map +1 -0
- package/dist/driver/gate.d.ts +36 -0
- package/dist/driver/gate.js +27 -0
- package/dist/driver/gate.js.map +1 -0
- package/dist/driver/launch.d.ts +43 -0
- package/dist/driver/launch.js +47 -0
- package/dist/driver/launch.js.map +1 -0
- package/dist/driver/page-hooks.d.ts +72 -0
- package/dist/driver/page-hooks.js +129 -0
- package/dist/driver/page-hooks.js.map +1 -0
- package/dist/driver/reports.d.ts +34 -0
- package/dist/driver/reports.js +42 -0
- package/dist/driver/reports.js.map +1 -0
- package/dist/driver/session.d.ts +285 -0
- package/dist/driver/session.js +773 -0
- package/dist/driver/session.js.map +1 -0
- package/dist/driver/settle.d.ts +41 -0
- package/dist/driver/settle.js +82 -0
- package/dist/driver/settle.js.map +1 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.js +41 -0
- package/dist/env.js.map +1 -0
- package/dist/fixtures.d.ts +13 -0
- package/dist/fixtures.js +13 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect/inspect.d.ts +70 -0
- package/dist/inspect/inspect.js +176 -0
- package/dist/inspect/inspect.js.map +1 -0
- package/dist/inspect/measure.d.ts +40 -0
- package/dist/inspect/measure.js +107 -0
- package/dist/inspect/measure.js.map +1 -0
- package/dist/loader.d.ts +28 -0
- package/dist/loader.js +143 -0
- package/dist/loader.js.map +1 -0
- package/dist/page/assets/index-DUom5amc.js +1 -0
- package/dist/page/index.html +18 -0
- package/dist/render/encode.d.ts +40 -0
- package/dist/render/encode.js +183 -0
- package/dist/render/encode.js.map +1 -0
- package/dist/render/ffmpeg.d.ts +13 -0
- package/dist/render/ffmpeg.js +72 -0
- package/dist/render/ffmpeg.js.map +1 -0
- package/dist/render/presentation.d.ts +19 -0
- package/dist/render/presentation.js +27 -0
- package/dist/render/presentation.js.map +1 -0
- package/dist/render/render.d.ts +59 -0
- package/dist/render/render.js +144 -0
- package/dist/render/render.js.map +1 -0
- package/dist/render/sampling.d.ts +47 -0
- package/dist/render/sampling.js +129 -0
- package/dist/render/sampling.js.map +1 -0
- package/dist/render/sequence.d.ts +24 -0
- package/dist/render/sequence.js +105 -0
- package/dist/render/sequence.js.map +1 -0
- package/dist/render/serve.d.ts +35 -0
- package/dist/render/serve.js +124 -0
- package/dist/render/serve.js.map +1 -0
- package/dist/review/review.d.ts +54 -0
- package/dist/review/review.js +229 -0
- package/dist/review/review.js.map +1 -0
- package/dist/scene.d.ts +23 -0
- package/dist/scene.js +2 -0
- package/dist/scene.js.map +1 -0
- package/dsl.md +119 -0
- package/package.json +76 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/README.md +105 -0
- package/plugin/agents/versioncam-reviewer.md +63 -0
- package/plugin/skills/versioncam/SKILL.md +235 -0
- package/plugin/skills/versioncam/authoring.md +226 -0
- package/plugin/skills/versioncam/onboarding.md +199 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* What a recording leaves behind besides its frames.
|
|
5
|
+
*
|
|
6
|
+
* A clip is written by someone who was not watching it run — increasingly,
|
|
7
|
+
* by something that cannot watch at all — so everything that would have been
|
|
8
|
+
* on screen has to be on disk. Two files: what the waits waited for, and, if
|
|
9
|
+
* it ended badly, the page at the moment it gave up.
|
|
10
|
+
*/
|
|
11
|
+
/** The name a reader looks for, in one place. */
|
|
12
|
+
export const SETTLES_FILE = "settles.json";
|
|
13
|
+
export const FAILURE_JSON = "failure.json";
|
|
14
|
+
export const FAILURE_PNG = "failure.png";
|
|
15
|
+
export function writeSettles(dir, reports) {
|
|
16
|
+
writeFileSync(join(dir, SETTLES_FILE), `${JSON.stringify(reports, null, 2)}\n`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The failure, then the picture of it.
|
|
20
|
+
*
|
|
21
|
+
* In that order deliberately: the message, the frame and the time can be
|
|
22
|
+
* reconstructed from nothing else, while a screenshot needs a live page and
|
|
23
|
+
* may not be available — a crashed browser, a context already gone. Losing the
|
|
24
|
+
* picture must not lose the rest, and neither may mask the error that caused
|
|
25
|
+
* this to be called in the first place.
|
|
26
|
+
*/
|
|
27
|
+
export async function writeFailure(page, dir, failure, options = {}) {
|
|
28
|
+
try {
|
|
29
|
+
writeFileSync(join(dir, FAILURE_JSON), `${JSON.stringify(failure, null, 2)}\n`);
|
|
30
|
+
writeFileSync(join(dir, FAILURE_PNG), await page.screenshot({
|
|
31
|
+
type: "png",
|
|
32
|
+
caret: "initial",
|
|
33
|
+
// At the same scale the clip's own frames were captured at, so a
|
|
34
|
+
// draft's evidence is as cheap to look at as its states are.
|
|
35
|
+
scale: options.draft ? "css" : "device",
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Nothing to do about it here, and the original failure is on its way up.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=reports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reports.js","sourceRoot":"","sources":["../../src/driver/reports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;;;;GAOG;AAEH,iDAAiD;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAC3C,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAC3C,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AAEzC,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,OAAuB;IAC/D,aAAa,CACX,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EACvB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxC,CAAC;AACJ,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAU,EACV,GAAW,EACX,OAAgB,EAChB,UAA+B,EAAE;IAEjC,IAAI,CAAC;QACH,aAAa,CACX,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EACvB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxC,CAAC;QACF,aAAa,CACX,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EACtB,MAAM,IAAI,CAAC,UAAU,CAAC;YACpB,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,SAAS;YAChB,iEAAiE;YACjE,6DAA6D;YAC7D,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;SACxC,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { Locator, Page } from "playwright";
|
|
2
|
+
import { loop as planLoop } from "../core/loop.js";
|
|
3
|
+
import type { LoopPlanner } from "../core/loop.js";
|
|
4
|
+
import type { HighlightStyle, Rect, Timeline, Vec2 } from "../core/timeline.js";
|
|
5
|
+
import type { RecordingTiming } from "../core/timing.js";
|
|
6
|
+
import type { SettleOptions, SettleReport } from "./settle.js";
|
|
7
|
+
import type { CaptureConfig, ResolvedConfig } from "../config.js";
|
|
8
|
+
export type SessionOptions = {
|
|
9
|
+
clipId: string;
|
|
10
|
+
title: string;
|
|
11
|
+
/** Resolved for this clip: the config's values with the clip's overrides. */
|
|
12
|
+
fps: number;
|
|
13
|
+
viewport: {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
};
|
|
17
|
+
dpr: number;
|
|
18
|
+
seed: number;
|
|
19
|
+
outputDir: string;
|
|
20
|
+
appCommit: string;
|
|
21
|
+
capture: Required<CaptureConfig>;
|
|
22
|
+
/**
|
|
23
|
+
* Capture at CSS scale rather than device scale, for a cheap look at a clip
|
|
24
|
+
* while writing it. The page is not told: same viewport, same device pixel
|
|
25
|
+
* ratio, same layout, same code paths — only the screenshot is smaller.
|
|
26
|
+
*/
|
|
27
|
+
draft: boolean;
|
|
28
|
+
config: ResolvedConfig;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Drives the app through an authored timeline and captures one image per
|
|
32
|
+
* output frame.
|
|
33
|
+
*
|
|
34
|
+
* The central idea: the clip's time is declared by the script, not measured
|
|
35
|
+
* from the wall clock. Each frame we apply that frame's input, advance the
|
|
36
|
+
* page's fake clock by exactly 1/fps, and capture. Real latency — a DuckDB
|
|
37
|
+
* query, a tile fetch, WebGL under software rendering — is absorbed by
|
|
38
|
+
* `settle()`, which advances the clock without capturing. Two runs of the
|
|
39
|
+
* same script therefore produce the same frames on any machine.
|
|
40
|
+
*/
|
|
41
|
+
export declare class RecordingSession {
|
|
42
|
+
readonly page: Page;
|
|
43
|
+
private readonly options;
|
|
44
|
+
private readonly timeline;
|
|
45
|
+
private readonly rng;
|
|
46
|
+
private readonly motion;
|
|
47
|
+
private readonly statesDir;
|
|
48
|
+
private readonly stateIndexByHash;
|
|
49
|
+
private readonly queue;
|
|
50
|
+
private readonly settleReports;
|
|
51
|
+
/** Authored time, in seconds. */
|
|
52
|
+
private t;
|
|
53
|
+
/** Index of the last captured frame; -1 before frame 0 exists. */
|
|
54
|
+
private frame;
|
|
55
|
+
private cursor;
|
|
56
|
+
private inFlight;
|
|
57
|
+
private lastSettleFrame;
|
|
58
|
+
private readonly timing;
|
|
59
|
+
/**
|
|
60
|
+
* Where the real mouse actually is. The planned cursor holds still for most
|
|
61
|
+
* of a clip, and telling Chromium to move a pointer to where it already is
|
|
62
|
+
* costs 8 ms and achieves nothing.
|
|
63
|
+
*/
|
|
64
|
+
private lastMouse;
|
|
65
|
+
/** What the page looked like to the probe when it was last photographed. */
|
|
66
|
+
private lastCaptured;
|
|
67
|
+
private lastCapturedFrame;
|
|
68
|
+
/**
|
|
69
|
+
* Set whenever the driver sends the page real input, and cleared by the next
|
|
70
|
+
* frame's capture decision.
|
|
71
|
+
*
|
|
72
|
+
* Input can change what the page looks like without changing anything the
|
|
73
|
+
* probe watches: `:active` while a button is held is pure CSS, driven by a
|
|
74
|
+
* pointer event, touching neither the DOM nor the element under the cursor.
|
|
75
|
+
* The gate would skip those frames and the clip would show a button that is
|
|
76
|
+
* never pressed.
|
|
77
|
+
*/
|
|
78
|
+
private inputPending;
|
|
79
|
+
constructor(page: Page, options: SessionOptions);
|
|
80
|
+
byTestId(testId: string): Locator;
|
|
81
|
+
byRole(role: Parameters<Page["getByRole"]>[0], options?: Parameters<Page["getByRole"]>[1]): Locator;
|
|
82
|
+
byLabel(text: string): Locator;
|
|
83
|
+
byPlaceholder(text: string): Locator;
|
|
84
|
+
byText(text: string | RegExp): Locator;
|
|
85
|
+
css(selector: string): Locator;
|
|
86
|
+
/** Hold still for `ms`, capturing frames. */
|
|
87
|
+
hold(ms: number): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Run raw Playwright without capturing or advancing authored time. For
|
|
90
|
+
* setup that should not appear in the clip: seeding cohorts, warming tiles,
|
|
91
|
+
* priming a chat conversation.
|
|
92
|
+
*
|
|
93
|
+
* The page's clock does not move either, however long `fn` takes: time the
|
|
94
|
+
* app needs to finish what `fn` started is what the next `settle()` is for.
|
|
95
|
+
*/
|
|
96
|
+
offCamera<T>(fn: (page: Page) => Promise<T>): Promise<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Wait for the page to stop working. Advances the page's clock but captures
|
|
99
|
+
* nothing, so the wait never reaches the timeline.
|
|
100
|
+
*/
|
|
101
|
+
settle(options?: SettleOptions): Promise<void>;
|
|
102
|
+
private defaultHook;
|
|
103
|
+
/**
|
|
104
|
+
* Open a path, sign in if the app needs it, wait for the page to be
|
|
105
|
+
* genuinely finished, and capture frame 0.
|
|
106
|
+
*
|
|
107
|
+
* Signing in is the app's own business: how it knows it is signed out, which
|
|
108
|
+
* fields, which button, what counts as done. The config supplies either a
|
|
109
|
+
* saved browser session — in which case there is nothing to do here — or a
|
|
110
|
+
* hook, which is called unconditionally after the navigation and decides for
|
|
111
|
+
* itself whether anything is needed.
|
|
112
|
+
*/
|
|
113
|
+
open(path: string, options?: {
|
|
114
|
+
settleTimeoutMs?: number;
|
|
115
|
+
}): Promise<void>;
|
|
116
|
+
/** Glide to a target without pressing anything. */
|
|
117
|
+
moveTo(target: Locator | Vec2, options?: {
|
|
118
|
+
duration?: number;
|
|
119
|
+
}): Promise<void>;
|
|
120
|
+
/** Move to a target and pause on it, the way a person does before clicking. */
|
|
121
|
+
hover(target: Locator | Vec2, options?: {
|
|
122
|
+
dwell?: number;
|
|
123
|
+
duration?: number;
|
|
124
|
+
}): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* A full click: approach, a short dwell on the target, press, release.
|
|
127
|
+
*
|
|
128
|
+
* Implemented on `page.mouse` rather than `locator.click()`, which would
|
|
129
|
+
* teleport the pointer to the element — the approach *is* the thing being
|
|
130
|
+
* recorded.
|
|
131
|
+
*/
|
|
132
|
+
click(target?: Locator | Vec2, options?: {
|
|
133
|
+
dwell?: number;
|
|
134
|
+
}): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Draw a lasso: press, follow a smoothed closed loop at constant speed,
|
|
137
|
+
* release. `planner` comes from `loop([...])` and is expressed in fractions
|
|
138
|
+
* of the target's box, so the stroke survives a layout change.
|
|
139
|
+
*/
|
|
140
|
+
/**
|
|
141
|
+
* Drag across a target, in fractions of its own box.
|
|
142
|
+
*
|
|
143
|
+
* A brush, a slider, a range handle: controls that respond to a stroke and
|
|
144
|
+
* ignore a click. The README promised this method for months before it
|
|
145
|
+
* existed, and the gap was not academic — a date-histogram clip on the
|
|
146
|
+
* first app this recorded clicked a bar to filter by month, which sets a
|
|
147
|
+
* zero-width d3 brush, which *clears* the filter rather than applying one.
|
|
148
|
+
* That clip never showed the thing its caption claimed, and nothing said
|
|
149
|
+
* so: the click lands, the page changes enough for a pixel diff, and the
|
|
150
|
+
* recording looks fine.
|
|
151
|
+
*
|
|
152
|
+
* Fractions rather than pixels for the same reason `lasso` uses them: a
|
|
153
|
+
* stroke authored against a real box survives the next padding change.
|
|
154
|
+
* `{ x1: 0.18, y1: 0.5, x2: 0.36, y2: 0.5 }` drags left-to-right across the
|
|
155
|
+
* middle. Beware the edges — a chart's axis labels live inside its own box,
|
|
156
|
+
* and a drag starting at x 0.08 selects that text instead of the control.
|
|
157
|
+
*/
|
|
158
|
+
drag(target: Locator | Rect, from: {
|
|
159
|
+
x: number;
|
|
160
|
+
y: number;
|
|
161
|
+
}, to: {
|
|
162
|
+
x: number;
|
|
163
|
+
y: number;
|
|
164
|
+
}, options?: {
|
|
165
|
+
duration?: number;
|
|
166
|
+
modifiers?: ("Shift" | "Alt")[];
|
|
167
|
+
}): Promise<void>;
|
|
168
|
+
lasso(target: Locator, planner: LoopPlanner, options?: {
|
|
169
|
+
duration?: number;
|
|
170
|
+
modifiers?: ("Shift" | "Alt")[];
|
|
171
|
+
}): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Type at a human cadence into `target`, clicking it first so the keystrokes
|
|
174
|
+
* have somewhere to land. Without the click the text goes to whatever had
|
|
175
|
+
* focus — usually nothing — and the clip shows an empty field being
|
|
176
|
+
* "typed" into.
|
|
177
|
+
*/
|
|
178
|
+
typeInto(target: Locator, text: string): Promise<void>;
|
|
179
|
+
/** Type at a human cadence. The cursor stays where it is. */
|
|
180
|
+
type(text: string): Promise<void>;
|
|
181
|
+
press(key: string): Promise<void>;
|
|
182
|
+
readonly camera: {
|
|
183
|
+
zoom: (target: Locator | Rect, scale?: number, options?: {
|
|
184
|
+
duration?: number;
|
|
185
|
+
}) => Promise<void>;
|
|
186
|
+
to: (rect: Rect, options?: {
|
|
187
|
+
duration?: number;
|
|
188
|
+
}) => void;
|
|
189
|
+
reset: (options?: {
|
|
190
|
+
duration?: number;
|
|
191
|
+
}) => void;
|
|
192
|
+
};
|
|
193
|
+
private pushCameraKey;
|
|
194
|
+
highlight(target: Locator | Rect, style: HighlightStyle, options?: {
|
|
195
|
+
for?: number;
|
|
196
|
+
}): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Put a line of text on the clip, from now until the next caption or the end.
|
|
199
|
+
*
|
|
200
|
+
* `from` defaults to the moment it is written, not to zero. Defaulting to
|
|
201
|
+
* zero meant a second caption covered the whole clip on top of the first, so
|
|
202
|
+
* every frame carried both lines drawn over each other — which reads on a
|
|
203
|
+
* contact sheet as ghosted text rather than as a caption bug, and cost a
|
|
204
|
+
* pilot most of a round to diagnose.
|
|
205
|
+
*/
|
|
206
|
+
caption(text: string, options?: {
|
|
207
|
+
from?: number;
|
|
208
|
+
to?: number;
|
|
209
|
+
}): void;
|
|
210
|
+
private pushHold;
|
|
211
|
+
/**
|
|
212
|
+
* Bounded on purpose. `boundingBox()` waits indefinitely by default, so a
|
|
213
|
+
* clip that reaches for a control the app did not render — a button that
|
|
214
|
+
* only appears once something is selected, say — hangs forever with no clue
|
|
215
|
+
* as to which step it was on. Fifteen seconds and a named failure instead.
|
|
216
|
+
*/
|
|
217
|
+
private boxOf;
|
|
218
|
+
/**
|
|
219
|
+
* Wait for a target to be on screen, advancing the page's clock a tick at a
|
|
220
|
+
* time while it is not.
|
|
221
|
+
*
|
|
222
|
+
* The clock is paused, so a target the app shows from its own timer — a menu
|
|
223
|
+
* that opens after a delay, results behind a debounce — would never arrive
|
|
224
|
+
* if this only waited. Ticking as `settle()` does brings it in the same
|
|
225
|
+
* number of ticks every run, and costs nothing in the usual case: a target
|
|
226
|
+
* already on screen is one round trip and no page time at all.
|
|
227
|
+
*/
|
|
228
|
+
private untilVisible;
|
|
229
|
+
/**
|
|
230
|
+
* Run `fn` with the page's clock ticking alongside real time, the way it
|
|
231
|
+
* runs in a browser nobody is driving.
|
|
232
|
+
*
|
|
233
|
+
* Only for code the recorder hands the page to and cannot see into, and
|
|
234
|
+
* never while a frame is being made: the ticks land wherever the machine's
|
|
235
|
+
* speed puts them, which is exactly what the frames must not depend on.
|
|
236
|
+
*/
|
|
237
|
+
private whileTicking;
|
|
238
|
+
/**
|
|
239
|
+
* Which kind of locator failure this was.
|
|
240
|
+
*
|
|
241
|
+
* Playwright reports both of them as a rejected wait, and they call for
|
|
242
|
+
* opposite repairs. Nothing matched means the clip reached for something the
|
|
243
|
+
* app never rendered — look at the page again. Several matched is a
|
|
244
|
+
* strict-mode violation: the locator found the text, on more than one
|
|
245
|
+
* element, and the beat is fine as long as the locator names one of them.
|
|
246
|
+
* Saying "nothing matched" for the second sent a pilot hunting a control
|
|
247
|
+
* that was on screen the whole time.
|
|
248
|
+
*/
|
|
249
|
+
private whyLocatorFailed;
|
|
250
|
+
private resolvePoint;
|
|
251
|
+
private resolveSize;
|
|
252
|
+
/**
|
|
253
|
+
* The frame loop. Everything that advances authored time comes through here.
|
|
254
|
+
*/
|
|
255
|
+
private advanceTo;
|
|
256
|
+
/**
|
|
257
|
+
* Does this frame need its own screenshot?
|
|
258
|
+
*
|
|
259
|
+
* Every clause errs towards capturing, because the two mistakes are not
|
|
260
|
+
* equal: capturing a frame that did not change wastes 25 ms, and skipping
|
|
261
|
+
* one that did puts a wrong picture in the video.
|
|
262
|
+
*/
|
|
263
|
+
private shouldCapture;
|
|
264
|
+
/** Press or release, recording it and flagging the frame as changed. */
|
|
265
|
+
private pressMouse;
|
|
266
|
+
/** Move the real pointer, unless it is already there. */
|
|
267
|
+
private moveMouse;
|
|
268
|
+
private captureFrame;
|
|
269
|
+
/** What this recording cost in real time. Diagnostics, never content. */
|
|
270
|
+
get cost(): RecordingTiming;
|
|
271
|
+
/** Every settle that happened, in order: what each waited for, and for how long. */
|
|
272
|
+
get settles(): SettleReport[];
|
|
273
|
+
/**
|
|
274
|
+
* How far the clip had got. Meaningless as content — it is where a failure
|
|
275
|
+
* happened, which is the first thing a repair needs and the one thing the
|
|
276
|
+
* script cannot be read to find out.
|
|
277
|
+
*/
|
|
278
|
+
get at(): {
|
|
279
|
+
frame: number;
|
|
280
|
+
t: number;
|
|
281
|
+
};
|
|
282
|
+
/** Write the timeline out. Called by `clip()` once the script has run. */
|
|
283
|
+
finish(): Timeline;
|
|
284
|
+
}
|
|
285
|
+
export { planLoop as loop };
|