@kvman/kvvideo 0.1.0 → 0.1.1

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 (242) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.ts +26 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/brand/brand.d.ts +17 -0
  5. package/dist/brand/brand.d.ts.map +1 -0
  6. package/dist/brand/brand.js +44 -0
  7. package/dist/brand/brand.js.map +1 -0
  8. package/dist/browser/launch.d.ts +2 -0
  9. package/dist/browser/launch.d.ts.map +1 -1
  10. package/dist/browser/launch.js +7 -1
  11. package/dist/browser/launch.js.map +1 -1
  12. package/dist/compose/ass-file.d.ts +5 -0
  13. package/dist/compose/ass-file.d.ts.map +1 -1
  14. package/dist/compose/ass-file.js +32 -3
  15. package/dist/compose/ass-file.js.map +1 -1
  16. package/dist/compose/background.d.ts +23 -0
  17. package/dist/compose/background.d.ts.map +1 -0
  18. package/dist/compose/background.js +31 -0
  19. package/dist/compose/background.js.map +1 -0
  20. package/dist/compose/camera.d.ts +32 -0
  21. package/dist/compose/camera.d.ts.map +1 -0
  22. package/dist/compose/camera.js +98 -0
  23. package/dist/compose/camera.js.map +1 -0
  24. package/dist/compose/caption-font.d.ts +2 -0
  25. package/dist/compose/caption-font.d.ts.map +1 -1
  26. package/dist/compose/caption-font.js +2 -1
  27. package/dist/compose/caption-font.js.map +1 -1
  28. package/dist/compose/compose-drawing.d.ts +19 -0
  29. package/dist/compose/compose-drawing.d.ts.map +1 -0
  30. package/dist/compose/compose-drawing.js +38 -0
  31. package/dist/compose/compose-drawing.js.map +1 -0
  32. package/dist/compose/compose-input.d.ts +139 -0
  33. package/dist/compose/compose-input.d.ts.map +1 -1
  34. package/dist/compose/compose-input.js +48 -9
  35. package/dist/compose/compose-input.js.map +1 -1
  36. package/dist/compose/compose-plan.d.ts +36 -0
  37. package/dist/compose/compose-plan.d.ts.map +1 -0
  38. package/dist/compose/compose-plan.js +55 -0
  39. package/dist/compose/compose-plan.js.map +1 -0
  40. package/dist/compose/compose-size.d.ts +50 -0
  41. package/dist/compose/compose-size.d.ts.map +1 -0
  42. package/dist/compose/compose-size.js +72 -0
  43. package/dist/compose/compose-size.js.map +1 -0
  44. package/dist/compose/drawn-images.d.ts +19 -0
  45. package/dist/compose/drawn-images.d.ts.map +1 -0
  46. package/dist/compose/drawn-images.js +75 -0
  47. package/dist/compose/drawn-images.js.map +1 -0
  48. package/dist/compose/final-pass.d.ts +34 -0
  49. package/dist/compose/final-pass.d.ts.map +1 -0
  50. package/dist/compose/final-pass.js +106 -0
  51. package/dist/compose/final-pass.js.map +1 -0
  52. package/dist/compose/frame-layout.d.ts +30 -0
  53. package/dist/compose/frame-layout.d.ts.map +1 -0
  54. package/dist/compose/frame-layout.js +33 -0
  55. package/dist/compose/frame-layout.js.map +1 -0
  56. package/dist/compose/overlays.d.ts +20 -0
  57. package/dist/compose/overlays.d.ts.map +1 -0
  58. package/dist/compose/overlays.js +30 -0
  59. package/dist/compose/overlays.js.map +1 -0
  60. package/dist/compose/page-again.d.ts +26 -0
  61. package/dist/compose/page-again.d.ts.map +1 -0
  62. package/dist/compose/page-again.js +50 -0
  63. package/dist/compose/page-again.js.map +1 -0
  64. package/dist/compose/player-copies.d.ts +5 -1
  65. package/dist/compose/player-copies.d.ts.map +1 -1
  66. package/dist/compose/player-copies.js +19 -16
  67. package/dist/compose/player-copies.js.map +1 -1
  68. package/dist/compose/register-compose.d.ts.map +1 -1
  69. package/dist/compose/register-compose.js +106 -20
  70. package/dist/compose/register-compose.js.map +1 -1
  71. package/dist/compose/render.d.ts +13 -4
  72. package/dist/compose/render.d.ts.map +1 -1
  73. package/dist/compose/render.js +48 -22
  74. package/dist/compose/render.js.map +1 -1
  75. package/dist/compose/scene-args.d.ts +32 -7
  76. package/dist/compose/scene-args.d.ts.map +1 -1
  77. package/dist/compose/scene-args.js +77 -16
  78. package/dist/compose/scene-args.js.map +1 -1
  79. package/dist/compose/scene-look.d.ts +31 -0
  80. package/dist/compose/scene-look.d.ts.map +1 -0
  81. package/dist/compose/scene-look.js +46 -0
  82. package/dist/compose/scene-look.js.map +1 -0
  83. package/dist/compose/scene-plan.d.ts +0 -2
  84. package/dist/compose/scene-plan.d.ts.map +1 -1
  85. package/dist/compose/scene-plan.js +7 -5
  86. package/dist/compose/scene-plan.js.map +1 -1
  87. package/dist/compose/sound-effects.d.ts +23 -0
  88. package/dist/compose/sound-effects.d.ts.map +1 -0
  89. package/dist/compose/sound-effects.js +40 -0
  90. package/dist/compose/sound-effects.js.map +1 -0
  91. package/dist/compose/srt.d.ts +3 -0
  92. package/dist/compose/srt.d.ts.map +1 -0
  93. package/dist/compose/srt.js +10 -0
  94. package/dist/compose/srt.js.map +1 -0
  95. package/dist/compose/transitions.d.ts +16 -0
  96. package/dist/compose/transitions.d.ts.map +1 -0
  97. package/dist/compose/transitions.js +38 -0
  98. package/dist/compose/transitions.js.map +1 -0
  99. package/dist/connector.d.ts +67 -14
  100. package/dist/connector.d.ts.map +1 -1
  101. package/dist/connector.js +27 -8
  102. package/dist/connector.js.map +1 -1
  103. package/dist/files/sidecar.d.ts +209 -0
  104. package/dist/files/sidecar.d.ts.map +1 -0
  105. package/dist/files/sidecar.js +66 -0
  106. package/dist/files/sidecar.js.map +1 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +7 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/narration/alignment.d.ts +15 -0
  111. package/dist/narration/alignment.d.ts.map +1 -0
  112. package/dist/narration/alignment.js +21 -0
  113. package/dist/narration/alignment.js.map +1 -0
  114. package/dist/narration/captions.d.ts +22 -4
  115. package/dist/narration/captions.d.ts.map +1 -1
  116. package/dist/narration/captions.js +44 -16
  117. package/dist/narration/captions.js.map +1 -1
  118. package/dist/narration/join-args.d.ts +3 -0
  119. package/dist/narration/join-args.d.ts.map +1 -0
  120. package/dist/narration/join-args.js +19 -0
  121. package/dist/narration/join-args.js.map +1 -0
  122. package/dist/narration/narration-cache.d.ts +25 -0
  123. package/dist/narration/narration-cache.d.ts.map +1 -0
  124. package/dist/narration/narration-cache.js +36 -0
  125. package/dist/narration/narration-cache.js.map +1 -0
  126. package/dist/narration/piper.d.ts +2 -6
  127. package/dist/narration/piper.d.ts.map +1 -1
  128. package/dist/narration/piper.js +9 -15
  129. package/dist/narration/piper.js.map +1 -1
  130. package/dist/narration/pronunciations.d.ts +11 -0
  131. package/dist/narration/pronunciations.d.ts.map +1 -0
  132. package/dist/narration/pronunciations.js +40 -0
  133. package/dist/narration/pronunciations.js.map +1 -0
  134. package/dist/narration/register-narration.d.ts +8 -0
  135. package/dist/narration/register-narration.d.ts.map +1 -1
  136. package/dist/narration/register-narration.js +54 -26
  137. package/dist/narration/register-narration.js.map +1 -1
  138. package/dist/narration/register-voice.d.ts +27 -0
  139. package/dist/narration/register-voice.d.ts.map +1 -0
  140. package/dist/narration/register-voice.js +82 -0
  141. package/dist/narration/register-voice.js.map +1 -0
  142. package/dist/narration/sentence-speaker.d.ts +35 -0
  143. package/dist/narration/sentence-speaker.d.ts.map +1 -0
  144. package/dist/narration/sentence-speaker.js +44 -0
  145. package/dist/narration/sentence-speaker.js.map +1 -0
  146. package/dist/narration/speech-services.d.ts +23 -3
  147. package/dist/narration/speech-services.d.ts.map +1 -1
  148. package/dist/narration/speech-services.js +27 -10
  149. package/dist/narration/speech-services.js.map +1 -1
  150. package/dist/narration/voice-args.d.ts +3 -0
  151. package/dist/narration/voice-args.d.ts.map +1 -0
  152. package/dist/narration/voice-args.js +10 -0
  153. package/dist/narration/voice-args.js.map +1 -0
  154. package/dist/programs/media-duration.d.ts +12 -0
  155. package/dist/programs/media-duration.d.ts.map +1 -1
  156. package/dist/programs/media-duration.js +13 -0
  157. package/dist/programs/media-duration.js.map +1 -1
  158. package/dist/programs/run-program.d.ts +1 -0
  159. package/dist/programs/run-program.d.ts.map +1 -1
  160. package/dist/programs/run-program.js +1 -0
  161. package/dist/programs/run-program.js.map +1 -1
  162. package/dist/record/address.d.ts +1 -1
  163. package/dist/record/address.d.ts.map +1 -1
  164. package/dist/record/address.js +5 -7
  165. package/dist/record/address.js.map +1 -1
  166. package/dist/record/cursor-script.d.ts +2 -1
  167. package/dist/record/cursor-script.d.ts.map +1 -1
  168. package/dist/record/cursor-script.js +52 -14
  169. package/dist/record/cursor-script.js.map +1 -1
  170. package/dist/record/encoder-args.js +1 -1
  171. package/dist/record/motion.d.ts +19 -0
  172. package/dist/record/motion.d.ts.map +1 -0
  173. package/dist/record/motion.js +59 -0
  174. package/dist/record/motion.js.map +1 -0
  175. package/dist/record/narration-cues.d.ts +14 -0
  176. package/dist/record/narration-cues.d.ts.map +1 -0
  177. package/dist/record/narration-cues.js +12 -0
  178. package/dist/record/narration-cues.js.map +1 -0
  179. package/dist/record/page-ready.d.ts +4 -0
  180. package/dist/record/page-ready.d.ts.map +1 -0
  181. package/dist/record/page-ready.js +15 -0
  182. package/dist/record/page-ready.js.map +1 -0
  183. package/dist/record/page-route.d.ts +13 -0
  184. package/dist/record/page-route.d.ts.map +1 -0
  185. package/dist/record/page-route.js +64 -0
  186. package/dist/record/page-route.js.map +1 -0
  187. package/dist/record/pointer.d.ts +6 -7
  188. package/dist/record/pointer.d.ts.map +1 -1
  189. package/dist/record/pointer.js +11 -10
  190. package/dist/record/pointer.js.map +1 -1
  191. package/dist/record/recorder.d.ts +17 -7
  192. package/dist/record/recorder.d.ts.map +1 -1
  193. package/dist/record/recorder.js +48 -18
  194. package/dist/record/recorder.js.map +1 -1
  195. package/dist/record/register-record.d.ts.map +1 -1
  196. package/dist/record/register-record.js +47 -13
  197. package/dist/record/register-record.js.map +1 -1
  198. package/dist/record/run-step.d.ts +16 -3
  199. package/dist/record/run-step.d.ts.map +1 -1
  200. package/dist/record/run-step.js +43 -23
  201. package/dist/record/run-step.js.map +1 -1
  202. package/dist/record/screencast.js +1 -1
  203. package/dist/record/steps.d.ts +54 -1
  204. package/dist/record/steps.d.ts.map +1 -1
  205. package/dist/record/steps.js +34 -16
  206. package/dist/record/steps.js.map +1 -1
  207. package/dist/review/analysis.d.ts +19 -0
  208. package/dist/review/analysis.d.ts.map +1 -0
  209. package/dist/review/analysis.js +36 -0
  210. package/dist/review/analysis.js.map +1 -0
  211. package/dist/review/frames.d.ts +9 -0
  212. package/dist/review/frames.d.ts.map +1 -0
  213. package/dist/review/frames.js +28 -0
  214. package/dist/review/frames.js.map +1 -0
  215. package/dist/review/register-review.d.ts +24 -0
  216. package/dist/review/register-review.d.ts.map +1 -0
  217. package/dist/review/register-review.js +81 -0
  218. package/dist/review/register-review.js.map +1 -0
  219. package/dist/review/sheet-args.d.ts +11 -0
  220. package/dist/review/sheet-args.d.ts.map +1 -0
  221. package/dist/review/sheet-args.js +24 -0
  222. package/dist/review/sheet-args.js.map +1 -0
  223. package/dist/settings.d.ts +4 -0
  224. package/dist/settings.d.ts.map +1 -1
  225. package/dist/settings.js +4 -2
  226. package/dist/settings.js.map +1 -1
  227. package/dist/setup/ffmpeg-readiness.js +1 -1
  228. package/dist/setup/ffmpeg-readiness.js.map +1 -1
  229. package/dist/ui.d.ts.map +1 -1
  230. package/dist/ui.js +3 -2
  231. package/dist/ui.js.map +1 -1
  232. package/dist/web/components/player.css +1 -1
  233. package/dist/web/components/player.js +33 -21
  234. package/docs/guide.md +21 -9
  235. package/locales/ar.json +12 -1
  236. package/locales/en.json +12 -1
  237. package/package.json +11 -5
  238. package/sounds/SOURCES.md +13 -0
  239. package/sounds/click.wav +0 -0
  240. package/sounds/keys.wav +0 -0
  241. package/sounds/pop.wav +0 -0
  242. package/sounds/whoosh.wav +0 -0
