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
package/CHANGELOG.md CHANGED
@@ -2,29 +2,176 @@
2
2
 
3
3
  What a user of `versioncam` would want to know about each version, newest
4
4
  first. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
- and versions follow [semantic versioning](https://semver.org/): while the
5
+ and versions follow [semantic versioning](https://semver.org/). While the
6
6
  major version is 0, a minor version may change the config or the clip DSL,
7
7
  and its entry here says how.
8
8
 
9
- ## [Unreleased]
9
+ ## 0.2.0 (2026-09-29)
10
10
 
11
- ## [0.1.2] — 2026-09-28
11
+ A minor version: new config fields and new camera methods. A clip whose
12
+ camera moves never overlap records what it did before, to the byte.
13
+
14
+ ### Added
15
+
16
+ - `s.camera.follow()` follows the cursor at one scale until the next camera
17
+ line, for a clip that moves around the page: one line instead of a zoom
18
+ before each thing the cursor uses, which zoomed in and out and panned in
19
+ fits and starts. The camera keeps the cursor inside a dead zone in the
20
+ middle of the frame, never shows beyond the page's edge, and moves
21
+ smoothly without overshooting. Its path is worked out from the whole
22
+ cursor track once the clip has run, so it keeps up with a cursor that
23
+ crosses the page. The options are `scale` (1.35, exact rather than a
24
+ ceiling), `deadZone` (0.3 of the frame), `smoothing` (600 milliseconds)
25
+ and `duration` (how long it takes to ease in). Their defaults are new
26
+ fields of the config's `motion`: `followZoom`, `followDeadZone` and
27
+ `followSmoothingMs`. `s.camera.release()` stops following, and the camera
28
+ comes to rest on the framing it was heading for.
29
+ - `versioncam review` warns, under `onscreen`, about a click that lands
30
+ outside what the camera shows: the button changes, and no frame shows it
31
+ pressed.
32
+ - `--cwd <dir>` runs any command as if from `<dir>`: that directory's
33
+ config, `.env.recording` and `.env`, and any relative path the command is
34
+ given or prints. It can go anywhere on the line. It is for a monorepo, run
35
+ from the repository root or from inside the app. `npx` looks for
36
+ versioncam only where it runs and above, so `--cwd` refuses a directory
37
+ whose own versioncam is not the one running, and says to `cd` into it
38
+ instead.
39
+ - `versioncam doctor` reads the scenes module a config names, and lists its
40
+ scene ids, or says what `render` would refuse it for and how to fix it.
41
+ - `theme.presentation.captionInset` moves a caption that sits over the app:
42
+ `{ left: n }` or `{ right: n }`, pixels of the video in from that side,
43
+ and `right` puts it in the bottom-right corner. The default, `{ left: 40 }`,
44
+ is where it always sat, to the byte. It is for an app whose own controls
45
+ are where the caption was, such as an account menu at the foot of a
46
+ sidebar or a map's legend. The README's Configure section has a table of
47
+ every `presentation` field now, which a test holds to the type.
48
+
49
+ ### Changed
50
+
51
+ - A camera line written while the camera is still moving starts at once,
52
+ from where the camera is, and takes its whole `duration`. It used to wait
53
+ for the move before it to arrive and then run in whatever time was left,
54
+ so a series of pans stopped and snapped. Two camera lines written at the
55
+ same instant used to glide to the first and cut to the second; now the
56
+ second is the only one. A clip whose camera moves never overlap records
57
+ exactly the camera it did before, to the byte.
58
+ - A recording whose camera follows the cursor, or eases from one move into
59
+ another, keeps its camera as a key a frame, with a new ease, `"linear"`.
60
+ A renderer from an earlier version draws such a recording the same way.
61
+ - `versioncam review` names a camera still moving at the end of a clip once,
62
+ at its last key, rather than once for every key past the end.
63
+ - `versioncam init` below a repository root says where Claude Code finds the
64
+ skill: in a session started in that directory or below it, and from the
65
+ root only after the session has read or edited a file there.
66
+ - `render` checks everything it was asked for before it renders anything.
67
+ An id that matches no recording and no scene, or a `--scene` the scenes
68
+ module does not define, stops it with what there is to render. An id that
69
+ matched nothing used to be passed over without a word.
70
+ - `versioncam --help` and the README say plainly that `render` with no ids
71
+ renders every recording on disk, stale ones from drafts, trial runs and
72
+ renamed clips included.
73
+
74
+ ### Fixed
75
+
76
+ - The authoring skill works in a monorepo wherever the session started. It
77
+ looks for `versioncam.config.ts` across the whole repository before
78
+ deciding anything, works in the directory that holds it, and asks which
79
+ when there are several. It used to look only in the directory the session
80
+ was in. Invoked at a repository root with the app below it, it said
81
+ versioncam was not installed, and past that it would have set the root up
82
+ as a second app, with a second config.
83
+ - A scenes module whose scenes are not its default export is refused, with
84
+ the file's name and the shape it should have: a `Record<string, Scene>`
85
+ keyed by scene id. A module whose record was a named export used to count
86
+ as having no scenes, so `render` rendered none, said nothing, and stitched
87
+ a sequence without them. An array, a single scene, or an entry with no
88
+ `draw`, no whole `durationFrames` or no `fps` is refused the same way.
89
+ `render` and `preview --scene` refuse it before a browser starts, and the
90
+ render page refuses it too, for anything that renders a scene without the
91
+ CLI.
92
+ - `render --scene <id>` renders that scene whatever ids come with it. The
93
+ ids used to filter the named scenes as well, so
94
+ `render 02 04 --scene 01-intro --sequence` rendered no scene and stitched
95
+ the recordings without it. Ids still choose among the scenes `--scene`
96
+ does not name.
97
+ - `render --sequence` says what the sequence was stitched without: the ids
98
+ in the config's `sequence` that this run did not render, and the ones it
99
+ rendered that the `sequence` leaves out. The first note was in the code
100
+ and could never print. With fewer than two of the `sequence`'s clips
101
+ rendered, it says a sequence needs two, as it does with fewer than two
102
+ clips at all, rather than failing.
103
+ - `render --sequence` joins clips of different frame rates, such as a
104
+ 15 fps draft beside 60 fps recordings, or a scene or a clip with an `fps`
105
+ of its own. Every clip is brought to the highest rate among them, so none
106
+ loses a frame. It used to print ffmpeg's error that the clips' timebases
107
+ or frame rates do not match, and stop without a sequence. Clips that
108
+ share one rate are stitched exactly as before.
109
+ - A sequence that fails in ffmpeg leaves the `sequence.mp4` of an earlier
110
+ run as it was, and says that the one there is an earlier run's. It used
111
+ to leave that file empty, or an empty one where there had been none:
112
+ ffmpeg wrote straight to it, and had emptied it by the time it failed.
113
+ The sequence is now written beside it and takes its place only once it
114
+ is complete.
115
+ - A clip or scene that fails partway through `render` stops it at once, and
116
+ leaves the last good render as it was. A scene that threw at a frame used
117
+ to leave both ffmpeg encoders waiting for frames that would never come:
118
+ `render` printed the error and then never exited, which in CI is a job
119
+ that hangs until it is killed. ffmpeg also wrote straight over the
120
+ finished files, so a playable half-clip, and the failed run's poster, took
121
+ the place of the last good ones. The clip, its webm, its poster and its
122
+ contact sheet are now each written beside the last and take its place only
123
+ once complete, as the sequence is.
124
+ - A contact sheet's evenly spread tiles no longer land in the dissolve the
125
+ renderer draws after a settle, where a tile showed the page before the
126
+ wait ghosting through the page after it. The sheet `render` writes and
127
+ `versioncam sheet` without `--at-marks` move such a tile to the first
128
+ frame after the dissolve, as `--at-marks` already did for its own tiles.
129
+ The example app's first clip had one. A scene's sheet is unchanged, since
130
+ a scene never dissolves. `evenFrames` is exported beside `uniformFrames`
131
+ and `markedFrames`.
132
+
133
+ ## 0.1.3 (2026-09-28)
134
+
135
+ ### Added
136
+
137
+ - A recording lists the steps its script took: `steps` in `timeline.json`,
138
+ each DSL call the script made and the frame it began on. There is one per
139
+ line of the script: `typeInto` is one step, not also the click it makes, and
140
+ a locator is an argument, not a step. A reader holding the script can say
141
+ which line produced which moment.
142
+ - A recording names the versioncam that made it: `meta.recorder`, the package
143
+ version. A page that shows the recording can print the version that
144
+ recorded it instead of the latest release.
145
+
146
+ ### Changed
147
+
148
+ - The DSL reference gives every method its own heading and every option its
149
+ default, and `versioncam dsl` prints it. It used to say `camera.zoom`
150
+ defaults to 1.65. The default is 1.5, as far as the camera goes before
151
+ captured pixels are upscaled.
152
+ - `versioncam --help` groups the commands by stage: set up, write, record,
153
+ review, output.
154
+ - The CLI's messages say things in sentences and brackets rather than
155
+ dashes. `record` says "1 cut" rather than "1 cuts". `stability` separates
156
+ the pair it compared from the result with a `·`, so a script matching its
157
+ output needs the new line.
158
+
159
+ ## 0.1.2 (2026-09-28)
12
160
 
13
161
  ### Fixed
14
162
 
15
163
  - Typing followed straight away by another action no longer loses its last
16
- character. `type` and `typeInto` queue keystrokes at a human cadence, and
17
- whenever the typing ended between two frames the last keystroke stayed
18
- queued until after the next step — so `typeInto(…)` then `press("Enter")`
164
+ character. `type` and `typeInto` queue keystrokes at a human cadence.
165
+ Whenever the typing ended between two frames, the last keystroke stayed
166
+ queued until after the next step. So `typeInto(…)` then `press("Enter")`
19
167
  sent the text without its last character, which then began the next
20
168
  message. Whether a clip was affected depended only on its timing.
169
+ - A point written as `{ x, y }`, the shape a bounding box gives you, fails
170
+ with "A point is [x, y], not { x, y }", which names the fix. It used to
171
+ reach Playwright as if it were a locator and fail as "locator.count is not
172
+ a function". `dsl.md` says that a point is `[x, y]`.
21
173
 
22
- - A point written as `{ x, y }` — the shape a bounding box gives you — fails
23
- with "A point is [x, y], not { x, y }", naming the fix. It used to reach
24
- Playwright as if it were a locator and fail as "locator.count is not a
25
- function". `dsl.md` says that a point is `[x, y]`.
26
-
27
- ## [0.1.1] — 2026-09-28
174
+ ## 0.1.1 (2026-09-28)
28
175
 
29
176
  The first version published by the release workflow rather than by hand.
30
177
 
@@ -32,77 +179,80 @@ The first version published by the release workflow rather than by hand.
32
179
 
33
180
  - Where there is no `versioncam.config.ts`, `doctor` and every other command
34
181
  say how to get one: `npx versioncam init`, then `/versioncam`, whose first
35
- run writes it from the repository — or by hand, a default export from
36
- `defineRecorder()`. They used to offer only the second, which is the long
37
- way round for anyone using the skill.
182
+ run writes it from the repository. Or write it by hand, a default export
183
+ from `defineRecorder()`. They used to offer only the second, which is the
184
+ long way round for anyone using the skill.
38
185
  - The skill's plugin manifest links to version.cam, and no longer to a
39
186
  repository nobody outside can open.
40
187
 
41
- ## [0.1.0] — 2026-09-25
188
+ ## 0.1.0 (2026-09-25)
42
189
 
43
- The first published version. It was built in work packages before it had a
44
- version number, so this entry is grouped by them, newest first.
190
+ The first published version, and no longer on npm: install 0.1.1 or later.
191
+ It was built in work packages before it had a version number, so this entry
192
+ is grouped by them, newest first.
45
193
 
46
194
  ### Publishing
47
195
 
48
196
  - Licensed under the Functional Source License 1.1 with Apache 2.0 as the
49
- future licence (`FSL-1.1-ALv2`): use it on your own apps and in your own
50
- CI, commercial work included; no competing product or service; each
51
- version is also available under Apache 2.0 two years after its release.
197
+ future licence (`FSL-1.1-ALv2`). Use it on your own apps and in your own
198
+ CI, commercial work included, but not in a competing product or service.
199
+ Each version is also available under Apache 2.0 two years after its
200
+ release.
52
201
  - `versioncam install` installs the Chromium that this version's Playwright
53
- expects — `--with-deps` adds its system libraries on Linux CI — and
54
- `doctor` names it when the browser is missing.
202
+ expects, and `--with-deps` adds its system libraries on Linux CI. `doctor`
203
+ names it when the browser is missing.
55
204
 
56
205
  ### Reproducible on Linux
57
206
 
58
207
  - A draft recording reproduces on Linux. Chromium now repaints whole tiles
59
- (`--disable-partial-raster`): at a draft's CSS scale, repainting only what
208
+ (`--disable-partial-raster`). At a draft's CSS scale, repainting only what
60
209
  the page reported as changed had left one pixel of a just-typed letter
61
210
  stale in about half of all runs on a CI runner. Full recordings are
62
211
  byte-identical with and without it.
63
- - The page's clock moves only when the recorder moves it — 1/fps a frame,
64
- 16 ms a tick while it waits — instead of running on real time between
65
- frames, where one slow screenshot had moved it 840 ms and fired an app's
66
- timer seven frames early. `offCamera` spends no time on the page's clock;
67
- a sign-in hook runs with the clock ticking; a target that is not yet on
212
+ - The page's clock moves only when the recorder moves it: 1/fps a frame, and
213
+ 16 ms a tick while it waits. It used to run on real time between frames,
214
+ where one slow screenshot had moved it 840 ms and fired an app's timer
215
+ seven frames early. `offCamera` spends no time on the page's clock. A
216
+ sign-in hook runs with the clock ticking. A target that is not yet on
68
217
  screen is waited for in ticks, so it arrives on the same tick every run.
69
218
 
70
- ### Versioncam — 2026-09-24
219
+ ### Versioncam (2026-09-24)
71
220
 
72
221
  - One name for everything: the package and CLI `versioncam`, the config
73
222
  `versioncam.config.ts`, the output directory `.versioncam/`, the skill
74
223
  `/versioncam`.
75
- - `webServer` in the config, Playwright's fields: every command that drives
76
- the app starts it when nothing answers, and stops everything it started.
224
+ - `webServer` in the config, with Playwright's fields. Every command that
225
+ drives the app starts it when nothing answers, and stops everything it
226
+ started.
77
227
  - `versioncam stability` records a clip twice the same way and says whether
78
228
  the app reproduces, with the two pictures behind the first frame that
79
- differs. `frame --at <label>` renders the frame a beat shows; `inspect`
80
- says where each name came from; `measure` prints what each match says.
81
- - One skill and one reviewer file, installed by `versioncam init`; the
229
+ differs. `frame --at <label>` renders the frame a beat shows. `inspect`
230
+ says where each name came from. `measure` prints what each match says.
231
+ - One skill and one reviewer file, installed by `versioncam init`. The
82
232
  skill's first run in a repository writes the config and proves the app
83
233
  records the same way twice before it writes a clip.
84
234
 
85
- ### The authoring loop — 2026-09-22
235
+ ### The authoring loop (2026-09-22)
86
236
 
87
237
  - The agent skill: inspect the app, write a clip, record it, have a fresh
88
- reviewer judge a contact sheet, fix what it finds. No API key; the model
89
- is whichever agent session runs it.
90
- - `versioncam review` — the checks that need no eyes; `sheet --at-marks`,
91
- sampled at the beats; recordings that describe their beats (`marks`) and
92
- their waits (`settles.json`); `failure.png` and `failure.json` when a clip
93
- breaks; `s.drag`; `cursorStart`; review bounds on a clip's length that the
94
- app sets.
238
+ reviewer judge a contact sheet, fix what it finds. No API key: the model is
239
+ whichever agent session runs it.
240
+ - `versioncam review`, the checks that need no eyes. `sheet --at-marks`,
241
+ sampled at the beats. Recordings that describe their beats (`marks`) and
242
+ their waits (`settles.json`). `failure.png` and `failure.json` when a clip
243
+ breaks. `s.drag`. `cursorStart`. Bounds on a clip's length that the app
244
+ sets for review.
95
245
 
96
- ### Speed — 2026-09-21
246
+ ### Speed (2026-09-21)
97
247
 
98
248
  - Capture on change: a frame is photographed only when the page can have
99
- changed, which skips 81–95 % of frames on the example app and leaves the
249
+ changed. That skips 81% to 95% of frames on the example app and leaves the
100
250
  recording identical.
101
251
  - `--draft`: fifteen frames a second at CSS scale, for looking at while
102
252
  writing a clip.
103
253
  - `record` prints where the time went.
104
254
 
105
- ### The package — 2026-09-21
255
+ ### The package (2026-09-21)
106
256
 
107
257
  - The recorder, extracted from the prototype it grew up in: a config
108
258
  boundary (`defineRecorder`), the clip DSL, a renderer with presentation as