versioncam 0.1.3 → 0.3.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 (99) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/README.md +152 -15
  3. package/dist/cli/commands/check.js +45 -4
  4. package/dist/cli/commands/check.js.map +1 -1
  5. package/dist/cli/commands/doctor.js +39 -2
  6. package/dist/cli/commands/doctor.js.map +1 -1
  7. package/dist/cli/commands/init.js +25 -0
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/preview.js +10 -1
  10. package/dist/cli/commands/preview.js.map +1 -1
  11. package/dist/cli/commands/publish.d.ts +17 -0
  12. package/dist/cli/commands/publish.js +203 -0
  13. package/dist/cli/commands/publish.js.map +1 -0
  14. package/dist/cli/commands/record.js +2 -0
  15. package/dist/cli/commands/record.js.map +1 -1
  16. package/dist/cli/commands/render.d.ts +17 -0
  17. package/dist/cli/commands/render.js +98 -21
  18. package/dist/cli/commands/render.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 +19 -2
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/cli/usage.d.ts +3 -2
  25. package/dist/cli/usage.js +17 -4
  26. package/dist/cli/usage.js.map +1 -1
  27. package/dist/config.d.ts +35 -1
  28. package/dist/config.js +44 -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 +10 -2
  43. package/dist/core/timeline.js.map +1 -1
  44. package/dist/driver/clip.d.ts +6 -0
  45. package/dist/driver/clip.js +6 -3
  46. package/dist/driver/clip.js.map +1 -1
  47. package/dist/driver/reports.d.ts +34 -2
  48. package/dist/driver/reports.js +31 -3
  49. package/dist/driver/reports.js.map +1 -1
  50. package/dist/driver/session.d.ts +36 -1
  51. package/dist/driver/session.js +72 -14
  52. package/dist/driver/session.js.map +1 -1
  53. package/dist/index.d.ts +11 -3
  54. package/dist/index.js +6 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/loader.d.ts +7 -1
  57. package/dist/loader.js +23 -8
  58. package/dist/loader.js.map +1 -1
  59. package/dist/page/assets/index-fZtacWyp.js +2 -0
  60. package/dist/page/index.html +1 -1
  61. package/dist/publish/client.d.ts +38 -0
  62. package/dist/publish/client.js +118 -0
  63. package/dist/publish/client.js.map +1 -0
  64. package/dist/publish/index.d.ts +12 -0
  65. package/dist/publish/index.js +11 -0
  66. package/dist/publish/index.js.map +1 -0
  67. package/dist/publish/manifest.d.ts +46 -0
  68. package/dist/publish/manifest.js +133 -0
  69. package/dist/publish/manifest.js.map +1 -0
  70. package/dist/publish/protocol.d.ts +203 -0
  71. package/dist/publish/protocol.js +56 -0
  72. package/dist/publish/protocol.js.map +1 -0
  73. package/dist/render/encode.d.ts +27 -1
  74. package/dist/render/encode.js +115 -37
  75. package/dist/render/encode.js.map +1 -1
  76. package/dist/render/presentation.d.ts +12 -0
  77. package/dist/render/presentation.js +15 -0
  78. package/dist/render/presentation.js.map +1 -1
  79. package/dist/render/render.js +19 -1
  80. package/dist/render/render.js.map +1 -1
  81. package/dist/render/sampling.d.ts +13 -0
  82. package/dist/render/sampling.js +47 -19
  83. package/dist/render/sampling.js.map +1 -1
  84. package/dist/render/scenes.d.ts +16 -0
  85. package/dist/render/scenes.js +108 -0
  86. package/dist/render/scenes.js.map +1 -0
  87. package/dist/render/sequence.d.ts +21 -1
  88. package/dist/render/sequence.js +71 -14
  89. package/dist/render/sequence.js.map +1 -1
  90. package/dist/review/review.js +46 -21
  91. package/dist/review/review.js.map +1 -1
  92. package/dsl.md +37 -0
  93. package/package.json +6 -2
  94. package/plugin/README.md +7 -2
  95. package/plugin/agents/versioncam-reviewer.md +3 -2
  96. package/plugin/skills/versioncam/SKILL.md +60 -14
  97. package/plugin/skills/versioncam/authoring.md +6 -1
  98. package/plugin/skills/versioncam/onboarding.md +17 -17
  99. package/dist/page/assets/index-DUom5amc.js +0 -1
