@wasao/kagemusha 0.2.0 → 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.
Files changed (85) hide show
  1. package/README.md +103 -453
  2. package/dist/commands/capture.js +57 -35
  3. package/dist/commands/capture.js.map +1 -1
  4. package/dist/commands/edit.d.ts +1 -1
  5. package/dist/commands/edit.d.ts.map +1 -1
  6. package/dist/commands/edit.js +43 -7
  7. package/dist/commands/edit.js.map +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +75 -44
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/login.js +3 -14
  12. package/dist/commands/login.js.map +1 -1
  13. package/dist/editor/inject-script/annotations.d.ts +11 -0
  14. package/dist/editor/inject-script/annotations.d.ts.map +1 -0
  15. package/dist/editor/inject-script/annotations.js +409 -0
  16. package/dist/editor/inject-script/annotations.js.map +1 -0
  17. package/dist/editor/inject-script/bridge.d.ts +13 -0
  18. package/dist/editor/inject-script/bridge.d.ts.map +1 -0
  19. package/dist/editor/inject-script/bridge.js +33 -0
  20. package/dist/editor/inject-script/bridge.js.map +1 -0
  21. package/dist/editor/inject-script/crop.d.ts +9 -0
  22. package/dist/editor/inject-script/crop.d.ts.map +1 -0
  23. package/dist/editor/inject-script/crop.js +236 -0
  24. package/dist/editor/inject-script/crop.js.map +1 -0
  25. package/dist/editor/inject-script/dom.d.ts +7 -0
  26. package/dist/editor/inject-script/dom.d.ts.map +1 -0
  27. package/dist/editor/inject-script/dom.js +32 -0
  28. package/dist/editor/inject-script/dom.js.map +1 -0
  29. package/dist/editor/inject-script/index.d.ts +2 -0
  30. package/dist/editor/inject-script/index.d.ts.map +1 -0
  31. package/dist/editor/inject-script/index.js +56 -0
  32. package/dist/editor/inject-script/index.js.map +1 -0
  33. package/dist/editor/inject-script/record.d.ts +5 -0
  34. package/dist/editor/inject-script/record.d.ts.map +1 -0
  35. package/dist/editor/inject-script/record.js +398 -0
  36. package/dist/editor/inject-script/record.js.map +1 -0
  37. package/dist/editor/inject-script/selector.d.ts +6 -0
  38. package/dist/editor/inject-script/selector.d.ts.map +1 -0
  39. package/dist/editor/inject-script/selector.js +112 -0
  40. package/dist/editor/inject-script/selector.js.map +1 -0
  41. package/dist/editor/inject-script/state.d.ts +27 -0
  42. package/dist/editor/inject-script/state.d.ts.map +1 -0
  43. package/dist/editor/inject-script/state.js +26 -0
  44. package/dist/editor/inject-script/state.js.map +1 -0
  45. package/dist/editor/inject-script/svg.d.ts +7 -0
  46. package/dist/editor/inject-script/svg.d.ts.map +1 -0
  47. package/dist/editor/inject-script/svg.js +39 -0
  48. package/dist/editor/inject-script/svg.js.map +1 -0
  49. package/dist/editor/inject-script/toolbar.d.ts +14 -0
  50. package/dist/editor/inject-script/toolbar.d.ts.map +1 -0
  51. package/dist/editor/inject-script/toolbar.js +240 -0
  52. package/dist/editor/inject-script/toolbar.js.map +1 -0
  53. package/dist/editor/inject-script/types.d.ts +102 -0
  54. package/dist/editor/inject-script/types.d.ts.map +1 -0
  55. package/dist/editor/inject-script/types.js +5 -0
  56. package/dist/editor/inject-script/types.js.map +1 -0
  57. package/dist/editor/inject-script.js +1248 -699
  58. package/dist/index.js +9 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/lib/canonical.d.ts +35 -3
  61. package/dist/lib/canonical.d.ts.map +1 -1
  62. package/dist/lib/canonical.js +85 -25
  63. package/dist/lib/canonical.js.map +1 -1
  64. package/dist/lib/diff.d.ts +23 -4
  65. package/dist/lib/diff.d.ts.map +1 -1
  66. package/dist/lib/diff.js +5 -6
  67. package/dist/lib/diff.js.map +1 -1
  68. package/dist/lib/page-ready.d.ts +18 -0
  69. package/dist/lib/page-ready.d.ts.map +1 -0
  70. package/dist/lib/page-ready.js +19 -0
  71. package/dist/lib/page-ready.js.map +1 -0
  72. package/dist/lib/screenshot.d.ts +4 -1
  73. package/dist/lib/screenshot.d.ts.map +1 -1
  74. package/dist/lib/screenshot.js +31 -5
  75. package/dist/lib/screenshot.js.map +1 -1
  76. package/dist/lib/staging.d.ts +0 -1
  77. package/dist/lib/staging.d.ts.map +1 -1
  78. package/dist/lib/staging.js +0 -3
  79. package/dist/lib/staging.js.map +1 -1
  80. package/dist/types.d.ts +5 -0
  81. package/dist/types.d.ts.map +1 -1
  82. package/package.json +20 -10
  83. package/dist/editor/inject-script.d.ts +0 -2
  84. package/dist/editor/inject-script.d.ts.map +0 -1
  85. package/dist/editor/inject-script.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
