@wasao/kagemusha 0.1.1 → 0.3.4
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/README.md +168 -79
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +26 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/capture.d.ts +3 -2
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +256 -20
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/discover.d.ts +2 -0
- package/dist/commands/discover.d.ts.map +1 -0
- package/dist/commands/discover.js +62 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/edit.d.ts +1 -1
- package/dist/commands/edit.d.ts.map +1 -1
- package/dist/commands/edit.js +82 -26
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +240 -105
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +33 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +131 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/validate.js +1 -1
- package/dist/commands/validate.js.map +1 -1
- package/dist/editor/inject-script/annotations.d.ts +11 -0
- package/dist/editor/inject-script/annotations.d.ts.map +1 -0
- package/dist/editor/inject-script/annotations.js +409 -0
- package/dist/editor/inject-script/annotations.js.map +1 -0
- package/dist/editor/inject-script/bridge.d.ts +13 -0
- package/dist/editor/inject-script/bridge.d.ts.map +1 -0
- package/dist/editor/inject-script/bridge.js +33 -0
- package/dist/editor/inject-script/bridge.js.map +1 -0
- package/dist/editor/inject-script/crop.d.ts +9 -0
- package/dist/editor/inject-script/crop.d.ts.map +1 -0
- package/dist/editor/inject-script/crop.js +236 -0
- package/dist/editor/inject-script/crop.js.map +1 -0
- package/dist/editor/inject-script/dom.d.ts +7 -0
- package/dist/editor/inject-script/dom.d.ts.map +1 -0
- package/dist/editor/inject-script/dom.js +32 -0
- package/dist/editor/inject-script/dom.js.map +1 -0
- package/dist/editor/inject-script/index.d.ts +2 -0
- package/dist/editor/inject-script/index.d.ts.map +1 -0
- package/dist/editor/inject-script/index.js +56 -0
- package/dist/editor/inject-script/index.js.map +1 -0
- package/dist/editor/inject-script/record.d.ts +5 -0
- package/dist/editor/inject-script/record.d.ts.map +1 -0
- package/dist/editor/inject-script/record.js +398 -0
- package/dist/editor/inject-script/record.js.map +1 -0
- package/dist/editor/inject-script/selector.d.ts +6 -0
- package/dist/editor/inject-script/selector.d.ts.map +1 -0
- package/dist/editor/inject-script/selector.js +112 -0
- package/dist/editor/inject-script/selector.js.map +1 -0
- package/dist/editor/inject-script/state.d.ts +27 -0
- package/dist/editor/inject-script/state.d.ts.map +1 -0
- package/dist/editor/inject-script/state.js +26 -0
- package/dist/editor/inject-script/state.js.map +1 -0
- package/dist/editor/inject-script/svg.d.ts +7 -0
- package/dist/editor/inject-script/svg.d.ts.map +1 -0
- package/dist/editor/inject-script/svg.js +39 -0
- package/dist/editor/inject-script/svg.js.map +1 -0
- package/dist/editor/inject-script/toolbar.d.ts +14 -0
- package/dist/editor/inject-script/toolbar.d.ts.map +1 -0
- package/dist/editor/inject-script/toolbar.js +240 -0
- package/dist/editor/inject-script/toolbar.js.map +1 -0
- package/dist/editor/inject-script/types.d.ts +102 -0
- package/dist/editor/inject-script/types.d.ts.map +1 -0
- package/dist/editor/inject-script/types.js +5 -0
- package/dist/editor/inject-script/types.js.map +1 -0
- package/dist/editor/inject-script.js +1276 -353
- package/dist/index.js +34 -16
- package/dist/index.js.map +1 -1
- package/dist/lib/annotate.d.ts +2 -2
- package/dist/lib/annotate.d.ts.map +1 -1
- package/dist/lib/annotate.js +35 -43
- package/dist/lib/annotate.js.map +1 -1
- package/dist/lib/auth.d.ts +18 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +45 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/aws-error.d.ts +7 -0
- package/dist/lib/aws-error.d.ts.map +1 -0
- package/dist/lib/aws-error.js +74 -0
- package/dist/lib/aws-error.js.map +1 -0
- package/dist/lib/canonical.d.ts +54 -0
- package/dist/lib/canonical.d.ts.map +1 -0
- package/dist/lib/canonical.js +152 -0
- package/dist/lib/canonical.js.map +1 -0
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +23 -13
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/crawl.d.ts +1 -1
- package/dist/lib/crawl.d.ts.map +1 -1
- package/dist/lib/crawl.js +213 -20
- package/dist/lib/crawl.js.map +1 -1
- package/dist/lib/definition.d.ts +2 -0
- package/dist/lib/definition.d.ts.map +1 -0
- package/dist/lib/definition.js +6 -0
- package/dist/lib/definition.js.map +1 -0
- package/dist/lib/diff.d.ts +71 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +40 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/login-error.d.ts +10 -0
- package/dist/lib/login-error.d.ts.map +1 -0
- package/dist/lib/login-error.js +13 -0
- package/dist/lib/login-error.js.map +1 -0
- package/dist/lib/page-ready.d.ts +18 -0
- package/dist/lib/page-ready.d.ts.map +1 -0
- package/dist/lib/page-ready.js +19 -0
- package/dist/lib/page-ready.js.map +1 -0
- package/dist/lib/screenshot.d.ts +11 -2
- package/dist/lib/screenshot.d.ts.map +1 -1
- package/dist/lib/screenshot.js +73 -64
- package/dist/lib/screenshot.js.map +1 -1
- package/dist/lib/staging.d.ts +7 -0
- package/dist/lib/staging.d.ts.map +1 -0
- package/dist/lib/staging.js +21 -0
- package/dist/lib/staging.js.map +1 -0
- package/dist/types.d.ts +10 -23
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -3
- package/dist/commands/preview.d.ts +0 -6
- package/dist/commands/preview.d.ts.map +0 -1
- package/dist/commands/preview.js +0 -33
- package/dist/commands/preview.js.map +0 -1
- package/dist/commands/run.d.ts +0 -6
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands/run.js +0 -39
- package/dist/commands/run.js.map +0 -1
- package/dist/editor/editor/editor.html +0 -313
- package/dist/editor/editor/inject.ts +0 -385
- package/dist/editor/editor.html +0 -338
- package/dist/editor/inject-script.cjs +0 -398
- package/dist/editor/inject-script.cjs.map +0 -1
- package/dist/editor/inject-script.d.cts +0 -2
- package/dist/editor/inject-script.d.cts.map +0 -1
- package/dist/editor/inject-script.d.ts +0 -2
- package/dist/editor/inject-script.d.ts.map +0 -1
- package/dist/editor/inject-script.js.map +0 -1
- package/dist/editor/inject.d.ts +0 -2
- package/dist/editor/inject.d.ts.map +0 -1
- package/dist/editor/inject.js +0 -385
- package/dist/editor/inject.js.map +0 -1
- package/dist/lib/upload.d.ts +0 -9
- package/dist/lib/upload.d.ts.map +0 -1
- package/dist/lib/upload.js +0 -43
- package/dist/lib/upload.js.map +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface Dimensions {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* URLs of the canonical artifacts on the remote (= S3). Only populated when
|
|
7
|
+
* `capture` actually pushed (= default mode, S3 destination). `--dry-run` and
|
|
8
|
+
* `local` destination leave this undefined.
|
|
9
|
+
*
|
|
10
|
+
* - `after`: the new `latest.png` URL
|
|
11
|
+
* - `before`: the previous `latest.png` URL (= `previous.png`). Undefined on
|
|
12
|
+
* the first push for this id (no prior version existed).
|
|
13
|
+
*
|
|
14
|
+
* No `diff` URL — kagemusha intentionally does not publish a pre-generated
|
|
15
|
+
* diff visualization. Consumers compare before vs after raw images instead.
|
|
16
|
+
*/
|
|
17
|
+
export interface ResultUrls {
|
|
18
|
+
after: string;
|
|
19
|
+
before?: string;
|
|
20
|
+
}
|
|
21
|
+
export type DiffStatus = {
|
|
22
|
+
id: string;
|
|
23
|
+
status: "unchanged";
|
|
24
|
+
} | {
|
|
25
|
+
id: string;
|
|
26
|
+
status: "new";
|
|
27
|
+
urls?: ResultUrls;
|
|
28
|
+
} | {
|
|
29
|
+
id: string;
|
|
30
|
+
status: "missing";
|
|
31
|
+
reason?: string;
|
|
32
|
+
} | {
|
|
33
|
+
id: string;
|
|
34
|
+
status: "changed";
|
|
35
|
+
reason: "pixel-diff";
|
|
36
|
+
diffPercentage: number;
|
|
37
|
+
urls?: ResultUrls;
|
|
38
|
+
} | {
|
|
39
|
+
id: string;
|
|
40
|
+
status: "changed";
|
|
41
|
+
reason: "layout-diff";
|
|
42
|
+
canonical: Dimensions;
|
|
43
|
+
staging: Dimensions;
|
|
44
|
+
urls?: ResultUrls;
|
|
45
|
+
};
|
|
46
|
+
export interface DiffOptions {
|
|
47
|
+
/** Color difference threshold per pixel (0-1). Lower = stricter. Default 0.1 */
|
|
48
|
+
pixelThreshold?: number;
|
|
49
|
+
/** Ignore anti-aliased pixels (treat them as equal). Default true */
|
|
50
|
+
ignoreAntiAliasing?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export type DiffResult = {
|
|
53
|
+
match: true;
|
|
54
|
+
} | {
|
|
55
|
+
match: false;
|
|
56
|
+
reason: "layout-diff";
|
|
57
|
+
canonical: Dimensions;
|
|
58
|
+
staging: Dimensions;
|
|
59
|
+
} | {
|
|
60
|
+
match: false;
|
|
61
|
+
reason: "pixel-diff";
|
|
62
|
+
diffCount: number;
|
|
63
|
+
diffPercentage: number;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Compare two PNG files using pixelmatch. Returns a structured result with
|
|
67
|
+
* the diff percentage; we deliberately don't write a diff PNG anywhere
|
|
68
|
+
* (= the red overlay is alarming + adds little value over the raw pair).
|
|
69
|
+
*/
|
|
70
|
+
export declare const diffImages: (baseline: string, current: string, options?: DiffOptions) => Promise<DiffResult>;
|
|
71
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GACnB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD;IACA,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,UAAU,CAAC;CACjB,GACD;IACA,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;CACjB,CAAC;AAEL,MAAM,WAAW,WAAW;IAC3B,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,MAAM,UAAU,GACnB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IACA,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;CACnB,GACD;IACA,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACtB,CAAC;AAKL;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACtB,UAAU,MAAM,EAChB,SAAS,MAAM,EACf,UAAS,WAAgB,KACvB,OAAO,CAAC,UAAU,CAkCpB,CAAC"}
|
package/dist/lib/diff.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import pixelmatch from "pixelmatch";
|
|
3
|
+
import { PNG } from "pngjs";
|
|
4
|
+
const readPng = (filePath) => PNG.sync.read(fs.readFileSync(filePath));
|
|
5
|
+
/**
|
|
6
|
+
* Compare two PNG files using pixelmatch. Returns a structured result with
|
|
7
|
+
* the diff percentage; we deliberately don't write a diff PNG anywhere
|
|
8
|
+
* (= the red overlay is alarming + adds little value over the raw pair).
|
|
9
|
+
*/
|
|
10
|
+
export const diffImages = async (baseline, current, options = {}) => {
|
|
11
|
+
const a = readPng(baseline);
|
|
12
|
+
const b = readPng(current);
|
|
13
|
+
if (a.width !== b.width || a.height !== b.height) {
|
|
14
|
+
return {
|
|
15
|
+
match: false,
|
|
16
|
+
reason: "layout-diff",
|
|
17
|
+
canonical: { width: a.width, height: a.height },
|
|
18
|
+
staging: { width: b.width, height: b.height },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const { width, height } = a;
|
|
22
|
+
// pixelmatch requires an output buffer even though we discard it.
|
|
23
|
+
const diff = new PNG({ width, height });
|
|
24
|
+
const diffCount = pixelmatch(a.data, b.data, diff.data, width, height, {
|
|
25
|
+
threshold: options.pixelThreshold ?? 0.1,
|
|
26
|
+
// pixelmatch's `includeAA: true` means "check AA pixels" (= flag them).
|
|
27
|
+
// Our `ignoreAntiAliasing` defaults true (= skip AA), so we invert.
|
|
28
|
+
includeAA: options.ignoreAntiAliasing === false,
|
|
29
|
+
});
|
|
30
|
+
if (diffCount === 0) {
|
|
31
|
+
return { match: true };
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
match: false,
|
|
35
|
+
reason: "pixel-diff",
|
|
36
|
+
diffCount,
|
|
37
|
+
diffPercentage: (diffCount / (width * height)) * 100,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAkE5B,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAO,EAAE,CACzC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC9B,QAAgB,EAChB,OAAe,EACf,UAAuB,EAAE,EACH,EAAE;IACxB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3B,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAClD,OAAO;YACN,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,aAAa;YACrB,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;YAC/C,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;SAC7C,CAAC;IACH,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,kEAAkE;IAClE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;QACtE,SAAS,EAAE,OAAO,CAAC,cAAc,IAAI,GAAG;QACxC,wEAAwE;QACxE,oEAAoE;QACpE,SAAS,EAAE,OAAO,CAAC,kBAAkB,KAAK,KAAK;KAC/C,CAAC,CAAC;IAEH,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACN,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,YAAY;QACpB,SAAS;QACT,cAAc,EAAE,CAAC,SAAS,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,GAAG;KACpD,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker error thrown by `kagemusha login` after it has already printed a
|
|
3
|
+
* friendly diagnostic + screenshot path to stderr. Callers (e.g. capture)
|
|
4
|
+
* should treat this as "already explained, just exit" — no extra log, no
|
|
5
|
+
* stack trace.
|
|
6
|
+
*/
|
|
7
|
+
export declare class LoginError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=login-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-error.d.ts","sourceRoot":"","sources":["../../src/lib/login-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI3B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker error thrown by `kagemusha login` after it has already printed a
|
|
3
|
+
* friendly diagnostic + screenshot path to stderr. Callers (e.g. capture)
|
|
4
|
+
* should treat this as "already explained, just exit" — no extra log, no
|
|
5
|
+
* stack trace.
|
|
6
|
+
*/
|
|
7
|
+
export class LoginError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "LoginError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=login-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-error.js","sourceRoot":"","sources":["../../src/lib/login-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACpC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC1B,CAAC;CACD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Page = import("playwright-chromium").Page;
|
|
2
|
+
/**
|
|
3
|
+
* After `page.goto({ waitUntil: "load" })`, wait for the SPA to settle before
|
|
4
|
+
* capturing or letting the user edit. Strategy:
|
|
5
|
+
*
|
|
6
|
+
* 1. Best-effort `networkidle` with a 3s cap — succeeds quickly on pages
|
|
7
|
+
* whose initial API fetches complete fast. SPAs with permanent socket
|
|
8
|
+
* connections never reach networkidle, but we cap the wait so they don't
|
|
9
|
+
* hang the run.
|
|
10
|
+
* 2. 500ms hydration buffer so React / Vue have time to mount after the
|
|
11
|
+
* initial render.
|
|
12
|
+
*
|
|
13
|
+
* Pages that need more than this should add a per-definition
|
|
14
|
+
* `beforeCapture: [{action:"waitForSelector",selector:"..."}]`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const waitForPageReady: (page: Page) => Promise<void>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=page-ready.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-ready.d.ts","sourceRoot":"","sources":["../../src/lib/page-ready.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,GAAG,OAAO,qBAAqB,EAAE,IAAI,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,IAAI,KAAG,OAAO,CAAC,IAAI,CAG/D,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* After `page.goto({ waitUntil: "load" })`, wait for the SPA to settle before
|
|
3
|
+
* capturing or letting the user edit. Strategy:
|
|
4
|
+
*
|
|
5
|
+
* 1. Best-effort `networkidle` with a 3s cap — succeeds quickly on pages
|
|
6
|
+
* whose initial API fetches complete fast. SPAs with permanent socket
|
|
7
|
+
* connections never reach networkidle, but we cap the wait so they don't
|
|
8
|
+
* hang the run.
|
|
9
|
+
* 2. 500ms hydration buffer so React / Vue have time to mount after the
|
|
10
|
+
* initial render.
|
|
11
|
+
*
|
|
12
|
+
* Pages that need more than this should add a per-definition
|
|
13
|
+
* `beforeCapture: [{action:"waitForSelector",selector:"..."}]`.
|
|
14
|
+
*/
|
|
15
|
+
export const waitForPageReady = async (page) => {
|
|
16
|
+
await page.waitForLoadState("networkidle", { timeout: 3000 }).catch(() => { });
|
|
17
|
+
await page.waitForTimeout(500);
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=page-ready.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-ready.js","sourceRoot":"","sources":["../../src/lib/page-ready.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,IAAU,EAAiB,EAAE;IACnE,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC,CAAC"}
|
package/dist/lib/screenshot.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { CaptureAction, KagemushaConfig, ScreenshotDefinition } from "../types.js";
|
|
2
|
+
type Page = import("playwright-chromium").Page;
|
|
3
|
+
export interface CaptureFailure {
|
|
4
|
+
id: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const captureScreenshots: (config: KagemushaConfig, definitions: ScreenshotDefinition[], projectRoot: string, options?: {
|
|
8
|
+
outputDir?: string;
|
|
9
|
+
}) => Promise<CaptureFailure[]>;
|
|
10
|
+
export declare const executeActions: (page: Page, actions: CaptureAction[]) => Promise<void>;
|
|
11
|
+
export {};
|
|
3
12
|
//# sourceMappingURL=screenshot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,MAAM,aAAa,CAAC;AAMrB,KAAK,IAAI,GAAG,OAAO,qBAAqB,EAAE,IAAI,CAAC;AAc/C,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,kBAAkB,GAC9B,QAAQ,eAAe,EACvB,aAAa,oBAAoB,EAAE,EACnC,aAAa,MAAM,EACnB,UAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,KAClC,OAAO,CAAC,cAAc,EAAE,CA6B1B,CAAC;AA8EF,eAAO,MAAM,cAAc,GAC1B,MAAM,IAAI,EACV,SAAS,aAAa,EAAE,KACtB,OAAO,CAAC,IAAI,CAqDd,CAAC"}
|
package/dist/lib/screenshot.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
|
|
3
|
+
import { drawAnnotations } from "./annotate.js";
|
|
4
|
+
import { defaultContextOptions } from "./auth.js";
|
|
5
|
+
import { getOutputDir } from "./canonical.js";
|
|
6
|
+
import { waitForPageReady } from "./page-ready.js";
|
|
7
|
+
const loadPlaywright = async () => {
|
|
4
8
|
try {
|
|
5
9
|
return await import("playwright-chromium");
|
|
6
10
|
}
|
|
@@ -8,43 +12,33 @@ async function loadPlaywright() {
|
|
|
8
12
|
throw new Error("Playwright is required for screenshot capture.\n" +
|
|
9
13
|
"Install it with: npm install -D playwright && npx playwright install chromium");
|
|
10
14
|
}
|
|
11
|
-
}
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const outputDir = path.join(projectRoot, SCREENSHOTS_DIR);
|
|
15
|
+
};
|
|
16
|
+
export const captureScreenshots = async (config, definitions, projectRoot, options = {}) => {
|
|
17
|
+
const outputDir = options.outputDir ?? getOutputDir(config, projectRoot);
|
|
15
18
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
16
19
|
const { chromium } = await loadPlaywright();
|
|
17
20
|
const browser = await chromium.launch({ headless: true });
|
|
18
|
-
const
|
|
21
|
+
const failures = [];
|
|
19
22
|
try {
|
|
20
|
-
const context = await
|
|
23
|
+
const context = await browser.newContext(defaultContextOptions(config, projectRoot));
|
|
21
24
|
for (const def of definitions) {
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
try {
|
|
26
|
+
await captureOne(context, config, def, outputDir);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
30
|
+
failures.push({ id: def.id, reason });
|
|
31
|
+
console.error(` ⚠ ${def.id}: ${reason}`);
|
|
32
|
+
}
|
|
24
33
|
}
|
|
25
34
|
await context.close();
|
|
26
35
|
}
|
|
27
36
|
finally {
|
|
28
37
|
await browser.close();
|
|
29
38
|
}
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
async
|
|
33
|
-
const viewport = config.screenshot.defaultViewport;
|
|
34
|
-
const context = await browser.newContext({
|
|
35
|
-
viewport: { width: viewport.width, height: viewport.height },
|
|
36
|
-
deviceScaleFactor: viewport.deviceScaleFactor ?? 2,
|
|
37
|
-
});
|
|
38
|
-
if (config.auth) {
|
|
39
|
-
const page = await context.newPage();
|
|
40
|
-
const loginUrl = new URL(config.auth.loginUrl, config.app.baseUrl).toString();
|
|
41
|
-
await page.goto(loginUrl);
|
|
42
|
-
await executeActions(page, config.auth.steps);
|
|
43
|
-
await page.close();
|
|
44
|
-
}
|
|
45
|
-
return context;
|
|
46
|
-
}
|
|
47
|
-
async function captureOne(context, config, def, outputDir) {
|
|
39
|
+
return failures;
|
|
40
|
+
};
|
|
41
|
+
const captureOne = async (context, config, def, outputDir) => {
|
|
48
42
|
const page = await context.newPage();
|
|
49
43
|
if (def.viewport) {
|
|
50
44
|
await page.setViewportSize({
|
|
@@ -53,43 +47,32 @@ async function captureOne(context, config, def, outputDir) {
|
|
|
53
47
|
});
|
|
54
48
|
}
|
|
55
49
|
const url = resolveUrl(config.app.baseUrl, def.url, def.urlParams);
|
|
56
|
-
await page.goto(url, { waitUntil: "
|
|
50
|
+
await page.goto(url, { waitUntil: "load", timeout: 60000 });
|
|
51
|
+
await waitForPageReady(page);
|
|
57
52
|
if (def.hideElements?.length) {
|
|
58
53
|
await hideElements(page, def.hideElements);
|
|
59
54
|
}
|
|
60
55
|
if (def.beforeCapture?.length) {
|
|
61
56
|
await executeActions(page, def.beforeCapture);
|
|
62
57
|
}
|
|
63
|
-
const
|
|
64
|
-
await takeScreenshot(page, def, rawPath);
|
|
58
|
+
const buffer = await takeScreenshotBuffer(page, def);
|
|
65
59
|
await page.close();
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
60
|
+
const finalPath = path.join(outputDir, `${def.id}.png`);
|
|
61
|
+
if (def.decorations?.length) {
|
|
62
|
+
const dpr = config.screenshot.defaultViewport.deviceScaleFactor ?? 2;
|
|
63
|
+
await drawAnnotations(buffer, finalPath, def.decorations, def.capture, dpr);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
fs.writeFileSync(finalPath, buffer);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const takeScreenshotBuffer = async (page, def) => {
|
|
75
70
|
switch (def.capture.mode) {
|
|
76
71
|
case "fullPage":
|
|
77
|
-
await page.screenshot({
|
|
78
|
-
break;
|
|
79
|
-
case "selector": {
|
|
80
|
-
const element = await page.waitForSelector(def.capture.selector, {
|
|
81
|
-
timeout: 10000,
|
|
82
|
-
});
|
|
83
|
-
if (!element) {
|
|
84
|
-
throw new Error(`Element not found: ${def.capture.selector} (definition: ${def.id})`);
|
|
85
|
-
}
|
|
86
|
-
await element.screenshot({ path: outputPath });
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
72
|
+
return await page.screenshot({ fullPage: true });
|
|
89
73
|
case "crop": {
|
|
90
74
|
const { start, end } = def.capture.crop;
|
|
91
|
-
await page.screenshot({
|
|
92
|
-
path: outputPath,
|
|
75
|
+
return await page.screenshot({
|
|
93
76
|
clip: {
|
|
94
77
|
x: start.x,
|
|
95
78
|
y: start.y,
|
|
@@ -97,23 +80,40 @@ async function takeScreenshot(page, def, outputPath) {
|
|
|
97
80
|
height: end.y - start.y,
|
|
98
81
|
},
|
|
99
82
|
});
|
|
100
|
-
break;
|
|
101
83
|
}
|
|
84
|
+
default:
|
|
85
|
+
console.warn(` ⚠ ${def.id}: unknown capture mode "${def.capture.mode}", falling back to fullPage.`);
|
|
86
|
+
return await page.screenshot({ fullPage: true });
|
|
102
87
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
88
|
+
};
|
|
89
|
+
// Returns false (= skip the step) when `optional: true` and the selector
|
|
90
|
+
// doesn't match anything on the page. The page.$ probe completes
|
|
91
|
+
// instantly — no Playwright timeout involved.
|
|
92
|
+
const isPresent = async (page, selector) => (await page.$(selector)) !== null;
|
|
93
|
+
// Exported so the `edit` command can replay beforeCapture before injecting
|
|
94
|
+
// the editor — that way users author annotations on the same page state
|
|
95
|
+
// kagemusha will eventually screenshot.
|
|
96
|
+
export const executeActions = async (page, actions) => {
|
|
105
97
|
for (const action of actions) {
|
|
106
98
|
switch (action.action) {
|
|
107
99
|
case "click":
|
|
100
|
+
if (action.optional && !(await isPresent(page, action.selector)))
|
|
101
|
+
break;
|
|
108
102
|
await page.click(action.selector);
|
|
109
103
|
break;
|
|
110
104
|
case "type":
|
|
105
|
+
if (action.optional && !(await isPresent(page, action.selector)))
|
|
106
|
+
break;
|
|
111
107
|
await page.fill(action.selector, action.text);
|
|
112
108
|
break;
|
|
113
109
|
case "select":
|
|
110
|
+
if (action.optional && !(await isPresent(page, action.selector)))
|
|
111
|
+
break;
|
|
114
112
|
await page.selectOption(action.selector, action.value);
|
|
115
113
|
break;
|
|
116
114
|
case "hover":
|
|
115
|
+
if (action.optional && !(await isPresent(page, action.selector)))
|
|
116
|
+
break;
|
|
117
117
|
await page.hover(action.selector);
|
|
118
118
|
break;
|
|
119
119
|
case "scroll":
|
|
@@ -130,9 +130,18 @@ async function executeActions(page, actions) {
|
|
|
130
130
|
await page.waitForTimeout(action.ms);
|
|
131
131
|
break;
|
|
132
132
|
case "waitForSelector":
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
try {
|
|
134
|
+
await page.waitForSelector(action.selector, {
|
|
135
|
+
timeout: action.timeout ?? 10000,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
// `optional: true` turns wait-for-selector failures into a
|
|
140
|
+
// no-op (= the rest of beforeCapture continues). Without
|
|
141
|
+
// optional, the timeout bubbles up and fails the capture.
|
|
142
|
+
if (!action.optional)
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
136
145
|
break;
|
|
137
146
|
case "waitForNavigation":
|
|
138
147
|
await page.waitForLoadState("networkidle", {
|
|
@@ -144,8 +153,8 @@ async function executeActions(page, actions) {
|
|
|
144
153
|
break;
|
|
145
154
|
}
|
|
146
155
|
}
|
|
147
|
-
}
|
|
148
|
-
async
|
|
156
|
+
};
|
|
157
|
+
const hideElements = async (page, selectors) => {
|
|
149
158
|
for (const selector of selectors) {
|
|
150
159
|
await page.evaluate((sel) => {
|
|
151
160
|
document.querySelectorAll(sel).forEach((el) => {
|
|
@@ -153,8 +162,8 @@ async function hideElements(page, selectors) {
|
|
|
153
162
|
});
|
|
154
163
|
}, selector);
|
|
155
164
|
}
|
|
156
|
-
}
|
|
157
|
-
|
|
165
|
+
};
|
|
166
|
+
const resolveUrl = (baseUrl, urlPath, params) => {
|
|
158
167
|
let resolved = urlPath;
|
|
159
168
|
if (params) {
|
|
160
169
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -162,5 +171,5 @@ function resolveUrl(baseUrl, urlPath, params) {
|
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
173
|
return new URL(resolved, baseUrl).toString();
|
|
165
|
-
}
|
|
174
|
+
};
|
|
166
175
|
//# sourceMappingURL=screenshot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const getStagingDir: (projectRoot: string) => string;
|
|
2
|
+
export declare const getStagingPath: (projectRoot: string, id: string) => string;
|
|
3
|
+
export declare const ensureStagingDirs: (projectRoot: string) => void;
|
|
4
|
+
/** Move staging file to canonical location, overwriting if it exists. */
|
|
5
|
+
export declare const promoteToCanonical: (stagingPath: string, canonicalPath: string) => void;
|
|
6
|
+
export declare const cleanupStaging: (projectRoot: string) => void;
|
|
7
|
+
//# sourceMappingURL=staging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../src/lib/staging.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MAChB,CAAC;AAErC,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,EAAE,IAAI,MAAM,KAAG,MAChB,CAAC;AAElD,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,KAAG,IAEvD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,GAC9B,aAAa,MAAM,EACnB,eAAe,MAAM,KACnB,IAIF,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,KAAG,IAKpD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const STAGING_DIR = path.join(".kagemusha", ".staging");
|
|
4
|
+
export const getStagingDir = (projectRoot) => path.join(projectRoot, STAGING_DIR);
|
|
5
|
+
export const getStagingPath = (projectRoot, id) => path.join(projectRoot, STAGING_DIR, `${id}.png`);
|
|
6
|
+
export const ensureStagingDirs = (projectRoot) => {
|
|
7
|
+
fs.mkdirSync(getStagingDir(projectRoot), { recursive: true });
|
|
8
|
+
};
|
|
9
|
+
/** Move staging file to canonical location, overwriting if it exists. */
|
|
10
|
+
export const promoteToCanonical = (stagingPath, canonicalPath) => {
|
|
11
|
+
fs.mkdirSync(path.dirname(canonicalPath), { recursive: true });
|
|
12
|
+
fs.copyFileSync(stagingPath, canonicalPath);
|
|
13
|
+
fs.rmSync(stagingPath, { force: true });
|
|
14
|
+
};
|
|
15
|
+
export const cleanupStaging = (projectRoot) => {
|
|
16
|
+
const dir = getStagingDir(projectRoot);
|
|
17
|
+
if (fs.existsSync(dir)) {
|
|
18
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=staging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.js","sourceRoot":"","sources":["../../src/lib/staging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AAExD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,WAAmB,EAAU,EAAE,CAC5D,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAErC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,WAAmB,EAAE,EAAU,EAAU,EAAE,CACzE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,WAAmB,EAAQ,EAAE;IAC9D,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,WAAmB,EACnB,aAAqB,EACd,EAAE;IACT,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC5C,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,WAAmB,EAAQ,EAAE;IAC3D,MAAM,GAAG,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACF,CAAC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ export interface KagemushaConfig {
|
|
|
2
2
|
app: {
|
|
3
3
|
baseUrl: string;
|
|
4
4
|
};
|
|
5
|
-
auth?: {
|
|
6
|
-
loginUrl: string;
|
|
7
|
-
steps: CaptureAction[];
|
|
8
|
-
};
|
|
9
5
|
screenshot: {
|
|
10
6
|
defaultViewport: Viewport;
|
|
11
7
|
defaultDiffThreshold: number;
|
|
12
8
|
};
|
|
9
|
+
auth?: {
|
|
10
|
+
scriptPath?: string;
|
|
11
|
+
};
|
|
13
12
|
publish?: {
|
|
14
13
|
destination: "local" | "s3" | "intercom";
|
|
15
14
|
outputDir?: string;
|
|
@@ -39,7 +38,7 @@ export interface ScreenshotDefinition {
|
|
|
39
38
|
urlParams?: Record<string, string>;
|
|
40
39
|
viewport?: Viewport;
|
|
41
40
|
beforeCapture?: CaptureAction[];
|
|
42
|
-
capture:
|
|
41
|
+
capture: CaptureSpec;
|
|
43
42
|
decorations?: Decoration[];
|
|
44
43
|
hideElements?: string[];
|
|
45
44
|
intercom?: {
|
|
@@ -50,17 +49,21 @@ export interface ScreenshotDefinition {
|
|
|
50
49
|
export type CaptureAction = {
|
|
51
50
|
action: "click";
|
|
52
51
|
selector: string;
|
|
52
|
+
optional?: boolean;
|
|
53
53
|
} | {
|
|
54
54
|
action: "type";
|
|
55
55
|
selector: string;
|
|
56
56
|
text: string;
|
|
57
|
+
optional?: boolean;
|
|
57
58
|
} | {
|
|
58
59
|
action: "select";
|
|
59
60
|
selector: string;
|
|
60
61
|
value: string;
|
|
62
|
+
optional?: boolean;
|
|
61
63
|
} | {
|
|
62
64
|
action: "hover";
|
|
63
65
|
selector: string;
|
|
66
|
+
optional?: boolean;
|
|
64
67
|
} | {
|
|
65
68
|
action: "scroll";
|
|
66
69
|
selector?: string;
|
|
@@ -72,6 +75,7 @@ export type CaptureAction = {
|
|
|
72
75
|
action: "waitForSelector";
|
|
73
76
|
selector: string;
|
|
74
77
|
timeout?: number;
|
|
78
|
+
optional?: boolean;
|
|
75
79
|
} | {
|
|
76
80
|
action: "waitForNavigation";
|
|
77
81
|
timeout?: number;
|
|
@@ -79,11 +83,8 @@ export type CaptureAction = {
|
|
|
79
83
|
action: "evaluate";
|
|
80
84
|
script: string;
|
|
81
85
|
};
|
|
82
|
-
export type
|
|
86
|
+
export type CaptureSpec = {
|
|
83
87
|
mode: "fullPage";
|
|
84
|
-
} | {
|
|
85
|
-
mode: "selector";
|
|
86
|
-
selector: string;
|
|
87
88
|
} | {
|
|
88
89
|
mode: "crop";
|
|
89
90
|
crop: {
|
|
@@ -151,18 +152,4 @@ export interface LabelDecoration {
|
|
|
151
152
|
background?: string;
|
|
152
153
|
};
|
|
153
154
|
}
|
|
154
|
-
export interface CaptureResult {
|
|
155
|
-
id: string;
|
|
156
|
-
rawPath: string;
|
|
157
|
-
annotatedPath: string;
|
|
158
|
-
timestamp: string;
|
|
159
|
-
}
|
|
160
|
-
export interface CompareResult {
|
|
161
|
-
id: string;
|
|
162
|
-
status: "unchanged" | "minor" | "changed";
|
|
163
|
-
diffRate: number;
|
|
164
|
-
beforePath?: string;
|
|
165
|
-
afterPath?: string;
|
|
166
|
-
diffPath?: string;
|
|
167
|
-
}
|
|
168
155
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|