versioncam 0.1.2 → 0.2.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 (88) hide show
  1. package/CHANGELOG.md +196 -46
  2. package/README.md +486 -243
  3. package/bin/versioncam.js +1 -1
  4. package/dist/app-server.js +2 -2
  5. package/dist/app-server.js.map +1 -1
  6. package/dist/cli/commands/doctor.js +41 -4
  7. package/dist/cli/commands/doctor.js.map +1 -1
  8. package/dist/cli/commands/init.js +26 -1
  9. package/dist/cli/commands/init.js.map +1 -1
  10. package/dist/cli/commands/preview.js +10 -1
  11. package/dist/cli/commands/preview.js.map +1 -1
  12. package/dist/cli/commands/record.js +3 -2
  13. package/dist/cli/commands/record.js.map +1 -1
  14. package/dist/cli/commands/render.d.ts +17 -0
  15. package/dist/cli/commands/render.js +79 -22
  16. package/dist/cli/commands/render.js.map +1 -1
  17. package/dist/cli/commands/stability.js +4 -4
  18. package/dist/cli/commands/stability.js.map +1 -1
  19. package/dist/cli/cwd.d.ts +46 -0
  20. package/dist/cli/cwd.js +102 -0
  21. package/dist/cli/cwd.js.map +1 -0
  22. package/dist/cli/main.js +11 -2
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/cli/usage.d.ts +10 -5
  25. package/dist/cli/usage.js +42 -24
  26. package/dist/cli/usage.js.map +1 -1
  27. package/dist/config.d.ts +24 -0
  28. package/dist/config.js +25 -0
  29. package/dist/config.js.map +1 -1
  30. package/dist/core/camera-track.d.ts +63 -0
  31. package/dist/core/camera-track.js +230 -0
  32. package/dist/core/camera-track.js.map +1 -0
  33. package/dist/core/camera.d.ts +14 -1
  34. package/dist/core/camera.js +18 -2
  35. package/dist/core/camera.js.map +1 -1
  36. package/dist/core/follow.d.ts +129 -0
  37. package/dist/core/follow.js +151 -0
  38. package/dist/core/follow.js.map +1 -0
  39. package/dist/core/motion-defaults.d.ts +29 -0
  40. package/dist/core/motion-defaults.js +29 -0
  41. package/dist/core/motion-defaults.js.map +1 -1
  42. package/dist/core/timeline.d.ts +32 -2
  43. package/dist/core/timeline.js +10 -0
  44. package/dist/core/timeline.js.map +1 -1
  45. package/dist/driver/clip.d.ts +4 -1
  46. package/dist/driver/clip.js +9 -1
  47. package/dist/driver/clip.js.map +1 -1
  48. package/dist/driver/session.d.ts +35 -1
  49. package/dist/driver/session.js +81 -12
  50. package/dist/driver/session.js.map +1 -1
  51. package/dist/driver/settle.js +2 -2
  52. package/dist/driver/settle.js.map +1 -1
  53. package/dist/index.d.ts +2 -2
  54. package/dist/index.js +1 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/inspect/inspect.js +1 -1
  57. package/dist/loader.d.ts +7 -1
  58. package/dist/loader.js +23 -8
  59. package/dist/loader.js.map +1 -1
  60. package/dist/page/assets/index-fZtacWyp.js +2 -0
  61. package/dist/page/index.html +2 -2
  62. package/dist/render/encode.d.ts +12 -1
  63. package/dist/render/encode.js +94 -35
  64. package/dist/render/encode.js.map +1 -1
  65. package/dist/render/presentation.d.ts +12 -0
  66. package/dist/render/presentation.js +15 -0
  67. package/dist/render/presentation.js.map +1 -1
  68. package/dist/render/render.js +19 -2
  69. package/dist/render/render.js.map +1 -1
  70. package/dist/render/sampling.d.ts +13 -0
  71. package/dist/render/sampling.js +48 -20
  72. package/dist/render/sampling.js.map +1 -1
  73. package/dist/render/scenes.d.ts +16 -0
  74. package/dist/render/scenes.js +108 -0
  75. package/dist/render/scenes.js.map +1 -0
  76. package/dist/render/sequence.d.ts +18 -0
  77. package/dist/render/sequence.js +67 -13
  78. package/dist/render/sequence.js.map +1 -1
  79. package/dist/review/review.js +48 -23
  80. package/dist/review/review.js.map +1 -1
  81. package/dsl.md +267 -92
  82. package/package.json +2 -2
  83. package/plugin/README.md +58 -47
  84. package/plugin/agents/versioncam-reviewer.md +3 -2
  85. package/plugin/skills/versioncam/SKILL.md +60 -14
  86. package/plugin/skills/versioncam/authoring.md +6 -1
  87. package/plugin/skills/versioncam/onboarding.md +17 -17
  88. package/dist/page/assets/index-DUom5amc.js +0 -1
