@vfrey/vfrey 0.0.1 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +78 -3
  3. package/accounts.example.json +61 -0
  4. package/accounts.first-run.json +41 -0
  5. package/bin/vfrey.mjs +36 -0
  6. package/integration-tools/README.md +43 -0
  7. package/integration-tools/image/pillow.md +32 -0
  8. package/integration-tools/image/playwright.md +33 -0
  9. package/integration-tools/image/resvg.md +36 -0
  10. package/integration-tools/image/satori.md +36 -0
  11. package/integration-tools/image/sharp.md +38 -0
  12. package/integration-tools/video/blender.md +34 -0
  13. package/integration-tools/video/ffmpeg.md +41 -0
  14. package/integration-tools/video/manim.md +37 -0
  15. package/integration-tools/video/motion-canvas.md +37 -0
  16. package/integration-tools/video/remotion.md +50 -0
  17. package/integration-tools/video/revideo.md +32 -0
  18. package/package.json +74 -6
  19. package/skills/vfrey-orchestrator/SKILL.md +15 -0
  20. package/skills/vfrey-orchestrator/channel-video/SKILL.md +41 -0
  21. package/skills/vfrey-product/SKILL.md +63 -0
  22. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-one.svg +14 -0
  23. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-two.svg +22 -0
  24. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/index.html +70 -0
  25. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-one.svg +14 -0
  26. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-two.svg +22 -0
  27. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/index.tsx +49 -0
  28. package/skills/vfrey-product/examples/concurrency-vs-parallelism/script.json +15 -0
  29. package/skills/vfrey-product/examples/octopus-facts/index.html +60 -0
  30. package/skills/vfrey-product/examples/octopus-facts/octopus.svg +8 -0
  31. package/skills/vfrey-product/examples/octopus-facts/script.json +14 -0
  32. package/skills/vfrey-product/push/SKILL.md +15 -0
  33. package/skills/vfrey-product/scenes/NOTICE +5 -0
  34. package/skills/vfrey-product/scenes/blocks.md +57 -0
  35. package/skills/vfrey-product/scenes/craft.md +118 -0
  36. package/skills/vfrey-product/scenes/motion.md +88 -0
  37. package/src/analytics/dashboard.ts +109 -0
  38. package/src/analytics/index.ts +260 -0
  39. package/src/api/client.ts +282 -0
  40. package/src/api/commands.ts +330 -0
  41. package/src/api/config.ts +34 -0
  42. package/src/api/creators.ts +463 -0
  43. package/src/api/credentials.ts +152 -0
  44. package/src/api/mail.ts +333 -0
  45. package/src/api/topics.ts +207 -0
  46. package/src/api/types.ts +205 -0
  47. package/src/cli.ts +467 -0
  48. package/src/cloud/executor.ts +281 -0
  49. package/src/cloud/job.ts +106 -0
  50. package/src/cloud/render-job.ts +187 -0
  51. package/src/config.ts +309 -0
  52. package/src/dashboard-link.ts +180 -0
  53. package/src/dashboard-start.ts +103 -0
  54. package/src/desk/channel.ts +64 -0
  55. package/src/desk/cli.ts +216 -0
  56. package/src/desk/creators-route.ts +129 -0
  57. package/src/desk/fonts/JetBrainsMono-OFL.txt +93 -0
  58. package/src/desk/fonts/google-sans-flex-latin.woff2 +0 -0
  59. package/src/desk/fonts/jetbrains-mono-medium.woff2 +0 -0
  60. package/src/desk/http.ts +460 -0
  61. package/src/desk/mail-route.ts +103 -0
  62. package/src/desk/overlay.ts +112 -0
  63. package/src/desk/preview.ts +422 -0
  64. package/src/desk/screens.ts +149 -0
  65. package/src/desk/secrets.ts +143 -0
  66. package/src/desk/series.ts +127 -0
  67. package/src/desk/state.ts +77 -0
  68. package/src/desk/studio-route.ts +119 -0
  69. package/src/desk/tick-runner.ts +66 -0
  70. package/src/desk/tick.ts +56 -0
  71. package/src/desk/topics-route.ts +41 -0
  72. package/src/desk/ui/app.css +505 -0
  73. package/src/desk/ui/app.js +185 -0
  74. package/src/desk/ui/apple-icon.png +0 -0
  75. package/src/desk/ui/components.js +79 -0
  76. package/src/desk/ui/core.js +156 -0
  77. package/src/desk/ui/favicon.ico +0 -0
  78. package/src/desk/ui/icon.svg +1 -0
  79. package/src/desk/ui/icons.js +49 -0
  80. package/src/desk/ui/index.html +41 -0
  81. package/src/desk/ui/metrics-math.d.ts +22 -0
  82. package/src/desk/ui/metrics-math.js +135 -0
  83. package/src/desk/ui/theme.js +5 -0
  84. package/src/desk/ui/views-creators.js +209 -0
  85. package/src/desk/ui/views-keys.js +114 -0
  86. package/src/desk/ui/views-mail.js +188 -0
  87. package/src/desk/ui/views-misc.js +123 -0
  88. package/src/desk/ui/views-review.js +119 -0
  89. package/src/desk/ui/views-runs.js +350 -0
  90. package/src/desk/ui/views-screens.js +163 -0
  91. package/src/desk/ui/views-series.js +318 -0
  92. package/src/desk/ui/views-topics.js +87 -0
  93. package/src/desk/verbs.ts +361 -0
  94. package/src/doctor.ts +212 -0
  95. package/src/drain.ts +14 -0
  96. package/src/engage/index.ts +217 -0
  97. package/src/generate/index.ts +110 -0
  98. package/src/generate/script.ts +152 -0
  99. package/src/generate/title.ts +78 -0
  100. package/src/help.ts +233 -0
  101. package/src/home.ts +45 -0
  102. package/src/llm/client.ts +177 -0
  103. package/src/log.ts +7 -0
  104. package/src/oauth/tokens.ts +189 -0
  105. package/src/oauth/vault.ts +72 -0
  106. package/src/pipeline/review.ts +642 -0
  107. package/src/pipeline/run.ts +443 -0
  108. package/src/pipeline/series-guard.ts +26 -0
  109. package/src/pipeline/source-fetch.ts +69 -0
  110. package/src/pipeline/stages.ts +728 -0
  111. package/src/pipeline/topics-source.ts +90 -0
  112. package/src/product/commands.ts +296 -0
  113. package/src/product/config-file.ts +38 -0
  114. package/src/product/engine-choice.ts +89 -0
  115. package/src/product/engines.ts +103 -0
  116. package/src/product/insights.ts +50 -0
  117. package/src/product/keys.ts +67 -0
  118. package/src/product/paths.ts +18 -0
  119. package/src/product/probes.ts +88 -0
  120. package/src/product/push-flow.ts +90 -0
  121. package/src/product/render-video.ts +21 -0
  122. package/src/product/short.ts +62 -0
  123. package/src/product/update.ts +32 -0
  124. package/src/project/canonical.ts +14 -0
  125. package/src/project/commands.ts +177 -0
  126. package/src/project/dashboard-status.ts +28 -0
  127. package/src/project/discover.ts +19 -0
  128. package/src/project/index-file.ts +73 -0
  129. package/src/project/marker.ts +87 -0
  130. package/src/project/migrate.ts +201 -0
  131. package/src/project/names.ts +27 -0
  132. package/src/project/notice.ts +16 -0
  133. package/src/project/open.ts +88 -0
  134. package/src/project/scaffold.ts +112 -0
  135. package/src/project/templates.ts +42 -0
  136. package/src/project/videos.ts +64 -0
  137. package/src/publish/facebook.ts +91 -0
  138. package/src/publish/index.ts +12 -0
  139. package/src/publish/instagram.ts +97 -0
  140. package/src/publish/rate.ts +62 -0
  141. package/src/publish/session.ts +242 -0
  142. package/src/publish/tiktok.ts +202 -0
  143. package/src/publish/youtube.ts +177 -0
  144. package/src/queue/desk-ops.ts +1170 -0
  145. package/src/queue/seal.ts +3 -0
  146. package/src/queue/store.ts +1006 -0
  147. package/src/remotion/AiLabel.tsx +26 -0
  148. package/src/remotion/Root.tsx +73 -0
  149. package/src/remotion/defaults.ts +26 -0
  150. package/src/remotion/films/claude/ClaudeFilm.tsx +1014 -0
  151. package/src/remotion/films/engine/EngineMark.tsx +17 -0
  152. package/src/remotion/films/rea/ReaShort.tsx +552 -0
  153. package/src/remotion/films/rea/drawings.tsx +907 -0
  154. package/src/remotion/films/rea/timing.ts +144 -0
  155. package/src/remotion/index.ts +4 -0
  156. package/src/remotion/templates/EditorialSplit.tsx +39 -0
  157. package/src/remotion/templates/KineticType.tsx +39 -0
  158. package/src/remotion/templates/SignalBoard.tsx +50 -0
  159. package/src/remotion/timing.ts +22 -0
  160. package/src/render/chrome.ts +78 -0
  161. package/src/render/engine-mark.ts +29 -0
  162. package/src/render/framey-template.ts +130 -0
  163. package/src/render/html-engine.ts +149 -0
  164. package/src/render/node-crypto-stub.ts +11 -0
  165. package/src/render/probe.ts +29 -0
  166. package/src/render/props.ts +17 -0
  167. package/src/render/vertical-short.ts +35 -0
  168. package/src/render/webpack.ts +33 -0
  169. package/src/render/worker.ts +65 -0
  170. package/src/scene/assets/fonts/TitanOne-OFL.txt +94 -0
  171. package/src/scene/assets/fonts/titan-one.woff2 +0 -0
  172. package/src/scene/assets/gsap/LICENSE.txt +8 -0
  173. package/src/scene/assets/gsap/gsap.min.js +11 -0
  174. package/src/scene/audio.ts +54 -0
  175. package/src/scene/blocks/LICENSE +190 -0
  176. package/src/scene/blocks/NOTICE +5 -0
  177. package/src/scene/blocks/bar-chart-race/bar-chart-race.html +585 -0
  178. package/src/scene/blocks/beat-freeze-cut/beat-freeze-cut.html +874 -0
  179. package/src/scene/blocks/camcorder-hud/camcorder-hud.html +164 -0
  180. package/src/scene/blocks/camera-dolly-zoom/camera-dolly-zoom.html +355 -0
  181. package/src/scene/blocks/code-diff/code-diff.html +817 -0
  182. package/src/scene/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html +248 -0
  183. package/src/scene/blocks/code-typing/code-typing.html +764 -0
  184. package/src/scene/blocks/data-chart/data-chart.html +452 -0
  185. package/src/scene/blocks/flowchart-vertical/flowchart-vertical.html +454 -0
  186. package/src/scene/blocks/hw-scribble-transition/hw-scribble-transition.html +153 -0
  187. package/src/scene/blocks/index.json +824 -0
  188. package/src/scene/blocks/lt-accent-underline/lt-accent-underline.html +107 -0
  189. package/src/scene/blocks/lt-bold-block/lt-bold-block.html +109 -0
  190. package/src/scene/blocks/lt-clean-bar/lt-clean-bar.html +112 -0
  191. package/src/scene/blocks/lt-dark-card/lt-dark-card.html +111 -0
  192. package/src/scene/blocks/lt-kicker-name/lt-kicker-name.html +114 -0
  193. package/src/scene/blocks/lt-mask-reveal/lt-mask-reveal.html +120 -0
  194. package/src/scene/blocks/lt-neon-border/lt-neon-border.html +391 -0
  195. package/src/scene/blocks/lt-stack-bars/lt-stack-bars.html +109 -0
  196. package/src/scene/blocks/macos-notification/macos-notification.html +194 -0
  197. package/src/scene/blocks/mk-background/mk-background.html +236 -0
  198. package/src/scene/blocks/mk-callout-highlight/mk-callout-highlight.html +183 -0
  199. package/src/scene/blocks/mk-clone-wall-transition/mk-clone-wall-transition.html +226 -0
  200. package/src/scene/blocks/mk-line-graph/mk-line-graph.html +293 -0
  201. package/src/scene/blocks/mk-progress-stat/mk-progress-stat.html +186 -0
  202. package/src/scene/blocks/mk-specs-list/mk-specs-list.html +223 -0
  203. package/src/scene/blocks/news-ticker/news-ticker.html +409 -0
  204. package/src/scene/blocks/organic-light-leak-overlay/organic-light-leak-overlay.html +126 -0
  205. package/src/scene/blocks/oscilloscope-trace/oscilloscope-trace.html +519 -0
  206. package/src/scene/blocks/snow/snow.html +205 -0
  207. package/src/scene/blocks/split-flap-board/split-flap-board.html +347 -0
  208. package/src/scene/blocks/spotify-card/spotify-card.html +294 -0
  209. package/src/scene/blocks/starfield/starfield.html +188 -0
  210. package/src/scene/blocks/thread-message-stack/thread-message-stack.html +239 -0
  211. package/src/scene/blocks/week-in-merges/week-in-merges.html +470 -0
  212. package/src/scene/blocks/x-post/x-post.html +437 -0
  213. package/src/scene/blocks/yt-comment-card/yt-comment-card.html +319 -0
  214. package/src/scene/blocks/yt-lcd-background/yt-lcd-background.html +307 -0
  215. package/src/scene/blocks/yt-prism-title/yt-prism-title.html +205 -0
  216. package/src/scene/blocks.ts +229 -0
  217. package/src/scene/command.ts +272 -0
  218. package/src/scene/figure.ts +181 -0
  219. package/src/scene/glyphs.ts +107 -0
  220. package/src/scene/init.ts +86 -0
  221. package/src/scene/inspect/css-tree.d.ts +6 -0
  222. package/src/scene/inspect/page-scripts.ts +272 -0
  223. package/src/scene/inspect/report.ts +405 -0
  224. package/src/scene/inspect/run.ts +200 -0
  225. package/src/scene/inspect/source.ts +236 -0
  226. package/src/scene/label.ts +131 -0
  227. package/src/scene/net-guard.ts +74 -0
  228. package/src/scene/render.ts +292 -0
  229. package/src/scene/runtime/browser.ts +199 -0
  230. package/src/scene/runtime/core.ts +181 -0
  231. package/src/scene/runtime/react.tsx +115 -0
  232. package/src/scene/runtime/transitions.ts +133 -0
  233. package/src/scene/sanitize.ts +181 -0
  234. package/src/scene/scene.ts +152 -0
  235. package/src/scene/studio-command.ts +48 -0
  236. package/src/scene/studio-skin.ts +59 -0
  237. package/src/scene/studio.ts +337 -0
  238. package/src/scene/supply.ts +111 -0
  239. package/src/scene/timing.ts +288 -0
  240. package/src/scene/transitions/LICENSE +190 -0
  241. package/src/scene/transitions/NOTICE +5 -0
  242. package/src/scene/transitions/SOURCE.json +24 -0
  243. package/src/scene/transitions/transitions.global.js +38 -0
  244. package/src/scene/transitions.ts +26 -0
  245. package/src/service/all-projects.ts +153 -0
  246. package/src/service/commands.ts +256 -0
  247. package/src/service/due.ts +82 -0
  248. package/src/service/lock.ts +101 -0
  249. package/src/service/log-file.ts +56 -0
  250. package/src/service/state.ts +41 -0
  251. package/src/service/units.ts +90 -0
  252. package/src/service/worker.ts +51 -0
  253. package/src/skill-agents.ts +74 -0
  254. package/src/skill-install.ts +334 -0
  255. package/src/sync-write.ts +21 -0
  256. package/src/tick-report.ts +205 -0
  257. package/src/tts.ts +124 -0
  258. package/vendor/@vfrey/contracts/dist/account.d.ts +97 -0
  259. package/vendor/@vfrey/contracts/dist/account.js +27 -0
  260. package/vendor/@vfrey/contracts/dist/checks.d.ts +8 -0
  261. package/vendor/@vfrey/contracts/dist/checks.js +1 -0
  262. package/vendor/@vfrey/contracts/dist/credentials/crypto.d.ts +4 -0
  263. package/vendor/@vfrey/contracts/dist/credentials/crypto.js +38 -0
  264. package/vendor/@vfrey/contracts/dist/domain/advice-persona.d.ts +10 -0
  265. package/vendor/@vfrey/contracts/dist/domain/advice-persona.js +24 -0
  266. package/vendor/@vfrey/contracts/dist/domain/backoff.d.ts +13 -0
  267. package/vendor/@vfrey/contracts/dist/domain/backoff.js +27 -0
  268. package/vendor/@vfrey/contracts/dist/domain/cadence.d.ts +29 -0
  269. package/vendor/@vfrey/contracts/dist/domain/cadence.js +95 -0
  270. package/vendor/@vfrey/contracts/dist/domain/creators.d.ts +146 -0
  271. package/vendor/@vfrey/contracts/dist/domain/creators.js +2 -0
  272. package/vendor/@vfrey/contracts/dist/domain/disclosure.d.ts +127 -0
  273. package/vendor/@vfrey/contracts/dist/domain/disclosure.js +250 -0
  274. package/vendor/@vfrey/contracts/dist/domain/errors.d.ts +34 -0
  275. package/vendor/@vfrey/contracts/dist/domain/errors.js +68 -0
  276. package/vendor/@vfrey/contracts/dist/domain/redact.d.ts +1 -0
  277. package/vendor/@vfrey/contracts/dist/domain/redact.js +21 -0
  278. package/vendor/@vfrey/contracts/dist/domain/revenue.d.ts +13 -0
  279. package/vendor/@vfrey/contracts/dist/domain/revenue.js +16 -0
  280. package/vendor/@vfrey/contracts/dist/domain/safety.d.ts +20 -0
  281. package/vendor/@vfrey/contracts/dist/domain/safety.js +61 -0
  282. package/vendor/@vfrey/contracts/dist/domain/similarity.d.ts +13 -0
  283. package/vendor/@vfrey/contracts/dist/domain/similarity.js +100 -0
  284. package/vendor/@vfrey/contracts/dist/domain/templates.d.ts +18 -0
  285. package/vendor/@vfrey/contracts/dist/domain/templates.js +67 -0
  286. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.d.ts +19 -0
  287. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.js +52 -0
  288. package/vendor/@vfrey/contracts/dist/domain/topics.d.ts +78 -0
  289. package/vendor/@vfrey/contracts/dist/domain/topics.js +4 -0
  290. package/vendor/@vfrey/contracts/dist/domain/types.d.ts +216 -0
  291. package/vendor/@vfrey/contracts/dist/domain/types.js +1 -0
  292. package/vendor/@vfrey/contracts/dist/domain/variation.d.ts +57 -0
  293. package/vendor/@vfrey/contracts/dist/domain/variation.js +278 -0
  294. package/vendor/@vfrey/contracts/dist/events.d.ts +20 -0
  295. package/vendor/@vfrey/contracts/dist/events.js +1 -0
  296. package/vendor/@vfrey/contracts/dist/http.d.ts +11 -0
  297. package/vendor/@vfrey/contracts/dist/http.js +37 -0
  298. package/vendor/@vfrey/contracts/dist/index.d.ts +22 -0
  299. package/vendor/@vfrey/contracts/dist/index.js +22 -0
  300. package/vendor/@vfrey/contracts/dist/log.d.ts +2 -0
  301. package/vendor/@vfrey/contracts/dist/log.js +10 -0
  302. package/vendor/@vfrey/contracts/dist/mail.d.ts +55 -0
  303. package/vendor/@vfrey/contracts/dist/mail.js +1 -0
  304. package/vendor/@vfrey/contracts/package.json +18 -0
  305. package/vendor/@vfrey/ingest/dist/index.d.ts +41 -0
  306. package/vendor/@vfrey/ingest/dist/index.js +221 -0
  307. package/vendor/@vfrey/ingest/dist/parse.d.ts +5 -0
  308. package/vendor/@vfrey/ingest/dist/parse.js +165 -0
  309. package/vendor/@vfrey/ingest/package.json +21 -0
  310. package/vendor/framey/LICENSE +190 -0
  311. package/vendor/framey/NOTICE +15 -0
  312. package/vendor/framey/SOURCE.json +24 -0
  313. package/vendor/framey/STUDIO-SOURCE.json +9 -0
  314. package/vendor/framey/bin/framey-localize-fonts.mjs +12 -0
  315. package/vendor/framey/bin/framey.mjs +16 -0
  316. package/vendor/framey/dist/add-VMJD5KBS.js +75 -0
  317. package/vendor/framey/dist/animatedGifPrep-QPKGJ6PM.js +43 -0
  318. package/vendor/framey/dist/api.d.ts +211 -0
  319. package/vendor/framey/dist/api.js +76 -0
  320. package/vendor/framey/dist/appHistory-WW56FZTT.js +31 -0
  321. package/vendor/framey/dist/asset-WQKGOELE.js +26 -0
  322. package/vendor/framey/dist/audio-RSJWVWRA.js +1936 -0
  323. package/vendor/framey/dist/batchRender-3HPICDCT.js +366 -0
  324. package/vendor/framey/dist/beats-HL2BDUZI.js +273 -0
  325. package/vendor/framey/dist/benchmark-RWCWHI5Z.js +230 -0
  326. package/vendor/framey/dist/browser-ECYET3Q2.js +206 -0
  327. package/vendor/framey/dist/bundleWithLocalizedFonts-ALYBQEQT.js +52 -0
  328. package/vendor/framey/dist/captureCompositionFrame-4DA6SEH2.js +94 -0
  329. package/vendor/framey/dist/catalog-5JVDEUWK.js +979 -0
  330. package/vendor/framey/dist/catch-up-NEBCL5NO.js +106 -0
  331. package/vendor/framey/dist/check-F5N544ML.js +111 -0
  332. package/vendor/framey/dist/checkBrowser-LZ66CMJG.js +110 -0
  333. package/vendor/framey/dist/chunk-2K7Z2HVP.js +372 -0
  334. package/vendor/framey/dist/chunk-2UZQHDM7.js +323 -0
  335. package/vendor/framey/dist/chunk-2X4PQEYM.js +34 -0
  336. package/vendor/framey/dist/chunk-3422KMTD.js +168 -0
  337. package/vendor/framey/dist/chunk-3FYD4YEK.js +72 -0
  338. package/vendor/framey/dist/chunk-3GXQPCJC.js +682 -0
  339. package/vendor/framey/dist/chunk-3LM6YPBR.js +179 -0
  340. package/vendor/framey/dist/chunk-442JNXRM.js +20 -0
  341. package/vendor/framey/dist/chunk-4BNN5FGX.js +182 -0
  342. package/vendor/framey/dist/chunk-4P7NX5BM.js +11532 -0
  343. package/vendor/framey/dist/chunk-4UMPIPCQ.js +88 -0
  344. package/vendor/framey/dist/chunk-4YYJOFKS.js +28 -0
  345. package/vendor/framey/dist/chunk-54HJ63Q2.js +29 -0
  346. package/vendor/framey/dist/chunk-5ETJX6WM.js +89 -0
  347. package/vendor/framey/dist/chunk-5SG54JRS.js +68 -0
  348. package/vendor/framey/dist/chunk-634HSJ3J.js +58 -0
  349. package/vendor/framey/dist/chunk-66LREXGS.js +48 -0
  350. package/vendor/framey/dist/chunk-6BEFEWFB.js +25 -0
  351. package/vendor/framey/dist/chunk-6BNTGNGD.js +286 -0
  352. package/vendor/framey/dist/chunk-6DDPOHMK.js +140 -0
  353. package/vendor/framey/dist/chunk-6GV5XSOR.js +214 -0
  354. package/vendor/framey/dist/chunk-6JR2XUEE.js +127 -0
  355. package/vendor/framey/dist/chunk-73QVATIM.js +362 -0
  356. package/vendor/framey/dist/chunk-7FMUTGOH.js +279 -0
  357. package/vendor/framey/dist/chunk-7JQFJ6JJ.js +291 -0
  358. package/vendor/framey/dist/chunk-AHPGAESX.js +113 -0
  359. package/vendor/framey/dist/chunk-AHX34EFP.js +15 -0
  360. package/vendor/framey/dist/chunk-AJ5CSAEZ.js +487 -0
  361. package/vendor/framey/dist/chunk-B7RJV74O.js +5047 -0
  362. package/vendor/framey/dist/chunk-BL4U6U52.js +254 -0
  363. package/vendor/framey/dist/chunk-BSQ7XANM.js +210 -0
  364. package/vendor/framey/dist/chunk-BZ56MTDX.js +6 -0
  365. package/vendor/framey/dist/chunk-C5BO7PMT.js +282 -0
  366. package/vendor/framey/dist/chunk-CFUCHO4T.js +358 -0
  367. package/vendor/framey/dist/chunk-CZGHFATT.js +31 -0
  368. package/vendor/framey/dist/chunk-DLA44CMS.js +5649 -0
  369. package/vendor/framey/dist/chunk-DPVXBVLL.js +31292 -0
  370. package/vendor/framey/dist/chunk-DQBY7FRR.js +397 -0
  371. package/vendor/framey/dist/chunk-EAMLBAHA.js +154 -0
  372. package/vendor/framey/dist/chunk-EOVYQ3IG.js +143 -0
  373. package/vendor/framey/dist/chunk-EVMY7IPL.js +612 -0
  374. package/vendor/framey/dist/chunk-FG4BBFZI.js +122 -0
  375. package/vendor/framey/dist/chunk-FI4I7CME.js +56 -0
  376. package/vendor/framey/dist/chunk-FLREUEHW.js +2081 -0
  377. package/vendor/framey/dist/chunk-FUFP7A7S.js +1126 -0
  378. package/vendor/framey/dist/chunk-FV3PIKGH.js +56 -0
  379. package/vendor/framey/dist/chunk-FXMKHHPO.js +123 -0
  380. package/vendor/framey/dist/chunk-GGZAM5VH.js +1187 -0
  381. package/vendor/framey/dist/chunk-GKZEVJIB.js +39 -0
  382. package/vendor/framey/dist/chunk-GMECXFAN.js +57 -0
  383. package/vendor/framey/dist/chunk-GNEFDLFB.js +621 -0
  384. package/vendor/framey/dist/chunk-GPMLSOEG.js +19274 -0
  385. package/vendor/framey/dist/chunk-GPYVT5EE.js +59 -0
  386. package/vendor/framey/dist/chunk-H6IEMZEV.js +40 -0
  387. package/vendor/framey/dist/chunk-HEFQCIXR.js +170 -0
  388. package/vendor/framey/dist/chunk-HFQ4CWHA.js +118 -0
  389. package/vendor/framey/dist/chunk-HPDTCOKE.js +304 -0
  390. package/vendor/framey/dist/chunk-HTUHQKDB.js +548 -0
  391. package/vendor/framey/dist/chunk-HU2SPFNG.js +103 -0
  392. package/vendor/framey/dist/chunk-HVW7ABXD.js +19 -0
  393. package/vendor/framey/dist/chunk-IG7NDKXO.js +26 -0
  394. package/vendor/framey/dist/chunk-II3J3WXK.js +246 -0
  395. package/vendor/framey/dist/chunk-IR35BCLK.js +13 -0
  396. package/vendor/framey/dist/chunk-ISN5GRYI.js +20 -0
  397. package/vendor/framey/dist/chunk-IUONB55J.js +669 -0
  398. package/vendor/framey/dist/chunk-IVSUQR2B.js +161 -0
  399. package/vendor/framey/dist/chunk-IXU6PQSW.js +259 -0
  400. package/vendor/framey/dist/chunk-IZCW2NKT.js +29 -0
  401. package/vendor/framey/dist/chunk-JGXJU5PF.js +68 -0
  402. package/vendor/framey/dist/chunk-JHOCK3DG.js +27 -0
  403. package/vendor/framey/dist/chunk-JVBRJMOP.js +121 -0
  404. package/vendor/framey/dist/chunk-K4MJX7SR.js +17 -0
  405. package/vendor/framey/dist/chunk-K4WKGXRC.js +18 -0
  406. package/vendor/framey/dist/chunk-KGPLIH7B.js +127 -0
  407. package/vendor/framey/dist/chunk-KLIKCX6Q.js +63 -0
  408. package/vendor/framey/dist/chunk-KXTKKBOC.js +61 -0
  409. package/vendor/framey/dist/chunk-KZI44OPA.js +185 -0
  410. package/vendor/framey/dist/chunk-LEKZNRKI.js +45 -0
  411. package/vendor/framey/dist/chunk-LFGJWOVA.js +61 -0
  412. package/vendor/framey/dist/chunk-LLMSX6V6.js +17 -0
  413. package/vendor/framey/dist/chunk-LTGTN2TH.js +195 -0
  414. package/vendor/framey/dist/chunk-M2WBOQ2Q.js +54 -0
  415. package/vendor/framey/dist/chunk-MI5FTLYK.js +399 -0
  416. package/vendor/framey/dist/chunk-MLQP2QUF.js +1809 -0
  417. package/vendor/framey/dist/chunk-MMQ52NIZ.js +956 -0
  418. package/vendor/framey/dist/chunk-ND46XKK3.js +742 -0
  419. package/vendor/framey/dist/chunk-NYEXNSHM.js +504 -0
  420. package/vendor/framey/dist/chunk-O4YRB27L.js +57 -0
  421. package/vendor/framey/dist/chunk-O7BGSQ5F.js +92 -0
  422. package/vendor/framey/dist/chunk-OFQWTKPW.js +117 -0
  423. package/vendor/framey/dist/chunk-P5ZBLXJ6.js +91 -0
  424. package/vendor/framey/dist/chunk-P6LJ4Y32.js +4662 -0
  425. package/vendor/framey/dist/chunk-PKZ7RK5N.js +281 -0
  426. package/vendor/framey/dist/chunk-PURHAXFL.js +15 -0
  427. package/vendor/framey/dist/chunk-Q5X2B5WT.js +29 -0
  428. package/vendor/framey/dist/chunk-QAMKOG67.js +248 -0
  429. package/vendor/framey/dist/chunk-R6YBK4XX.js +514 -0
  430. package/vendor/framey/dist/chunk-RGLQURIX.js +2997 -0
  431. package/vendor/framey/dist/chunk-RNV3DM4S.js +3573 -0
  432. package/vendor/framey/dist/chunk-ROEW726S.js +4638 -0
  433. package/vendor/framey/dist/chunk-RQVDTPGD.js +6 -0
  434. package/vendor/framey/dist/chunk-RVZHGOGR.js +26 -0
  435. package/vendor/framey/dist/chunk-RXATE6FZ.js +385 -0
  436. package/vendor/framey/dist/chunk-S3S4TGEH.js +424 -0
  437. package/vendor/framey/dist/chunk-SDYDNHEE.js +11202 -0
  438. package/vendor/framey/dist/chunk-SSWBU67I.js +173 -0
  439. package/vendor/framey/dist/chunk-ST35C4K6.js +28 -0
  440. package/vendor/framey/dist/chunk-SZNTYTIV.js +990 -0
  441. package/vendor/framey/dist/chunk-T2EUWCJE.js +29 -0
  442. package/vendor/framey/dist/chunk-T6AGUTZJ.js +444 -0
  443. package/vendor/framey/dist/chunk-THJALHFJ.js +11131 -0
  444. package/vendor/framey/dist/chunk-TIQB63MX.js +18128 -0
  445. package/vendor/framey/dist/chunk-U242RTCT.js +134 -0
  446. package/vendor/framey/dist/chunk-UJZREAV6.js +3901 -0
  447. package/vendor/framey/dist/chunk-UMD3V7CU.js +264 -0
  448. package/vendor/framey/dist/chunk-UOFAE5RF.js +42 -0
  449. package/vendor/framey/dist/chunk-UTEIUGAZ.js +29 -0
  450. package/vendor/framey/dist/chunk-VAS6HQX3.js +138 -0
  451. package/vendor/framey/dist/chunk-VC76R6W2.js +27 -0
  452. package/vendor/framey/dist/chunk-VFZ7W5W7.js +226 -0
  453. package/vendor/framey/dist/chunk-VHNLZAE2.js +348 -0
  454. package/vendor/framey/dist/chunk-VMC4WSH5.js +278 -0
  455. package/vendor/framey/dist/chunk-VORQIH64.js +108 -0
  456. package/vendor/framey/dist/chunk-VTXFRIHO.js +186 -0
  457. package/vendor/framey/dist/chunk-W2LGJSEN.js +182 -0
  458. package/vendor/framey/dist/chunk-WKBM7NDG.js +32 -0
  459. package/vendor/framey/dist/chunk-WMQNIQDJ.js +2057 -0
  460. package/vendor/framey/dist/chunk-WN4XZC5F.js +504 -0
  461. package/vendor/framey/dist/chunk-WQ6ZXHFQ.js +133 -0
  462. package/vendor/framey/dist/chunk-X7HBSTCZ.js +53 -0
  463. package/vendor/framey/dist/chunk-X7MTZ4T2.js +6 -0
  464. package/vendor/framey/dist/chunk-XPTOJZJF.js +308 -0
  465. package/vendor/framey/dist/chunk-XT6MXC7A.js +6 -0
  466. package/vendor/framey/dist/chunk-XT7X6QCP.js +6 -0
  467. package/vendor/framey/dist/chunk-XTMDWNEC.js +2820 -0
  468. package/vendor/framey/dist/chunk-XUHOIDKG.js +6 -0
  469. package/vendor/framey/dist/chunk-XX7LTW5Y.js +138 -0
  470. package/vendor/framey/dist/chunk-Y3YGTZEG.js +382 -0
  471. package/vendor/framey/dist/chunk-Y5SBSGN7.js +346 -0
  472. package/vendor/framey/dist/chunk-YCMVI4MZ.js +625 -0
  473. package/vendor/framey/dist/chunk-YOR4CTCE.js +261 -0
  474. package/vendor/framey/dist/chunk-YPRBGITP.js +2748 -0
  475. package/vendor/framey/dist/chunk-ZKDMAOFU.js +32 -0
  476. package/vendor/framey/dist/chunk-ZW6DURPF.js +1318 -0
  477. package/vendor/framey/dist/clean-ZJOGDKDM.js +336 -0
  478. package/vendor/framey/dist/cli.js +439 -0
  479. package/vendor/framey/dist/cloudrun-HGJ3B6ZV.js +767 -0
  480. package/vendor/framey/dist/commands/contrast-audit.browser.js +643 -0
  481. package/vendor/framey/dist/commands/layout-audit.browser.js +2914 -0
  482. package/vendor/framey/dist/commands/motion-sample.browser.js +74 -0
  483. package/vendor/framey/dist/commands/motion-signature.browser.js +475 -0
  484. package/vendor/framey/dist/compare-W7UY3BAZ.js +463 -0
  485. package/vendor/framey/dist/compiler-TM3W6GYF.js +136 -0
  486. package/vendor/framey/dist/component-QF5VGYMU.js +176 -0
  487. package/vendor/framey/dist/compositions-XT3PMNDF.js +238 -0
  488. package/vendor/framey/dist/contactSheet-7W6GYHBQ.js +24 -0
  489. package/vendor/framey/dist/deploy-6FJPWKF2.js +299 -0
  490. package/vendor/framey/dist/destroy-HE2RIPKZ.js +57 -0
  491. package/vendor/framey/dist/deterministicFonts-RADD3BSX.js +50 -0
  492. package/vendor/framey/dist/dist-4S2FGGVY.js +422 -0
  493. package/vendor/framey/dist/dist-BE4B2EZE.js +32 -0
  494. package/vendor/framey/dist/dist-EWXJGN63.js +129 -0
  495. package/vendor/framey/dist/docs-VKIRFEEX.js +131 -0
  496. package/vendor/framey/dist/doctor-2UNJ2G7U.js +74 -0
  497. package/vendor/framey/dist/dom-37K6YOH4.js +14 -0
  498. package/vendor/framey/dist/dotEnv-7PZQTVIM.js +54 -0
  499. package/vendor/framey/dist/events-ACY3P2PA.js +76 -0
  500. package/vendor/framey/dist/events-IHB3B2MK.js +69 -0
  501. package/vendor/framey/dist/ffmpeg-DR5LGGSK.js +63 -0
  502. package/vendor/framey/dist/figma-BODVUPHB.js +63 -0
  503. package/vendor/framey/dist/fontData.generated-NXAOI4GH.js +13 -0
  504. package/vendor/framey/dist/fontLocalizeCli.js +142 -0
  505. package/vendor/framey/dist/gpuPolicy-XLEXORZY.js +49 -0
  506. package/vendor/framey/dist/grade-compare-GAXFQ74C.js +661 -0
  507. package/vendor/framey/dist/gsapParser-IEUSLYDJ.js +2885 -0
  508. package/vendor/framey/dist/gsapParserAcorn-LZJKCFQI.js +30 -0
  509. package/vendor/framey/dist/help-VCB5X2HS.js +184 -0
  510. package/vendor/framey/dist/history-ENGZEVFT.js +471 -0
  511. package/vendor/framey/dist/hyperframe.manifest.json +22 -0
  512. package/vendor/framey/dist/hyperframe.runtime.iife.js +1132 -0
  513. package/vendor/framey/dist/hyperframes-player.global.js +478 -0
  514. package/vendor/framey/dist/hyperframes-slideshow.global.js +148 -0
  515. package/vendor/framey/dist/info-WPCJHBMU.js +182 -0
  516. package/vendor/framey/dist/init-G4WGLYGD.js +1176 -0
  517. package/vendor/framey/dist/inspect-5IQ6CNEQ.js +106 -0
  518. package/vendor/framey/dist/installer-ML4HWUV7.js +40 -0
  519. package/vendor/framey/dist/keyframes-Q6XJO7VW.js +2755 -0
  520. package/vendor/framey/dist/lambda-A7JC74JF.js +468 -0
  521. package/vendor/framey/dist/layout-YET6XNXA.js +95 -0
  522. package/vendor/framey/dist/lint-I7FZN2VJ.js +162 -0
  523. package/vendor/framey/dist/lintProject-PDB3UN72.js +30 -0
  524. package/vendor/framey/dist/manager-CD6LJ7ZN.js +45 -0
  525. package/vendor/framey/dist/manager-WA4F2GST.js +67 -0
  526. package/vendor/framey/dist/media-treatment-BXYS4WVA.js +1030 -0
  527. package/vendor/framey/dist/media-use-LWH3MANK.js +113 -0
  528. package/vendor/framey/dist/models-K363FYM4.js +195 -0
  529. package/vendor/framey/dist/motionShot-GPAVWXLH.js +640 -0
  530. package/vendor/framey/dist/normalize-FASIPBUM.js +24 -0
  531. package/vendor/framey/dist/normalize-audio-VDWYRHN6.js +268 -0
  532. package/vendor/framey/dist/open-E3TB6P6J.js +128 -0
  533. package/vendor/framey/dist/parakeetRunner-UP34UOI2.js +58 -0
  534. package/vendor/framey/dist/pipeline-VXIUYEBU.js +64 -0
  535. package/vendor/framey/dist/play-BUTDQ4DV.js +353 -0
  536. package/vendor/framey/dist/policies-OLDYGFK5.js +309 -0
  537. package/vendor/framey/dist/preflight-2KZ2GWAZ.js +55 -0
  538. package/vendor/framey/dist/present-GXYSM7NX.js +353 -0
  539. package/vendor/framey/dist/preview-EKZ33JOJ.js +1910 -0
  540. package/vendor/framey/dist/producer-SKU5EV3J.js +13 -0
  541. package/vendor/framey/dist/progress-ZWB4Z4ET.js +76 -0
  542. package/vendor/framey/dist/projectConfig-3MZAYVTS.js +60 -0
  543. package/vendor/framey/dist/publication-DTKWNF74.js +33 -0
  544. package/vendor/framey/dist/remove-background-OY5L7XN2.js +265 -0
  545. package/vendor/framey/dist/render-CDV4GDFX.js +187 -0
  546. package/vendor/framey/dist/render-YE5YAMOD.js +2620 -0
  547. package/vendor/framey/dist/render-batch-ZAMFPCLL.js +274 -0
  548. package/vendor/framey/dist/renderSetupWorker.js +120 -0
  549. package/vendor/framey/dist/resolver-D5BIPSEW.js +42 -0
  550. package/vendor/framey/dist/runtimeVersion.js +18 -0
  551. package/vendor/framey/dist/sam-WFZXI4F7.js +23 -0
  552. package/vendor/framey/dist/shaderTransitionWorker.js +44 -0
  553. package/vendor/framey/dist/sherpa-RQWLV7I3.js +79 -0
  554. package/vendor/framey/dist/sherpaWorker.js +84 -0
  555. package/vendor/framey/dist/sites-AMOVABTK.js +54 -0
  556. package/vendor/framey/dist/skill-ZHVLH7KX.js +15 -0
  557. package/vendor/framey/dist/slideshow-SJGK4RQS.js +24 -0
  558. package/vendor/framey/dist/snapshot-TUXL6LBQ.js +1515 -0
  559. package/vendor/framey/dist/src-JHQGB2S6.js +215 -0
  560. package/vendor/framey/dist/src-Z5PGEMXB.js +532 -0
  561. package/vendor/framey/dist/state-B6H3WHCF.js +28 -0
  562. package/vendor/framey/dist/staticProjectServer-DFVZOUGI.js +63 -0
  563. package/vendor/framey/dist/studio/assets/hyperframes-player-DLaKjw0L.js +478 -0
  564. package/vendor/framey/dist/studio/assets/index-BX3KHhGX.js +71 -0
  565. package/vendor/framey/dist/studio/assets/index-CYbvOVp-.css +1 -0
  566. package/vendor/framey/dist/studio/assets/index-Dp3QEUGL.js +402 -0
  567. package/vendor/framey/dist/studio/assets/index-qrTAXbqc.js +531 -0
  568. package/vendor/framey/dist/studio/favicon.svg +14 -0
  569. package/vendor/framey/dist/studio/icons/timeline/audio.svg +7 -0
  570. package/vendor/framey/dist/studio/icons/timeline/captions.svg +5 -0
  571. package/vendor/framey/dist/studio/icons/timeline/composition.svg +12 -0
  572. package/vendor/framey/dist/studio/icons/timeline/image.svg +18 -0
  573. package/vendor/framey/dist/studio/icons/timeline/music.svg +10 -0
  574. package/vendor/framey/dist/studio/icons/timeline/text.svg +3 -0
  575. package/vendor/framey/dist/studio/index.html +23 -0
  576. package/vendor/framey/dist/studio-api-2NECGRMB.js +179 -0
  577. package/vendor/framey/dist/studioSelectionClient-IUUPKUHZ.js +25 -0
  578. package/vendor/framey/dist/studioServer-N2OPUMOQ.js +1569 -0
  579. package/vendor/framey/dist/synthesize-PKTB5ATG.js +183 -0
  580. package/vendor/framey/dist/system-7F3UEHQT.js +27 -0
  581. package/vendor/framey/dist/telemetry-DQGPEKM6.js +145 -0
  582. package/vendor/framey/dist/telemetry-UN55N6U6.js +77 -0
  583. package/vendor/framey/dist/timeline-LHUUMK63.js +1295 -0
  584. package/vendor/framey/dist/tokens-OLECWDFK.js +92 -0
  585. package/vendor/framey/dist/transcribe-O7HQKMIV.js +331 -0
  586. package/vendor/framey/dist/transcribe-UJESPBT5.js +76 -0
  587. package/vendor/framey/dist/tts-YCCQHZD4.js +234 -0
  588. package/vendor/framey/dist/urlDownloader-H2LXLG4U.js +48 -0
  589. package/vendor/framey/dist/usage-HG4WMM7A.js +2167 -0
  590. package/vendor/framey/dist/validate-F2XMN6EV.js +87 -0
  591. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/GSAP-LICENSE.txt +8 -0
  592. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/CustomEase.min.js +11 -0
  593. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js +11 -0
  594. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/gsap.min.js +11 -0
  595. package/vendor/framey/dist/warmCheck.js +220 -0
  596. package/vendor/framey/dist/warmCheckClient.js +139 -0
  597. package/vendor/framey/dist/workspaceBuildCheck-XYD2YTFW.js +133 -0
  598. package/vendor/framey-player/LICENSE +190 -0
  599. package/vendor/framey-player/NOTICE +16 -0
  600. package/vendor/framey-player/SOURCE.json +6 -0
  601. package/vendor/framey-player/hyperframes-player.global.js +478 -0
  602. package/vendor/framey.pin +1 -0
  603. package/bin/index.js +0 -3