@@ -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)) {
@@ -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}), so 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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"}
package/dsl.md CHANGED
@@ -182,6 +182,11 @@ One key, at once: `"Enter"`, `"Escape"`, `"Meta+a"`.
182
182
 
183
183
  None of these spends clip time, so each needs frames after it.
184
184
 
185
+ Each camera line starts from wherever the camera is, still or moving, and
186
+ eases into its new framing over its `duration`. A line written while the
187
+ camera is still on its way somewhere takes over from there at once. Two
188
+ written at the same instant are one: the second.
189
+
185
190
  ### `camera.zoom(target, scale?, options?)`
186
191
 
187
192
  Frame a locator or a rect, padded by 12% of its size on each side, and keep
@@ -220,6 +225,38 @@ classic mistake, and `versioncam review` warns about it.
220
225
  |---|---|---|
221
226
  | `duration` | `1100` | How long the camera takes to get there. |
222
227
 
228
+ ### `camera.follow(options?)`
229
+
230
+ Follow the cursor at one scale until the next camera line. For a clip that
231
+ moves around the page, one `follow` does what a `zoom` before each thing the
232
+ cursor uses cannot: the camera stays at one scale and moves with the cursor
233
+ without stopping.
234
+
235
+ The camera keeps the cursor inside a dead zone in the middle of the frame and
236
+ moves only when the cursor leaves it, so a move to the next field of a form
237
+ does not move the picture. It never shows beyond the page's edge, and never
238
+ overshoots. Its path is worked out from the whole cursor track once the clip
239
+ has run, so it sets off with the cursor rather than trailing behind it.
240
+
241
+ It starts centred on the cursor, easing in from wherever the camera was. The
242
+ next `zoom`, `to`, `reset` or `follow` eases out of it the same way.
243
+
244
+ | Option | Default | What it sets |
245
+ |---|---|---|
246
+ | `scale` | `1.35` | How far in. Unlike a zoom's, it is exact: the frame is `viewport.width / scale` wide the whole time. |
247
+ | `deadZone` | `0.3` | The share of the frame, around its middle, the cursor can move in without moving the camera. `0` answers every movement. |
248
+ | `smoothing` | `600` | How softly the camera moves, in milliseconds: about how long it takes over a move the cursor makes at once. Larger is softer. |
249
+ | `duration` | `1100` | How long the camera takes to get from where it was into the follow. |
250
+
251
+ The defaults are `followZoom`, `followDeadZone`, `followSmoothingMs` and
252
+ `zoomDurationMs` in the config's `motion`, so an app can set its own.
253
+
254
+ ### `camera.release()`
255
+
256
+ Stop following. The camera comes to rest on the framing it was heading for,
257
+ with the cursor in it, and stays there until the next camera line. When the
258
+ camera is not following, it does nothing.
259
+
223
260
  ### `highlight(target, style, options?)`
224
261
 
225
262
  A `"ring"` around a locator or a rect, or a `"spotlight"` on it, drawn at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versioncam",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Record demo clips of a real web app: scripted, deterministic, re-recorded on every push.",
6
6
  "license": "FSL-1.1-ALv2",
@@ -8,7 +8,7 @@
8
8
  "homepage": "https://version.cam",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/petbul/versioncam.git",
11
+ "url": "git+https://github.com/versioncam/versioncam.git",
12
12
  "directory": "packages/recorder"
13
13
  },
14
14
  "bugs": "https://github.com/petbul/versioncam-home/issues",
@@ -40,6 +40,10 @@
40
40
  "./fixtures": {
41
41
  "types": "./dist/fixtures.d.ts",
42
42
  "import": "./dist/fixtures.js"
43
+ },
44
+ "./publish": {
45
+ "types": "./dist/publish/index.d.ts",
46
+ "import": "./dist/publish/index.js"
43
47
  }
44
48
  },