@@ -10,6 +10,44 @@ export function uniformFrames(durationFrames, count = SHEET_TILES) {
10
10
  const tiles = Math.max(2, Math.min(count, durationFrames));
11
11
  return Array.from({ length: tiles }, (_, i) => Math.round((i / (tiles - 1)) * (durationFrames - 1)));
12
12
  }
13
+ /**
14
+ * The first frame at or after `frame` that is not inside a dissolve.
15
+ *
16
+ * The renderer dissolves between states for `crossfadeFrames` after every
17
+ * cut, and a tile taken inside that window is two pages at once. A reviewer
18
+ * shown one said so and declined to judge the beat from it, which is the
19
+ * right answer to a question that should not have been asked.
20
+ *
21
+ * Two settles a few frames apart overlap their dissolves, and the first frame
22
+ * after one can be inside the next, so the frame moves on until it is clear
23
+ * of all of them. Never past the last frame, which has nothing after it.
24
+ */
25
+ function pastCrossfade(frame, durationFrames, crossfadeFrames, cuts) {
26
+ let clear = frame;
27
+ for (;;) {
28
+ const cut = cuts.find((c) => clear > c && clear <= c + crossfadeFrames);
29
+ if (cut === undefined)
30
+ break;
31
+ clear = cut + crossfadeFrames + 1;
32
+ }
33
+ return Math.min(clear, durationFrames - 1);
34
+ }
35
+ /**
36
+ * Evenly spaced, ends included, and none of them inside a dissolve: the
37
+ * frames a sheet shows when nobody chose them.
38
+ *
39
+ * An even spread knows nothing about the clip, so it lands wherever the
40
+ * spacing puts it, and after a settle that can be the renderer's crossfade: a
41
+ * plain sheet of the example app showed two tiles with one page ghosting
42
+ * through the next, which `--at-marks` had stopped doing for its own tiles
43
+ * long before. A tile inside a dissolve moves to the first frame after it.
44
+ * The first frame is never inside one, the last stays the last, and two
45
+ * tiles that land on the same frame are one.
46
+ */
47
+ export function evenFrames(durationFrames, count = SHEET_TILES, crossfadeFrames = 0, cuts = []) {
48
+ const tiles = uniformFrames(durationFrames, count).map((frame) => pastCrossfade(frame, durationFrames, crossfadeFrames, cuts));
49
+ return [...new Set(tiles)].sort((a, b) => a - b);
50
+ }
13
51
  /**
14
52
  * Frame 0, every beat, and the last frame.
15
53
  *
@@ -23,23 +61,13 @@ export function uniformFrames(durationFrames, count = SHEET_TILES) {
23
61
  */
