@kvman/kvvideo 0.1.0 → 0.1.2

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 (258) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.ts +31 -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 +49 -0
  41. package/dist/compose/compose-size.d.ts.map +1 -0
  42. package/dist/compose/compose-size.js +67 -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 +104 -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/transitions.d.ts +16 -0
  92. package/dist/compose/transitions.d.ts.map +1 -0
  93. package/dist/compose/transitions.js +38 -0
  94. package/dist/compose/transitions.js.map +1 -0
  95. package/dist/connector.d.ts +79 -14
  96. package/dist/connector.d.ts.map +1 -1
  97. package/dist/connector.js +33 -8
  98. package/dist/connector.js.map +1 -1
  99. package/dist/files/sidecar.d.ts +209 -0
  100. package/dist/files/sidecar.d.ts.map +1 -0
  101. package/dist/files/sidecar.js +66 -0
  102. package/dist/files/sidecar.js.map +1 -0
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +9 -1
  105. package/dist/index.js.map +1 -1
  106. package/dist/look/look-at.d.ts +15 -0
  107. package/dist/look/look-at.d.ts.map +1 -0
  108. package/dist/look/look-at.js +20 -0
  109. package/dist/look/look-at.js.map +1 -0
  110. package/dist/look/look-input.d.ts +22 -0
  111. package/dist/look/look-input.d.ts.map +1 -0
  112. package/dist/look/look-input.js +25 -0
  113. package/dist/look/look-input.js.map +1 -0
  114. package/dist/look/register-look.d.ts +3 -0
  115. package/dist/look/register-look.d.ts.map +1 -0
  116. package/dist/look/register-look.js +52 -0
  117. package/dist/look/register-look.js.map +1 -0
  118. package/dist/narration/alignment.d.ts +15 -0
  119. package/dist/narration/alignment.d.ts.map +1 -0
  120. package/dist/narration/alignment.js +21 -0
  121. package/dist/narration/alignment.js.map +1 -0
  122. package/dist/narration/captions.d.ts +22 -4
  123. package/dist/narration/captions.d.ts.map +1 -1
  124. package/dist/narration/captions.js +44 -16
  125. package/dist/narration/captions.js.map +1 -1
  126. package/dist/narration/join-args.d.ts +3 -0
  127. package/dist/narration/join-args.d.ts.map +1 -0
  128. package/dist/narration/join-args.js +19 -0
  129. package/dist/narration/join-args.js.map +1 -0
  130. package/dist/narration/narration-cache.d.ts +25 -0
  131. package/dist/narration/narration-cache.d.ts.map +1 -0
  132. package/dist/narration/narration-cache.js +36 -0
  133. package/dist/narration/narration-cache.js.map +1 -0
  134. package/dist/narration/piper.d.ts +2 -6
  135. package/dist/narration/piper.d.ts.map +1 -1
  136. package/dist/narration/piper.js +9 -15
  137. package/dist/narration/piper.js.map +1 -1
  138. package/dist/narration/pronunciations.d.ts +11 -0
  139. package/dist/narration/pronunciations.d.ts.map +1 -0
  140. package/dist/narration/pronunciations.js +40 -0
  141. package/dist/narration/pronunciations.js.map +1 -0
  142. package/dist/narration/register-narration.d.ts +8 -0
  143. package/dist/narration/register-narration.d.ts.map +1 -1
  144. package/dist/narration/register-narration.js +54 -26
  145. package/dist/narration/register-narration.js.map +1 -1
  146. package/dist/narration/register-voice.d.ts +27 -0
  147. package/dist/narration/register-voice.d.ts.map +1 -0
  148. package/dist/narration/register-voice.js +82 -0
  149. package/dist/narration/register-voice.js.map +1 -0
  150. package/dist/narration/sentence-speaker.d.ts +35 -0
  151. package/dist/narration/sentence-speaker.d.ts.map +1 -0
  152. package/dist/narration/sentence-speaker.js +44 -0
  153. package/dist/narration/sentence-speaker.js.map +1 -0
  154. package/dist/narration/speech-services.d.ts +23 -3
  155. package/dist/narration/speech-services.d.ts.map +1 -1
  156. package/dist/narration/speech-services.js +27 -10
  157. package/dist/narration/speech-services.js.map +1 -1
  158. package/dist/narration/voice-args.d.ts +3 -0
  159. package/dist/narration/voice-args.d.ts.map +1 -0
  160. package/dist/narration/voice-args.js +10 -0
  161. package/dist/narration/voice-args.js.map +1 -0
  162. package/dist/programs/media-duration.d.ts +12 -0
  163. package/dist/programs/media-duration.d.ts.map +1 -1
  164. package/dist/programs/media-duration.js +13 -0
  165. package/dist/programs/media-duration.js.map +1 -1
  166. package/dist/programs/run-program.d.ts +1 -0
  167. package/dist/programs/run-program.d.ts.map +1 -1
  168. package/dist/programs/run-program.js +1 -0
  169. package/dist/programs/run-program.js.map +1 -1
  170. package/dist/record/address.d.ts +1 -1
  171. package/dist/record/address.d.ts.map +1 -1
  172. package/dist/record/address.js +5 -7
  173. package/dist/record/address.js.map +1 -1
  174. package/dist/record/cursor-script.d.ts +2 -1
  175. package/dist/record/cursor-script.d.ts.map +1 -1
  176. package/dist/record/cursor-script.js +52 -14
  177. package/dist/record/cursor-script.js.map +1 -1
  178. package/dist/record/encoder-args.js +1 -1
  179. package/dist/record/motion.d.ts +19 -0
  180. package/dist/record/motion.d.ts.map +1 -0
  181. package/dist/record/motion.js +59 -0
  182. package/dist/record/motion.js.map +1 -0
  183. package/dist/record/narration-cues.d.ts +14 -0
  184. package/dist/record/narration-cues.d.ts.map +1 -0
  185. package/dist/record/narration-cues.js +12 -0
  186. package/dist/record/narration-cues.js.map +1 -0
  187. package/dist/record/page-clock.d.ts +7 -0
  188. package/dist/record/page-clock.d.ts.map +1 -0
  189. package/dist/record/page-clock.js +13 -0
  190. package/dist/record/page-clock.js.map +1 -0
  191. package/dist/record/page-ready.d.ts +4 -0
  192. package/dist/record/page-ready.d.ts.map +1 -0
  193. package/dist/record/page-ready.js +15 -0
  194. package/dist/record/page-ready.js.map +1 -0
  195. package/dist/record/page-route.d.ts +13 -0
  196. package/dist/record/page-route.d.ts.map +1 -0
  197. package/dist/record/page-route.js +64 -0
  198. package/dist/record/page-route.js.map +1 -0
  199. package/dist/record/pointer.d.ts +6 -7
  200. package/dist/record/pointer.d.ts.map +1 -1
  201. package/dist/record/pointer.js +11 -10
  202. package/dist/record/pointer.js.map +1 -1
  203. package/dist/record/record-source.d.ts +16 -0
  204. package/dist/record/record-source.d.ts.map +1 -0
  205. package/dist/record/record-source.js +11 -0
  206. package/dist/record/record-source.js.map +1 -0
  207. package/dist/record/recorder.d.ts +19 -7
  208. package/dist/record/recorder.d.ts.map +1 -1
  209. package/dist/record/recorder.js +52 -18
  210. package/dist/record/recorder.js.map +1 -1
  211. package/dist/record/register-record.d.ts.map +1 -1
  212. package/dist/record/register-record.js +45 -13
  213. package/dist/record/register-record.js.map +1 -1
  214. package/dist/record/run-step.d.ts +16 -3
  215. package/dist/record/run-step.d.ts.map +1 -1
  216. package/dist/record/run-step.js +43 -23
  217. package/dist/record/run-step.js.map +1 -1
  218. package/dist/record/screencast.js +1 -1
  219. package/dist/record/steps.d.ts +56 -1
  220. package/dist/record/steps.d.ts.map +1 -1
  221. package/dist/record/steps.js +36 -18
  222. package/dist/record/steps.js.map +1 -1
  223. package/dist/review/analysis.d.ts +19 -0
  224. package/dist/review/analysis.d.ts.map +1 -0
  225. package/dist/review/analysis.js +36 -0
  226. package/dist/review/analysis.js.map +1 -0
  227. package/dist/review/frames.d.ts +9 -0
  228. package/dist/review/frames.d.ts.map +1 -0
  229. package/dist/review/frames.js +28 -0
  230. package/dist/review/frames.js.map +1 -0
  231. package/dist/review/register-review.d.ts +24 -0
  232. package/dist/review/register-review.d.ts.map +1 -0
  233. package/dist/review/register-review.js +81 -0
  234. package/dist/review/register-review.js.map +1 -0
  235. package/dist/review/sheet-args.d.ts +11 -0
  236. package/dist/review/sheet-args.d.ts.map +1 -0
  237. package/dist/review/sheet-args.js +24 -0
  238. package/dist/review/sheet-args.js.map +1 -0
  239. package/dist/settings.d.ts +4 -0
  240. package/dist/settings.d.ts.map +1 -1
  241. package/dist/settings.js +4 -2
  242. package/dist/settings.js.map +1 -1
  243. package/dist/setup/ffmpeg-readiness.js +1 -1
  244. package/dist/setup/ffmpeg-readiness.js.map +1 -1
  245. package/dist/ui.d.ts.map +1 -1
  246. package/dist/ui.js +3 -2
  247. package/dist/ui.js.map +1 -1
  248. package/dist/web/components/player.css +1 -1
  249. package/dist/web/components/player.js +33 -21
  250. package/docs/guide.md +39 -10
  251. package/locales/ar.json +12 -1
  252. package/locales/en.json +12 -1
  253. package/package.json +11 -5
  254. package/sounds/SOURCES.md +13 -0
  255. package/sounds/click.wav +0 -0
  256. package/sounds/keys.wav +0 -0
  257. package/sounds/pop.wav +0 -0
  258. package/sounds/whoosh.wav +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @kvman/kvvideo