45
49
  "files": [
package/plugin/README.md CHANGED
@@ -39,8 +39,8 @@ claude --plugin-dir node_modules/versioncam/plugin
39
39
 
40
40
  ## What it does
41
41
 
42
- **The first run**, in a directory with no `versioncam.config.ts`, sets the
43
- repository up (`skills/versioncam/onboarding.md`):
42
+ **The first run**, in a repository with no `versioncam.config.ts`, sets it
43
+ up (`skills/versioncam/onboarding.md`):
44
44
 
45
45
  1. It works out how the app starts and where it is served, from
46
46
  `package.json`, the framework's config and the README. When there is more
@@ -54,6 +54,11 @@ repository up (`skills/versioncam/onboarding.md`):
54
54
 
55
55
  Nothing is committed.
56
56
 
57
+ **In a monorepo** the skill first looks for `versioncam.config.ts` across the
58
+ whole repository, wherever the session started, and then works in the
59
+ directory that holds it. With several apps, it asks which. With none, the
60
+ first run writes the config where `versioncam init` put the skill.
61
+
57
62
  **Every run** then goes like this (`skills/versioncam/SKILL.md`):
58
63
 
59
64
  1. `versioncam doctor`, which starts the app, and `inspect /`.
@@ -27,8 +27,9 @@ sheet and its id. Read the sheet and say whether the clip is watchable.
27
27
  not evidence that it happened. If a tile does not show it, it did not
28
28
  happen as far as this review is concerned.
29
29
  - If a tile is too small to be sure, run `npx versioncam frame <id> <n>` for that
30
- frame at full size and read the PNG it prints. That is the only shell
31
- command you run.
30
+ frame at full size, with the `--cwd <dir>` you were given on the end if you
31
+ were given one, and read the PNG it prints. That is the only shell command
32
+ you run.
32
33
  - **Tile *n* is not frame *n*.** You are given the frame number behind each
33
34
  tile; pull those. A reviewer that guessed pulled frames 1 to 16 of a clip
34
35
  whose beat fired at frame 29, watched nothing happen, and failed a clip that
@@ -32,19 +32,63 @@ Follow these steps in order. Two more files sit beside this one, in this
32
32
  skill's directory: `onboarding.md` and `authoring.md`. They are briefs, read
33
33
  when a step says to.
34
34
 
35
- ## 0. Which phase
36
-
37
- First, check versioncam is installed where Node will find it:
38
- `ls -d node_modules/versioncam`, then the same in each parent directory up to
39
- the repository root (`../node_modules/versioncam`, …) — a workspace hoists it.
40
- If none has it, **stop** and say it must be installed first
41
- (`npm i -D versioncam`, then `npx versioncam install`). Every step below runs
42
- `npx versioncam`, and `npx` with nothing installed downloads the latest
43
- published version — not necessarily the one this repository will pin, and
35
+ ## 0. Where, and which phase
36
+
37
+ Everything below runs in **the app's directory**: the one that holds
38
+ `versioncam.config.ts` (or `.js`, `.mjs`). That need not be the working
39
+ directory. In a monorepo, a session started at the repository root can run
40
+ this skill from an app's `.claude/skills/` further down, and one started
41
+ inside an app can run a skill installed above it. So before deciding
42
+ anything, find the config, in the whole repository rather than only here.
43
+
44
+ **Find it.** Glob `**/versioncam.config.{ts,js,mjs}` from the top of the
45
+ repository (the directory holding `.git`), and leave out anything under
46
+ `node_modules/`, `.versioncam/` or `.claude/worktrees/`, which is another
47
+ checkout. Files on disk are what count, committed or not: a first run's
48
+ config may not be committed yet, and a deleted one is gone.
49
+
50
+ - **One** → its directory is the app's directory.
51
+ - **Several** → ask which app with `AskUserQuestion`, listing their
52
+ directories, unless the request already names one. With no one to ask,
53
+ stop and list them.
54
+ - **None** → this is the first run, and the config goes where
55
+ `versioncam init` put this skill. This skill's directory is
56
+ `${CLAUDE_SKILL_DIR}` (if that is not a path, it is the directory you read
57
+ this file from). If it is `<dir>/.claude/skills/versioncam` inside this
58
+ repository, the app's directory is `<dir>`. Otherwise it is the working
59
+ directory.
60
+
61
+ **Check versioncam is installed** where the app will find it: `ls -d
62
+ <the app's directory>/node_modules/versioncam`, then the same in each
63
+ directory above it up to the repository root, since a workspace hoists it.
64
+ If none has it, **stop** and say it must be installed in the app's directory
65
+ first (`npm i -D versioncam`, then `npx versioncam install`). Every step below
66
+ runs `npx versioncam`, and `npx` with nothing installed downloads the latest
67
+ published version: not necessarily the one this repository will pin, and
44
68
  with no Chromium to record with.
45
69
 
46
- Then: is there a `versioncam.config.ts` (or `.js`, `.mjs`) in the working
47
- directory?
70
+ **Go there**, if the app's directory is not the working directory, and say in
71
+ one line which directory it is:
72
+
73
+ - **Below where this session started** (a session at the repository root):
74
+ `cd` into it by its absolute path. The shell stays there for every later
75
+ command, and a subagent you spawn starts there too. Do not reach it with
76
+ `--cwd` from above: `npx` looks for versioncam in the directory it runs in
77
+ and in those above it, never below, so it would not run the app's own.
78
+ - **Anywhere else** (a session started inside the app, below its config): a
79
+ session cannot `cd` out of the directory it started in. Put `--cwd <the
80
+ app's directory>` on the end of every `npx versioncam` command instead.
81
+ `npx` then finds the app's versioncam by looking up from where you are,
82
+ and each command works as if run in the app's directory, so a relative path
83
+ you give it, or it prints, is that directory's. Your own `mkdir`, `cp` and
84
+ files take absolute paths.
85
+
86
+ If a `cd` does not stay (a later `pwd` prints where you started), begin each
87
+ command with `cd <the app's directory> && ` instead. Either way, tell each
88
+ subagent you spawn the app's directory and how you are running commands
89
+ there.
90
+
91
+ Then, did you find a config?
48
92
 
49
93
  - **No** → this is the repository's first run. Read `onboarding.md` and do all
50
94
  of it, then continue with step 1.
@@ -91,7 +135,8 @@ directories; do not summarise it away. Copy `beats.json` beside it.
91
135
  host makes subagents cheap, hand the authoring to a subagent instead of doing
92
136
  it here. Spawn a plain one (general-purpose) whose prompt is the full text of
93
137
  `authoring.md`, followed by the intent, the clip id, the clips directory (the
94
- config's `clips`), the `inspect /` output inline, and the beats path. **Keep the
138
+ config's `clips`), the `inspect /` output inline, the beats path, and the
139
+ app's directory, with the `--cwd` flag if step 0 had you use it. **Keep the
95
140
  agent id it returns**: that id is how you continue the same author in later
96
141
  rounds, with `SendMessage`, everything it learned about the page still in its
97
142
  context — spawning a new one would start from nothing and repeat the same
@@ -114,8 +159,9 @@ The reviewer is given only:
114
159
  - the sheet path,
115
160
  - **the frame numbers `sheet --at-marks` just printed**, in order, one per tile,
116
161
  - the contents of `.versioncam/author/<id>/beats.json`,
117
- - the clip id (so it can call `npx versioncam frame <id> <n>` — from this
118
- directory, which you name if a subagent on your host starts somewhere else).
162
+ - the clip id, so it can call `npx versioncam frame <id> <n>`: from this
163
+ directory, which you name if a subagent on your host starts somewhere
164
+ else, and with the `--cwd` flag on the end if step 0 had you use one.
119
165
 
120
166
  The frame numbers are not optional. A reviewer given only the id assumed tile
121
167
  *n* was frame *n*, pulled frames 1 to 16 — every one of them before the beat
@@ -9,7 +9,9 @@ each thing it claims to show.
9
9
  You have: the intent, the clip id, the clips directory, and the output of
10
10
  `npx versioncam inspect /` for the start page — whether you are the session
11
11
  running the skill or a subagent it handed this brief to. Work from the
12
- directory that holds `versioncam.config.ts`.
12
+ directory that holds `versioncam.config.ts`. If you were given a `--cwd <dir>`
13
+ to use instead, put it on the end of every `npx versioncam` command, and write
14
+ files by their absolute paths.
13
15
 
14
16
  ## Look before you write
15
17
 
@@ -196,6 +198,8 @@ happened again the first time an agent used it.
196
198
  | drag or lasso selected nothing | `measure`, re-author geometry as fractions of the real box |
197
199
  | duration drift | adjust holds, never the beats |
198
200
  | tooltip / menu open in a frame it should not be | move the cursor start; add a hold before the next action |
201
+ | the caption covers something a beat points at | `theme.presentation.captionInset: { right: 40 }` in the config moves it to the bottom-right corner; it moves every clip's caption, so say so in what you write down |
202
+ | the camera zooms in and out, or stops and starts between targets | one `s.camera.follow()` where the clip starts moving around the page, instead of a `zoom` before each thing the cursor uses; `s.camera.reset()` or `release()` where it should stop |
199
203
 
200
204
  Two more, from the tools rather than the app:
201
205
 
@@ -203,6 +207,7 @@ Two more, from the tools rather than the app:
203
207
  |---|---|
204
208
  | a locator "matched N elements" | the text appears in more than one place — use `byRole` with a name, or a test id |
205
209
  | review warns that a track never rendered | you wrote a camera, highlight or caption with no frames after it; add a hold |
210
+ | review warns that a click is outside what the camera shows | the camera was framing something else when the button went down: zoom to what the click is on before it, or follow the cursor |
206
211
 
207
212
  ## What you write down
208
213
 
@@ -1,11 +1,11 @@
1
1
  # The first run: setting a repository up
2
2
 
3
- There is no `versioncam.config.ts` here, so before any clip can be written the
4
- repository has to be taught three things: how its app starts, whether it signs
5
- in, and what "the page is done" means. Then one question has to be answered
6
- before anything is authored: **does this app record the same way twice?** A
7
- clip is only as deterministic as the app it films, and one written against an
8
- app that is not fails CI for reasons nobody wrote.
3
+ There is no `versioncam.config.ts` in this repository, so before any clip can
4
+ be written the repository has to be taught three things: how its app starts,
5
+ whether it signs in, and what "the page is done" means. Then one question has
6
+ to be answered before anything is authored: **does this app record the same
7
+ way twice?** A clip is only as deterministic as the app it films, and one
8
+ written against an app that is not fails CI for reasons nobody wrote.
9
9
 
10
10
  You do not run the app yourself — not with `npm run dev`, not in the
11
11
  background, not ever in this phase. You write what you learn into the config,
@@ -13,11 +13,10 @@ and `versioncam` starts the app from it, every time, and stops it again. If
13
13
  you find yourself wanting to start a server by hand, the config is wrong;
14
14
  fix the config.
15
15
 
16
- Work in the directory the skill was run from; the config goes here. If there
17
- is no config here but `Glob` finds a `versioncam.config.*` further down, stop
18
- and say to run the skill from that directory instead: every `versioncam`
19
- command reads the config from the directory it runs in, and a second config
20
- would split the repository in two.
16
+ Work in the app's directory, the one step 0 of the skill chose: the config
17
+ goes there, and every `versioncam` command reads it from there. Step 0 found
18
+ no `versioncam.config.*` anywhere in the repository, so this is the only one.
19
+ A second would split the repository in two.
21
20
 
22
21
  ## 1. How the app runs
23
22
 
@@ -82,7 +81,7 @@ say what the person needs to do: nothing below works on a sign-in screen.
82
81
 
83
82
  ## 3. Write the config
84
83
 
85
- `versioncam.config.ts` in this directory, with only what the app needs:
84
+ `versioncam.config.ts` in the app's directory, with only what the app needs:
86
85
 
87
86
  ```ts
88
87
  import { defineRecorder } from "versioncam";
@@ -101,11 +100,12 @@ for most apps, and a field nobody needed is a question the next person has to
101
100
  answer. Say in a comment above each field you did write why it has the value
102
101
  it has — the port came from the dev script, the command from the README.
103
102
 
104
- Then: create `demos/clips/`, and make sure `.versioncam/` is ignored —
105
- recordings, renders and a saved sign-in go there. If a `.gitignore` here, or
106
- in a directory above this one up to the repository root, already says so,
107
- leave it; otherwise add the line to the nearest one, creating one here if
108
- there is none, and leave the rest of the file alone.
103
+ Then: create `demos/clips/` in the app's directory, and make sure
104
+ `.versioncam/` is ignored — recordings, renders and a saved sign-in go there.
105
+ If a `.gitignore` in the app's directory, or in a directory above it up to
106
+ the repository root, already says so, leave it; otherwise add the line to the
107
+ nearest one, creating one in the app's directory if there is none, and leave
108
+ the rest of the file alone.
109
109
 
110
110
  ## 4. `npx versioncam doctor`
111
111
 
@@ -1 +0,0 @@
1
- (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){let n=t(e);return n*n*n*(10+n*(-15+6*n))}function t(e){return e<=0?0:e>=1?1:e}function n(e,t,n){return e+(t-e)*n}function r(e,t,r){let i=e.x+e.w/2,a=e.y+e.h/2,o=t.x+t.w/2,s=t.y+t.h/2,c=e.w*(t.w/e.w)**+r,l=e.h*(t.h/e.h)**+r,u=n(i,o,r),d=n(a,s,r);return{x:u-c/2,y:d-l/2,w:c,h:l}}function i(t,n){if(t.length===0)throw Error(`evalCamera called with no keys`);if(n<=t[0].t)return t[0].rect;for(let i=1;i<t.length;i+=1){let a=t[i-1],o=t[i];if(n>=o.t)continue;if(o.ease===`hold`)return a.rect;let s=o.t-a.t,c=s<=0?1:(n-a.t)/s;return r(a.rect,o.rect,e(c))}return t[t.length-1].rect}function a(e,t,n,r){let i=1-r,a=i*i,o=2*i*r,s=r*r;return[a*e[0]+o*t[0]+s*n[0],a*e[1]+o*t[1]+s*n[1]]}function o(t,n){if(t.length===0)return[0,0];let r=t[0];if(n<=r.t0)return r.kind===`move`?r.from:r.at;for(let r of t){if(n>r.t1)continue;if(r.kind===`hold`)return r.at;let t=r.t1-r.t0,i=t<=0?1:(n-r.t0)/t;return a(r.from,r.ctrl,r.to,e(i))}let i=t[t.length-1];return i.kind===`move`?i.to:i.at}function s(e,t,n){let r=n.w/t.w;return[n.x+(e[0]-t.x)*r,n.y+(e[1]-t.y)*r]}function c(e,t,n){let r=n.w/t.w;return{x:n.x+(e.x-t.x)*r,y:n.y+(e.y-t.y)*r,w:e.w*r,h:e.h*r}}function l(e,t){return{x:e.x*t,y:e.y*t,w:e.w*t,h:e.h*t}}function u(e,t,n){let r=Math.min(e.width,e.height)*n,i=e.width-r*2,a=e.height-r*2,o=t.width/t.height,s=i,c=s/o;return c>a&&(c=a,s=c*o),{x:(e.width-s)/2,y:(e.height-c)/2,w:s,h:c}}function d(e,n,r){return r<=0?1:t((e-n+1)/r)}function f(e,n,r,i){return e<=n||e>=r?0:i<=0?1:Math.min(t((e-n)/i),t((r-e)/i))}function p(e,t){return e/t}function m(e,t,n){let r=n?.padding??0;return!n||r<=0?{x:0,y:0,w:e.width,h:e.height}:u(e,t,r)}function h(e){let t=e?.caption??`auto`;return t===`none`?`none`:t===`below`||t===`overlay`?t:(e?.padding??0)>0?`below`:`overlay`}var g={radius:16,hairline:`rgba(255, 255, 255, 0.16)`,shadow:`rgba(0, 0, 0, 0.45)`,shadowBlur:60,shadowOffsetY:18,captionFont:`600 30px "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif`,captionColor:`rgba(255, 255, 255, 0.94)`,captionShadow:`rgba(0, 0, 0, 0.5)`,captionScrim:`rgba(0, 0, 0, 0.45)`,captionScrimHeight:86,cursorFill:`#ffffff`,cursorStroke:`rgba(0, 0, 0, 0.55)`,cursorShadow:`rgba(0, 0, 0, 0.35)`,ringWidth:3,ringRadius:10,spotlightDim:.55,spotlightTint:`6, 2, 16`};function _(e,t){let n=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!n)return e;let r=Number.parseInt(n[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${t})`}function v(e,t,n){let r=Math.min(n,t.w/2,t.h/2);e.beginPath(),e.moveTo(t.x+r,t.y),e.arcTo(t.x+t.w,t.y,t.x+t.w,t.y+t.h,r),e.arcTo(t.x+t.w,t.y+t.h,t.x,t.y+t.h,r),e.arcTo(t.x,t.y+t.h,t.x,t.y,r),e.arcTo(t.x,t.y,t.x+t.w,t.y,r),e.closePath()}function y(e,t,n){if(typeof n==`string`)e.fillStyle=n;else{let r=e.createLinearGradient(0,0,t.width,t.height);r.addColorStop(0,n[0]),r.addColorStop(1,n[1]),e.fillStyle=r}e.fillRect(0,0,t.width,t.height)}function b(e,t,n){for(let r of e.cuts)if(t>r&&t<=r+n)return{current:e.frames[t],previous:e.frames[r],alpha:d(t,r+1,n)};return{current:e.frames[t],previous:null,alpha:1}}function x(e,t,n,r,i,a,o){let s=l(i,t.viewport.dpr),{current:c,previous:u,alpha:d}=b(t,r,o.motion.crossfadeFrames),f=o.theme.presentation?.frame??!1;e.save(),f&&(v(e,a,g.radius),e.clip());let p=(t,r)=>{let i=n.images[t];i&&(e.globalAlpha=r,e.drawImage(i,s.x,s.y,s.w,s.h,a.x,a.y,a.w,a.h))};u!==null&&u!==c&&p(u,1),p(c,u===null?1:d),e.globalAlpha=1,e.restore()}function S(e,t){e.save(),e.shadowColor=g.shadow,e.shadowBlur=g.shadowBlur,e.shadowOffsetY=g.shadowOffsetY,e.fillStyle=`rgba(0,0,0,1)`,v(e,t,g.radius),e.fill(),e.restore()}function C(e,t){e.save(),e.strokeStyle=g.hairline,e.lineWidth=1,v(e,t,g.radius),e.stroke(),e.restore()}function w(e,t){return{x:e.x-t,y:e.y-t,w:e.w+t*2,h:e.h+t*2}}function T(e,t,n,r,i,a){let o=a.theme.presentation?.frame??!1;for(let s of t.highlights){let t=f(n,s.t0,s.t1,.25);if(t<=0)continue;let l=c(s.rect,r,i);e.save(),e.globalAlpha=t,s.style===`spotlight`?(e.save(),v(e,i,o?g.radius:0),e.clip(),e.fillStyle=`rgba(${g.spotlightTint}, ${g.spotlightDim})`,e.fillRect(i.x,i.y,i.w,i.h),e.globalCompositeOperation=`destination-out`,v(e,w(l,8),g.ringRadius),e.fill(),e.restore()):(e.strokeStyle=a.theme.accent,e.lineWidth=g.ringWidth,e.shadowColor=_(a.theme.accent,.35),e.shadowBlur=18,v(e,w(l,6),g.ringRadius),e.stroke()),e.restore()}}function E(e,t,n){let r=!1,i=[],a=0;for(let o of e.pointer){if(o.t>t)break;if(o.kind===`down`)r=!0,a=o.t;else{r=!1;let e=t-o.t;e>=0&&e<=n.motion.rippleMs/1e3&&i.push({at:o.at,age:e})}}let o=r?t-a:0,s=r?Math.min(1,o/(n.motion.pressScaleMs/1e3)):0;return{scale:1-(1-n.motion.pressScale)*s,ripples:i}}function D(e,t,n,r,i){for(let a of t){let t=a.age/(i.motion.rippleMs/1e3),[o,c]=s(a.at,n,r);e.save(),e.globalAlpha=(1-t)*.55,e.strokeStyle=i.theme.accent,e.lineWidth=2.5,e.beginPath(),e.arc(o,c,6+t*i.motion.rippleRadius,0,Math.PI*2),e.stroke(),e.restore()}}function O(e,t,n,r){let i=r*n;e.save(),e.translate(t[0],t[1]),e.scale(i,i),e.shadowColor=g.cursorShadow,e.shadowBlur=6/i,e.shadowOffsetY=2/i,e.beginPath(),e.moveTo(0,0),e.lineTo(0,16.6),e.lineTo(4.1,12.9),e.lineTo(6.9,18.9),e.lineTo(9.6,17.6),e.lineTo(6.9,11.8),e.lineTo(12.1,11.5),e.closePath(),e.fillStyle=g.cursorFill,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=g.cursorStroke,e.lineWidth=1/i,e.stroke(),e.restore()}function k(e,t,n,r,i,a){let o=h(a.theme.presentation);if(o!==`none`)for(let s of t.captions){let t=f(n,s.t0,s.t1,a.motion.captionFadeS);if(t<=0)continue;if(e.save(),e.globalAlpha=t,o===`overlay`){let t=g.captionScrimHeight,n=e.createLinearGradient(0,i.height-t,0,i.height);n.addColorStop(0,`rgba(0, 0, 0, 0)`),n.addColorStop(1,g.captionScrim),e.fillStyle=n,e.fillRect(0,i.height-t,i.width,t)}e.font=g.captionFont,e.fillStyle=g.captionColor,e.textBaseline=`middle`,e.shadowColor=g.captionShadow,e.shadowBlur=8;let c=o===`overlay`?40:r.x,l=o===`overlay`?i.height-g.captionScrimHeight/2+4:(r.y+r.h+i.height)/2;e.fillText(s.text,c,l),e.restore()}}function A(e,t,n,r,a,c){let l=p(r,t.fps),u=i(t.camera,l),d=c.theme.presentation,f=m(a,t.viewport,d);d?.backdrop&&y(e,a,d.backdrop),d?.frame&&S(e,f),x(e,t,n,r,u,f,c),d?.frame&&C(e,f),T(e,t,l,u,f,c);let{scale:h,ripples:g}=E(t,l,c);D(e,g,u,f,c),O(e,s(o(t.cursor,l),u,f),h,c.theme.cursorScale),k(e,t,l,f,a,c)}var j=`modulepreload`,M=function(e,t){return new URL(e,t).href},N={},P=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=M(t,n),t=s(t),t in N)return;N[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:j,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},F=new URLSearchParams(location.search),I=F.has(`recording`),L=F.get(`scene`),R=F.get(`headless`)===`1`;R&&document.body.classList.add(`headless`);var z=document.getElementById(`stage`),B=document.getElementById(`scrub`),V=document.getElementById(`status`),H=document.getElementById(`play`),U=document.getElementById(`error`);function W(e){throw U.textContent=e,Error(e)}async function G(e,t){let n=await fetch(e);return n.ok||W(`Could not load ${t} (${e}): ${n.status}`),await n.json()}async function K(e){let t=await fetch(e);return t.ok||W(`Could not load ${e}: ${t.status}`),createImageBitmap(await t.blob())}function q(e){z.width=e.output.width,z.height=e.output.height,R?(z.style.width=`${e.output.width}px`,z.style.height=`${e.output.height}px`):(z.style.width=`min(100%, 1280px)`,z.style.height=`auto`);let t=z.getContext(`2d`,{alpha:!1});return t||W(`No 2D context`),t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`,t}function J(e,t,n,r){let i=!1;H.addEventListener(`click`,()=>{i=!i,H.textContent=i?`Pause`:`Play`;let a=performance.now(),o=s=>{i&&(s-a>=1e3/e&&(a=s,r((n()+1)%t)),requestAnimationFrame(o))};requestAnimationFrame(o)})}function Y(e,t,n,r){e.durationFrames=t,e.fps=n,e.setFrame=e=>new Promise(t=>{r(e),requestAnimationFrame(()=>requestAnimationFrame(()=>t()))})}async function X(e,t){let n=await G(`/recording/timeline.json`,`the recording`),r=q(t),i={images:await Promise.all(n.states.map(e=>K(`/recording/states/${e}`)))};await document.fonts.ready;let a=-1,o=e=>{let o=Math.max(0,Math.min(n.durationFrames-1,e));A(r,n,i,o,z,t),a=o,R||(B.value=String(o),V.textContent=`${o+1} / ${n.durationFrames} · ${(o/n.fps).toFixed(2)}s · state ${n.frames[o]}`)};B.max=String(n.durationFrames-1),B.addEventListener(`input`,()=>o(Number(B.value))),J(n.fps,n.durationFrames,()=>a,o),o(Number(F.get(`frame`)??0)),Y(e,n.durationFrames,n.fps,o)}async function Z(e,t,n){let r=(await P(()=>import(`/scenes.js`),[],import.meta.url)).default,i=r?.[n];i||W(`No scene "${n}". The scenes module exports: ${Object.keys(r??{}).join(`, `)||`(nothing)`}.`);let a=q(t);await document.fonts.ready;let o=-1,s=e=>{let t=Math.max(0,Math.min(i.durationFrames-1,e));i.draw(a,t,z),o=t,R||(B.value=String(t),V.textContent=`${t+1} / ${i.durationFrames} · ${(t/i.fps).toFixed(2)}s`)};B.max=String(i.durationFrames-1),B.addEventListener(`input`,()=>s(Number(B.value))),J(i.fps,i.durationFrames,()=>o,s),s(Number(F.get(`frame`)??0)),Y(e,i.durationFrames,i.fps,s)}async function Q(e){let t=await G(`/render-config.json`,`the render config`);if(L)return Z(e,t,L);if(I)return X(e,t);W(`Pass ?recording or ?scene=<id>.`)}var $={ready:Promise.resolve(),setFrame:async()=>{},durationFrames:0,fps:60};window.__render=$,$.ready=Q($);