@@ -0,0 +1,64 @@
1
+ import { existsSync, realpathSync, statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ // An HTML page of the workspace, recorded without a server (plan 16 §16.5; ADR 0115, 32): it is opened on an origin
4
+ // that never resolves, and each request to that origin is answered with the file at its path under the page's folder.
5
+ // A path that leaves the folder, through `..` or a symlink, or a file that doesn't exist, is answered 404.
6
+ export const pageOrigin = 'http://page.kvvideo.invalid';
7
+ const contentTypes = {
8
+ '.html': 'text/html; charset=utf-8',
9
+ '.htm': 'text/html; charset=utf-8',
10
+ '.css': 'text/css; charset=utf-8',
11
+ '.js': 'text/javascript; charset=utf-8',
12
+ '.mjs': 'text/javascript; charset=utf-8',
13
+ '.json': 'application/json',
14
+ '.txt': 'text/plain; charset=utf-8',
15
+ '.svg': 'image/svg+xml',
16
+ '.png': 'image/png',
17
+ '.jpg': 'image/jpeg',
18
+ '.jpeg': 'image/jpeg',
19
+ '.gif': 'image/gif',
20
+ '.webp': 'image/webp',
21
+ '.avif': 'image/avif',
22
+ '.ico': 'image/x-icon',
23
+ '.woff': 'font/woff',
24
+ '.woff2': 'font/woff2',
25
+ '.ttf': 'font/ttf',
26
+ '.otf': 'font/otf',
27
+ '.mp4': 'video/mp4',
28
+ '.webm': 'video/webm',
29
+ '.mp3': 'audio/mpeg',
30
+ '.wav': 'audio/wav',
31
+ '.ogg': 'audio/ogg',
32
+ };
33
+ /** The address a page file is opened at. */
34
+ export function pageAddress(page) {
35
+ return `${pageOrigin}/${encodeURIComponent(path.basename(page))}`;
36
+ }
37
+ function inside(folder, candidate) {
38
+ const relative = path.relative(folder, candidate);
39
+ return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
40
+ }
41
+ function decodedPath(address) {
42
+ const { pathname } = new URL(address);
43
+ try {
44
+ return decodeURIComponent(pathname).replace(/^\/+/, '');
45
+ }
46
+ catch (error) {
47
+ if (error instanceof URIError)
48
+ return undefined;
49
+ throw error;
50
+ }
51
+ }
52
+ /** What a request of the page's origin is answered with, from the page's folder. */
53
+ export function answerPageRequest(folder, address) {
54
+ const relative = decodedPath(address);
55
+ if (relative === undefined)
56
+ return { status: 404 };
57
+ const file = path.resolve(folder, relative);
58
+ if (!inside(folder, file) || !existsSync(file))
59
+ return { status: 404 };
60
+ if (!inside(realpathSync(folder), realpathSync(file)) || !statSync(file).isFile())
61
+ return { status: 404 };
62
+ return { status: 200, file, contentType: contentTypes[path.extname(file).toLowerCase()] ?? 'application/octet-stream' };
63
+ }
64
+ //# sourceMappingURL=page-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-route.js","sourceRoot":"","sources":["../../src/record/page-route.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,8 @@
1
1
  import type { Page } from 'playwright-core';
2
- export type Pointer = {
3
- x: number;
4
- y: number;
5
- };
6
- export declare function glideTo(page: Page, from: Pointer, to: Pointer): Promise<Pointer>;
7
- /** The center of the first visible match of a target, scrolled into view. */
8
- export declare function centerOf(page: Page, target: string): Promise<Pointer>;
2
+ import { type Point } from './motion.ts';
3
+ import type { Box } from './steps.ts';
4
+ export declare function glideTo(page: Page, from: Point, to: Point): Promise<Point>;
5
+ /** The box of the first visible match of a target, scrolled into view, in whole CSS pixels of the viewport. */
6
+ export declare function targetBox(page: Page, target: string): Promise<Box>;
7
+ export declare function centerOf(box: Box): Point;
9
8
  //# sourceMappingURL=pointer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pointer.d.ts","sourceRoot":"","sources":["../../src/record/pointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,MAAM,MAAM,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAOtF;AAED,6EAA6E;AAC7E,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3E"}
1
+ {"version":3,"file":"pointer.d.ts","sourceRoot":"","sources":["../../src/record/pointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAKtC,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAMhF;AAED,+GAA+G;AAC/G,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAOxE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,KAAK,CAExC"}
@@ -1,23 +1,24 @@
1
- // The mouse as the viewer sees it (ADR 0107, 19): it glides to each target over 600 ms, in 30 moves, so the drawn
2
- // cursor travels instead of jumping.
3
- const glideMoves = 30;
4
- const glideMs = 600;
1
+ import { frameMs, glidePath } from "./motion.js";
2
+ // The mouse as the viewer sees it (plan 16 §16.5; ADR 0115, 9): it glides to each target along an eased, gently
3
+ // curved path, one move a frame, so the drawn pointer travels instead of jumping.
5
4
  export async function glideTo(page, from, to) {
6
- for (let move = 1; move <= glideMoves; move += 1) {
7
- const share = move / glideMoves;
8
- await page.mouse.move(from.x + (to.x - from.x) * share, from.y + (to.y - from.y) * share);
9
- await page.waitForTimeout(glideMs / glideMoves);
5
+ for (const point of glidePath(from, to)) {
6
+ await page.mouse.move(point.x, point.y);
7
+ await page.waitForTimeout(frameMs);
10
8
  }
11
9
  return to;
12
10
  }
13
- /** The center of the first visible match of a target, scrolled into view. */
14
- export async function centerOf(page, target) {
11
+ /** The box of the first visible match of a target, scrolled into view, in whole CSS pixels of the viewport. */
12
+ export async function targetBox(page, target) {
15
13
  const element = page.locator(target).first();
16
14
  await element.waitFor({ state: 'visible', timeout: 10_000 });
17
15
  await element.scrollIntoViewIfNeeded({ timeout: 10_000 });
18
16
  const box = await element.boundingBox({ timeout: 10_000 });
19
17
  if (box === null)
20
18
  throw new Error(`${target} has no box on the page`);
19
+ return { x: Math.round(box.x), y: Math.round(box.y), width: Math.round(box.width), height: Math.round(box.height) };
20
+ }
21
+ export function centerOf(box) {
21
22
  return { x: box.x + box.width / 2, y: box.y + box.height / 2 };
22
23
  }
23
24
  //# sourceMappingURL=pointer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pointer.js","sourceRoot":"","sources":["../../src/record/pointer.ts"],"names":[],"mappings":"AAEA,kHAAkH;AAClH,qCAAqC;AAErC,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,OAAO,GAAG,GAAG,CAAC;AAIpB,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,IAAa,EAAE,EAAW;IAClE,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,UAAU,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,GAAG,UAAU,CAAC;QAChC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC1F,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,GAAG,UAAU,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAU,EAAE,MAAc;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IAC7C,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,CAAC,sBAAsB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,GAAG,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,yBAAyB,CAAC,CAAC;IACtE,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;AACjE,CAAC"}
1
+ {"version":3,"file":"pointer.js","sourceRoot":"","sources":["../../src/record/pointer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAc,MAAM,aAAa,CAAC;AAG7D,gHAAgH;AAChH,kFAAkF;AAElF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,IAAW,EAAE,EAAS;IAC9D,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAU,EAAE,MAAc;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IAC7C,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,CAAC,sBAAsB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,GAAG,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,yBAAyB,CAAC,CAAC;IACtE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;AACtH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAQ;IAC/B,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;AACjE,CAAC"}
@@ -1,24 +1,34 @@
1
- import type { Browser } from 'playwright-core';
1
+ import type { Browser, BrowserContext } from 'playwright-core';
2
2
  import { type Program } from '../programs/run-program.ts';
3
- import type { Step } from './steps.ts';
4
- export type RecordPlan = {
3
+ import { type Cues } from './narration-cues.ts';
4
+ import type { Action, Step } from './steps.ts';
5
+ export type RecordSource = {
6
+ kind: 'site';
5
7
  url: string;
8
+ } | {
9
+ kind: 'page';
10
+ file: string;
11
+ };
12
+ export type RecordPlan = {
13
+ source: RecordSource;
6
14
  steps: readonly Step[];
7
15
  width: number;
8
16
  height: number;
17
+ device: 'desktop' | 'phone';
18
+ cues: Cues | undefined;
9
19
  folder: string;
10
20
  ownPort: number | null;
11
21
  ffmpeg: Program;
12
22
  signal: AbortSignal;
13
23
  now(): number;
24
+ stepStarted(number: number, of: number): void;
14
25
  };
15
26
  export type Recording = {
16
27
  video: string;
17
28
  durationMs: number;
18
- steps: {
19
- do: Step['do'];
20
- atMs: number;
21
- }[];
29
+ actions: Action[];
22
30
  };
31
+ /** A fresh browser context for a recording: its size and device, kvman's port aborted, a page's files, the pointer. */
32
+ export declare function recordingContext(browser: Browser, plan: Pick<RecordPlan, 'source' | 'width' | 'height' | 'device' | 'ownPort'>): Promise<BrowserContext>;
23
33
  export declare function record(browser: Browser, plan: RecordPlan): Promise<Recording>;
24
34
  //# sourceMappingURL=recorder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/record/recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAQrE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,GAAG,IAAI,MAAM,CAAA;CAAE,CAAC;AAE7L,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC;AAqCzG,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAqBnF"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/record/recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAQ,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAMrE,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAK7D,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAO/C,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,IAAI,MAAM,CAAC;IACd,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAMjF,uHAAuH;AACvH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAa9J;AA0CD,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAsBnF"}
@@ -6,32 +6,58 @@ import { isOwnAddress } from "./address.js";
6
6
  import { cursorScript } from "./cursor-script.js";
7
7
  import { recordEncoderArgs } from "./encoder-args.js";
8
8
  import { frameWriter } from "./frame-writer.js";
9
+ import { waitUntilMs } from "./narration-cues.js";
10
+ import { answerPageRequest, pageAddress, pageOrigin } from "./page-route.js";
11
+ import { waitUntilReady } from "./page-ready.js";
9
12
  import { pausesAfter, runStep } from "./run-step.js";
10
13
  import { startScreencast } from "./screencast.js";
14
+ const scale = 2;
11
15
  const firstLine = (error) => (error instanceof Error ? (error.message.split('\n').find((line) => line.trim() !== '') ?? error.name) : String(error)).trim();
12
- async function contextFor(browser, plan) {
13
- const context = await browser.newContext({ viewport: { width: plan.width, height: plan.height }, deviceScaleFactor: 1 });
16
+ /** A fresh browser context for a recording: its size and device, kvman's port aborted, a page's files, the pointer. */
17
+ export async function recordingContext(browser, plan) {
18
+ const phone = plan.device === 'phone';
19
+ const context = await browser.newContext({ viewport: { width: plan.width, height: plan.height }, deviceScaleFactor: scale, isMobile: phone, hasTouch: phone });
14
20
  await context.route((url) => isOwnAddress(url, plan.ownPort), (route) => route.abort('blockedbyclient'));
15
- await context.addInitScript(cursorScript);
21
+ const { source } = plan;
22
+ if (source.kind === 'page') {
23
+ await context.route(`${pageOrigin}/**`, async (route) => {
24
+ const answer = answerPageRequest(path.dirname(source.file), route.request().url());
25
+ await (answer.status === 404 ? route.fulfill({ status: 404, body: '' }) : route.fulfill({ status: 200, path: answer.file, contentType: answer.contentType }));
26
+ });
27
+ }
28
+ await context.addInitScript(cursorScript(plan.device));
16
29
  return context;
17
30
  }
18
- async function open(page, url) {
31
+ async function open(page, source) {
32
+ const address = source.kind === 'site' ? source.url : pageAddress(source.file);
19
33
  try {
20
- await page.goto(url, { waitUntil: 'load', timeout: 30_000 });
34
+ await page.goto(address, { waitUntil: 'load', timeout: 30_000 });
35
+ await waitUntilReady(page);
21
36
  }
22
37
  catch (error) {
23
- throw kvvideoProblem('STEP_FAILED', `The page ${url} didn't load: ${firstLine(error)}`, { step: 0, reason: firstLine(error) });
38
+ throw kvvideoProblem('STEP_FAILED', `The page ${address} didn't load: ${firstLine(error)}`, { step: 0, reason: firstLine(error) });
24
39
  }
25
40
  }
41
+ async function waitForCue(page, plan, step, elapsed) {
42
+ if (step.do === 'wait' || step.atLine === undefined || plan.cues === undefined)
43
+ return;
44
+ const wait = waitUntilMs(plan.cues.lineStartsMs[step.atLine - 1] ?? 0, elapsed());
45
+ if (wait > 0)
46
+ await page.waitForTimeout(wait);
47
+ }
26
48
  async function play(page, plan, startedAt) {
49
+ const elapsed = () => Math.round(plan.now() - startedAt);
27
50
  await page.waitForTimeout(500);
28
- let pointer = { x: plan.width / 2, y: plan.height / 2 };
29
- await page.mouse.move(pointer.x, pointer.y);
30
- const done = [];
51
+ const state = { page, device: plan.device, pointer: { x: plan.width / 2, y: plan.height / 2 }, elapsed };
52
+ const actions = [];
31
53
  for (const [index, step] of plan.steps.entries()) {
32
- done.push({ do: step.do, atMs: Math.round(plan.now() - startedAt) });
54
+ plan.stepStarted(index + 1, plan.steps.length);
55
+ await waitForCue(page, plan, step, elapsed);
56
+ const atMs = elapsed();
33
57
  try {
34
- pointer = await runStep(page, step, pointer);
58
+ const result = await runStep(state, step);
59
+ state.pointer = result.pointer;
60
+ actions.push({ do: step.do, atMs, endMs: elapsed(), ...(result.act === undefined ? {} : { actMs: result.act.atMs, box: result.act.box }) });
35
61
  }
36
62
  catch (error) {
37
63
  throw kvvideoProblem('STEP_FAILED', `Step ${String(index + 1)} (${step.do}) failed: ${firstLine(error)}`, { step: index + 1, reason: firstLine(error) });
@@ -40,26 +66,30 @@ async function play(page, plan, startedAt) {
40
66
  await page.waitForTimeout(400);
41
67
  }
42
68
  await page.waitForTimeout(1000);
43
- return done;
69
+ const rest = plan.cues === undefined ? 0 : waitUntilMs(plan.cues.leastLengthMs, elapsed());
70
+ if (rest > 0)
71
+ await page.waitForTimeout(rest);
72
+ return actions;
44
73
  }
45
74
  export async function record(browser, plan) {
46
- const context = await contextFor(browser, plan);
75
+ const context = await recordingContext(browser, plan);
47
76
  const page = await context.newPage();
48
- await open(page, plan.url);
77
+ await open(page, plan.source);
78
+ const size = { width: plan.width * scale, height: plan.height * scale };
49
79
  const video = path.join(plan.folder, 'recording.mp4');
50
80
  const stopEncoder = new AbortController();
51
- const encoder = await startPiped(plan.ffmpeg, recordEncoderArgs(plan.width, plan.height, video), AbortSignal.any([plan.signal, stopEncoder.signal]));
81
+ const encoder = await startPiped(plan.ffmpeg, recordEncoderArgs(size.width, size.height, video), AbortSignal.any([plan.signal, stopEncoder.signal]));
52
82
  try {
53
83
  const startedAt = plan.now();
54
84
  const writer = frameWriter((frame) => encoder.write(frame), startedAt);
55
- const screencast = await startScreencast(page, plan, (frame, atMs) => writer.frame(frame, atMs), plan.now);
56
- const steps = await play(page, plan, startedAt).finally(() => screencast.stop());
85
+ const screencast = await startScreencast(page, size, (frame, atMs) => writer.frame(frame, atMs), plan.now);
86
+ const actions = await play(page, plan, startedAt).finally(() => screencast.stop());
57
87
  const endedAt = plan.now();
58
88
  await writer.finish(endedAt);
59
89
  const run = await encoder.end();
60
90
  if (run.exitCode !== 0)
61
91
  throw kvvideoProblem('RENDER_FAILED', `ffmpeg couldn't encode the recording (exit ${String(run.exitCode)}).`, { reason: lastLines(run.output) });
62
- return { video, durationMs: Math.round(endedAt - startedAt), steps };
92
+ return { video, durationMs: Math.round(endedAt - startedAt), actions };
63
93
  }
64
94
  finally {
65
95
  stopEncoder.abort();
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/record/recorder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/record/recorder.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"register-record.d.ts","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAetC,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CA+B7C"}
1
+ {"version":3,"file":"register-record.d.ts","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAuCtC,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAwC7C"}
@@ -3,17 +3,44 @@ import { launchChannel } from "../browser/launch.js";
3
3
  import { startBrowser } from "../browser/start-browser.js";
4
4
  import { answerPath } from "../files/answer-path.js";
5
5
  import { placeOutput, workFolder } from "../files/place-output.js";
6
- import { outputPath } from "../files/workspace-paths.js";
6
+ import { readSidecar, sidecarOutputPath, writeSidecar } from "../files/sidecar.js";
7
+ import { inputPath, outputPath } from "../files/workspace-paths.js";
7
8
  import { programOf } from "../programs/programs.js";
8
- import { localAddress } from "./address.js";
9
+ import { invalid } from "../problems.js";
10
+ import { audioExtensions } from "../compose/scene-plan.js";
11
+ import { recordableAddress } from "./address.js";
12
+ import { cuesOf } from "./narration-cues.js";
9
13
  import { ownPortOf } from "./own-port.js";
10
14
  import { record } from "./recorder.js";
11
15
  import { recordInputSchema, recordOutputSchema } from "./steps.js";
12
- // `video record` (plan 16 §16.5): every address and the output are checked before a browser starts; the browser is
13
- // closed when the call ends, fails, or is cancelled, and the work folder removed.
16
+ // `video record` (plan 16 §16.5): the addresses, the page, the narration, and the outputs are checked before a browser
17
+ // starts; the browser is closed when the call ends, fails, or is cancelled, and the work folder removed. The
18
+ // recording's sidecar is placed beside it (ADR 0115, 31).
19
+ const defaultSize = { desktop: { width: 1280, height: 720 }, phone: { width: 390, height: 844 } };
20
+ function sourceOf(root, input, ownPort) {
21
+ for (const step of input.steps ?? [])
22
+ if (step.do === 'goto')
23
+ recordableAddress(step.url, ownPort);
24
+ if (input.page !== undefined)
25
+ return { kind: 'page', file: inputPath(root, input.page, ['.html', '.htm']) };
26
+ return { kind: 'site', url: recordableAddress(input.url ?? '', ownPort).href };
27
+ }
28
+ function cuesFor(root, input) {
29
+ if (input.narration === undefined)
30
+ return undefined;
31
+ const narration = readSidecar(inputPath(root, input.narration, audioExtensions));
32
+ if (narration?.kind !== 'narration')
33
+ throw invalid(`${input.narration} has no captions beside it; make it with narrate or voice.`, { path: input.narration });
34
+ const lines = narration.captions.length;
35
+ for (const step of input.steps ?? []) {
36
+ if (step.do !== 'wait' && step.atLine !== undefined && step.atLine > lines)
37
+ throw invalid(`atLine ${String(step.atLine)} is past the narration's ${String(lines)} caption lines.`, { atLine: step.atLine, lines });
38
+ }
39
+ return cuesOf(narration);
40
+ }
14
41
  export function registerRecord(ctx) {
15
42
  ctx.registerCommand('kvvideo.clip.record', {
16
- description: 'Records a page of an app running on this computer, with its steps, as an MP4 file in the workspace.',
43
+ description: 'Records a website or an HTML page of the workspace, with its steps, as an MP4 file in the workspace.',
17
44
  input: recordInputSchema,
18
45
  output: recordOutputSchema,
19
46
  public: true,
@@ -22,21 +49,28 @@ export function registerRecord(ctx) {
22
49
  handle: async (input) => {
23
50
  const root = ctx.job.workspace.path;
24
51
  const ownPort = ownPortOf(process.env['KVMAN_HOME']);
25
- const url = localAddress(input.url, ownPort).href;
26
- for (const step of input.steps ?? [])
27
- if (step.do === 'goto')
28
- localAddress(step.url, ownPort);
29
- const output = outputPath(root, input.file, '.mp4', input.replace === true);
52
+ const source = sourceOf(root, input, ownPort);
53
+ const cues = cuesFor(root, input);
54
+ const replace = input.replace === true;
55
+ const output = outputPath(root, input.file, '.mp4', replace);
56
+ const sidecar = sidecarOutputPath(output, replace, input.file);
57
+ const device = input.device ?? 'desktop';
58
+ const width = input.width ?? defaultSize[device].width;
59
+ const height = input.height ?? defaultSize[device].height;
30
60
  const ffmpeg = await programOf(ctx, 'ffmpeg');
31
61
  const browser = await startBrowser(await ctx.settings.get('kvvideo.browser'), launchChannel);
32
62
  const work = workFolder();
33
63
  const stop = () => void browser.close();
34
64
  ctx.job.signal.addEventListener('abort', stop, { once: true });
35
65
  try {
36
- const plan = { url, steps: input.steps ?? [], width: input.width ?? 1280, height: input.height ?? 720, folder: work.folder, ownPort, ffmpeg, signal: ctx.job.signal, now: () => performance.now() };
37
- const recording = await record(browser, plan);
66
+ const stepStarted = (n, of) => ctx.job.progress({ key: 'kvvideo.progress.step', params: { n, of } });
67
+ const recording = await record(browser, { source, steps: input.steps ?? [], width, height, device, cues, folder: work.folder, ownPort, ffmpeg, signal: ctx.job.signal, now: () => performance.now(), stepStarted });
68
+ const steps = input.steps ?? [];
69
+ const sourceName = source.kind === 'site' ? source.url : answerPath(root, source.file);
70
+ const made = writeSidecar(work.folder, { kind: source.kind, source: sourceName, width, height, device, scale: 2, durationMs: recording.durationMs, steps: [...steps], actions: recording.actions });
38
71
  placeOutput(recording.video, output);
39
- return { file: answerPath(root, output), durationMs: recording.durationMs, steps: recording.steps };
72
+ placeOutput(made, sidecar);
73
+ return { file: answerPath(root, output), durationMs: recording.durationMs, steps: recording.actions };
40
74
  }
41
75
  finally {
42
76
  ctx.job.signal.removeEventListener('abort', stop);
@@ -1 +1 @@
1
- {"version":3,"file":"register-record.js","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"register-record.js","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAa,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAqB,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAoB,MAAM,YAAY,CAAC;AAErF,uHAAuH;AACvH,6GAA6G;AAC7G,0DAA0D;AAE1D,MAAM,WAAW,GAAG,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;AAElG,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAkB,EAAE,OAAsB;IACxE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE;QAAE,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM;YAAE,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnG,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IAC5G,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AACjF,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,KAAkB;IAC/C,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;IACjF,IAAI,SAAS,EAAE,IAAI,KAAK,WAAW;QAAE,MAAM,OAAO,CAAC,GAAG,KAAK,CAAC,SAAS,4DAA4D,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9J,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK;YAAE,MAAM,OAAO,CAAC,UAAU,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,4BAA4B,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACrN,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAQ;IACrC,GAAG,CAAC,eAAe,CAAC,qBAAqB,EAAE;QACzC,WAAW,EAAE,sGAAsG;QACnH,KAAK,EAAE,iBAAiB;QACxB,MAAM,EAAE,kBAAkB;QAC1B,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,OAAO;QAClB,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;YACpC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC;YACvC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC;YACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC;YACvD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;YAC1D,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,aAAa,CAAC,CAAC;YAC7F,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,GAAS,EAAE,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC;YAC9C,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,CAAC,CAAS,EAAE,EAAU,EAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC3H,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpN,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvF,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;gBACpM,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACrC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBAC3B,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;YACxG,CAAC;oBAAS,CAAC;gBACT,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAClD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -1,7 +1,20 @@
1
1
  import type { Page } from 'playwright-core';
2
- import { type Pointer } from './pointer.ts';
3
- import type { Step } from './steps.ts';
4
- export declare function runStep(page: Page, step: Step, pointer: Pointer): Promise<Pointer>;
2
+ import { type Point } from './motion.ts';
3
+ import type { Box, Step } from './steps.ts';
4
+ export type StepState = {
5
+ page: Page;
6
+ device: 'desktop' | 'phone';
7
+ pointer: Point;
8
+ elapsed(): number;
9
+ };
10
+ export type StepResult = {
11
+ pointer: Point;
12
+ act?: {
13
+ atMs: number;
14
+ box: Box;
15
+ };
16
+ };
17
+ export declare function runStep(state: StepState, step: Step): Promise<StepResult>;
5
18
  /** Whether the page is held a moment after the step, so the viewer sees what it did. */
6
19
  export declare function pausesAfter(step: Step): boolean;
7
20
  //# sourceMappingURL=run-step.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-step.d.ts","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAqB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAgBvC,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BxF;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE/C"}
1
+ {"version":3,"file":"run-step.d.ts","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAqC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAK5C,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,IAAI,MAAM,CAAA;CAAE,CAAC;AAEvG,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,GAAG,CAAA;KAAE,CAAA;CAAE,CAAC;AA+B9E,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAgC/E;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE/C"}
@@ -1,45 +1,65 @@
1
- import { centerOf, glideTo } from "./pointer.js";
2
- // What each step does on the page (plan 16 §16.5). It answers where the mouse is afterwards.
3
- const scrollStep = 100;
1
+ import { frameMs, scrollPlan, typingDelays } from "./motion.js";
2
+ import { centerOf, glideTo, targetBox } from "./pointer.js";
3
+ async function typeText(page, text) {
4
+ const delays = typingDelays(text);
5
+ for (const [index, character] of [...text].entries()) {
6
+ await page.waitForTimeout(delays[index] ?? 0);
7
+ await page.keyboard.type(character);
8
+ }
9
+ }
4
10
  async function scroll(page, pixels) {
5
- let left = pixels;
6
- while (left !== 0) {
7
- const part = Math.sign(left) * Math.min(scrollStep, Math.abs(left));
8
- await page.mouse.wheel(0, part);
9
- await page.waitForTimeout(30);
10
- left -= part;
11
+ for (const move of scrollPlan(pixels).moves) {
12
+ if (move !== 0)
13
+ await page.mouse.wheel(0, move);
14
+ await page.waitForTimeout(frameMs);
11
15
  }
12
16
  }
13
- export async function runStep(page, step, pointer) {
17
+ // Brings the pointer or the finger to the target, and answers where it is and when it arrived.
18
+ async function reach(state, target) {
19
+ const box = await targetBox(state.page, target);
20
+ const center = centerOf(box);
21
+ if (state.device === 'phone')
22
+ return { pointer: center, act: { atMs: state.elapsed(), box } };
23
+ const pointer = await glideTo(state.page, state.pointer, center);
24
+ return { pointer, act: { atMs: state.elapsed(), box } };
25
+ }
26
+ async function press(state, at) {
27
+ if (state.device === 'phone')
28
+ await state.page.touchscreen.tap(at.x, at.y);
29
+ else
30
+ await state.page.mouse.click(at.x, at.y);
31
+ }
32
+ export async function runStep(state, step) {
33
+ const { page } = state;
14
34
  switch (step.do) {
15
35
  case 'goto':
16
36
  await page.goto(step.url, { waitUntil: 'load', timeout: 30_000 });
17
- return pointer;
37
+ return { pointer: state.pointer };
18
38
  case 'click': {
19
- const at = await glideTo(page, pointer, await centerOf(page, step.target));
20
- await page.mouse.click(at.x, at.y);
21
- return at;
39
+ const reached = await reach(state, step.target);
40
+ await press(state, reached.pointer);
41
+ return reached;
22
42
  }
23
43
  case 'type': {
24
- const at = await glideTo(page, pointer, await centerOf(page, step.target));
25
- await page.mouse.click(at.x, at.y);
26
- await page.keyboard.type(step.text, { delay: 50 });
27
- return at;
44
+ const reached = await reach(state, step.target);
45
+ await press(state, reached.pointer);
46
+ await typeText(page, step.text);
47
+ return reached;
28
48
  }
29
49
  case 'press':
30
50
  await page.keyboard.press(step.key);
31
- return pointer;
51
+ return { pointer: state.pointer };
32
52
  case 'hover':
33
- return glideTo(page, pointer, await centerOf(page, step.target));
53
+ return reach(state, step.target);
34
54
  case 'scroll':
35
55
  await scroll(page, step.pixels);
36
- return pointer;
56
+ return { pointer: state.pointer };
37
57
  case 'wait':
38
58
  await page.waitForTimeout(step.ms);
39
- return pointer;
59
+ return { pointer: state.pointer };
40
60
  case 'waitFor':
41
61
  await page.locator(step.target).first().waitFor({ state: 'visible', timeout: 10_000 });
42
- return pointer;
62
+ return { pointer: state.pointer };
43
63
  }
44
64
  }
45
65
  /** Whether the page is held a moment after the step, so the viewer sees what it did. */
@@ -1 +1 @@
1
- {"version":3,"file":"run-step.js","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-step.js","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ export async function startScreencast(page, size, onFrame, now) {
23
23
  }
24
24
  });
25
25
  });
26
- await session.send('Page.startScreencast', { format: 'jpeg', quality: 90, maxWidth: size.width, maxHeight: size.height, everyNthFrame: 1 });
26
+ await session.send('Page.startScreencast', { format: 'jpeg', quality: 95, maxWidth: size.width, maxHeight: size.height, everyNthFrame: 1 });
27
27
  return {
28
28
  stop: async () => {
29
29
  stopped = true;