24
62
  export function markedFrames(durationFrames, marks, count = SHEET_TILES, crossfadeFrames = 0, cuts = []) {
25
63
  const inRange = (frame) => Number.isInteger(frame) && frame >= 0 && frame < durationFrames;
26
- // Past the crossfade, wherever a frame would land inside one.
27
- //
28
- // The renderer dissolves between states for `crossfadeFrames` after every
29
- // cut, and a tile taken inside that window is two pages at once. A reviewer
30
- // shown one said so and declined to judge the beat from it, which is the
31
- // right answer to a question that should not have been asked.
32
- //
33
- // A mark is the worst case — it sits on the cut's first frame, where the
34
- // blend has barely begun and the tile still shows what was on screen
35
- // *before* the wait — but any evenly spread tile can land there too. Marks
36
- // stay where they are, because `review` wants the state boundary; only the
37
- // picture waits for the dissolve to finish.
38
- const settled = (frame) => {
39
- const cut = cuts.find((c) => frame > c && frame <= c + crossfadeFrames);
40
- const clear = cut === undefined ? frame : cut + crossfadeFrames + 1;
41
- return Math.min(clear, durationFrames - 1);
42
- };
64
+ // A mark is the worst case of a tile inside a dissolve: it sits on the
65
+ // cut's first frame, where the blend has barely begun and the tile still
66
+ // shows what was on screen *before* the wait. `beatFrame` moves its tile
67
+ // past the dissolve that starts there; the marks themselves stay where
68
+ // they are, because `review` wants the state boundary. The spare tiles
69
+ // below come from `evenFrames`, which moves them past any dissolve they
70
+ // land in.
43
71
  const wanted = [
44
72
  0,
45
73
  ...marks.map((m) => beatFrame(m, durationFrames, crossfadeFrames)),
@@ -55,10 +83,10 @@ export function markedFrames(durationFrames, marks, count = SHEET_TILES, crossfa
55
83
  // the shape of the clip around them.
56
84
  if (unique.length < count) {
57
85
  const filled = new Set(unique);
58
- for (const frame of uniformFrames(durationFrames, count)) {
86
+ for (const frame of evenFrames(durationFrames, count, crossfadeFrames, cuts)) {
59
87
  if (filled.size >= count)
60
88
  break;
61
- filled.add(settled(frame));
89
+ filled.add(frame);
62
90
  }
63
91
  return [...filled].sort((a, b) => a - b);
64
92
  }
@@ -98,7 +126,7 @@ timeline, wanted, crossfadeFrames = 0) {
98
126
  const marks = timeline.marks ?? [];
99
127
  if (marks.length === 0) {
100
128
  return {
101
- error: "This recording has no beats to go by — no labelled settle(), or it " +
129
+ error: "This recording has no beats to go by: it has no labelled settle(), or it " +
102
130
  "was made before marks existed. Ask for a frame number or --t instead.",
103
131
  };
104
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../src/render/sampling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../src/render/sampling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Scene } from "../scene.js";
2
+ /**
3
+ * The scenes a module defines, by id, or why there are none to read.
4
+ *
5
+ * `file` names the module in the message: its path, wherever the path is
6
+ * known. The message is three lines at most (what is wrong, the shape, and
7
+ * the one-line fix when there is an obvious one), so `doctor` can print the
8
+ * first as its finding and the rest beneath it.
9
+ */
10
+ export declare function readScenes(module: Record<string, unknown>, file: string): {
11
+ scenes: Record<string, Scene>;
12
+ } | {
13
+ error: string;
14
+ };
15
+ /** What to say when scenes are asked for that the module does not define. */
16
+ export declare function missingScenes(asked: string[], defined: string[], file: string | null): string;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * What a scenes module has to export, checked the same way wherever one is
3
+ * read: in Node, where the CLI lists the ids to render and filter, and in the
4
+ * render page, which draws them.
5
+ *
6
+ * Both used to take `module.default` and carry on with whatever it was. A
7
+ * module whose record of scenes was a named export gave them nothing at all:
8
+ * `render` found no scenes, said nothing, and stitched a film without them.
9
+ * Anything that is not a record of scenes now stops the command, naming the
10
+ * file and the shape it should have.
11
+ *
12
+ * A record with nothing in it is still a record, and renders no scenes, which
13
+ * is what it says.
14
+ */
15
+ /** The shape, as the second line of every refusal. */
16
+ const SHAPE = "versioncam reads scenes from a module's default export: a Record<string, Scene> " +
17
+ "keyed by scene id, as in `export default { [intro.id]: intro }`.";
18
+ /** For anything but a plain object, which is never what is being described. */
19
+ function describe(value) {
20
+ if (value === null || value === undefined)
21
+ return String(value);
22
+ if (Array.isArray(value))
23
+ return "an array";
24
+ return `a ${typeof value}`;
25
+ }
26
+ /** A value as it was written, so `"60"` does not read as the number 60. */
27
+ function shown(value) {
28
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
29
+ }
30
+ function isPlainObject(value) {
31
+ return value !== null && typeof value === "object" && !Array.isArray(value);
32
+ }
33
+ /** What is wrong with one entry, or null when it can be drawn. */
34
+ function entryProblem(key, value) {
35
+ if (!isPlainObject(value))
36
+ return `"${key}" is ${describe(value)}, not a scene`;
37
+ const { draw, durationFrames, fps } = value;
38
+ if (typeof draw !== "function") {
39
+ return `"${key}" has no draw(ctx, frame, canvas)`;
40
+ }
41
+ if (!Number.isInteger(durationFrames) || durationFrames < 1) {
42
+ return `"${key}" has durationFrames ${shown(durationFrames)}, where a whole number of frames, 1 or more, belongs`;
43
+ }
44
+ if (typeof fps !== "number" || !Number.isFinite(fps) || fps <= 0) {
45
+ return `"${key}" has fps ${shown(fps)}, where a number above 0 belongs`;
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * What is wrong with a would-be record of scenes, as the end of a sentence
51
+ * that starts "default-exports", or null when nothing is.
52
+ *
53
+ * A single scene and an array are singled out because they are the two
54
+ * mistakes a clip file invites: a clip file default-exports one clip or an
55
+ * array of them, and a scenes module does neither.
56
+ */
57
+ function recordProblem(value) {
58
+ if (!isPlainObject(value))
59
+ return describe(value);
60
+ if (typeof value.draw === "function") {
61
+ return "a single scene rather than a record of them";
62
+ }
63
+ for (const [key, entry] of Object.entries(value)) {
64
+ const problem = entryProblem(key, entry);
65
+ if (problem)
66
+ return `a record in which ${problem}`;
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * The scenes a module defines, by id, or why there are none to read.
72
+ *
73
+ * `file` names the module in the message: its path, wherever the path is
74
+ * known. The message is three lines at most (what is wrong, the shape, and
75
+ * the one-line fix when there is an obvious one), so `doctor` can print the
76
+ * first as its finding and the rest beneath it.
77
+ */
78
+ export function readScenes(module, file) {
79
+ if (module.default === undefined) {
80
+ const named = Object.keys(module).filter((name) => name !== "default");
81
+ const lines = [
82
+ `${file} has no default export.` +
83
+ (named.length > 0 ? ` It exports ${named.join(", ")}.` : ""),
84
+ SHAPE,
85
+ ];
86
+ // The failure that made this check: the record was there, under a name.
87
+ const records = named.filter((name) => recordProblem(module[name]) === null &&
88
+ Object.keys(module[name]).length > 0);
89
+ if (records.length === 1) {
90
+ lines.push(`${records[0]} is one: add \`export default ${records[0]}\`.`);
91
+ }
92
+ return { error: lines.join("\n") };
93
+ }
94
+ const problem = recordProblem(module.default);
95
+ if (problem) {
96
+ return { error: `${file} default-exports ${problem}.\n${SHAPE}` };
97
+ }
98
+ return { scenes: module.default };
99
+ }
100
+ /** What to say when scenes are asked for that the module does not define. */
101
+ export function missingScenes(asked, defined, file) {
102
+ const names = asked.map((id) => `"${id}"`).join(", ");
103
+ const head = `No scene${asked.length === 1 ? "" : "s"} called ${names}.`;
104
+ if (file === null)
105
+ return `${head} The config names no \`scenes\` module.`;
106
+ return `${head} ${file} defines ${defined.length > 0 ? defined.join(", ") : "none"}.`;
107
+ }
108
+ //# sourceMappingURL=scenes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenes.js","sourceRoot":"","sources":["../../src/render/scenes.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,33 @@
1
+ /**
2
+ * A rendered clip. `frameRate` is the fraction ffprobe gives, `60/1` or
3
+ * `30000/1001`, kept as written rather than divided out: it is the rate the
4
+ * filter may convert every clip to, the fastest clip included, and only the
5
+ * exact rate leaves that clip's frames where they were.
6
+ */
1
7
  export type SequenceInput = {
2
8
  id: string;
3
9
  path: string;
4
10
  duration: number;
11
+ frameRate: string;
5
12
  };
6
13
  /** Duration of a video file, in seconds. Blocking; ffprobe is quick. */
7
14
  export declare function probeDuration(path: string): number;
15
+ /** Frame rate of a video file, as the fraction ffprobe gives. Blocking too. */
16
+ export declare function probeFrameRate(path: string): string;
8
17
  /**
9
18
  * The `xfade` chain. Each transition's `offset` is measured from the start of
10
19
  * everything accumulated so far, and every crossfade shortens the total by its
11
20
  * own duration — which is the part that is easy to get wrong and shows up as
12
21
  * a clip that starts before the previous one has finished.
22
+ *
23
+ * `xfade` joins streams of one frame rate and one timebase only, and clips
24
+ * need not share a rate: a 15 fps draft among 60 fps recordings, or a scene
25
+ * or a clip with an `fps` of its own. When the rates differ, every clip goes
26
+ * through `fps` at the highest of them first. The highest, so that no clip
27
+ * loses a frame: a slower clip's frames are each held as long as they were.
28
+ * Every clip, the fastest included, because `fps` sets the timebase as well,
29
+ * and a clip left alone would keep its own. When the rates agree, the filter
30
+ * is the one it always was.
13
31
  */
14
32
  export declare function buildFilter(inputs: SequenceInput[]): {
15
33
  filter: string;
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { existsSync, statSync } from "node:fs";
2
+ import { existsSync, renameSync, rmSync, statSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { ffmpegPath, ffprobeSync } from "./ffmpeg.js";
5
5
  /**
@@ -32,22 +32,62 @@ export function probeDuration(path) {
32
32
  }
33
33
  return seconds;
34
34
  }
35
+ /** Frame rate of a video file, as the fraction ffprobe gives. Blocking too. */
36
+ export function probeFrameRate(path) {
37
+ const out = ffprobeSync([
38
+ "-v",
39
+ "error",
40
+ "-select_streams",
41
+ "v:0",
42
+ "-show_entries",
43
+ "stream=r_frame_rate",
44
+ "-of",
45
+ "default=nw=1:nk=1",
46
+ path,
47
+ ]).trim();
48
+ const rate = rateOf(out);
49
+ if (!Number.isFinite(rate) || rate <= 0) {
50
+ throw new Error(`Could not read a frame rate from ${path}`);
51
+ }
52
+ return out;
53
+ }
54
+ /** A fraction as ffprobe prints one, `30000/1001`, as a number. */
55
+ function rateOf(fraction) {
56
+ const [num, den = "1"] = fraction.split("/");
57
+ return Number(num) / Number(den);
58
+ }
35
59
  /**
36
60
  * The `xfade` chain. Each transition's `offset` is measured from the start of
37
61
  * everything accumulated so far, and every crossfade shortens the total by its
38
62
  * own duration — which is the part that is easy to get wrong and shows up as
39
63
  * a clip that starts before the previous one has finished.
64
+ *
65
+ * `xfade` joins streams of one frame rate and one timebase only, and clips
66
+ * need not share a rate: a 15 fps draft among 60 fps recordings, or a scene
67
+ * or a clip with an `fps` of its own. When the rates differ, every clip goes
68
+ * through `fps` at the highest of them first. The highest, so that no clip
69
+ * loses a frame: a slower clip's frames are each held as long as they were.
70
+ * Every clip, the fastest included, because `fps` sets the timebase as well,
71
+ * and a clip left alone would keep its own. When the rates agree, the filter
72
+ * is the one it always was.
40
73
  */
41
74
  export function buildFilter(inputs) {
42
75
  if (inputs.length === 1)
43
76
  return { filter: "", label: "0:v", total: inputs[0].duration };
44
77
  const parts = [];
45
- let current = "0:v";
78
+ const rates = inputs.map((input) => rateOf(input.frameRate));
79
+ const mixed = new Set(rates).size > 1;
80
+ if (mixed) {
81
+ const highest = inputs[rates.indexOf(Math.max(...rates))].frameRate;
82
+ inputs.forEach((_, i) => parts.push(`[${i}:v]fps=${highest}[f${i}]`));
83
+ }
84
+ const stream = (i) => (mixed ? `f${i}` : `${i}:v`);
85
+ let current = stream(0);
46
86
  let accumulated = inputs[0].duration;
47
87
  for (let i = 1; i < inputs.length; i += 1) {
48
88
  const offset = accumulated - TRANSITION_S;
49
89
  const label = i === inputs.length - 1 ? "out" : `x${i}`;
50
- parts.push(`[${current}][${i}:v]xfade=transition=fade:duration=${TRANSITION_S}:offset=${offset.toFixed(3)}[${label}]`);
90
+ parts.push(`[${current}][${stream(i)}]xfade=transition=fade:duration=${TRANSITION_S}:offset=${offset.toFixed(3)}[${label}]`);
51
91
  current = label;
52
92
  accumulated += inputs[i].duration - TRANSITION_S;
53
93
  }
@@ -62,14 +102,25 @@ export async function buildSequence(ids, outDir) {
62
102
  missing.push(id);
63
103
  continue;
64
104
  }
65
- inputs.push({ id, path, duration: probeDuration(path) });
105
+ inputs.push({
106
+ id,
107
+ path,
108
+ duration: probeDuration(path),
109
+ frameRate: probeFrameRate(path),
110
+ });
66
111
  }
67
112
  if (inputs.length < 2) {
68
113
  throw new Error(`Need at least two rendered clips to build a sequence; have ${inputs.length}.`);
69
114
  }
70
115
  const { filter, label, total } = buildFilter(inputs);
71
116
  const out = join(outDir, "sequence.mp4");
72
- await new Promise((resolve, reject) => {
117
+ // Made beside the film and moved over it once ffmpeg has finished. ffmpeg
118
+ // opens its output before it finds out whether it can join the clips, and
119
+ // a sequence written straight to sequence.mp4 that failed left it empty,
120
+ // whatever film had been there. `.mp4` last: ffmpeg picks the muxer by it.
121
+ const partial = join(outDir, "sequence.partial.mp4");
122
+ let stderr = "";
123
+ const code = await new Promise((resolve) => {
73
124
  const child = spawn(ffmpegPath(), [
74
125
  "-y",
75
126
  ...inputs.flatMap((input) => ["-i", input.path]),
@@ -87,19 +138,22 @@ export async function buildSequence(ids, outDir) {
87
138
  "yuv420p",
88
139
  "-movflags",
89
140
  "+faststart",
90
- out,
141
+ partial,
91
142
  ], { stdio: ["ignore", "ignore", "pipe"] });
92
- let stderr = "";
93
143
  child.stderr.on("data", (c) => {
94
144
  stderr += c.toString();
95
145
  });
96
- child.on("close", (code) => {
97
- if (code === 0)
98
- resolve();
99
- else
100
- reject(new Error(`sequence failed:\n${stderr.slice(-1200)}`));
101
- });
146
+ child.on("close", resolve);
102
147
  });
148
+ if (code !== 0) {
149
+ rmSync(partial, { force: true });
150
+ // The film still there is not this sequence, and could pass for it.
151
+ const kept = existsSync(out)
152
+ ? `\n${out} is from an earlier run, left as it was.`
153
+ : "";
154
+ throw new Error(`sequence failed:\n${stderr.slice(-1200)}${kept}`);
155
+ }
156
+ renameSync(partial, out);
103
157
  return { path: out, total, used: inputs.map((i) => i.id), missing };
104
158
  }
105
159
  //# sourceMappingURL=sequence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sequence.js","sourceRoot":"","sources":["../../src/render/sequence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sequence.js","sourceRoot":"","sources":["../../src/render/sequence.ts"],"names":[],"mappings":"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"}
@@ -61,7 +61,7 @@ function checkBeats(timeline, beats) {
61
61
  detail: missing.length === 0
62
62
  ? `${beats.length} beats, each with a mark`
63
63
  : `no settle labelled ${missing.map((b) => `"${b.label}"`).join(", ")}` +
64
- ` — the clip has ${[...labels].map((l) => `"${l}"`).join(", ") || "none"}`,
64
+ ` (the clip has ${[...labels].map((l) => `"${l}"`).join(", ") || "none"})`,
65
65
  };
66
66
  }
67
67
  /**
@@ -113,9 +113,13 @@ function checkRendered(timeline) {
113
113
  const lastFrame = timeline.durationFrames - 1;
114
114
  const late = (t) => frameAt(t, timeline.fps) > lastFrame;
115
115
  const unseen = [];
116
- for (const key of timeline.camera) {
117
- if (late(key.t))
118
- unseen.push(`a camera key at ${seconds(key.t)}`);
116
+ // Once for the camera, at its last key. A camera that follows the cursor
117
+ // or blends between moves has a key a frame, and one still under way at
118
+ // the end left dozens past it: one line saying where the camera would have
119
+ // stopped says everything they do.
120
+ const lastKey = timeline.camera[timeline.camera.length - 1];
121
+ if (lastKey && late(lastKey.t)) {
122
+ unseen.push(`a camera key at ${seconds(lastKey.t)}`);
119
123
  }
120
124
  for (const highlight of timeline.highlights) {
121
125
  if (late(highlight.t0)) {
@@ -138,7 +142,7 @@ function checkRendered(timeline) {
138
142
  check: "rendered",
139
143
  pass: false,
140
144
  warning: true,
141
- detail: `${unseen.join(", ")} — the clip ends at frame ${lastFrame} ` +
145
+ detail: `${unseen.join(", ")}: the clip ends at frame ${lastFrame} ` +
142
146
  `(${seconds(timeline.durationFrames / timeline.fps)}), so nothing renders them. ` +
143
147
  "A track write costs no time; give it a `hold` to be seen in.",
144
148
  };
@@ -154,14 +158,21 @@ function checkRendered(timeline) {
154
158
  * not `review`, which had no clause for it, and not the video, which simply
155
159
  * lacks the ring it was supposed to end on.
156
160
  *
161
+ * A click is the other thing a clip points at. The ripple is drawn where the
162
+ * cursor was, and a click the camera is framing something else for is a
163
+ * ripple drawn off the edge of the frame: the button changes, and nobody saw
164
+ * it pressed. A camera that follows the cursor promises this never happens,
165
+ * and this is where that promise is checked, for every other camera too.
166
+ *
157
167
  * A warning rather than a failure: the camera can legitimately be somewhere
158
168
  * else entirely by the time a highlight runs, and only the renderer knows for
159
- * certain. But a highlight wholly outside the viewport is almost always a
160
- * mistake, and it is invisible in every other way.
169
+ * certain. But a highlight or a click wholly outside the frame is almost
170
+ * always a mistake, and it is invisible in every other way.
161
171
  */
162
172
  function checkOnScreen(timeline) {
163
173
  const { width, height } = timeline.viewport;
164
174
  const full = { x: 0, y: 0, w: width, h: height };
175
+ const cameraAt = (t) => timeline.camera.length > 0 ? evalCamera(timeline.camera, t) : full;
165
176
  // Against the camera, not the viewport.
166
177
  //
167
178
  // The first version of this clause compared a highlight to the whole page,
@@ -175,32 +186,46 @@ function checkOnScreen(timeline) {
175
186
  // Sampled at both ends of the highlight because the camera can be moving
176
187
  // through it: a ring that starts cropped and ends visible is still a ring
177
188
  // someone can read.
178
- const visible = (h) => {
179
- const rects = timeline.camera.length > 0
180
- ? [evalCamera(timeline.camera, h.t0), evalCamera(timeline.camera, h.t1)]
181
- : [full];
182
- return rects.some((r) => h.rect.x + h.rect.w > r.x &&
183
- h.rect.y + h.rect.h > r.y &&
184
- h.rect.x < r.x + r.w &&
185
- h.rect.y < r.y + r.h);
186
- };
189
+ const visible = (h) => [cameraAt(h.t0), cameraAt(h.t1)].some((r) => h.rect.x + h.rect.w > r.x &&
190
+ h.rect.y + h.rect.h > r.y &&
191
+ h.rect.x < r.x + r.w &&
192
+ h.rect.y < r.y + r.h);
187
193
  const offscreen = timeline.highlights.filter((h) => !visible(h));
188
- if (offscreen.length === 0) {
194
+ // A click, at the moment the button goes down.
195
+ const presses = timeline.pointer.filter((p) => p.kind === "down");
196
+ const unseen = presses.filter((p) => {
197
+ const r = cameraAt(p.t);
198
+ const [x, y] = p.at;
199
+ return x < r.x || x > r.x + r.w || y < r.y || y > r.y + r.h;
200
+ });
201
+ if (offscreen.length === 0 && unseen.length === 0) {
189
202
  return {
190
203
  check: "onscreen",
191
204
  pass: true,
192
- detail: `${timeline.highlights.length} highlight(s), all inside the viewport`,
205
+ detail: `${timeline.highlights.length} highlight(s) and ${presses.length} ` +
206
+ "click(s), all inside what the camera shows",
193
207
  };
194
208
  }
195
- const where = offscreen
196
- .map((h) => `y ${Math.round(h.rect.y)}–${Math.round(h.rect.y + h.rect.h)}`)
197
- .join(", ");
209
+ const found = [];
210
+ if (offscreen.length > 0) {
211
+ const where = offscreen
212
+ .map((h) => `y ${Math.round(h.rect.y)}–${Math.round(h.rect.y + h.rect.h)}`)
213
+ .join(", ");
214
+ found.push(`${offscreen.length} highlight(s) outside what the camera is showing ` +
215
+ `(${where}), so no frame can show them`);
216
+ }
217
+ if (unseen.length > 0) {
218
+ const where = unseen
219
+ .map((p) => `at ${seconds(p.t)} on (${Math.round(p.at[0])}, ${Math.round(p.at[1])})`)
220
+ .join(", ");
221
+ found.push(`${unseen.length} click(s) outside what the camera is showing (${where}), ` +
222
+ "so no frame shows the press");
223
+ }
198
224
  return {
199
225
  check: "onscreen",
200
226
  pass: false,
201
227
  warning: true,
202
- detail: `${offscreen.length} highlight(s) outside what the camera is showing ` +
203
- `(${where}) — drawn where no frame can show them`,
228
+ detail: found.join("; "),
204
229
  };
205
230
  }
206
231
  export function reviewRecording(input) {
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/review/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/review/review.ts"],"names":[],"mappings":"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"}