2
4
  import chalk from "chalk";
3
5
  import { Command } from "commander";
4
6
  import { addCommand } from "./commands/add.js";
@@ -9,8 +11,13 @@ import { initCommand } from "./commands/init.js";
9
11
  import { listCommand } from "./commands/list.js";
10
12
  import { loginCommand } from "./commands/login.js";
11
13
  import { validateCommand } from "./commands/validate.js";
14
+ // Read version from package.json at runtime so release-please only needs to
15
+ // update one file (= package.json) on every bump. dist/index.js sits next
16
+ // to dist/, so package.json is two levels up (= ../package.json).
17
+ const pkgPath = fileURLToPath(new URL("../package.json", import.meta.url));
18
+ const VERSION = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
12
19
  const BANNER = `
13
- ${chalk.bold("kagemusha")} ${chalk.gray("v0.2.0")}
20
+ ${chalk.bold("kagemusha")} ${chalk.gray(`v${VERSION}`)}
14
21
  ${chalk.dim("The shadow warrior for your documentation.")}
15
22
 
16
23
  ${chalk.white("Auto-update help center screenshots")}
@@ -19,7 +26,7 @@ const BANNER = `
19
26
  const program = new Command();
20
27
  program
21
28
  .name("kagemusha")
22
- .version("0.2.0")
29
+ .version(VERSION)
23
30
  .addHelpText("beforeAll", BANNER)