2
2
 
3
- The [kvman](https://github.com/ibndeif/kvman) extension that makes videos of the person's app from a chat: it records pages in the installed Chrome or Edge, narrates a script with ElevenLabs, OpenAI, or Piper, and joins them with burned-in captions into an MP4 with ffmpeg. It is bundled with kvman, and needs `@kvman/kvcoder` and `@kvman/kvwebui`. Type `/video` in a chat to turn it on.
3
+ The [kvman](https://github.com/ibndeif/kvman) extension that makes any kind of video from a chat (a lesson, an explainer, a tutorial, a promotion, a story, a social clip): it records websites and HTML pages in the installed Chrome or Edge, narrates a script with ElevenLabs, OpenAI, or Piper, and joins them with burned-in captions into an MP4 with ffmpeg. It is bundled with kvman, and needs `@kvman/kvcoder` and `@kvman/kvwebui`. Type `/video` in a chat to turn it on.
4
4
 
5
5
  ## Fonts
6
6
 
package/dist/api.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import type { z } from '@kvman/sdk';
2
2
  import type { composeInputSchema, composeOutputSchema } from './compose/compose-input.ts';
3
+ import type { lookInputSchema, lookOutputSchema } from './look/look-input.ts';
3
4
  import type { narrateInputSchema, narrateOutputSchema } from './narration/register-narration.ts';
4
5
  import type { recordInputSchema, recordOutputSchema } from './record/steps.ts';
6
+ import type { reviewOutputSchema } from './review/register-review.ts';
7
+ import type { voiceInputSchema, voiceOutputSchema } from './narration/register-voice.ts';
8
+ import type { Brand } from './brand/brand.ts';
5
9
  import type { BrowserChoice, NarrationService } from './settings.ts';
6
10
  type Empty = Record<string, never>;
7
11
  type Channel = 'chrome' | 'msedge';
@@ -26,6 +30,24 @@ declare module '@kvman/sdk' {
26
30
  input: z.input<typeof recordInputSchema>;
27
31
  output: z.output<typeof recordOutputSchema>;
28
32
  };
33
+ 'kvvideo.frame.capture': {
34
+ input: z.input<typeof lookInputSchema>;
35
+ output: z.output<typeof lookOutputSchema>;
36
+ };
37
+ 'kvvideo.brand.set': {
38
+ input: Brand;
39
+ output: Empty;
40
+ };
41
+ 'kvvideo.voice.make': {
42
+ input: z.input<typeof voiceInputSchema>;
43
+ output: z.output<typeof voiceOutputSchema>;
44
+ };
45
+ 'kvvideo.video.review': {
46
+ input: {
47
+ file: string;
48
+ };
49
+ output: z.output<typeof reviewOutputSchema>;
50
+ };
29
51
  }
30
52
  interface Queries {
31
53
  'kvvideo.setup.get': {
@@ -64,11 +86,20 @@ declare module '@kvman/sdk' {
64
86
  scripts: ('latin' | 'arabic')[];
65
87
  }[];
66
88
  };
89
+ 'kvvideo.brand.get': {
90
+ input: Empty;
91
+ output: Brand | null;
92
+ };
67
93
  }
68
94
  interface Settings {
69
95
  'kvvideo.narration.service': NarrationService | null;
70
96
  'kvvideo.narration.voice': string | null;
71
97
  'kvvideo.narration.model': string | null;
98
+ 'kvvideo.narration.pronunciations': {
99
+ word: string;
100
+ say: string;
101
+ }[];
102
+ 'kvvideo.brand': Brand | null;
72
103
  'kvvideo.browser': BrowserChoice;
73
104
  'kvvideo.programs.ffmpeg': string;
74
105
  'kvvideo.programs.ffprobe': string;
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { z, type Ctx } from '@kvman/sdk';
2
+ export declare const brandSchema: z.ZodObject<{
3
+ logo: z.ZodNullable<z.ZodString>;
4
+ colors: z.ZodObject<{
5
+ primary: z.ZodString;
6
+ background: z.ZodString;
7
+ text: z.ZodString;
8
+ }, z.core.$strict>;
9
+ font: z.ZodNullable<z.ZodString>;
10
+ }, z.core.$strict>;
11
+ /** A workspace's brand. */
12
+ export type Brand = z.output<typeof brandSchema>;
13
+ export declare const logoExtensions: string[];
14
+ /** Checks a brand's files and font in the workspace `root`. */
15
+ export declare function checkBrand(root: string, brand: Brand): void;
16
+ export declare function registerBrand(ctx: Ctx): void;
17
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../src/brand/brand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AASzC,eAAO,MAAM,WAAW;;;;;;;;kBAItB,CAAC;AAEH,2BAA2B;AAC3B,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAEjD,eAAO,MAAM,cAAc,UAA6C,CAAC;AAEzE,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAG3D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAuB5C"}
@@ -0,0 +1,44 @@
1
+ import { z } from '@kvman/sdk';
2
+ import { captionFont } from "../compose/caption-font.js";
3
+ import { inputPath } from "../files/workspace-paths.js";
4
+ // The workspace's brand (plan 16 §16.2 and §16.4; ADR 0115, 21 and 43): its logo, colours, and font, read with
5
+ // `video brand` and set with `video brand-set` without asking, since the person agreed to it in the script.
6
+ const colour = z.string().regex(/^#[0-9a-fA-F]{6}$/, 'A colour is #RRGGBB.');
7
+ export const brandSchema = z.strictObject({
8
+ logo: z.string().min(1).max(1000).nullable().describe('A .png, .svg, .jpg, or .webp file in the workspace, or null.'),
9
+ colors: z.strictObject({ primary: colour, background: colour, text: colour }),
10
+ font: z.string().min(1).max(1000).nullable().describe('A bundled font (video fonts), a .ttf or .otf file in the workspace, or null.'),
11
+ });
12
+ export const logoExtensions = ['.png', '.svg', '.jpg', '.jpeg', '.webp'];
13
+ /** Checks a brand's files and font in the workspace `root`. */
14
+ export function checkBrand(root, brand) {
15
+ if (brand.logo !== null)
16
+ inputPath(root, brand.logo, logoExtensions);
17
+ if (brand.font !== null)
18
+ captionFont(root, brand.font, []);
19
+ }
20
+ export function registerBrand(ctx) {
21
+ ctx.registerSetting('kvvideo.brand', { description: "The workspace's brand for its videos: a logo file, the primary, background, and text colours, and a font; null for none.", schema: brandSchema.nullable(), default: null });
22
+ ctx.registerQuery('kvvideo.brand.get', {
23
+ description: "Gives the workspace's brand for videos: its logo, colours, and font, or null.",
24
+ input: z.strictObject({}),
25
+ output: brandSchema.nullable(),
26
+ public: true,
27
+ timeoutMs: 60_000,
28
+ handle: async () => ctx.settings.get('kvvideo.brand'),
29
+ });
30
+ ctx.registerCommand('kvvideo.brand.set', {
31
+ description: "Sets the workspace's brand for videos: its logo, colours, and font.",
32
+ input: brandSchema,
33
+ output: z.strictObject({}),
34
+ public: true,
35
+ retries: 0,
36
+ timeoutMs: 60_000,
37
+ handle: async (input) => {
38
+ checkBrand(ctx.job.workspace.path, input);
39
+ await ctx.settings.set('kvvideo.brand', input, { scope: 'workspace' });
40
+ return {};
41
+ },
42
+ });
43
+ }
44
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/brand/brand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAY,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD,+GAA+G;AAC/G,4GAA4G;AAE5G,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,YAAY,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;IACrH,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC7E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8EAA8E,CAAC;CACtI,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEzE,+DAA+D;AAC/D,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAY;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;QAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;QAAE,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAQ;IACpC,GAAG,CAAC,eAAe,CAAC,eAAe,EAAE,EAAE,WAAW,EAAE,0HAA0H,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACjO,GAAG,CAAC,aAAa,CAAC,mBAAmB,EAAE;QACrC,WAAW,EAAE,+EAA+E;QAC5F,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;QACzB,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE;QAC9B,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC;KACtD,CAAC,CAAC;IACH,GAAG,CAAC,eAAe,CAAC,mBAAmB,EAAE;QACvC,WAAW,EAAE,qEAAqE;QAClF,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1B,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC1C,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;YACvE,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import type { Browser } from 'playwright-core';
2
2
  import type { Channel } from './channels.ts';
3
3
  export declare function launchChannel(channel: Channel): Promise<Browser>;
4
+ /** The browser that draws a scene's frames and overlays, at one pixel per CSS pixel (ADR 0115, 36). */
5
+ export declare function launchDrawing(channel: Channel): Promise<Browser>;
4
6
  //# sourceMappingURL=launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/browser/launch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAM7C,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE"}
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/browser/launch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAO7C,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE;AAED,uGAAuG;AACvG,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE"}
@@ -1,7 +1,13 @@
1
- // The installed Chrome or Edge, driven headless through playwright-core (ADR 0107, 9): no browser is downloaded.
1
+ // The installed Chrome or Edge, driven headless through playwright-core (ADR 0107, 9): no browser is downloaded. It
2
+ // draws at 2× density, so the screencast's frames are twice the page's CSS size (ADR 0115, 8).
2
3
  // playwright-core is imported when a browser starts, not when kvvideo loads, since every worker loads kvvideo and
3
4
  // most never record.
4
5
  export async function launchChannel(channel) {
6
+ const { chromium } = await import('playwright-core');
7
+ return chromium.launch({ channel, headless: true, args: ['--force-device-scale-factor=2'] });
8
+ }
9
+ /** The browser that draws a scene's frames and overlays, at one pixel per CSS pixel (ADR 0115, 36). */
10
+ export async function launchDrawing(channel) {
5
11
  const { chromium } = await import('playwright-core');
6
12
  return chromium.launch({ channel, headless: true });
7
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/browser/launch.ts"],"names":[],"mappings":"AAGA,iHAAiH;AACjH,kHAAkH;AAClH,qBAAqB;AAErB,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB;IAClD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/browser/launch.ts"],"names":[],"mappings":"AAGA,oHAAoH;AACpH,+FAA+F;AAC/F,kHAAkH;AAClH,qBAAqB;AAErB,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB;IAClD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,+BAA+B,CAAC,EAAE,CAAC,CAAC;AAC/F,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB;IAClD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC"}
@@ -1,12 +1,17 @@
1
1
  import type { Caption } from '../narration/captions.ts';
2
+ export type CaptionLook = 'boxed' | 'outline' | 'highlight';
2
3
  export type CaptionStyle = {
3
4
  width: number;
4
5
  height: number;
5
6
  family: string;
6
7
  size: number | undefined;
7
8
  position: 'bottom' | 'top';
9
+ look: CaptionLook;
10
+ highlight: string;
8
11
  };
9
12
  /** A caption's text as ASS shows it: no override braces, no backslashes, one line. */
10
13
  export declare function assText(text: string): string;
14
+ /** `#RRGGBB` as an ASS colour, `&HAABBGGRR`, opaque. */
15
+ export declare function assColour(colour: string): string;
11
16
  export declare function assFile(style: CaptionStyle, captions: readonly Caption[]): string;
12
17
  //# sourceMappingURL=ass-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ass-file.d.ts","sourceRoot":"","sources":["../../src/compose/ass-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAMxD,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,CAAC;AAWnI,sFAAsF;AACtF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAsBjF"}
1
+ {"version":3,"file":"ass-file.d.ts","sourceRoot":"","sources":["../../src/compose/ass-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAQxD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAWzK,sFAAsF;AACtF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;AAiCD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAoBjF"}
@@ -10,11 +10,39 @@ function timeOf(ms) {
10
10
  export function assText(text) {
11
11
  return text.replace(/[{}\\]/g, '').replace(/\s+/g, ' ').trim();
12
12
  }
13
- export function assFile(style, captions) {
13
+ /** `#RRGGBB` as an ASS colour, `&HAABBGGRR`, opaque. */
14
+ export function assColour(colour) {
15
+ return `&H00${`${colour.slice(5, 7)}${colour.slice(3, 5)}${colour.slice(1, 3)}`.toUpperCase()}`;
16
+ }
17
+ const box = '&H59000000';
18
+ function styleLine(name, style, primary, secondary) {
14
19
  const size = style.size ?? Math.round(style.height / 18);
15
20
  const alignment = style.position === 'top' ? 8 : 2;
16
21
  const margin = Math.round(style.height * 0.06);
17
22
  const family = style.family.replace(/,/g, ' ');
23
+ const border = style.look === 'outline' ? `0,0,1,3,0` : `0,0,3,${String(Math.round(size / 3))},0`;
24
+ const outline = style.look === 'outline' ? '&H00000000' : box;
25
+ const back = style.look === 'outline' ? '&H00000000' : box;
26
+ return `Style: ${name},${family},${String(size)},${primary},${secondary},${outline},${back},0,0,0,0,100,100,${border},${String(alignment)},40,40,${String(margin)},-1`;
27
+ }
28
+ // A line's words, each held as ASS karaoke from the line's start: the words already spoken take the primary colour.
29
+ function karaoke(caption) {
30
+ const centiseconds = (ms) => Math.max(0, Math.round(ms / 10));
31
+ const first = caption.words[0]?.startMs ?? caption.startMs;
32
+ const parts = first > caption.startMs ? [`{\\k${String(centiseconds(first - caption.startMs))}}`] : [];
33
+ caption.words.forEach((word, index) => {
34
+ const next = caption.words[index + 1]?.startMs ?? caption.endMs;
35
+ parts.push(`{\\k${String(centiseconds(next - word.startMs))}}${assText(word.text)}${index === caption.words.length - 1 ? '' : ' '}`);
36
+ });
37
+ return parts.join('');
38
+ }
39
+ function dialogue(style, caption) {
40
+ const timed = style.look === 'highlight' && caption.words !== undefined && caption.words.length > 0;
41
+ const text = timed && caption.words !== undefined ? karaoke({ ...caption, words: caption.words }) : assText(caption.text);
42
+ return `Dialogue: 0,${timeOf(caption.startMs)},${timeOf(caption.endMs)},${timed ? 'Spoken' : 'Default'},,0,0,0,,${text}`;
43
+ }
44
+ export function assFile(style, captions) {
45
+ const white = '&H00FFFFFF';
18
46
  return [
19
47
  '[Script Info]',
20
48
  'ScriptType: v4.00+',
@@ -25,11 +53,12 @@ export function assFile(style, captions) {
25
53
  '',
26
54
  '[V4+ Styles]',
27
55
  'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding',
28
- `Style: Default,${family},${String(size)},&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,3,0,${String(alignment)},40,40,${String(margin)},-1`,
56
+ styleLine('Default', style, white, white),
57
+ ...(style.look === 'highlight' ? [styleLine('Spoken', style, assColour(style.highlight), white)] : []),
29
58
  '',
30
59
  '[Events]',
31
60
  'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text',
32
- ...captions.map((caption) => `Dialogue: 0,${timeOf(caption.startMs)},${timeOf(caption.endMs)},Default,,0,0,0,,${assText(caption.text)}`),
61
+ ...captions.map((caption) => dialogue(style, caption)),
33
62
  '',
34
63
  ].join('\n');
35
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ass-file.js","sourceRoot":"","sources":["../../src/compose/ass-file.ts"],"names":[],"mappings":"AAQA,SAAS,MAAM,CAAC,EAAU;IACxB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;IACpD,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC;AACvF,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAmB,EAAE,QAA4B;IACvE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO;QACL,eAAe;QACf,oBAAoB;QACpB,aAAa,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QAClC,aAAa,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;QACnC,cAAc;QACd,4BAA4B;QAC5B,EAAE;QACF,cAAc;QACd,+OAA+O;QAC/O,kBAAkB,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,0EAA0E,MAAM,CAAC,SAAS,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,KAAK;QAChK,EAAE;QACF,UAAU;QACV,iFAAiF;QACjF,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,eAAe,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxI,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"ass-file.js","sourceRoot":"","sources":["../../src/compose/ass-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { Brand } from '../brand/brand.ts';
2
+ import type { Size } from './frame-layout.ts';
3
+ export type Background = {
4
+ kind: 'image';
5
+ file: string;
6
+ } | {
7
+ kind: 'gradient';
8
+ from: string;
9
+ to: string;
10
+ };
11
+ /** `colour` mixed with `share` of `other`, as #rrggbb. */
12
+ export declare function mixed(colour: string, other: string, share: number): string;
13
+ /** The scene's background, from its `background` (a colour or an image file already checked) and the brand. */
14
+ export declare function backgroundOf(background: {
15
+ colour: string;
16
+ } | {
17
+ file: string;
18
+ } | undefined, brand: Brand | null): Background;
19
+ /** ffmpeg's input for the background, `lengthMs` long at 30 frames a second. */
20
+ export declare function backgroundInput(background: Background, size: Size, lengthMs: number): string[];
21
+ /** The filter that makes the background input the picture's size. */
22
+ export declare function backgroundFit(background: Background, size: Size): string;
23
+ //# sourceMappingURL=background.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background.d.ts","sourceRoot":"","sources":["../../src/compose/background.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAM9C,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAI1G,0DAA0D;AAC1D,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG1E;AAED,+GAA+G;AAC/G,wBAAgB,YAAY,CAAC,UAAU,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,UAAU,CAK3H;AAID,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAK9F;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAIxE"}
@@ -0,0 +1,31 @@
1
+ const channels = (colour) => [1, 3, 5].map((at) => Number.parseInt(colour.slice(at, at + 2), 16));
2
+ /** `colour` mixed with `share` of `other`, as #rrggbb. */
3
+ export function mixed(colour, other, share) {
4
+ const [base, added] = [channels(colour), channels(other)];
5
+ return `#${base.map((value, index) => Math.round(value * (1 - share) + (added[index] ?? 0) * share).toString(16).padStart(2, '0')).join('')}`;
6
+ }
7
+ /** The scene's background, from its `background` (a colour or an image file already checked) and the brand. */
8
+ export function backgroundOf(background, brand) {
9
+ if (background !== undefined && 'file' in background)
10
+ return { kind: 'image', file: background.file };
11
+ const colour = background?.colour ?? brand?.colors.background;
12
+ if (colour === undefined)
13
+ return { kind: 'gradient', from: '#0F172A', to: '#1E293B' };
14
+ return { kind: 'gradient', from: colour, to: brand === null ? colour : mixed(colour, brand.colors.primary, 0.18) };
15
+ }
16
+ const seconds = (ms) => (ms / 1000).toFixed(3);
17
+ /** ffmpeg's input for the background, `lengthMs` long at 30 frames a second. */
18
+ export function backgroundInput(background, size, lengthMs) {
19
+ const { width, height } = size;
20
+ if (background.kind === 'image')
21
+ return ['-loop', '1', '-framerate', '30', '-t', seconds(lengthMs), '-i', background.file];
22
+ const hex = (colour) => `0x${colour.slice(1)}`;
23
+ return ['-f', 'lavfi', '-t', seconds(lengthMs), '-i', `gradients=s=${String(width)}x${String(height)}:r=30:c0=${hex(background.from)}:c1=${hex(background.to)}:x0=0:y0=0:x1=${String(width)}:y1=${String(height)}:n=2:speed=0`];
24
+ }
25
+ /** The filter that makes the background input the picture's size. */
26
+ export function backgroundFit(background, size) {
27
+ const { width, height } = size;
28
+ const fit = background.kind === 'image' ? `scale=${String(width)}:${String(height)}:force_original_aspect_ratio=increase,crop=${String(width)}:${String(height)},` : '';
29
+ return `${fit}fps=30,format=yuv420p`;
30
+ }
31
+ //# sourceMappingURL=background.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"background.js","sourceRoot":"","sources":["../../src/compose/background.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Action } from '../record/steps.ts';
2
+ export type View = {
3
+ x: number;
4
+ y: number;
5
+ zoom: number;
6
+ };
7
+ /** One move of the view, from its start to its end. */
8
+ export type Shot = {
9
+ startMs: number;
10
+ endMs: number;
11
+ from: View;
12
+ to: View;
13
+ easing: 'cubic' | 'even';
14
+ };
15
+ export type CameraPlan = {
16
+ width: number;
17
+ height: number;
18
+ start: View;
19
+ shots: Shot[];
20
+ };
21
+ /** The camera of a recording of `width` × `height` CSS pixels, from its actions. */
22
+ export declare function cameraOfActions(actions: readonly Action[], width: number, height: number): CameraPlan;
23
+ /** An image's slow zoom over `lengthMs`; its size is the input's, read by ffmpeg. */
24
+ export declare function imageZoom(lengthMs: number): CameraPlan;
25
+ /** The view at `ms`. */
26
+ export declare function viewAt(plan: CameraPlan, ms: number): View;
27
+ /**
28
+ * The `perspective` filter that shows the plan's view of a frame `scale` times the plan's size, or of the input's own
29
+ * size for an image's zoom. `offsetMs` is where the part starts in the recording; the frames are at 30 a second.
30
+ */
31
+ export declare function perspectiveFilter(plan: CameraPlan, scale: number | 'input', offsetMs: number): string;
32
+ //# sourceMappingURL=camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../src/compose/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAO,MAAM,oBAAoB,CAAC;AAQtD,MAAM,MAAM,IAAI,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,uDAAuD;AACvD,MAAM,MAAM,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAEtG,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC;AAiBvF,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAuBrG;AAED,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAEtD;AAID,wBAAwB;AACxB,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAczD;AAmBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAarG"}
@@ -0,0 +1,98 @@
1
+ const maximumZoom = 1.6;
2
+ const panMs = 600;
3
+ const runGapMs = 2500;
4
+ const holdMs = 1500;
5
+ const outMs = 800;
6
+ const leastInMs = 400;
7
+ const whole = (width, height) => ({ x: width / 2, y: height / 2, zoom: 1 });
8
+ function stopView(box, width, height) {
9
+ const zoom = Math.max(1, Math.min(maximumZoom, (0.8 * width) / box.width, (0.8 * height) / box.height));
10
+ const keep = (center, side) => Math.min(Math.max(center, side / (2 * zoom)), side - side / (2 * zoom));
11
+ return { x: keep(box.x + box.width / 2, width), y: keep(box.y + box.height / 2, height), zoom };
12
+ }
13
+ /** The camera of a recording of `width` × `height` CSS pixels, from its actions. */
14
+ export function cameraOfActions(actions, width, height) {
15
+ const start = whole(width, height);
16
+ const shots = [];
17
+ let current = start;
18
+ let lastEndMs;
19
+ const easeOut = (endMs) => {
20
+ shots.push({ startMs: endMs + holdMs, endMs: endMs + holdMs + outMs, from: current, to: start, easing: 'cubic' });
21
+ current = start;
22
+ };
23
+ for (const action of actions) {
24
+ if ((action.do !== 'click' && action.do !== 'type') || action.box === undefined || action.actMs === undefined)
25
+ continue;
26
+ const target = stopView(action.box, width, height);
27
+ if (lastEndMs !== undefined && action.atMs - lastEndMs <= runGapMs) {
28
+ shots.push({ startMs: action.atMs, endMs: action.atMs + panMs, from: current, to: target, easing: 'cubic' });
29
+ }
30
+ else {
31
+ if (lastEndMs !== undefined)
32
+ easeOut(lastEndMs);
33
+ shots.push({ startMs: action.atMs, endMs: Math.max(action.actMs, action.atMs + leastInMs), from: current, to: target, easing: 'cubic' });
34
+ }
35
+ current = target;
36
+ lastEndMs = action.endMs;
37
+ }
38
+ if (lastEndMs !== undefined)
39
+ easeOut(lastEndMs);
40
+ return { width, height, start, shots };
41
+ }
42
+ /** An image's slow zoom over `lengthMs`; its size is the input's, read by ffmpeg. */
43
+ export function imageZoom(lengthMs) {
44
+ return { width: 1, height: 1, start: whole(1, 1), shots: [{ startMs: 0, endMs: lengthMs, from: whole(1, 1), to: { ...whole(1, 1), zoom: 1.08 }, easing: 'even' }] };
45
+ }
46
+ const cubic = (share) => (share < 0.5 ? 4 * share ** 3 : 1 - (-2 * share + 2) ** 3 / 2);
47
+ /** The view at `ms`. */
48
+ export function viewAt(plan, ms) {
49
+ let view = plan.start;
50
+ for (const shot of plan.shots) {
51
+ if (ms < shot.startMs)
52
+ break;
53
+ if (ms >= shot.endMs) {
54
+ view = shot.to;
55
+ continue;
56
+ }
57
+ const share = (ms - shot.startMs) / (shot.endMs - shot.startMs);
58
+ const eased = shot.easing === 'even' ? share : cubic(share);
59
+ const between = (key) => shot.from[key] + (shot.to[key] - shot.from[key]) * eased;
60
+ return { x: between('x'), y: between('y'), zoom: between('zoom') };
61
+ }
62
+ return view;
63
+ }
64
+ const number = (value) => String(Math.round(value * 10_000) / 10_000);
65
+ // One part of the view (x, y, or zoom) as an ffmpeg expression of the time `T` in ms.
66
+ function partExpression(plan, key, time) {
67
+ let expression = number(plan.shots.at(-1)?.to[key] ?? plan.start[key]);
68
+ for (let index = plan.shots.length - 1; index >= 0; index -= 1) {
69
+ const shot = plan.shots[index];
70
+ if (shot === undefined)
71
+ continue;
72
+ const before = number(plan.shots[index - 1]?.to[key] ?? plan.start[key]);
73
+ const share = `((${time}-${number(shot.startMs)})/${number(shot.endMs - shot.startMs)})`;
74
+ const eased = shot.easing === 'even' ? share : `if(lt(${share},0.5),4*pow(${share},3),1-pow(-2*${share}+2,3)/2)`;
75
+ const moving = `(${number(shot.from[key])}+${number(shot.to[key] - shot.from[key])}*${eased})`;
76
+ expression = `if(lt(${time},${number(shot.startMs)}),${before},if(lt(${time},${number(shot.endMs)}),${moving},${expression}))`;
77
+ }
78
+ return expression;
79
+ }
80
+ /**
81
+ * The `perspective` filter that shows the plan's view of a frame `scale` times the plan's size, or of the input's own
82
+ * size for an image's zoom. `offsetMs` is where the part starts in the recording; the frames are at 30 a second.
83
+ */
84
+ export function perspectiveFilter(plan, scale, offsetMs) {
85
+ const time = `(in*1000/30+${number(offsetMs)})`;
86
+ const zoom = partExpression(plan, 'zoom', time);
87
+ const [pageWidth, pageHeight] = scale === 'input' ? ['W', 'H'] : [number(plan.width * scale), number(plan.height * scale)];
88
+ const factor = scale === 'input' ? 'W' : number(scale);
89
+ const factorY = scale === 'input' ? 'H' : number(scale);
90
+ const halfWidth = `(${pageWidth}/(2*${zoom}))`;
91
+ const halfHeight = `(${pageHeight}/(2*${zoom}))`;
92
+ const left = `clip(${partExpression(plan, 'x', time)}*${factor}-${halfWidth},0,${pageWidth}-2*${halfWidth})`;
93
+ const top = `clip(${partExpression(plan, 'y', time)}*${factorY}-${halfHeight},0,${pageHeight}-2*${halfHeight})`;
94
+ const right = `${left}+2*${halfWidth}`;
95
+ const bottom = `${top}+2*${halfHeight}`;
96
+ return `perspective=x0='${left}':y0='${top}':x1='${right}':y1='${top}':x2='${left}':y2='${bottom}':x3='${right}':y3='${bottom}':interpolation=cubic:sense=source:eval=frame`;
97
+ }
98
+ //# sourceMappingURL=camera.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../../src/compose/camera.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,7 @@ export type CaptionFont = {
2
2
  family: string;
3
3
  file: string;
4
4
  };
5
+ /** A letter of the Arabic script. */
6
+ export declare const arabicLetter: RegExp;
5
7
  export declare function captionFont(root: string, named: string | undefined, texts: readonly string[]): CaptionFont;
6
8
  //# sourceMappingURL=caption-font.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"caption-font.d.ts","sourceRoot":"","sources":["../../src/compose/caption-font.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAS3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAS1G"}
1
+ {"version":3,"file":"caption-font.d.ts","sourceRoot":"","sources":["../../src/compose/caption-font.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,qCAAqC;AACrC,eAAO,MAAM,YAAY,QAAyE,CAAC;AAOnG,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAS1G"}
@@ -4,7 +4,8 @@ import { bundledFonts, fontsFolder } from "../fonts/bundled-fonts.js";
4
4
  import { fontFamily } from "../fonts/font-family.js";
5
5
  import { inputPath } from "../files/workspace-paths.js";
6
6
  import { invalid } from "../problems.js";
7
- const arabicLetter = /[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb50-\ufdff\ufe70-\ufeff]/u;
7
+ /** A letter of the Arabic script. */
8
+ export const arabicLetter = /[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb50-\ufdff\ufe70-\ufeff]/u;
8
9
  function bundled(name) {
9
10
  const font = bundledFonts.find((candidate) => candidate.name === name);
10
11
  return font === undefined ? undefined : { family: font.name, file: path.join(fontsFolder, font.file) };
@@ -1 +1 @@
1
- {"version":3,"file":"caption-font.js","sourceRoot":"","sources":["../../src/compose/caption-font.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQzC,MAAM,YAAY,GAAG,sEAAsE,CAAC;AAE5F,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACvE,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAyB,EAAE,KAAwB;IAC3F,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC3G,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,CAAC,4BAA4B,IAAI,2BAA2B,YAAY,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChQ,CAAC"}
1
+ {"version":3,"file":"caption-font.js","sourceRoot":"","sources":["../../src/compose/caption-font.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQzC,qCAAqC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,sEAAsE,CAAC;AAEnG,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACvE,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAyB,EAAE,KAAwB;IAC3F,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC3G,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,CAAC,4BAA4B,IAAI,2BAA2B,YAAY,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChQ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Brand } from '../brand/brand.ts';
2
+ import { type Painter } from './drawn-images.ts';
3
+ import type { ScenePicture } from './compose-plan.ts';
4
+ import type { SceneDrawn } from './scene-args.ts';
5
+ export declare const defaultAccent = "#FFD54A";
6
+ /** Whether a scene needs the browser to draw anything. */
7
+ export declare function needsDrawing(picture: ScenePicture): boolean;
8
+ export type DrawingContext = {
9
+ root: string;
10
+ folder: string;
11
+ picture: {
12
+ width: number;
13
+ height: number;
14
+ };
15
+ brand: Brand | null;
16
+ painter: Painter;
17
+ };
18
+ export declare function drawScene(context: DrawingContext, number: number, picture: ScenePicture): Promise<SceneDrawn>;
19
+ //# sourceMappingURL=compose-drawing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-drawing.d.ts","sourceRoot":"","sources":["../../src/compose/compose-drawing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAoD,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD,eAAO,MAAM,aAAa,YAAY,CAAC;AAEvC,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAE3D;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjJ,wBAAsB,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAyBnH"}
@@ -0,0 +1,38 @@
1
+ import path from 'node:path';
2
+ import { captionFont } from "./caption-font.js";
3
+ import { calloutHtml, chromeHtml, highlightHtml, maskHtml } from "./drawn-images.js";
4
+ // The images a scene's frame and overlays need (plan 16 §16.8; ADR 0115, 36 and 60), drawn by the browser into the
5
+ // work folder: a window's or a phone's chrome and the clip's mask at the picture's size, and each callout and
6
+ // highlight at the recording's own size. A blur needs no image.
7
+ export const defaultAccent = '#FFD54A';
8
+ /** Whether a scene needs the browser to draw anything. */
9
+ export function needsDrawing(picture) {
10
+ return picture.layout !== undefined || picture.look.overlays.some((overlay) => overlay.kind !== 'blur');
11
+ }
12
+ export async function drawScene(context, number, picture) {
13
+ const name = (part) => `scene-${String(number)}-${part}.png`;
14
+ const at = (file) => path.join(context.folder, file);
15
+ const { layout, source } = picture;
16
+ let chrome;
17
+ let mask;
18
+ if (layout !== undefined) {
19
+ chrome = name('chrome');
20
+ mask = name('mask');
21
+ await context.painter.draw(at(chrome), context.picture, chromeHtml(layout), true);
22
+ await context.painter.draw(at(mask), { width: layout.screen.width, height: layout.screen.height }, maskHtml({ width: layout.screen.width, height: layout.screen.height }, layout.radius), false);
23
+ }
24
+ const overlayImages = [];
25
+ for (const [index, overlay] of picture.look.overlays.entries()) {
26
+ if (overlay.kind === 'blur' || source === undefined) {
27
+ overlayImages.push(undefined);
28
+ continue;
29
+ }
30
+ const file = name(`overlay-${String(index + 1)}`);
31
+ const colour = context.brand?.colors.primary ?? defaultAccent;
32
+ const html = overlay.kind === 'highlight' ? highlightHtml(overlay.box, picture.look.scale) : calloutHtml(source, overlay.box, overlay.text ?? '', colour, captionFont(context.root, context.brand?.font ?? undefined, [overlay.text ?? '']));
33
+ await context.painter.draw(at(file), source, html, true);
34
+ overlayImages.push(file);
35
+ }
36
+ return { layout, chrome, mask, overlayImages };
37
+ }
38
+ //# sourceMappingURL=compose-drawing.js.map