@@ -0,0 +1,272 @@
1
+ import { mkdtemp, readFile, rm, stat } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { linesToBeats, scriptTemplate, validateScript } from "../generate/script.js";
5
+ import { dataDirFor } from "../home.js";
6
+ import { chooseEngine, readMachineEngine, readProjectEngine, unavailableMessage, failedMessage } from "../product/engine-choice.js";
7
+ import { engineById, engineStatus, type EngineProbes } from "../product/engines.js";
8
+ import { defaultProbes } from "../product/probes.js";
9
+ import { configFile, productDir } from "../product/paths.js";
10
+ import { readProductConfig } from "../product/config-file.js";
11
+ import { forcedFailure } from "../render/html-engine.js";
12
+ import { addBlock, blockCategories, blockIndex, listBlocks, type BlockInfo } from "./blocks.js";
13
+ import { initScene } from "./init.js";
14
+ import { runSceneStudio } from "./studio-command.js";
15
+ import { transitionIndex } from "./transitions.js";
16
+ import { formatText } from "./inspect/report.js";
17
+ import { inspectScene } from "./inspect/run.js";
18
+ import { renderScene, type SceneEngine } from "./render.js";
19
+ import { checkScene, SceneError, storeScene } from "./scene.js";
20
+
21
+ interface Io {
22
+ env: NodeJS.ProcessEnv;
23
+ stdout: (text: string) => void;
24
+ stderr: (text: string) => void;
25
+ probes?: EngineProbes;
26
+ }
27
+
28
+ function flag(argv: string[], name: string): string | null {
29
+ const at = argv.indexOf(name);
30
+ if (at === -1) return null;
31
+ const value = argv[at + 1];
32
+ return value && !value.startsWith("--") ? value : null;
33
+ }
34
+
35
+ const ENGINE_ALIASES: Record<string, string> = { framey: "vfrey-framey", "vfrey-framey": "vfrey-framey", hyperframes: "hyperframes", remotion: "remotion" };
36
+
37
+ const kb = (bytes: number) => `${(bytes / 1024).toFixed(1)} KB`;
38
+
39
+ function blockRows(blocks: BlockInfo[]): string {
40
+ const width = Math.max(...blocks.map((b) => b.name.length));
41
+ const cat = Math.max(...blocks.map((b) => b.category.length));
42
+ return blocks
43
+ .map((b) => {
44
+ const text = b.description.length > 78 ? `${b.description.slice(0, 77).trimEnd()}…` : b.description;
45
+ return `${b.name.padEnd(width)} ${b.category.padEnd(cat)} ${kb(b.bytes).padStart(7)} ${`${b.duration} s`.padStart(7)} ${text}`;
46
+ })
47
+ .join("\n");
48
+ }
49
+
50
+ /** `vfrey scene blocks [--json] [--category X]`. */
51
+ async function listCommand(argv: string[], io: Io): Promise<number> {
52
+ const category = flag(argv, "--category");
53
+ if (argv.includes("--category") && !category) {
54
+ io.stderr(`✗ --category needs a name.\nFix: choose one of ${(await blockCategories()).join(", ")}.\n`);
55
+ return 1;
56
+ }
57
+ const blocks = await listBlocks({ category: category ?? undefined });
58
+ if (category && blocks.length === 0) {
59
+ io.stderr(`✗ There is no block category "${category}".\nFix: choose one of ${(await blockCategories()).join(", ")}.\n`);
60
+ return 1;
61
+ }
62
+ if (argv.includes("--json")) {
63
+ const index = await blockIndex();
64
+ io.stdout(`${JSON.stringify({ source: index.source, commit: index.commit, count: blocks.length, blocks: blocks.map(({ files: _files, source: _source, ...rest }) => rest) }, null, 2)}\n`);
65
+ return 0;
66
+ }
67
+ const total = blocks.reduce((sum, b) => sum + b.bytes, 0);
68
+ io.stdout(`${blockRows(blocks)}\n\n${blocks.length} blocks, ${kb(total)}. Add one: npx vfrey scene add BLOCK DIR\n`);
69
+ return 0;
70
+ }
71
+
72
+ /** `vfrey scene transitions [--json]`. */
73
+ async function transitionsCommand(argv: string[], io: Io): Promise<number> {
74
+ const index = await transitionIndex();
75
+ if (argv.includes("--json")) {
76
+ io.stdout(`${JSON.stringify({ ...index, count: index.transitions.length, engines: { "vfrey-framey": true, hyperframes: true, remotion: false } }, null, 2)}\n`);
77
+ return 0;
78
+ }
79
+ const width = Math.max(...index.transitions.map((t) => t.name.length));
80
+ const rows = index.transitions.map((t) => `${t.name.padEnd(width)} ${(t.gpu ? "WebGL" : "no GPU").padEnd(6)} ${t.look}`).join("\n");
81
+ io.stdout(
82
+ `${rows}\n\n${index.transitions.length} transitions. In an HTML scene (vfrey-framey or hyperframes; not Remotion):\n` +
83
+ ` <script src="vfrey/transitions.js"></script> before <script src="vfrey/vfrey.js"></script>, then\n` +
84
+ ` vfrey.transition("#shot-a", "#shot-b", { type: "${index.default}", at: 3.2, duration: ${index.defaultSeconds} });\n`,
85
+ );
86
+ return 0;
87
+ }
88
+
89
+ /** `vfrey scene inspect DIR [--at S] [--selector CSS | --text "..."] [--script FILE] [--json]`. */
90
+ async function inspectCommand(argv: string[], io: Io): Promise<number> {
91
+ const fail = (message: string, fix: string) => {
92
+ io.stderr(`✗ ${message}\nFix: ${fix}\n`);
93
+ return 1;
94
+ };
95
+ const valued = ["--at", "--selector", "--text", "--script"];
96
+ const dir = argv.slice(1).find((a, i) => !a.startsWith("--") && !valued.includes(argv[i] ?? ""));
97
+ if (!dir) return fail("scene inspect needs a scene folder.", 'run `npx vfrey scene inspect my-scene --text "visible words"`.');
98
+ const atRaw = flag(argv, "--at");
99
+ const at = atRaw === null ? 0 : Number(atRaw);
100
+ if (!Number.isFinite(at) || at < 0) return fail(`--at needs a time in seconds, not "${atRaw}".`, "for example `--at 2.5`.");
101
+ const selector = flag(argv, "--selector");
102
+ const text = flag(argv, "--text");
103
+ for (const name of ["--selector", "--text", "--script", "--at"]) {
104
+ if (argv.includes(name) && flag(argv, name) === null) return fail(`${name} needs a value.`, `for example ${name === "--text" ? '--text "Big title"' : name === "--selector" ? "--selector '#title'" : name === "--at" ? "--at 2.5" : "--script script.json"}.`);
105
+ }
106
+ if (selector && text) return fail("Use --selector or --text, not both.", "pick the one that names the element best.");
107
+ const abs = path.resolve(dir);
108
+ let script;
109
+ const scriptFile = flag(argv, "--script");
110
+ if (scriptFile) {
111
+ const raw = await readFile(path.resolve(scriptFile), "utf8").catch(() => null);
112
+ if (raw === null) return fail(`Cannot read the script file ${scriptFile}.`, "check the path.");
113
+ const checked = validateScript(raw);
114
+ if (!checked.ok) return fail(`${scriptFile}: ${checked.message}`, checked.fix);
115
+ script = checked.script;
116
+ }
117
+ try {
118
+ const kind = await Promise.all([stat(path.join(abs, "index.html")).then(() => true, () => false), stat(path.join(abs, "index.tsx")).then(() => true, () => false)]);
119
+ if (!kind[0] && kind[1]) {
120
+ return fail(
121
+ `The scene in ${dir} is a Remotion scene (index.tsx); scene inspect reads HTML scenes (vfrey-framey or hyperframes), because it traces elements, CSS rules and GSAP tweens.`,
122
+ `look at it with \`npx vfrey render --scene ${dir} --engine remotion --frame 2.5 out.png\` and edit the component in index.tsx.`,
123
+ );
124
+ }
125
+ const report = await inspectScene({ dir: abs, at, selector, text, script, env: io.env });
126
+ io.stdout(argv.includes("--json") ? `${JSON.stringify(report, null, 2)}\n` : formatText(report));
127
+ return 0;
128
+ } catch (error) {
129
+ if (error instanceof SceneError) return fail(error.message, error.fix);
130
+ return fail(error instanceof Error ? error.message : String(error), "run `npx vfrey doctor`, then try again.");
131
+ }
132
+ }
133
+
134
+ /** `vfrey scene add BLOCK DIR [--force] [--json]`. */
135
+ async function addCommand(argv: string[], io: Io): Promise<number> {
136
+ const positional = argv.slice(1).filter((a) => !a.startsWith("--"));
137
+ const [name, dir] = positional;
138
+ if (!name || !dir) {
139
+ io.stderr("✗ scene add needs a block and a scene folder.\nFix: run `npx vfrey scene add lt-clean-bar my-scene`; `npx vfrey scene blocks` lists the blocks.\n");
140
+ return 1;
141
+ }
142
+ try {
143
+ const result = await addBlock(path.resolve(dir), name, { force: argv.includes("--force") });
144
+ if (argv.includes("--json")) {
145
+ io.stdout(`${JSON.stringify({ block: result.block.name, status: result.status, dest: result.dest, files: result.files, html: result.snippet.html, script: result.snippet.script }, null, 2)}\n`);
146
+ return 0;
147
+ }
148
+ const verb = result.status === "unchanged" ? "Already in the scene (unchanged)" : result.status === "replaced" ? "Replaced" : "Added";
149
+ io.stdout(`${verb}: ${path.relative(process.cwd(), result.dest) || result.dest} (${result.block.files.length} file, ${kb(result.block.bytes)})\n\n${result.snippet.text}`);
150
+ return 0;
151
+ } catch (error) {
152
+ if (error instanceof SceneError) {
153
+ io.stderr(`✗ ${error.message}\nFix: ${error.fix}\n`);
154
+ return 1;
155
+ }
156
+ throw error;
157
+ }
158
+ }
159
+
160
+ /** `vfrey scene init DIR [--engine framey|hyperframes|remotion]`, `scene blocks`, `scene add`. */
161
+ export async function runSceneCommand(argv: string[], io: Io): Promise<number> {
162
+ const [action, dir] = argv;
163
+ if (action === "blocks") return listCommand(argv, io);
164
+ if (action === "add") return addCommand(argv, io);
165
+ if (action === "transitions") return transitionsCommand(argv, io);
166
+ if (action === "studio") return runSceneStudio(argv.slice(1), io);
167
+ if (action === "inspect") return inspectCommand(argv, io);
168
+ if (action !== "init") {
169
+ io.stderr(`✗ Unknown scene command "${action ?? ""}".\nFix: use \`npx vfrey scene init DIR [--engine framey|hyperframes|remotion]\`, \`scene blocks\`, \`scene add BLOCK DIR\`, or \`scene transitions\`.\n`);
170
+ return 1;
171
+ }
172
+ if (!dir || dir.startsWith("--")) {
173
+ io.stderr("✗ scene init needs a folder.\nFix: run `npx vfrey scene init my-scene`.\n");
174
+ return 1;
175
+ }
176
+ const asked = flag(argv, "--engine") ?? "framey";
177
+ const id = ENGINE_ALIASES[asked.toLowerCase()];
178
+ if (!id) {
179
+ io.stderr(`✗ "${asked}" is not an engine that renders scenes.\nFix: choose framey, hyperframes, or remotion.\n`);
180
+ return 1;
181
+ }
182
+ const kind = id === "remotion" ? "tsx" : "html";
183
+ try {
184
+ const written = await initScene(path.resolve(dir), kind, io.env);
185
+ io.stdout(`Scene written: ${written[0]}\nvfrey's files (timing, helper, fonts, GSAP, art) are in ${written[1]}; they are rewritten at render time.\nPreview a frame: npx vfrey render --scene ${dir} --engine ${id} --frame 2.5 out.png\n`);
186
+ return 0;
187
+ } catch (error) {
188
+ io.stderr(`✗ ${error instanceof Error ? error.message : String(error)}\nFix: pick a folder that has no scene yet, or edit the one that is there.\n`);
189
+ return 1;
190
+ }
191
+ }
192
+
193
+ /** `vfrey render --scene DIR [--engine id] [--script FILE] [--frame SECONDS OUT.png | --output OUT.mp4]`. */
194
+ export async function runSceneRender(argv: string[], io: Io): Promise<number> {
195
+ const fail = (message: string, fix: string) => {
196
+ io.stderr(`✗ ${message}\nFix: ${fix}\n`);
197
+ return 1;
198
+ };
199
+ const dir = path.resolve(flag(argv, "--scene")!);
200
+ const dataDir = dataDirFor(io.env);
201
+ const dir0 = productDir(io.env);
202
+ const config = await readProductConfig(configFile(dir0), dir0);
203
+ const requested = flag(argv, "--engine");
204
+ let engine;
205
+ try {
206
+ engine = chooseEngine({ command: requested, project: readProjectEngine(), machine: readMachineEngine(dataDir) ?? config.defaultEngine }).engine;
207
+ } catch {
208
+ return fail(`"${requested}" is not an engine.`, "choose remotion, hyperframes, or vfrey-framey.");
209
+ }
210
+ try {
211
+ await checkScene(dir, engine.id);
212
+ } catch (error) {
213
+ if (error instanceof SceneError) return fail(error.message, error.fix);
214
+ throw error;
215
+ }
216
+ const probes = io.probes ?? (await defaultProbes(io.env));
217
+ if (engineStatus(engine, probes).availability === "unavailable") {
218
+ const [what, fix] = unavailableMessage(engine, probes).split(" Fix: ");
219
+ return fail(what ?? "", fix ?? "run `npx vfrey doctor`.");
220
+ }
221
+ let script;
222
+ const scriptFile = flag(argv, "--script");
223
+ if (scriptFile) {
224
+ let text: string;
225
+ try {
226
+ text = await readFile(path.resolve(scriptFile), "utf8");
227
+ } catch {
228
+ return fail(`Cannot read the script file ${scriptFile}.`, "check the path.");
229
+ }
230
+ const checked = validateScript(text);
231
+ if (!checked.ok) return fail(`${scriptFile}: ${checked.message}`, checked.fix);
232
+ script = checked.script;
233
+ } else {
234
+ script = validateScript(scriptTemplate());
235
+ if (!script.ok) return fail("The sample script is broken.", "report this bug.");
236
+ script = script.script;
237
+ }
238
+ const frameAtRaw = flag(argv, "--frame");
239
+ const frameAt = frameAtRaw === null ? undefined : Number(frameAtRaw);
240
+ if (frameAt !== undefined && (!Number.isFinite(frameAt) || frameAt < 0)) return fail(`--frame needs a time in seconds, not "${frameAtRaw}".`, "for example `--frame 2.5 out.png`.");
241
+ const positional = argv.filter((a, i) => !a.startsWith("--") && !["--scene", "--engine", "--script", "--frame", "--output", "--subject"].includes(argv[i - 1] ?? ""))[0];
242
+ const output = path.resolve(flag(argv, "--output") ?? positional ?? (frameAt === undefined ? path.join(config.outputDir, `scene-${engine.id}.mp4`) : "scene-frame.png"));
243
+ const work = await mkdtemp(path.join(os.tmpdir(), "vfrey-scene-"));
244
+ try {
245
+ const forced = forcedFailure(engine.id, io.env);
246
+ if (forced) throw new Error(forced);
247
+ const stored = await storeScene(dir, path.join(work, "scene"));
248
+ for (const message of stored.warnings) io.stderr(`! ${message}\n`);
249
+ await renderScene({
250
+ engine: engine.id as SceneEngine,
251
+ sceneDir: path.join(work, "scene"),
252
+ title: script.title,
253
+ lines: script.lines,
254
+ beats: linesToBeats(script.lines).map((b) => ({ role: b.role, text: b.text, caption: b.caption })),
255
+ voiceoverPath: null,
256
+ outputPath: output,
257
+ workDir: path.join(work, "render"),
258
+ frameAt,
259
+ env: io.env,
260
+ warn: (message) => io.stderr(`! ${message}\n`),
261
+ });
262
+ io.stdout(`${output}\n`);
263
+ return 0;
264
+ } catch (error) {
265
+ const [what, fix] = failedMessage(engine, error, probes).split(" Fix: ");
266
+ return fail(what ?? "", fix ?? "run `npx vfrey doctor`.");
267
+ } finally {
268
+ await rm(work, { recursive: true, force: true });
269
+ }
270
+ }
271
+
272
+
@@ -0,0 +1,181 @@
1
+ export const POSES = ["shrug", "pointLeft", "pointRight", "idea"] as const;
2
+ export type Pose = (typeof POSES)[number];
3
+
4
+ /**
5
+ * The stick figure, drawn from scratch as a pen sketch: every stroke goes through a few jittered points and is
6
+ * inked twice, a heavy pass and a thin offset pass, so the line weight is uneven and the ends are round.
7
+ */
8
+
9
+ type Pt = [number, number];
10
+
11
+ function rng(seed: number): () => number {
12
+ let s = seed >>> 0 || 1;
13
+ return () => {
14
+ s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
15
+ return s / 4294967296 - 0.5;
16
+ };
17
+ }
18
+
19
+ /** How heavy the pen is: the reference figure is drawn with a thick marker line. */
20
+ const HEAVY = 1.55;
21
+
22
+ const r1 = (n: number) => Math.round(n * 10) / 10;
23
+
24
+ function smooth(points: Pt[]): string {
25
+ if (points.length === 1) return `M${r1(points[0]![0])} ${r1(points[0]![1])}`;
26
+ let d = `M${r1(points[0]![0])} ${r1(points[0]![1])}`;
27
+ for (let i = 1; i < points.length - 1; i += 1) {
28
+ const [x, y] = points[i]!;
29
+ const [nx, ny] = points[i + 1]!;
30
+ d += `Q${r1(x)} ${r1(y)} ${r1((x + nx) / 2)} ${r1((y + ny) / 2)}`;
31
+ }
32
+ const end = points[points.length - 1]!;
33
+ return d + `L${r1(end[0])} ${r1(end[1])}`;
34
+ }
35
+
36
+ /** Adds a wobbling extra point on each long segment so a straight line is not ruler-straight. */
37
+ function sketch(points: Pt[], rand: () => number, amount: number): Pt[] {
38
+ const out: Pt[] = [points[0]!];
39
+ for (let i = 1; i < points.length; i += 1) {
40
+ const [x0, y0] = points[i - 1]!;
41
+ const [x1, y1] = points[i]!;
42
+ const len = Math.hypot(x1 - x0, y1 - y0);
43
+ const extra = Math.min(3, Math.floor(len / 70));
44
+ for (let k = 1; k <= extra; k += 1) {
45
+ const t = k / (extra + 1);
46
+ out.push([x0 + (x1 - x0) * t + rand() * amount * 2, y0 + (y1 - y0) * t + rand() * amount * 2]);
47
+ }
48
+ out.push([x1 + (i === points.length - 1 ? 0 : rand() * amount), y1 + (i === points.length - 1 ? 0 : rand() * amount)]);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ class Pen {
54
+ private parts: string[] = [];
55
+ private rand: () => number;
56
+ constructor(seed: number) {
57
+ this.rand = rng(seed);
58
+ }
59
+ stroke(points: Pt[], width0 = 13): this {
60
+ const width = Math.round(width0 * HEAVY);
61
+ const base = sketch(points, this.rand, 2.2);
62
+ this.parts.push(`<path d="${smooth(base)}" stroke-width="${width}"/>`);
63
+ const second = sketch(points.map(([x, y]): Pt => [x + 2.4, y - 1.6]), this.rand, 2.6);
64
+ this.parts.push(`<path d="${smooth(second)}" stroke-width="${Math.max(3, Math.round(width * 0.38))}"/>`);
65
+ return this;
66
+ }
67
+ dot(x: number, y: number, rx: number, ry: number, rot = 0): this {
68
+ this.parts.push(`<ellipse cx="${x}" cy="${y}" rx="${rx}" ry="${ry}" transform="rotate(${rot} ${x} ${y})" fill="#000" stroke-width="3"/>`);
69
+ return this;
70
+ }
71
+ /** A closed loop drawn by hand: the pen overshoots where it started. */
72
+ loop(cx: number, cy: number, rx: number, ry: number, width0 = 13, turn = 0): this {
73
+ const width = Math.round(width0 * HEAVY);
74
+ const pts: Pt[] = [];
75
+ const steps = 14;
76
+ for (let i = 0; i <= steps + 2; i += 1) {
77
+ const a = turn + (i / steps) * Math.PI * 2;
78
+ const wobble = 1 + this.rand() * 0.035 + (i > steps ? 0.045 : 0);
79
+ pts.push([cx + Math.cos(a) * rx * wobble, cy + Math.sin(a) * ry * wobble]);
80
+ }
81
+ this.parts.push(`<path d="${smooth(pts)}" stroke-width="${width}"/>`);
82
+ const again = pts.slice(2, 12).map(([x, y]): Pt => [x + 3, y - 2]);
83
+ this.parts.push(`<path d="${smooth(again)}" stroke-width="${Math.round(width * 0.38)}"/>`);
84
+ return this;
85
+ }
86
+ raw(svg: string): this {
87
+ this.parts.push(svg);
88
+ return this;
89
+ }
90
+ get svg(): string {
91
+ return this.parts.join("");
92
+ }
93
+ }
94
+
95
+ const HEAD: Pt = [300, 172];
96
+
97
+ function open(hand: Pt, dir: 1 | -1, pen: Pen): void {
98
+ // An open palm, facing up: a flat leaf with a thumb.
99
+ const [x, y] = hand;
100
+ pen.stroke([[x, y], [x + dir * 40, y - 14], [x + dir * 82, y - 4], [x + dir * 44, y + 14], [x + dir * 6, y + 10]], 10);
101
+ pen.stroke([[x + dir * 30, y - 12], [x + dir * 34, y - 30]], 8);
102
+ }
103
+
104
+ function legs(pen: Pen): void {
105
+ pen.stroke([[300, 232], [300, 310], [297, 440]], 15);
106
+ pen.stroke([[297, 440], [262, 560], [226, 690]], 15);
107
+ pen.stroke([[297, 440], [338, 560], [372, 690]], 15);
108
+ pen.loop(206, 700, 36, 11, 9);
109
+ pen.loop(392, 700, 36, 11, 9);
110
+ }
111
+
112
+ function faceLook(pen: Pen, mood: "puzzled" | "happy", gaze: number): void {
113
+ const gx = gaze;
114
+ pen.loop(HEAD[0], HEAD[1], 80, 80, 14, -1.2);
115
+ pen.stroke([[238, 112], [250, 96], [243, 86]], 4); // a tuft of hair
116
+ pen.stroke([[256, 100], [272, 88]], 4);
117
+ pen.dot(268 + gx, 170, 8, 13, 8).dot(334 + gx, 168, 8, 13, -6);
118
+ if (mood === "puzzled") {
119
+ pen.stroke([[246 + gx, 152], [268 + gx, 146], [292 + gx, 150]], 5); // flat brow
120
+ pen.stroke([[322 + gx, 140], [345 + gx, 122], [372 + gx, 136]], 5); // raised brow
121
+ pen.loop(308 + gx, 210, 8, 11, 4);
122
+ } else {
123
+ pen.stroke([[250 + gx, 150], [268 + gx, 142], [290 + gx, 148]], 5);
124
+ pen.stroke([[322 + gx, 146], [340 + gx, 138], [360 + gx, 146]], 5);
125
+ pen.stroke([[272 + gx, 206], [300 + gx, 226], [338 + gx, 204]], 5);
126
+ }
127
+ }
128
+
129
+ function questionMark(pen: Pen): void {
130
+ pen.stroke([[388, 62], [402, 38], [430, 34], [448, 54], [434, 80], [414, 94], [412, 114]], 9);
131
+ pen.dot(412, 140, 6, 6);
132
+ }
133
+
134
+ function ticks(pen: Pen, cx: number, cy: number, dir: 1 | -1): void {
135
+ pen.stroke([[cx, cy], [cx + dir * 26, cy - 20]], 6);
136
+ pen.stroke([[cx - dir * 4, cy + 28], [cx + dir * 24, cy + 18]], 6);
137
+ }
138
+
139
+ function bulb(pen: Pen, x: number, y: number): void {
140
+ pen.raw(`<path d="M${x - 26} ${y + 30}a44 44 0 1 1 52 0c-10 8-12 18-12 28h-28c0-10-2-20-12-28z" fill="#E3F2FD" stroke-width="9"/>`);
141
+ pen.stroke([[x - 14, y + 72], [x + 14, y + 72]], 8);
142
+ pen.stroke([[x - 8, y + 88], [x + 8, y + 88]], 7);
143
+ pen.stroke([[x - 6, y + 40], [x - 2, y + 22], [x + 6, y + 40]], 5);
144
+ for (const [dx, dy, ex, ey] of [[0, -60, 0, -78], [-50, -34, -66, -46], [50, -34, 66, -46], [-64, 8, -84, 10], [64, 8, 84, 10]] as const) {
145
+ pen.stroke([[x + dx, y + dy], [x + ex, y + ey]], 7);
146
+ }
147
+ }
148
+
149
+ /** The figure, as one SVG document of 600 by 780 units. Stroke is black, fill is none unless a shape says so. */
150
+ export function figureSvg(pose: Pose): string {
151
+ const pen = new Pen({ shrug: 11, pointLeft: 23, pointRight: 37, idea: 51 }[pose]);
152
+ legs(pen);
153
+ if (pose === "shrug") {
154
+ faceLook(pen, "puzzled", 0);
155
+ questionMark(pen);
156
+ pen.stroke([[300, 268], [226, 370], [150, 330]], 14);
157
+ pen.stroke([[300, 268], [374, 370], [450, 330]], 14);
158
+ open([142, 318], -1, pen);
159
+ open([458, 318], 1, pen);
160
+ ticks(pen, 88, 246, -1);
161
+ ticks(pen, 512, 246, 1);
162
+ } else if (pose === "pointLeft") {
163
+ faceLook(pen, "happy", -16);
164
+ pen.stroke([[300, 268], [226, 232], [136, 182]], 14);
165
+ pen.stroke([[136, 182], [96, 160], [52, 140]], 8); // the pointing finger
166
+ pen.stroke([[300, 268], [352, 352], [338, 438]], 14);
167
+ } else if (pose === "pointRight") {
168
+ faceLook(pen, "happy", 16);
169
+ pen.stroke([[300, 268], [374, 232], [464, 182]], 14);
170
+ pen.stroke([[464, 182], [504, 160], [548, 140]], 8);
171
+ pen.stroke([[300, 268], [248, 352], [262, 438]], 14);
172
+ } else {
173
+ faceLook(pen, "happy", 0);
174
+ pen.stroke([[300, 268], [236, 338], [262, 408]], 14);
175
+ pen.stroke([[300, 268], [380, 236], [424, 176]], 14);
176
+ pen.stroke([[424, 176], [430, 150]], 8);
177
+ bulb(pen, 470, 92);
178
+ }
179
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 780" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round">${pen.svg}</svg>`;
180
+ }
181
+
@@ -0,0 +1,107 @@
1
+ /** Simple line glyphs a scene may use or ignore: common ideas in a thick marker line, 400 by 400 units. */
2
+
3
+ export const GLYPH_NAMES = ["cpu", "threads", "lock", "database", "cloud", "server", "cache", "queue", "network", "file", "key", "gear", "arrow", "check", "cross", "question", "star", "clock", "bulb", "person", "chat", "globe"] as const;
4
+ export type GlyphName = (typeof GLYPH_NAMES)[number];
5
+
6
+ const INK = "#000000";
7
+ const ICE = "#E3F2FD";
8
+ const SKY = "#90CAF9";
9
+ const BLUE = "#2196F3";
10
+ const NAVY = "#0D47A1";
11
+
12
+ const open = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" fill="none" stroke="${INK}" stroke-width="13" stroke-linecap="round" stroke-linejoin="round">`;
13
+
14
+ const BODIES: Record<GlyphName, string> = {
15
+ cpu: `
16
+ <rect x="95" y="95" width="210" height="210" rx="24" fill="${ICE}"/>
17
+ <rect x="145" y="145" width="110" height="110" rx="14" fill="${BLUE}" stroke="${NAVY}"/>
18
+ <path d="M140 95V45M200 95V40M260 95V46M140 305v52M200 305v55M260 305v50M95 140H45M95 200H40M95 260h-50M305 140h52M305 200h55M305 260h50"/>`,
19
+ threads: `
20
+ <path d="M50 110C120 80 150 140 210 110S300 80 350 110" stroke="${BLUE}" stroke-width="16"/>
21
+ <path d="M50 200C110 170 160 230 215 200S300 170 350 200"/>
22
+ <path d="M50 290C130 260 150 320 210 290S300 262 350 290" stroke="${NAVY}"/>
23
+ <path d="M318 88l34 22-36 20M318 178l34 22-36 20M318 268l34 22-36 20" stroke-width="11"/>`,
24
+ lock: `
25
+ <path d="M130 175V130a70 70 0 0 1 140 0v45" stroke-width="18"/>
26
+ <rect x="85" y="170" width="230" height="180" rx="26" fill="${ICE}"/>
27
+ <circle cx="200" cy="248" r="24" fill="${BLUE}" stroke="${NAVY}"/>
28
+ <path d="M200 270v40"/>`,
29
+ database: `
30
+ <ellipse cx="200" cy="100" rx="120" ry="42" fill="${ICE}"/>
31
+ <path d="M80 100v200c0 24 54 42 120 42s120-18 120-42V100"/>
32
+ <path d="M80 200c0 24 54 42 120 42s120-18 120-42" stroke="${NAVY}"/>`,
33
+ cloud: `
34
+ <path d="M120 295a72 72 0 0 1-8-143 92 92 0 0 1 176 18 62 62 0 0 1 4 125z" fill="${ICE}"/>
35
+ <path d="M150 250c30-26 70-26 100 0" stroke="${BLUE}" stroke-width="15"/>
36
+ <path d="M200 228v-45m-26 22 26-26 26 26" stroke="${NAVY}" stroke-width="11"/>`,
37
+ server: `
38
+ <rect x="80" y="60" width="240" height="80" rx="16" fill="${ICE}"/>
39
+ <rect x="80" y="160" width="240" height="80" rx="16" fill="${ICE}"/>
40
+ <rect x="80" y="260" width="240" height="80" rx="16" fill="${ICE}"/>
41
+ <circle cx="125" cy="100" r="9" fill="${BLUE}" stroke="${BLUE}"/><circle cx="125" cy="200" r="9" fill="${BLUE}" stroke="${BLUE}"/><circle cx="125" cy="300" r="9" fill="${BLUE}" stroke="${BLUE}"/>
42
+ <path d="M190 100h95M190 200h95M190 300h95" stroke="${NAVY}"/>`,
43
+ cache: `
44
+ <path d="M232 40L120 215h72l-26 145 116-190h-74z" fill="${BLUE}" stroke="${NAVY}"/>
45
+ <path d="M60 130h50M40 200h60M65 270h45" stroke="${SKY}" stroke-width="12"/>`,
46
+ queue: `
47
+ <rect x="45" y="140" width="80" height="120" rx="12" fill="${ICE}"/>
48
+ <rect x="135" y="140" width="80" height="120" rx="12" fill="${ICE}"/>
49
+ <rect x="225" y="140" width="80" height="120" rx="12" fill="${BLUE}" stroke="${NAVY}"/>
50
+ <path d="M325 200h48m-20-22 22 22-22 22" stroke="${NAVY}"/>
51
+ <path d="M45 310h260" stroke="${SKY}" stroke-width="10"/>`,
52
+ network: `
53
+ <circle cx="200" cy="90" r="38" fill="${BLUE}" stroke="${NAVY}"/>
54
+ <circle cx="85" cy="300" r="38" fill="${ICE}"/>
55
+ <circle cx="315" cy="300" r="38" fill="${ICE}"/>
56
+ <path d="M178 124L108 270M222 124l70 146M123 300h154" stroke-width="11"/>`,
57
+ file: `
58
+ <path d="M100 50h130l80 80v220a8 8 0 0 1-8 8H100a8 8 0 0 1-8-8V58a8 8 0 0 1 8-8z" fill="${ICE}"/>
59
+ <path d="M230 50v80h80"/>
60
+ <path d="M135 200h135M135 245h135M135 290h90" stroke="${BLUE}" stroke-width="14"/>`,
61
+ key: `
62
+ <circle cx="125" cy="150" r="78" fill="${ICE}"/>
63
+ <circle cx="105" cy="130" r="18" fill="${BLUE}" stroke="${NAVY}"/>
64
+ <path d="M178 205L340 365M270 295l-34 34M310 335l-30 30"/>`,
65
+ gear: `
66
+ <path d="M170 40h60l10 42 36 15 38-22 42 42-22 38 15 36 42 10v60l-42 10-15 36 22 38-42 42-38-22-36 15-10 42h-60l-10-42-36-15-38 22-42-42 22-38-15-36-42-10v-60l42-10 15-36-22-38 42-42 38 22 36-15z" transform="translate(0 0) scale(.92) translate(17 17)" fill="${ICE}"/>
67
+ <circle cx="200" cy="200" r="52" fill="${BLUE}" stroke="${NAVY}"/>`,
68
+
69
+ arrow: `
70
+ <path d="M60 200h250m-100-100 100 100-100 100" stroke="${NAVY}" stroke-width="22"/>`,
71
+ check: `
72
+ <circle cx="200" cy="200" r="150" fill="${ICE}"/>
73
+ <path d="M120 205l55 55 105-120" stroke="${BLUE}" stroke-width="26"/>`,
74
+ cross: `
75
+ <circle cx="200" cy="200" r="150" fill="${ICE}"/>
76
+ <path d="M135 135l130 130M265 135L135 265" stroke="${NAVY}" stroke-width="26"/>`,
77
+ question: `
78
+ <circle cx="200" cy="200" r="150" fill="${ICE}"/>
79
+ <path d="M148 160c0-34 24-54 54-54s54 20 54 50c0 34-54 40-54 82" stroke="${BLUE}" stroke-width="22"/>
80
+ <circle cx="202" cy="296" r="11" fill="${NAVY}" stroke="${NAVY}"/>`,
81
+ star: `
82
+ <path d="M200 50l45 98 106 12-79 72 22 105-94-54-94 54 22-105-79-72 106-12z" fill="${ICE}"/>
83
+ <path d="M200 120l22 48 52 6-39 36 11 52-46-27-46 27 11-52-39-36 52-6z" fill="${BLUE}" stroke="${NAVY}" stroke-width="8"/>`,
84
+ clock: `
85
+ <circle cx="200" cy="200" r="150" fill="${ICE}"/>
86
+ <path d="M200 110v95l62 40" stroke="${NAVY}" stroke-width="20"/>
87
+ <circle cx="200" cy="200" r="10" fill="${BLUE}" stroke="${BLUE}"/>`,
88
+ bulb: `
89
+ <path d="M140 270a100 100 0 1 1 120 0c-14 12-18 26-18 42h-84c0-16-4-30-18-42z" fill="${ICE}"/>
90
+ <path d="M160 340h80M175 366h50" stroke="${NAVY}"/>
91
+ <path d="M185 240l15-40 15 40" stroke="${BLUE}" stroke-width="10"/>`,
92
+ person: `
93
+ <circle cx="200" cy="120" r="60" fill="${ICE}"/>
94
+ <path d="M70 350c0-85 55-130 130-130s130 45 130 130z" fill="${ICE}"/>`,
95
+ chat: `
96
+ <path d="M60 90h280a20 20 0 0 1 20 20v160a20 20 0 0 1-20 20H190l-70 60v-60H60a20 20 0 0 1-20-20V110a20 20 0 0 1 20-20z" fill="${ICE}"/>
97
+ <path d="M100 160h200M100 215h130" stroke="${BLUE}" stroke-width="14"/>`,
98
+ globe: `
99
+ <circle cx="200" cy="200" r="150" fill="${ICE}"/>
100
+ <ellipse cx="200" cy="200" rx="60" ry="150" stroke="${BLUE}"/>
101
+ <path d="M50 200h300M80 120h240M80 280h240" stroke="${NAVY}" stroke-width="9"/>`,
102
+ };
103
+
104
+ export function glyphSvg(name: GlyphName): string {
105
+ return `${open}${BODIES[name]}</svg>`;
106
+ }
107
+
@@ -0,0 +1,86 @@
1
+ import { mkdir, stat, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { linesToBeats, scriptTemplate, type ScriptFile } from "../generate/script.js";
4
+ import { estimateTiming } from "./timing.js";
5
+ import { writeSupply } from "./supply.js";
6
+
7
+ export const STARTER_HTML = `<!doctype html>
8
+ <!-- A vfrey scene: a HyperFrames composition. vfrey renders it at 1080x1920, 30 fps, for exactly as long as the
9
+ voice runs plus 0.3 s, and puts the AI-GENERATED label on top afterwards. Replace everything below. -->
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8" />
13
+ <meta name="viewport" content="width=1080, height=1920" />
14
+ <link rel="stylesheet" href="vfrey/fonts/fonts.css" /> <!-- Titan One, JetBrains Mono, Google Sans Flex -->
15
+ <script src="vfrey/gsap.min.js"></script> <!-- local GSAP: scenes cannot load from a CDN -->
16
+ <script src="vfrey/vfrey.js"></script> <!-- vfrey.timing, vfrey.captions, vfrey.at, vfrey.onWord, vfrey.line -->
17
+ <style>
18
+ html, body { margin: 0; width: 1080px; height: 1920px; overflow: hidden; }
19
+ #root { position: relative; width: 1080px; height: 1920px; background: #ffffff; color: #111; font-family: "Titan One", sans-serif; }
20
+ #title { position: absolute; left: 60px; right: 60px; top: 220px; text-align: center; font-size: 92px; line-height: 1.05; }
21
+ #dot { position: absolute; left: 440px; top: 780px; width: 200px; height: 200px; border-radius: 50%; background: #2196f3; }
22
+ #captions { position: absolute; left: 50px; right: 50px; bottom: 220px; height: 250px; } /* put captions anywhere */
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <!-- No data-duration needed: vfrey sets it from the voice. -->
27
+ <div id="root" data-composition-id="main" data-start="0" data-width="1080" data-height="1920">
28
+ <div id="title"></div>
29
+ <div id="dot"></div>
30
+ <div id="captions"></div>
31
+ </div>
32
+ <script>
33
+ const tl = vfrey.timeline(); // one paused GSAP timeline; vfrey.at / vfrey.captions add to it
34
+ document.getElementById("title").textContent = vfrey.timing.title;
35
+ tl.from("#title", { y: 60, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.1);
36
+ // Cue an animation on the second line of the script, at the moment it is spoken.
37
+ vfrey.onLine(Math.min(1, vfrey.timing.lines.length - 1), (tl, t) => tl.to("#dot", { scale: 1.6, duration: 0.4, ease: "back.out(2)" }, t));
38
+ vfrey.captions("#captions", { style: "box" }); // style: box | color | underline | pop | plain
39
+ </script>
40
+ </body>
41
+ </html>
42
+ `;
43
+
44
+ export const STARTER_TSX = `// A vfrey scene for Remotion. vfrey renders it at 1080x1920, 30 fps, for exactly as long as the voice runs plus
45
+ // 0.3 s, plays the voice, and puts the AI-GENERATED label on top afterwards. Replace everything below.
46
+ import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
47
+ import { AtLine, Captions, useTiming } from "@vfrey/scene"; // timing, captions and line cues from vfrey
48
+
49
+ export default function Scene() {
50
+ const timing = useTiming(); // words, lines and beats with start and end times in seconds
51
+ const frame = useCurrentFrame();
52
+ const { fps } = useVideoConfig();
53
+ const pop = spring({ frame, fps, config: { damping: 12 } });
54
+ return (
55
+ <AbsoluteFill style={{ background: "#ffffff", color: "#111", fontFamily: '"Titan One", sans-serif' }}>
56
+ <div style={{ position: "absolute", left: 60, right: 60, top: 220, textAlign: "center", fontSize: 92, lineHeight: 1.05, opacity: interpolate(pop, [0, 1], [0, 1]) }}>
57
+ {timing.title}
58
+ </div>
59
+ {/* Shown while line 1 is spoken. */}
60
+ <AtLine i={Math.min(1, timing.lines.length - 1)}>
61
+ <div style={{ position: "absolute", left: 440, top: 780, width: 200, height: 200, borderRadius: "50%", background: "#2196f3" }} />
62
+ </AtLine>
63
+ {/* captionStyle: box | color | underline | pop | plain. Position the block anywhere. */}
64
+ <Captions captionStyle="box" style={{ position: "absolute", left: 50, right: 50, bottom: 220, height: 250 }} />
65
+ </AbsoluteFill>
66
+ );
67
+ }
68
+ `;
69
+
70
+ function sampleScript(): ScriptFile {
71
+ return JSON.parse(scriptTemplate()) as ScriptFile;
72
+ }
73
+
74
+ /** Writes a starter scene into `dir`. The `vfrey/` folder is written too, so the HTML opens in a browser as it is. */
75
+ export async function initScene(dir: string, kind: "html" | "tsx", env: Record<string, string | undefined> = process.env): Promise<string[]> {
76
+ const entry = path.join(dir, kind === "html" ? "index.html" : "index.tsx");
77
+ if (await stat(entry).then(() => true, () => false)) {
78
+ throw new Error(`${entry} already exists.`);
79
+ }
80
+ await mkdir(dir, { recursive: true });
81
+ await writeFile(entry, kind === "html" ? STARTER_HTML : STARTER_TSX);
82
+ const script = sampleScript();
83
+ const beats = linesToBeats(script.lines).map((b) => ({ role: b.role, text: b.text, caption: b.caption }));
84
+ await writeSupply({ dir, timing: estimateTiming({ title: script.title, lines: script.lines, beats }), voiceFile: null, kind, env });
85
+ return [entry, path.join(dir, "vfrey")];
86
+ }
@@ -0,0 +1,6 @@
1
+ declare module "css-tree" {
2
+ export type CssNode = any;
3
+ export function parse(css: string, options?: Record<string, unknown>): CssNode;
4
+ export function generate(node: CssNode): string;
5
+ export function walk(node: CssNode, options: { visit?: string; enter?: (this: any, node: CssNode, item: any, list: any) => void }): void;
6
+ }