24
31
  .configureHelp({
25
32
  sortSubcommands: false,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,18 @@ import type { KagemushaConfig } from "../types.js";
2
2
  export declare const getOutputDir: (config: KagemushaConfig, projectRoot: string) => string;
3
3
  export declare const getCanonicalPath: (config: KagemushaConfig, projectRoot: string, id: string) => string;
4
4
  export type FetchResult = "ok" | "not-found";
5
+ /**
6
+ * URLs returned by `push()` so callers can include them in summary.json
7
+ * for downstream notification consumers (Slack, PR comments, etc.).
8
+ *
9
+ * - `after`: the new `latest.png` we just uploaded
10
+ * - `before`: the previous `latest.png`, copied to `previous.png` before
11
+ * being overwritten. Undefined when no prior version existed (= first push)
12
+ */
13
+ export interface PushUrls {
14
+ after: string;
15
+ before?: string;
16
+ }
5
17
  /**
6
18
  * S3-backed canonical store.
7
19
  * Local mode has no remote — outputDir itself is the source of truth.
@@ -11,11 +23,31 @@ export declare class S3Canonical {
11
23
  private readonly cdnBaseUrl?;
12
24
  private readonly client;
13
25
  constructor(bucket: string, cdnBaseUrl?: string | undefined);
14
- private keyOf;
26
+ private latestKey;
27
+ private previousKey;
28
+ private historyKey;
29
+ private urlFor;
15
30
  /** Download canonical for `id` to `localPath`. Returns "not-found" if absent. */
16
31
  fetch(id: string, localPath: string): Promise<FetchResult>;
17
- /** Upload `localPath` as the canonical for `id`. */
18
- push(id: string, localPath: string): Promise<void>;
32
+ /**
33
+ * Upload `localPath` as the canonical for `id`.
34
+ *
35
+ * Side effects on S3 for a single push:
36
+ * 1. Copy existing latest.png → previous.png (no-op if missing)
37
+ * 2. Upload localPath → latest.png
38
+ * 3. Upload localPath → history/<timestamp>.png
39
+ *
40
+ * Step 1 must complete first (otherwise the soon-to-be-overwritten latest
41
+ * would be replaced before the snapshot is taken). Steps 2-3 target
42
+ * different keys and run in parallel.
43
+ *
44
+ * Returns URLs (`before` / `after`) so callers can surface them in
45
+ * `reports/summary.json` (= public API). Consumers compare before vs after
46
+ * visually; kagemusha intentionally does not publish a pre-generated diff
47
+ * image (= pixelmatch's red overlay is alarming and adds little vs the
48
+ * raw pair).
49
+ */
50
+ push(id: string, localPath: string): Promise<PushUrls>;
19
51
  label(): string;
20
52
  }
21
53
  export declare const createS3Canonical: (config: KagemushaConfig) => S3Canonical | null;
@@ -1 +1 @@
1
- {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../../src/lib/canonical.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,eAAO,MAAM,YAAY,GACxB,QAAQ,eAAe,EACvB,aAAa,MAAM,KACjB,MAKF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC5B,QAAQ,eAAe,EACvB,aAAa,MAAM,EACnB,IAAI,MAAM,KACR,MAAmE,CAAC;AAEvE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,WAAW,CAAC;AAU7C;;;GAGG;AAEH,qBAAa,WAAW;IAItB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;gBAGhB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,YAAA;IAMrC,OAAO,CAAC,KAAK;IAIb,iFAAiF;IAC3E,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBhE,oDAAoD;IAC9C,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxD,KAAK,IAAI,MAAM;CAGf;AAED,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,eAAe,KACrB,WAAW,GAAG,IAOhB,CAAC"}
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../../src/lib/canonical.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,eAAO,MAAM,YAAY,GACxB,QAAQ,eAAe,EACvB,aAAa,MAAM,KACjB,MAKF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC5B,QAAQ,eAAe,EACvB,aAAa,MAAM,EACnB,IAAI,MAAM,KACR,MAAmE,CAAC;AAEvE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,WAAW,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAUD;;;GAGG;AAEH,qBAAa,WAAW;IAItB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;gBAGhB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,YAAA;IAMrC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,MAAM;IAKd,iFAAiF;IAC3E,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBhE;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuD5D,KAAK,IAAI,MAAM;CAGf;AASD,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,eAAe,KACrB,WAAW,GAAG,IAOhB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { GetObjectCommand, NoSuchKey, PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
3
+ import { CopyObjectCommand, GetObjectCommand, NoSuchKey, PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
4
4
  const DEFAULT_OUTPUT_DIR = "screenshots";
5
5
  export const getOutputDir = (config, projectRoot) => {
6
6
  const configured = config.publish?.outputDir ?? DEFAULT_OUTPUT_DIR;
@@ -31,13 +31,26 @@ export class S3Canonical {
31
31
  const region = extractRegionFromCdnBase(cdnBaseUrl);
32
32
  this.client = new S3Client(region ? { region } : {});
33
33
  }
34
- keyOf(id) {
34
+ latestKey(id) {
35
35
  return `${id}/latest.png`;
36
36
  }
37
+ previousKey(id) {
38
+ return `${id}/previous.png`;
39
+ }
40
+ historyKey(id, timestamp) {
41
+ // Group history snapshots under a sub-prefix so the bucket list
42
+ // shows `latest.png` / `previous.png` cleanly without historical
43
+ // snapshots interleaved alphabetically.
44
+ return `${id}/history/${timestamp}.png`;
45
+ }
46
+ urlFor(key) {
47
+ const base = this.cdnBaseUrl ?? `s3://${this.bucket}`;
48
+ return `${base.replace(/\/$/, "")}/${key}`;
49
+ }
37
50
  /** Download canonical for `id` to `localPath`. Returns "not-found" if absent. */
38
51
  async fetch(id, localPath) {
39
52
  try {
40
- const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: this.keyOf(id) }));
53
+ const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: this.latestKey(id) }));
41
54
  const bytes = await res.Body?.transformToByteArray();
42
55
  if (!bytes)
43
56
  return "not-found";
@@ -46,40 +59,87 @@ export class S3Canonical {
46
59
  return "ok";
47
60
  }
48
61
  catch (e) {
49
- if (e instanceof NoSuchKey)
50
- return "not-found";
51
- if (e?.name === "NoSuchKey")
52
- return "not-found";
53
- if (e?.Code === "NoSuchKey")
62
+ if (isNoSuchKey(e))
54
63
  return "not-found";
55
64
  throw e;
56
65
  }
57
66
  }
58
- /** Upload `localPath` as the canonical for `id`. */
67
+ /**
68
+ * Upload `localPath` as the canonical for `id`.
69
+ *
70
+ * Side effects on S3 for a single push:
71
+ * 1. Copy existing latest.png → previous.png (no-op if missing)
72
+ * 2. Upload localPath → latest.png
73
+ * 3. Upload localPath → history/<timestamp>.png
74
+ *
75
+ * Step 1 must complete first (otherwise the soon-to-be-overwritten latest
76
+ * would be replaced before the snapshot is taken). Steps 2-3 target
77
+ * different keys and run in parallel.
78
+ *
79
+ * Returns URLs (`before` / `after`) so callers can surface them in
80
+ * `reports/summary.json` (= public API). Consumers compare before vs after
81
+ * visually; kagemusha intentionally does not publish a pre-generated diff
82
+ * image (= pixelmatch's red overlay is alarming and adds little vs the
83
+ * raw pair).
84
+ */
59
85
  async push(id, localPath) {
60
86
  const body = fs.readFileSync(localPath);
61
- await this.client.send(new PutObjectCommand({
62
- Bucket: this.bucket,
63
- Key: this.keyOf(id),
64
- Body: body,
65
- ContentType: "image/png",
66
- CacheControl: "no-cache",
67
- }));
68
- // History snapshot for debug / rollback. Replace `:` so the key works
69
- // in URLs without %3A encoding (S3 accepts it but URL-embedding breaks).
87
+ // 1. Snapshot the soon-to-be-overwritten latest as `previous`.
88
+ // Uses CopyObject (= S3-side copy, no local round-trip). First push for
89
+ // this id has no latest yet — we swallow NoSuchKey and report `before:
90
+ // undefined` to the caller.
91
+ let beforeUrl;
92
+ try {
93
+ await this.client.send(new CopyObjectCommand({
94
+ Bucket: this.bucket,
95
+ CopySource: `${this.bucket}/${this.latestKey(id)}`,
96
+ Key: this.previousKey(id),
97
+ MetadataDirective: "REPLACE",
98
+ ContentType: "image/png",
99
+ CacheControl: "no-cache",
100
+ }));
101
+ beforeUrl = this.urlFor(this.previousKey(id));
102
+ }
103
+ catch (e) {
104
+ if (!isNoSuchKey(e))
105
+ throw e;
106
+ // first push for this id — no previous yet, leave beforeUrl undefined
107
+ }
108
+ // 2-3. Independent uploads — run in parallel (= ~2x faster than serial).
70
109
  const timestamp = new Date().toISOString().replaceAll(":", "-");
71
- const historyKey = `${id}/${timestamp}.png`;
72
- await this.client.send(new PutObjectCommand({
73
- Bucket: this.bucket,
74
- Key: historyKey,
75
- Body: body,
76
- ContentType: "image/png",
77
- }));
110
+ await Promise.all([
111
+ // 2. latest
112
+ this.client.send(new PutObjectCommand({
113
+ Bucket: this.bucket,
114
+ Key: this.latestKey(id),
115
+ Body: body,
116
+ ContentType: "image/png",
117
+ CacheControl: "no-cache",
118
+ })),
119
+ // 3. history snapshot
120
+ this.client.send(new PutObjectCommand({
121
+ Bucket: this.bucket,
122
+ Key: this.historyKey(id, timestamp),
123
+ Body: body,
124
+ ContentType: "image/png",
125
+ })),
126
+ ]);
127
+ return {
128
+ after: this.urlFor(this.latestKey(id)),
129
+ before: beforeUrl,
130
+ };
78
131
  }
79
132
  label() {
80
133
  return this.cdnBaseUrl ?? `s3://${this.bucket}`;
81
134
  }
82
135
  }
