screencast-axi 0.1.0

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +555 -0
  3. package/dist/bin/screencast-axi.d.ts +2 -0
  4. package/dist/bin/screencast-axi.js +16 -0
  5. package/dist/bin/screencast-axi.js.map +1 -0
  6. package/dist/src/auth/strategies.d.ts +54 -0
  7. package/dist/src/auth/strategies.js +137 -0
  8. package/dist/src/auth/strategies.js.map +1 -0
  9. package/dist/src/auth/types.d.ts +65 -0
  10. package/dist/src/auth/types.js +2 -0
  11. package/dist/src/auth/types.js.map +1 -0
  12. package/dist/src/browser.d.ts +100 -0
  13. package/dist/src/browser.js +168 -0
  14. package/dist/src/browser.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +160 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/commands/auth.d.ts +4 -0
  19. package/dist/src/commands/auth.js +207 -0
  20. package/dist/src/commands/auth.js.map +1 -0
  21. package/dist/src/commands/check.d.ts +12 -0
  22. package/dist/src/commands/check.js +103 -0
  23. package/dist/src/commands/check.js.map +1 -0
  24. package/dist/src/commands/doctor.d.ts +15 -0
  25. package/dist/src/commands/doctor.js +131 -0
  26. package/dist/src/commands/doctor.js.map +1 -0
  27. package/dist/src/commands/guide.d.ts +6 -0
  28. package/dist/src/commands/guide.js +257 -0
  29. package/dist/src/commands/guide.js.map +1 -0
  30. package/dist/src/commands/home.d.ts +12 -0
  31. package/dist/src/commands/home.js +78 -0
  32. package/dist/src/commands/home.js.map +1 -0
  33. package/dist/src/commands/list.d.ts +6 -0
  34. package/dist/src/commands/list.js +128 -0
  35. package/dist/src/commands/list.js.map +1 -0
  36. package/dist/src/commands/record.d.ts +7 -0
  37. package/dist/src/commands/record.js +266 -0
  38. package/dist/src/commands/record.js.map +1 -0
  39. package/dist/src/commands/scaffold.d.ts +4 -0
  40. package/dist/src/commands/scaffold.js +122 -0
  41. package/dist/src/commands/scaffold.js.map +1 -0
  42. package/dist/src/commands/setup.d.ts +14 -0
  43. package/dist/src/commands/setup.js +137 -0
  44. package/dist/src/commands/setup.js.map +1 -0
  45. package/dist/src/config.d.ts +126 -0
  46. package/dist/src/config.js +275 -0
  47. package/dist/src/config.js.map +1 -0
  48. package/dist/src/director.d.ts +217 -0
  49. package/dist/src/director.js +415 -0
  50. package/dist/src/director.js.map +1 -0
  51. package/dist/src/duration.d.ts +33 -0
  52. package/dist/src/duration.js +68 -0
  53. package/dist/src/duration.js.map +1 -0
  54. package/dist/src/encode.d.ts +67 -0
  55. package/dist/src/encode.js +225 -0
  56. package/dist/src/encode.js.map +1 -0
  57. package/dist/src/errors.d.ts +19 -0
  58. package/dist/src/errors.js +27 -0
  59. package/dist/src/errors.js.map +1 -0
  60. package/dist/src/flags.d.ts +39 -0
  61. package/dist/src/flags.js +130 -0
  62. package/dist/src/flags.js.map +1 -0
  63. package/dist/src/forensics.d.ts +51 -0
  64. package/dist/src/forensics.js +107 -0
  65. package/dist/src/forensics.js.map +1 -0
  66. package/dist/src/hooks.d.ts +9 -0
  67. package/dist/src/hooks.js +20 -0
  68. package/dist/src/hooks.js.map +1 -0
  69. package/dist/src/index.d.ts +18 -0
  70. package/dist/src/index.js +18 -0
  71. package/dist/src/index.js.map +1 -0
  72. package/dist/src/inventory.d.ts +45 -0
  73. package/dist/src/inventory.js +89 -0
  74. package/dist/src/inventory.js.map +1 -0
  75. package/dist/src/manifest.d.ts +89 -0
  76. package/dist/src/manifest.js +137 -0
  77. package/dist/src/manifest.js.map +1 -0
  78. package/dist/src/output.d.ts +8 -0
  79. package/dist/src/output.js +2 -0
  80. package/dist/src/output.js.map +1 -0
  81. package/dist/src/overlay.d.ts +105 -0
  82. package/dist/src/overlay.js +302 -0
  83. package/dist/src/overlay.js.map +1 -0
  84. package/dist/src/reference.d.ts +1 -0
  85. package/dist/src/reference.js +108 -0
  86. package/dist/src/reference.js.map +1 -0
  87. package/dist/src/run.d.ts +82 -0
  88. package/dist/src/run.js +321 -0
  89. package/dist/src/run.js.map +1 -0
  90. package/dist/src/skill.d.ts +12 -0
  91. package/dist/src/skill.js +69 -0
  92. package/dist/src/skill.js.map +1 -0
  93. package/dist/src/toolchain.d.ts +62 -0
  94. package/dist/src/toolchain.js +127 -0
  95. package/dist/src/toolchain.js.map +1 -0
  96. package/dist/src/types.d.ts +88 -0
  97. package/dist/src/types.js +18 -0
  98. package/dist/src/types.js.map +1 -0
  99. package/dist/src/version.d.ts +10 -0
  100. package/dist/src/version.js +11 -0
  101. package/dist/src/version.js.map +1 -0
  102. package/package.json +93 -0
  103. package/skills/screencast-axi/SKILL.md +49 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,IAAI,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACrD,MAAM,IAAI,eAAe,CAAC,iCAAiC,EAAE,kBAAkB,EAAE;YAC/E,0BAA0B;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,eAAe,CAAC,uCAAuC,EAAE,iBAAiB,EAAE;QACpF,6FAA6F;QAC7F,+DAA+D;KAChE,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Public authoring surface.