136
+ const isNoSuchKey = (e) => {
137
+ if (e instanceof NoSuchKey)
138
+ return true;
139
+ const name = e?.name;
140
+ const code = e?.Code;
141
+ return name === "NoSuchKey" || code === "NoSuchKey";
142
+ };
83
143
  export const createS3Canonical = (config) => {
84
144
  const publish = config.publish;
85
145
  if (publish?.destination !== "s3")
@@ -1 +1 @@
1
- {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../src/lib/canonical.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../src/lib/canonical.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,29 @@ export interface Dimensions {
2
2
  width: number;
3
3
  height: number;
4
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
+ }
5
21
  export type DiffStatus = {
6
22
  id: string;
7
23
  status: "unchanged";
8
24
  } | {
9
25
  id: string;
10
26
  status: "new";
27
+ urls?: ResultUrls;
11
28
  } | {
12
29
  id: string;
13
30
  status: "missing";
@@ -17,13 +34,14 @@ export type DiffStatus = {
17
34
  status: "changed";
18
35
  reason: "pixel-diff";
19
36
  diffPercentage: number;
20
- diffPath: string;
37
+ urls?: ResultUrls;
21
38
  } | {
22
39
  id: string;
23
40
  status: "changed";
24
41
  reason: "layout-diff";
25
42
  canonical: Dimensions;
26
43
  staging: Dimensions;
44
+ urls?: ResultUrls;
27
45
  };
28
46
  export interface DiffOptions {
29
47
  /** Color difference threshold per pixel (0-1). Lower = stricter. Default 0.1 */
@@ -45,8 +63,9 @@ export type DiffResult = {
45
63
  diffPercentage: number;
46
64
  };
47
65
  /**
48
- * Compare two PNG files using pixelmatch and write the diff visualization.
49
- * If dimensions differ, returns layout-diff without writing a diff image.
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).
50
69
  */
51
- export declare const diffImages: (baseline: string, current: string, diffPath: string, options?: DiffOptions) => Promise<DiffResult>;
70
+ export declare const diffImages: (baseline: string, current: string, options?: DiffOptions) => Promise<DiffResult>;
52
71
  //# sourceMappingURL=diff.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;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,CAAA;CAAE,GAC7B;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,QAAQ,EAAE,MAAM,CAAC;CAChB,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;CACnB,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;;;GAGG;AACH,eAAO,MAAM,UAAU,GACtB,UAAU,MAAM,EAChB,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,UAAS,WAAgB,KACvB,OAAO,CAAC,UAAU,CAoCpB,CAAC"}
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 CHANGED
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
- import path from "node:path";
3
2
  import pixelmatch from "pixelmatch";
4
3
  import { PNG } from "pngjs";
5
4
  const readPng = (filePath) => PNG.sync.read(fs.readFileSync(filePath));
6
5
  /**
7
- * Compare two PNG files using pixelmatch and write the diff visualization.
8
- * If dimensions differ, returns layout-diff without writing a diff image.
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
9
  */
10
- export const diffImages = async (baseline, current, diffPath, options = {}) => {
10
+ export const diffImages = async (baseline, current, options = {}) => {
11
11
  const a = readPng(baseline);
12
12
  const b = readPng(current);
13
13
  if (a.width !== b.width || a.height !== b.height) {
@@ -19,6 +19,7 @@ export const diffImages = async (baseline, current, diffPath, options = {}) => {
19
19
  };
20
20
  }
21
21
  const { width, height } = a;
22
+ // pixelmatch requires an output buffer even though we discard it.
22
23
  const diff = new PNG({ width, height });
23
24
  const diffCount = pixelmatch(a.data, b.data, diff.data, width, height, {
24
25
  threshold: options.pixelThreshold ?? 0.1,
@@ -29,8 +30,6 @@ export const diffImages = async (baseline, current, diffPath, options = {}) => {
29
30
  if (diffCount === 0) {
30
31
  return { match: true };
31
32
  }
32
- fs.mkdirSync(path.dirname(diffPath), { recursive: true });
33
- fs.writeFileSync(diffPath, PNG.sync.write(diff));
34
33
  return {
35
34
  match: false,
36
35
  reason: "pixel-diff",
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAgD5B,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAO,EAAE,CACzC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE1C;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC9B,QAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,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,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,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjD,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"}
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,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"}
@@ -1,4 +1,5 @@
1
- import type { KagemushaConfig, ScreenshotDefinition } from "../types.js";
1
+ import type { CaptureAction, KagemushaConfig, ScreenshotDefinition } from "../types.js";
2
+ type Page = import("playwright-chromium").Page;
2
3
  export interface CaptureFailure {
3
4
  id: string;
4
5
  reason: string;
@@ -6,4 +7,6 @@ export interface CaptureFailure {
6
7
  export declare const captureScreenshots: (config: KagemushaConfig, definitions: ScreenshotDefinition[], projectRoot: string, options?: {
7
8
  outputDir?: string;
8
9
  }) => Promise<CaptureFailure[]>;
10
+ export declare const executeActions: (page: Page, actions: CaptureAction[]) => Promise<void>;
11
+ export {};
9
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,EAEX,eAAe,EACf,oBAAoB,EACpB,MAAM,aAAa,CAAC;AAmBrB,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"}
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"}
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { drawAnnotations } from "./annotate.js";
4
4
  import { defaultContextOptions } from "./auth.js";
5
5
  import { getOutputDir } from "./canonical.js";
6
+ import { waitForPageReady } from "./page-ready.js";
6
7
  const loadPlaywright = async () => {
7
8
  try {
8
9
  return await import("playwright-chromium");
@@ -46,7 +47,8 @@ const captureOne = async (context, config, def, outputDir) => {
46
47
  });
47
48
  }
48
49
  const url = resolveUrl(config.app.baseUrl, def.url, def.urlParams);
49
- await page.goto(url, { waitUntil: "networkidle", timeout: 60000 });
50
+ await page.goto(url, { waitUntil: "load", timeout: 60000 });
51
+ await waitForPageReady(page);
50
52
  if (def.hideElements?.length) {
51
53
  await hideElements(page, def.hideElements);
52
54
  }
@@ -84,19 +86,34 @@ const takeScreenshotBuffer = async (page, def) => {
84
86
  return await page.screenshot({ fullPage: true });
85
87
  }
86
88
  };
87
- const executeActions = async (page, actions) => {
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) => {
88
97
  for (const action of actions) {
89
98
  switch (action.action) {
90
99
  case "click":
100
+ if (action.optional && !(await isPresent(page, action.selector)))
101
+ break;
91
102
  await page.click(action.selector);
92
103
  break;
93
104
  case "type":
105
+ if (action.optional && !(await isPresent(page, action.selector)))
106
+ break;
94
107
  await page.fill(action.selector, action.text);
95
108
  break;
96
109
  case "select":
110
+ if (action.optional && !(await isPresent(page, action.selector)))
111
+ break;
97
112
  await page.selectOption(action.selector, action.value);
98
113
  break;
99
114
  case "hover":
115
+ if (action.optional && !(await isPresent(page, action.selector)))
116
+ break;
100
117
  await page.hover(action.selector);
101
118
  break;
102
119
  case "scroll":
@@ -113,9 +130,18 @@ const executeActions = async (page, actions) => {
113
130
  await page.waitForTimeout(action.ms);
114
131
  break;
115
132
  case "waitForSelector":
116
- await page.waitForSelector(action.selector, {
117
- timeout: action.timeout ?? 10000,
118
- });
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
+ }
119
145
  break;
120
146
  case "waitForNavigation":
121
147
  await page.waitForLoadState("networkidle", {
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/lib/screenshot.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  export declare const getStagingDir: (projectRoot: string) => string;
2
2
  export declare const getStagingPath: (projectRoot: string, id: string) => string;
3
- export declare const getReportDiffPath: (projectRoot: string, id: string) => string;
4
3
  export declare const ensureStagingDirs: (projectRoot: string) => void;
5
4
  /** Move staging file to canonical location, overwriting if it exists. */
6
5
  export declare const promoteToCanonical: (stagingPath: string, canonicalPath: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../src/lib/staging.ts"],"names":[],"mappings":"AAMA,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,EAAE,IAAI,MAAM,KAAG,MACd,CAAC;AAEvD,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,KAAG,IAGvD,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"}
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"}
@@ -1,13 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  const STAGING_DIR = path.join(".kagemusha", ".staging");
4
- const REPORTS_DIR = path.join("reports", "diff");
5
4
  export const getStagingDir = (projectRoot) => path.join(projectRoot, STAGING_DIR);
6
5
  export const getStagingPath = (projectRoot, id) => path.join(projectRoot, STAGING_DIR, `${id}.png`);
7
- export const getReportDiffPath = (projectRoot, id) => path.join(projectRoot, REPORTS_DIR, `${id}.diff.png`);
8
6
  export const ensureStagingDirs = (projectRoot) => {
9
7
  fs.mkdirSync(getStagingDir(projectRoot), { recursive: true });
10
- fs.mkdirSync(path.join(projectRoot, REPORTS_DIR), { recursive: true });
11
8
  };
12
9
  /** Move staging file to canonical location, overwriting if it exists. */
13
10
  export const promoteToCanonical = (stagingPath, canonicalPath) => {
@@ -1 +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;AACxD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEjD,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,EAAE,EAAU,EAAU,EAAE,CAC5E,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AAEvD,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;IAC9D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACxE,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"}
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"}