3
+ *
4
+ * Playwright is referenced through `import type` only, so importing this
5
+ * module costs nothing at runtime and works in a project that has no browser
6
+ * installed - a site reading clip metadata at build time should not have to
7
+ * install one.
8
+ */
9
+ export { VERSION } from "./version.js";
10
+ export { Director, type DirectorOptions } from "./director.js";
11
+ export { defineScenario, isScenario, type DefinedScenario, type Scenario, type ScenarioContext, type Target, type Viewport, } from "./types.js";
12
+ export { DEFAULT_OVERLAY_THEME, resolveOverlayTheme, type OverlayApi, type OverlayTheme, type ResolvedOverlayTheme, } from "./overlay.js";
13
+ export { DEFAULT_ENCODE_SETTINGS, encode, type EncodeOptions, type EncodeResult, type EncodeSettings, } from "./encode.js";
14
+ export { detectToolchain, installHint, type BinaryInfo, type PosterEncoder, type Toolchain, } from "./toolchain.js";
15
+ export { defineConfig, loadConfig, loadScenarios, type BrowserConfig, type ResolvedConfig, type ScreencastConfig, } from "./config.js";
16
+ export { clipFilesFor, readManifest, type ClipFiles, type ManifestEntry } from "./manifest.js";
17
+ export { basicAuth, noAuth, profileAuth, storageStateAuth, type BasicAuthOptions, type ProfileAuthOptions, type StorageStateOptions, } from "./auth/strategies.js";
18
+ export type { AuthContext, AuthIdentity, AuthPreflightContext, AuthStrategy, ContextPatch, } from "./auth/types.js";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Public authoring surface.
3
+ *
4
+ * Playwright is referenced through `import type` only, so importing this
5
+ * module costs nothing at runtime and works in a project that has no browser
6
+ * installed - a site reading clip metadata at build time should not have to
7
+ * install one.
8
+ */
9
+ export { VERSION } from "./version.js";
10
+ export { Director } from "./director.js";
11
+ export { defineScenario, isScenario, } from "./types.js";
12
+ export { DEFAULT_OVERLAY_THEME, resolveOverlayTheme, } from "./overlay.js";
13
+ export { DEFAULT_ENCODE_SETTINGS, encode, } from "./encode.js";
14
+ export { detectToolchain, installHint, } from "./toolchain.js";
15
+ export { defineConfig, loadConfig, loadScenarios, } from "./config.js";
16
+ export { clipFilesFor, readManifest } from "./manifest.js";
17
+ export { basicAuth, noAuth, profileAuth, storageStateAuth, } from "./auth/strategies.js";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAwB,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,UAAU,GAMX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GAIpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,EACvB,MAAM,GAIP,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,WAAW,GAIZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,aAAa,GAId,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAsC,MAAM,eAAe,CAAC;AAC/F,OAAO,EACL,SAAS,EACT,MAAM,EACN,WAAW,EACX,gBAAgB,GAIjB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { type ResolvedConfig } from "./config.js";
2
+ import { type ManifestEntry, type ManifestProblem } from "./manifest.js";
3
+ /**
4
+ * What the clip library actually contains, cross-referenced.
5
+ *
6
+ * Every read-only command answers some version of "what do I need to re-shoot",
7
+ * and deriving that from scratch means reading the manifest, listing the output
8
+ * directory and diffing both against the scenario list. Computing it once, in
9
+ * one place, is what lets the CLI answer in a single call instead of making an
10
+ * agent do three and guess.
11
+ */
12
+ export type ClipStatus =
13
+ /** Recorded, and the scenario has not changed since. */
14
+ "recorded"
15
+ /** Recorded, but the scenario has changed - the clip shows something stale. */
16
+ | "stale" | "never-recorded"
17
+ /** In the manifest, but a file it claims is missing from disk. */
18
+ | "incomplete";
19
+ export interface InventoryRow {
20
+ readonly id: string;
21
+ readonly status: ClipStatus;
22
+ readonly title: string;
23
+ readonly steps: number;
24
+ readonly durationMs: number;
25
+ readonly file: string;
26
+ readonly entry?: ManifestEntry;
27
+ /** Deliverables the manifest claims but disk does not have. */
28
+ readonly missing: readonly string[];
29
+ /** Why it is stale, when it is. */
30
+ readonly staleReason?: "narration changed" | "scenario changed";
31
+ }
32
+ export interface Inventory {
33
+ readonly rows: readonly InventoryRow[];
34
+ /** Manifest entries with no scenario behind them any more. */
35
+ readonly orphans: readonly ManifestEntry[];
36
+ /** Media in the output directory that no manifest entry claims. */
37
+ readonly strays: readonly string[];
38
+ readonly problems: readonly ManifestProblem[];
39
+ readonly counts: Readonly<Record<ClipStatus, number>>;
40
+ }
41
+ export declare function buildInventory(config: ResolvedConfig): Promise<Inventory>;
42
+ /** One-line summary, so every command phrases the same fact the same way. */
43
+ export declare function summarise(inventory: Inventory): string;
44
+ /** Absolute path of a clip's files, for `show`. */
45
+ export declare function filesOf(row: InventoryRow, outDir: string): string[];
@@ -0,0 +1,89 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { basename, extname, join } from "node:path";
3
+ import { loadScenarios } from "./config.js";
4
+ import { claimedFiles, hashSteps, hashText, missingFiles, readManifest, } from "./manifest.js";
5
+ import { readFile } from "node:fs/promises";
6
+ const MEDIA = new Set([".mp4", ".webm", ".webp", ".gif", ".png"]);
7
+ async function statusOf(loaded, entry) {
8
+ if (!entry)
9
+ return { status: "never-recorded", missing: [] };
10
+ const { scenario, file } = loaded;
11
+ // Narration first: it is the cheaper check and the more common change, and
12
+ // it is the one that makes a clip actively wrong rather than merely dated -
13
+ // the caption on screen no longer matches what the scenario says.
14
+ if (entry.stepsHash && entry.stepsHash !== hashSteps(scenario.steps)) {
15
+ return { status: "stale", missing: [], staleReason: "narration changed" };
16
+ }
17
+ if (entry.sourceHash) {
18
+ const source = await readFile(file, "utf8").catch(() => null);
19
+ if (source !== null && hashText(source) !== entry.sourceHash) {
20
+ return { status: "stale", missing: [], staleReason: "scenario changed" };
21
+ }
22
+ }
23
+ return { status: "recorded", missing: [] };
24
+ }
25
+ export async function buildInventory(config) {
26
+ const loaded = await loadScenarios(config);
27
+ const { entries, problems } = readManifest(config.outDir);
28
+ const byId = new Map(entries.map((e) => [e.id, e]));
29
+ const rows = [];
30
+ for (const item of loaded) {
31
+ const entry = byId.get(item.scenario.id);
32
+ const state = await statusOf(item, entry);
33
+ const missing = entry ? missingFiles(entry, config.outDir) : [];
34
+ rows.push({
35
+ id: item.scenario.id,
36
+ title: item.scenario.title,
37
+ steps: item.scenario.steps?.length ?? 0,
38
+ durationMs: entry?.durationMs ?? 0,
39
+ file: item.file,
40
+ ...(entry ? { entry } : {}),
41
+ ...state,
42
+ // A missing file beats a stale hash: the clip is not merely dated, it is
43
+ // not all there.
44
+ ...(missing.length > 0 ? { status: "incomplete", missing } : {}),
45
+ });
46
+ }
47
+ const known = new Set(loaded.map((l) => l.scenario.id));
48
+ const orphans = entries.filter((e) => !known.has(e.id));
49
+ // Built from what each entry says it wrote, so an animated WebP - which
50
+ // shares the `.webp` extension with the poster - is not mistaken for a
51
+ // leftover file.
52
+ const claimed = new Set();
53
+ for (const entry of entries)
54
+ for (const name of claimedFiles(entry))
55
+ claimed.add(name);
56
+ const strays = existsSync(config.outDir)
57
+ ? readdirSync(config.outDir)
58
+ .filter((f) => MEDIA.has(extname(f)) && !claimed.has(basename(f)))
59
+ .sort()
60
+ : [];
61
+ const counts = {
62
+ recorded: 0,
63
+ stale: 0,
64
+ "never-recorded": 0,
65
+ incomplete: 0,
66
+ };
67
+ for (const row of rows)
68
+ counts[row.status]++;
69
+ return { rows, orphans, strays, problems, counts };
70
+ }
71
+ /** One-line summary, so every command phrases the same fact the same way. */
72
+ export function summarise(inventory) {
73
+ const { rows, counts } = inventory;
74
+ if (rows.length === 0)
75
+ return "0 scenarios";
76
+ const parts = [`${rows.length} scenarios`];
77
+ for (const [state, n] of Object.entries(counts))
78
+ if (n > 0)
79
+ parts.push(`${n} ${state}`);
80
+ return parts.join(", ");
81
+ }
82
+ /** Absolute path of a clip's files, for `show`. */
83
+ export function filesOf(row, outDir) {
84
+ if (!row.entry)
85
+ return [];
86
+ const formats = row.entry.formats ?? ["mp4", "webm", "webp"];
87
+ return formats.map((f) => join(outDir, `${row.id}.${f}`)).filter((f) => existsSync(f));
88
+ }
89
+ //# sourceMappingURL=inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory.js","sourceRoot":"","sources":["../../src/inventory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The manifest: what the recorder wrote, in a form a website can read.
3
+ *
4
+ * Deliberately dependency-free (node builtins only) and exported on its own
5
+ * subpath, so a site importing it at build time does not pull in Playwright or
6
+ * a browser it has no use for.
7
+ */
8
+ export declare const MANIFEST_FILENAME = "manifest.json";
9
+ export interface ManifestEntry {
10
+ /** File stem shared by every deliverable of this clip. */
11
+ readonly id: string;
12
+ readonly title: string;
13
+ readonly description: string;
14
+ /** The narration, which is also the readable text of the workflow. */
15
+ readonly steps?: readonly string[];
16
+ readonly width: number;
17
+ readonly height: number;
18
+ readonly durationMs: number;
19
+ /** ISO 8601. Doubles as the upload date in schema.org markup. */
20
+ readonly recordedAt: string;
21
+ /** Playback multiplier the take was recorded at. */
22
+ readonly pace?: number;
23
+ /** Device preset, when the take used one. */
24
+ readonly device?: string;
25
+ /** Extensions actually written, e.g. ["mp4","webm","webp"]. */
26
+ readonly formats?: readonly string[];
27
+ /**
28
+ * Hash of the scenario source at record time.
29
+ *
30
+ * What makes "this clip is stale" answerable without opening the video: if
31
+ * the scenario has changed since, the clip shows something the code no
32
+ * longer does.
33
+ */
34
+ readonly sourceHash?: string;
35
+ /**
36
+ * Hash of the narration at record time.
37
+ *
38
+ * Separates the two reasons a manifest and a scenario can disagree: steps
39
+ * that were never written down (a safe backfill) versus wording that changed
40
+ * while the video still shows the old line (needs a reshoot). Without it
41
+ * those look identical and a human has to judge every field.
42
+ */
43
+ readonly stepsHash?: string;
44
+ readonly recorderVersion?: string;
45
+ }
46
+ export declare function hashText(text: string): string;
47
+ export declare function hashSteps(steps: readonly string[] | undefined): string;
48
+ export declare function manifestPath(dir: string): string;
49
+ export interface ManifestProblem {
50
+ readonly index: number;
51
+ readonly reason: string;
52
+ }
53
+ export interface ValidationResult {
54
+ readonly entries: ManifestEntry[];
55
+ readonly problems: ManifestProblem[];
56
+ }
57
+ /**
58
+ * Validates parsed JSON into entries, keeping the good ones.
59
+ *
60
+ * A half-written manifest should cost you the clips it mangled, not the whole
61
+ * page: a site that renders four good clips and falls back for a fifth is a
62
+ * better outcome than one that throws during a build.
63
+ */
64
+ export declare function validateManifest(value: unknown): ValidationResult;
65
+ /** Reads and validates the manifest in `dir`. A missing file is not an error. */
66
+ export declare function readManifest(dir: string): ValidationResult;
67
+ /** Writes entries sorted by id, so a reshoot produces a minimal diff. */
68
+ export declare function writeManifest(dir: string, entries: readonly ManifestEntry[]): string;
69
+ /** Inserts or replaces one entry, leaving the rest untouched. */
70
+ export declare function upsertEntry(dir: string, entry: ManifestEntry): string;
71
+ export interface ClipFiles {
72
+ readonly mp4: string;
73
+ readonly webm: string;
74
+ readonly poster: string;
75
+ readonly gif?: string;
76
+ readonly animatedWebp?: string;
77
+ }
78
+ /**
79
+ * Absolute paths to a clip's deliverables.
80
+ *
81
+ * The poster extension follows what was actually written, because a machine
82
+ * without a WebP encoder produces a PNG - a consumer that assumed `.webp`
83
+ * would link a file that is not there.
84
+ */
85
+ export declare function clipFilesFor(entry: ManifestEntry, dir: string): ClipFiles;
86
+ /** Every file an entry claims, by name. What `check` compares disk against. */
87
+ export declare function claimedFiles(entry: ManifestEntry): string[];
88
+ /** Whether every file the entry claims is actually on disk. */
89
+ export declare function missingFiles(entry: ManifestEntry, dir: string): string[];
@@ -0,0 +1,137 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ /**
5
+ * The manifest: what the recorder wrote, in a form a website can read.
6
+ *
7
+ * Deliberately dependency-free (node builtins only) and exported on its own
8
+ * subpath, so a site importing it at build time does not pull in Playwright or
9
+ * a browser it has no use for.
10
+ */
11
+ export const MANIFEST_FILENAME = "manifest.json";
12
+ export function hashText(text) {
13
+ return createHash("sha256").update(text).digest("hex").slice(0, 16);
14
+ }
15
+ export function hashSteps(steps) {
16
+ return hashText(JSON.stringify(steps ?? []));
17
+ }
18
+ export function manifestPath(dir) {
19
+ return dir.endsWith(".json") ? dir : join(dir, MANIFEST_FILENAME);
20
+ }
21
+ const REQUIRED_STRINGS = ["id", "title", "description", "recordedAt"];
22
+ const REQUIRED_NUMBERS = ["width", "height", "durationMs"];
23
+ /**
24
+ * Validates parsed JSON into entries, keeping the good ones.
25
+ *
26
+ * A half-written manifest should cost you the clips it mangled, not the whole
27
+ * page: a site that renders four good clips and falls back for a fifth is a
28
+ * better outcome than one that throws during a build.
29
+ */
30
+ export function validateManifest(value) {
31
+ if (!Array.isArray(value)) {
32
+ return { entries: [], problems: [{ index: -1, reason: "manifest is not an array" }] };
33
+ }
34
+ const entries = [];
35
+ const problems = [];
36
+ const seen = new Set();
37
+ value.forEach((raw, index) => {
38
+ if (typeof raw !== "object" || raw === null) {
39
+ problems.push({ index, reason: "entry is not an object" });
40
+ return;
41
+ }
42
+ const entry = raw;
43
+ for (const key of REQUIRED_STRINGS) {
44
+ if (typeof entry[key] !== "string" || entry[key].length === 0) {
45
+ problems.push({ index, reason: `missing or empty \`${key}\`` });
46
+ return;
47
+ }
48
+ }
49
+ for (const key of REQUIRED_NUMBERS) {
50
+ if (typeof entry[key] !== "number" || !Number.isFinite(entry[key])) {
51
+ problems.push({ index, reason: `\`${key}\` is not a number` });
52
+ return;
53
+ }
54
+ }
55
+ if (entry["steps"] !== undefined && !Array.isArray(entry["steps"])) {
56
+ problems.push({ index, reason: "`steps` is not an array" });
57
+ return;
58
+ }
59
+ const id = entry["id"];
60
+ if (seen.has(id)) {
61
+ problems.push({ index, reason: `duplicate id \`${id}\`` });
62
+ return;
63
+ }
64
+ seen.add(id);
65
+ entries.push(entry);
66
+ });
67
+ return { entries, problems };
68
+ }
69
+ /** Reads and validates the manifest in `dir`. A missing file is not an error. */
70
+ export function readManifest(dir) {
71
+ const file = manifestPath(dir);
72
+ if (!existsSync(file))
73
+ return { entries: [], problems: [] };
74
+ let parsed;
75
+ try {
76
+ parsed = JSON.parse(readFileSync(file, "utf8"));
77
+ }
78
+ catch (error) {
79
+ return {
80
+ entries: [],
81
+ problems: [{ index: -1, reason: `manifest is not valid JSON: ${String(error)}` }],
82
+ };
83
+ }
84
+ return validateManifest(parsed);
85
+ }
86
+ /** Writes entries sorted by id, so a reshoot produces a minimal diff. */
87
+ export function writeManifest(dir, entries) {
88
+ const file = manifestPath(dir);
89
+ mkdirSync(dirname(file), { recursive: true });
90
+ const sorted = [...entries].sort((a, b) => a.id.localeCompare(b.id));
91
+ writeFileSync(file, `${JSON.stringify(sorted, null, 2)}\n`);
92
+ return file;
93
+ }
94
+ /** Inserts or replaces one entry, leaving the rest untouched. */
95
+ export function upsertEntry(dir, entry) {
96
+ const { entries } = readManifest(dir);
97
+ const next = entries.filter((e) => e.id !== entry.id);
98
+ next.push(entry);
99
+ return writeManifest(dir, next);
100
+ }
101
+ /**
102
+ * Absolute paths to a clip's deliverables.
103
+ *
104
+ * The poster extension follows what was actually written, because a machine
105
+ * without a WebP encoder produces a PNG - a consumer that assumed `.webp`
106
+ * would link a file that is not there.
107
+ */
108
+ export function clipFilesFor(entry, dir) {
109
+ const formats = entry.formats ?? ["mp4", "webm", "webp"];
110
+ const poster = formats.includes("png") ? "png" : "webp";
111
+ return {
112
+ mp4: join(dir, `${entry.id}.mp4`),
113
+ webm: join(dir, `${entry.id}.webm`),
114
+ poster: join(dir, `${entry.id}.${poster}`),
115
+ ...(formats.includes("gif") ? { gif: join(dir, `${entry.id}.gif`) } : {}),
116
+ ...(formats.includes("anim.webp") ? { animatedWebp: join(dir, `${entry.id}.anim.webp`) } : {}),
117
+ };
118
+ }
119
+ /** Every file an entry claims, by name. What `check` compares disk against. */
120
+ export function claimedFiles(entry) {
121
+ const formats = entry.formats ?? ["mp4", "webm", "webp"];
122
+ return formats.map((f) => `${entry.id}.${f}`);
123
+ }
124
+ /** Whether every file the entry claims is actually on disk. */
125
+ export function missingFiles(entry, dir) {
126
+ const files = clipFilesFor(entry, dir);
127
+ return [
128
+ files.mp4,
129
+ files.webm,
130
+ files.poster,
131
+ ...(files.gif ? [files.gif] : []),
132
+ ...(files.animatedWebp ? [files.animatedWebp] : []),
133
+ ]
134
+ .filter((f) => !existsSync(f))
135
+ .map((f) => basename(f));
136
+ }
137
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Structural mirror of the SDK's `AxiRenderable`.
3
+ *
4
+ * `axi-sdk-js` does not export its `output` module, so the renderable type is
5
+ * restated here rather than reached for through a deep import.
6
+ */
7
+ export type AxiStructuredOutput = Record<string, unknown>;
8
+ export type AxiRenderable = string | AxiStructuredOutput;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/output.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Page overlay injected into every document of a recording session.
3
+ *
4
+ * Playwright videos do not contain the mouse pointer, which makes any clip of a
5
+ * drag or a click unreadable. This draws a synthetic pointer that follows the
6
+ * real one (Playwright's `mouse.move` dispatches genuine `mousemove` events, so
7
+ * the overlay stays in sync for free), plus a click ripple, a drag ghost and a
8
+ * caption bar.
9
+ *
10
+ * Everything lives in a shadow root so no page CSS can reach it, and the host
11
+ * is `pointer-events: none` so it never intercepts a click.
12
+ */
13
+ export interface OverlayApi {
14
+ caption(text: string | null): void;
15
+ setPointerVisible(visible: boolean): void;
16
+ setDragging(dragging: boolean): void;
17
+ }
18
+ declare global {
19
+ interface Window {
20
+ __screencast?: OverlayApi;
21
+ }
22
+ }
23
+ export interface OverlayTheme {
24
+ /** Ripple and drag-ghost colour. Any CSS colour; hex gets alpha variants. */
25
+ readonly accent: string;
26
+ /** Replace the browser's own cursor with the drawn one. */
27
+ readonly hideNativeCursor: boolean;
28
+ /**
29
+ * Selectors hidden for the duration of the recording.
30
+ *
31
+ * Development-only chrome belongs here - a framework's error badge, a
32
+ * cookie banner, a staging ribbon - so it never lands in footage that gets
33
+ * shown to someone.
34
+ */
35
+ readonly hideSelectors: readonly string[];
36
+ readonly pointer: {
37
+ readonly size: number;
38
+ readonly fill: string;
39
+ readonly stroke: string;
40
+ readonly strokeWidth: number;
41
+ };
42
+ readonly ripple: {
43
+ readonly enabled: boolean;
44
+ readonly scale: number;
45
+ readonly durationMs: number;
46
+ };
47
+ readonly dragGhost: {
48
+ readonly enabled: boolean;
49
+ readonly width: number;
50
+ readonly height: number;
51
+ readonly radius: number;
52
+ };
53
+ readonly caption: {
54
+ readonly position: "top" | "bottom";
55
+ /** Distance from that edge, in px. */
56
+ readonly offset: number;
57
+ readonly maxWidth: string;
58
+ readonly background: string;
59
+ readonly color: string;
60
+ readonly fontSize: number;
61
+ readonly fontFamily: string;
62
+ readonly fontWeight: number;
63
+ readonly radius: number;
64
+ readonly padding: string;
65
+ readonly fadeMs: number;
66
+ readonly shadow: string;
67
+ };
68
+ }
69
+ export declare const DEFAULT_OVERLAY_THEME: OverlayTheme;
70
+ /**
71
+ * Theme with every colour already resolved to a concrete CSS string.
72
+ *
73
+ * The alpha variants are computed here rather than in the page so that
74
+ * {@link installOverlay} stays a dumb interpolator: it has to survive being
75
+ * stringified, and every helper it would otherwise call is one more thing that
76
+ * can break in that trip.
77
+ */
78
+ export interface ResolvedOverlayTheme extends OverlayTheme {
79
+ readonly accentSoft: string;
80
+ readonly accentEdge: string;
81
+ readonly accentRipple: string;
82
+ readonly accentRippleEdge: string;
83
+ }
84
+ export declare function resolveOverlayTheme(overrides?: DeepPartial<OverlayTheme>): ResolvedOverlayTheme;
85
+ export type DeepPartial<T> = {
86
+ [K in keyof T]?: T[K] extends readonly unknown[] ? T[K] : T[K] extends object ? DeepPartial<T[K]> : T[K];
87
+ };
88
+ export declare function mergeThemes(base: DeepPartial<OverlayTheme> | undefined, over: DeepPartial<OverlayTheme> | undefined): DeepPartial<OverlayTheme>;
89
+ /**
90
+ * Serialised form of {@link installOverlay}, ready for `addInitScript`.
91
+ *
92
+ * The function is stringified rather than passed directly because this file is
93
+ * compiled by esbuild under `keepNames` in some toolchains, which rewrites
94
+ * every inner function as `__name(fn, "fn")`. That helper only exists in the
95
+ * Node module scope, so the function throws `__name is not defined` the moment
96
+ * Playwright evaluates it in the page - silently, unless a `pageerror`
97
+ * listener is attached. Re-declaring `__name` as identity in the wrapper is
98
+ * enough, and costs nothing if a future toolchain stops emitting it.
99
+ *
100
+ * The theme travels as JSON in the same wrapper, so theming does not
101
+ * reintroduce a function reference that would have to survive the same trip.
102
+ */
103
+ export declare function overlayInitScript(theme: ResolvedOverlayTheme): string;
104
+ /** Runs inside the page via `addInitScript`. Must be fully self-contained. */
105
+ export declare function installOverlay(theme: ResolvedOverlayTheme): void;