frameyard 0.7.0 → 0.9.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 (334) hide show
  1. package/CHANGELOG.md +164 -7
  2. package/GLOSSARY.md +152 -18
  3. package/README.md +328 -285
  4. package/dist/assets/integrations.d.ts +8 -4
  5. package/dist/assets/integrations.d.ts.map +1 -1
  6. package/dist/assets/integrations.js +8 -4
  7. package/dist/assets/integrations.js.map +1 -1
  8. package/dist/assets/provenance.d.ts +2 -2
  9. package/dist/assets/provenance.d.ts.map +1 -1
  10. package/dist/assets/provenance.js +5 -3
  11. package/dist/assets/provenance.js.map +1 -1
  12. package/dist/assets/tts-supertonic.d.ts +28 -0
  13. package/dist/assets/tts-supertonic.d.ts.map +1 -0
  14. package/dist/assets/tts-supertonic.js +156 -0
  15. package/dist/assets/tts-supertonic.js.map +1 -0
  16. package/dist/assets/tts.d.ts +18 -6
  17. package/dist/assets/tts.d.ts.map +1 -1
  18. package/dist/assets/tts.js +219 -97
  19. package/dist/assets/tts.js.map +1 -1
  20. package/dist/cli.js +1705 -106
  21. package/dist/cli.js.map +1 -1
  22. package/dist/composition/descriptor.d.ts +17 -3
  23. package/dist/composition/descriptor.d.ts.map +1 -1
  24. package/dist/composition/descriptor.js +51 -28
  25. package/dist/composition/descriptor.js.map +1 -1
  26. package/dist/composition/format.d.ts +12 -0
  27. package/dist/composition/format.d.ts.map +1 -0
  28. package/dist/composition/format.js +28 -0
  29. package/dist/composition/format.js.map +1 -0
  30. package/dist/composition/index.d.ts +2 -1
  31. package/dist/composition/index.d.ts.map +1 -1
  32. package/dist/composition/index.js +1 -0
  33. package/dist/composition/index.js.map +1 -1
  34. package/dist/context.d.ts +82 -0
  35. package/dist/context.d.ts.map +1 -0
  36. package/dist/context.js +277 -0
  37. package/dist/context.js.map +1 -0
  38. package/dist/contracts.d.ts +80 -6
  39. package/dist/contracts.d.ts.map +1 -1
  40. package/dist/contracts.js +3 -0
  41. package/dist/contracts.js.map +1 -1
  42. package/dist/doctor.d.ts +14 -1
  43. package/dist/doctor.d.ts.map +1 -1
  44. package/dist/doctor.js +166 -7
  45. package/dist/doctor.js.map +1 -1
  46. package/dist/errors.d.ts +1 -1
  47. package/dist/errors.d.ts.map +1 -1
  48. package/dist/errors.js.map +1 -1
  49. package/dist/fonts/add.d.ts +41 -0
  50. package/dist/fonts/add.d.ts.map +1 -0
  51. package/dist/fonts/add.js +259 -0
  52. package/dist/fonts/add.js.map +1 -0
  53. package/dist/fonts/binary.d.ts +4 -0
  54. package/dist/fonts/binary.d.ts.map +1 -0
  55. package/dist/fonts/binary.js +21 -0
  56. package/dist/fonts/binary.js.map +1 -0
  57. package/dist/fonts/css.d.ts +4 -0
  58. package/dist/fonts/css.d.ts.map +1 -0
  59. package/dist/fonts/css.js +50 -0
  60. package/dist/fonts/css.js.map +1 -0
  61. package/dist/fonts/fontsource.d.ts +107 -0
  62. package/dist/fonts/fontsource.d.ts.map +1 -0
  63. package/dist/fonts/fontsource.js +565 -0
  64. package/dist/fonts/fontsource.js.map +1 -0
  65. package/dist/fonts/index.d.ts +11 -0
  66. package/dist/fonts/index.d.ts.map +1 -0
  67. package/dist/fonts/index.js +11 -0
  68. package/dist/fonts/index.js.map +1 -0
  69. package/dist/fonts/install.d.ts +24 -0
  70. package/dist/fonts/install.d.ts.map +1 -0
  71. package/dist/fonts/install.js +145 -0
  72. package/dist/fonts/install.js.map +1 -0
  73. package/dist/fonts/manifest.d.ts +21 -0
  74. package/dist/fonts/manifest.d.ts.map +1 -0
  75. package/dist/fonts/manifest.js +96 -0
  76. package/dist/fonts/manifest.js.map +1 -0
  77. package/dist/fonts/options.d.ts +9 -0
  78. package/dist/fonts/options.d.ts.map +1 -0
  79. package/dist/fonts/options.js +66 -0
  80. package/dist/fonts/options.js.map +1 -0
  81. package/dist/fonts/schema.d.ts +4 -0
  82. package/dist/fonts/schema.d.ts.map +1 -0
  83. package/dist/fonts/schema.js +316 -0
  84. package/dist/fonts/schema.js.map +1 -0
  85. package/dist/fonts/summary.d.ts +16 -0
  86. package/dist/fonts/summary.d.ts.map +1 -0
  87. package/dist/fonts/summary.js +22 -0
  88. package/dist/fonts/summary.js.map +1 -0
  89. package/dist/fonts/types.d.ts +56 -0
  90. package/dist/fonts/types.d.ts.map +1 -0
  91. package/dist/fonts/types.js +8 -0
  92. package/dist/fonts/types.js.map +1 -0
  93. package/dist/fonts/verify.d.ts +33 -0
  94. package/dist/fonts/verify.d.ts.map +1 -0
  95. package/dist/fonts/verify.js +200 -0
  96. package/dist/fonts/verify.js.map +1 -0
  97. package/dist/html/document-adapter.d.ts +3 -3
  98. package/dist/html/document-adapter.d.ts.map +1 -1
  99. package/dist/html/document-adapter.js +1 -1
  100. package/dist/html/document-adapter.js.map +1 -1
  101. package/dist/html/index.d.ts +1 -1
  102. package/dist/html/index.d.ts.map +1 -1
  103. package/dist/html/parse.d.ts +10 -2
  104. package/dist/html/parse.d.ts.map +1 -1
  105. package/dist/html/parse.js +14 -5
  106. package/dist/html/parse.js.map +1 -1
  107. package/dist/html/validate.d.ts +12 -1
  108. package/dist/html/validate.d.ts.map +1 -1
  109. package/dist/html/validate.js +292 -61
  110. package/dist/html/validate.js.map +1 -1
  111. package/dist/index.d.ts +4 -4
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -2
  114. package/dist/index.js.map +1 -1
  115. package/dist/inspect.d.ts +61 -0
  116. package/dist/inspect.d.ts.map +1 -0
  117. package/dist/inspect.js +325 -0
  118. package/dist/inspect.js.map +1 -0
  119. package/dist/lint.d.ts +63 -0
  120. package/dist/lint.d.ts.map +1 -0
  121. package/dist/lint.js +265 -0
  122. package/dist/lint.js.map +1 -0
  123. package/dist/policy.d.ts +69 -0
  124. package/dist/policy.d.ts.map +1 -0
  125. package/dist/policy.js +361 -0
  126. package/dist/policy.js.map +1 -0
  127. package/dist/preview/main.d.ts +2 -2
  128. package/dist/preview/main.d.ts.map +1 -1
  129. package/dist/preview/main.js +35 -14
  130. package/dist/preview/main.js.map +1 -1
  131. package/dist/preview/timeline-data.d.ts +17 -65
  132. package/dist/preview/timeline-data.d.ts.map +1 -1
  133. package/dist/preview/timeline-host.d.ts.map +1 -1
  134. package/dist/preview/timeline-host.js +73 -38
  135. package/dist/preview/timeline-host.js.map +1 -1
  136. package/dist/preview/timeline-main.d.ts.map +1 -1
  137. package/dist/preview/timeline-main.js +47 -3
  138. package/dist/preview/timeline-main.js.map +1 -1
  139. package/dist/project.d.ts +13 -8
  140. package/dist/project.d.ts.map +1 -1
  141. package/dist/project.js +20 -17
  142. package/dist/project.js.map +1 -1
  143. package/dist/renderer/browser.d.ts +2 -2
  144. package/dist/renderer/browser.d.ts.map +1 -1
  145. package/dist/renderer/browser.js +10 -4
  146. package/dist/renderer/browser.js.map +1 -1
  147. package/dist/renderer/ffmpeg.d.ts +44 -1
  148. package/dist/renderer/ffmpeg.d.ts.map +1 -1
  149. package/dist/renderer/ffmpeg.js +371 -73
  150. package/dist/renderer/ffmpeg.js.map +1 -1
  151. package/dist/renderer/index.d.ts +40 -4
  152. package/dist/renderer/index.d.ts.map +1 -1
  153. package/dist/renderer/index.js +100 -25
  154. package/dist/renderer/index.js.map +1 -1
  155. package/dist/renderer/keyframe-expressions.d.ts +39 -0
  156. package/dist/renderer/keyframe-expressions.d.ts.map +1 -0
  157. package/dist/renderer/keyframe-expressions.js +180 -0
  158. package/dist/renderer/keyframe-expressions.js.map +1 -0
  159. package/dist/renderer/timeline.d.ts +2 -0
  160. package/dist/renderer/timeline.d.ts.map +1 -1
  161. package/dist/renderer/timeline.js +168 -106
  162. package/dist/renderer/timeline.js.map +1 -1
  163. package/dist/runtime/host.d.ts.map +1 -1
  164. package/dist/runtime/host.js +159 -31
  165. package/dist/runtime/host.js.map +1 -1
  166. package/dist/scaffold.d.ts +3 -0
  167. package/dist/scaffold.d.ts.map +1 -1
  168. package/dist/scaffold.js +48 -317
  169. package/dist/scaffold.js.map +1 -1
  170. package/dist/server/composition-html.d.ts +15 -7
  171. package/dist/server/composition-html.d.ts.map +1 -1
  172. package/dist/server/composition-html.js +72 -16
  173. package/dist/server/composition-html.js.map +1 -1
  174. package/dist/server/server.d.ts.map +1 -1
  175. package/dist/server/server.js +56 -6
  176. package/dist/server/server.js.map +1 -1
  177. package/dist/server/timeline-preview-data.d.ts.map +1 -1
  178. package/dist/server/timeline-preview-data.js +12 -23
  179. package/dist/server/timeline-preview-data.js.map +1 -1
  180. package/dist/server/timeline-preview-html.d.ts.map +1 -1
  181. package/dist/server/timeline-preview-html.js +20 -3
  182. package/dist/server/timeline-preview-html.js.map +1 -1
  183. package/dist/templates.d.ts +16 -0
  184. package/dist/templates.d.ts.map +1 -0
  185. package/dist/templates.js +186 -0
  186. package/dist/templates.js.map +1 -0
  187. package/dist/timeline/easing.d.ts +36 -0
  188. package/dist/timeline/easing.d.ts.map +1 -0
  189. package/dist/timeline/easing.js +225 -0
  190. package/dist/timeline/easing.js.map +1 -0
  191. package/dist/timeline/keyframes.d.ts +23 -0
  192. package/dist/timeline/keyframes.d.ts.map +1 -0
  193. package/dist/timeline/keyframes.js +95 -0
  194. package/dist/timeline/keyframes.js.map +1 -0
  195. package/dist/timeline/plan.d.ts +6 -12
  196. package/dist/timeline/plan.d.ts.map +1 -1
  197. package/dist/timeline/plan.js +17 -30
  198. package/dist/timeline/plan.js.map +1 -1
  199. package/dist/timeline/resolve.d.ts +16 -3
  200. package/dist/timeline/resolve.d.ts.map +1 -1
  201. package/dist/timeline/resolve.js +119 -13
  202. package/dist/timeline/resolve.js.map +1 -1
  203. package/dist/timeline/scene.d.ts +126 -0
  204. package/dist/timeline/scene.d.ts.map +1 -0
  205. package/dist/timeline/scene.js +109 -0
  206. package/dist/timeline/scene.js.map +1 -0
  207. package/dist/timeline/schema.d.ts +38 -0
  208. package/dist/timeline/schema.d.ts.map +1 -1
  209. package/dist/timeline/schema.js +290 -9
  210. package/dist/timeline/schema.js.map +1 -1
  211. package/dist/timeline/transitions.d.ts +21 -0
  212. package/dist/timeline/transitions.d.ts.map +1 -0
  213. package/dist/timeline/transitions.js +23 -0
  214. package/dist/timeline/transitions.js.map +1 -0
  215. package/dist/version.d.ts +1 -1
  216. package/dist/version.js +1 -1
  217. package/docs/adr/0007-make-html-documents-the-composition-unit.md +2 -2
  218. package/docs/adr/0008-share-the-composition-document-grammar.md +1 -1
  219. package/docs/adr/0010-make-timelines-the-render-unit.md +3 -2
  220. package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +1 -1
  221. package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +1 -1
  222. package/docs/adr/0014-compose-timeline-clips-with-css-blend-modes.md +38 -0
  223. package/docs/adr/0015-inherit-composition-format-from-the-project.md +28 -0
  224. package/docs/adr/0016-author-compositions-as-html-fragments.md +32 -0
  225. package/docs/adr/0017-render-timeless-compositions-as-stills.md +99 -0
  226. package/docs/adr/0018-animate-timeline-clips-with-keyframes.md +35 -0
  227. package/docs/adr/0019-make-same-track-overlap-the-transition-window.md +36 -0
  228. package/docs/adr/0020-crossfade-audio-clips-on-audio-tracks.md +27 -0
  229. package/docs/adr/0021-project-timeline-scenes-once.md +32 -0
  230. package/docs/adr/0022-gate-heavy-operations-with-a-workspace-resource-policy.md +35 -0
  231. package/docs/adr/0023-pin-fonts-as-project-assets.md +98 -0
  232. package/docs/migration-0.4.md +5 -3
  233. package/docs/migration-0.8.md +163 -0
  234. package/docs/preview-testing.md +23 -3
  235. package/docs/{v0.1-design.md → v0.x-design.md} +86 -53
  236. package/examples/blend/README.md +106 -0
  237. package/examples/blend/assets/ink.mp4 +0 -0
  238. package/examples/blend/compositions/aurora.html +55 -0
  239. package/examples/blend/compositions/backdrop.html +74 -0
  240. package/examples/blend/compositions/labels.html +149 -0
  241. package/examples/blend/fy.config.js +11 -0
  242. package/examples/blend/scripts/make-ink.sh +26 -0
  243. package/examples/blend/timelines/blend.timeline.json +155 -0
  244. package/examples/demo/README.md +2 -2
  245. package/examples/demo/compositions/demo.html +243 -251
  246. package/examples/intro/README.md +71 -0
  247. package/examples/intro/assets/fonts/figtree/LICENSE.txt +93 -0
  248. package/examples/intro/assets/fonts/figtree/figtree-latin-wght-normal.woff2 +0 -0
  249. package/examples/intro/assets/fonts/instrument-serif/LICENSE.txt +93 -0
  250. package/examples/intro/assets/fonts/instrument-serif/instrument-serif-latin-400-italic.woff2 +0 -0
  251. package/examples/intro/assets/fonts/space-mono/LICENSE.txt +93 -0
  252. package/examples/intro/assets/fonts/space-mono/space-mono-latin-400-normal.woff2 +0 -0
  253. package/examples/intro/assets/fonts/space-mono/space-mono-latin-700-normal.woff2 +0 -0
  254. package/examples/intro/assets/grain.png +0 -0
  255. package/examples/intro/assets/score.ogg +0 -0
  256. package/examples/intro/compositions/brand.html +412 -0
  257. package/examples/intro/compositions/chrome.controller.js +63 -0
  258. package/examples/intro/compositions/chrome.html +256 -0
  259. package/examples/intro/compositions/lattice.controller.js +148 -0
  260. package/examples/intro/compositions/lattice.html +308 -0
  261. package/examples/intro/compositions/opening.html +504 -0
  262. package/examples/intro/compositions/studio.html +930 -0
  263. package/examples/intro/compositions/theme.css +218 -0
  264. package/examples/intro/fy.config.js +13 -0
  265. package/examples/intro/fy.fonts.json +100 -0
  266. package/examples/intro/scripts/make-grain.mjs +79 -0
  267. package/examples/intro/scripts/make-score.mjs +702 -0
  268. package/examples/intro/timelines/intro.timeline.json +60 -0
  269. package/examples/keyframes/README.md +76 -0
  270. package/examples/keyframes/compositions/backdrop.html +60 -0
  271. package/examples/keyframes/compositions/card.html +99 -0
  272. package/examples/keyframes/fy.config.js +10 -0
  273. package/examples/keyframes/timelines/keyframes.timeline.json +60 -0
  274. package/examples/nested/README.md +112 -0
  275. package/examples/nested/assets/pulse.wav +0 -0
  276. package/examples/nested/compositions/backdrop.html +84 -0
  277. package/examples/nested/compositions/card.html +275 -0
  278. package/examples/nested/compositions/overlay.html +307 -0
  279. package/examples/nested/fy.config.js +13 -0
  280. package/examples/nested/scripts/make-pulse.sh +20 -0
  281. package/examples/nested/timelines/chapter-a.timeline.json +39 -0
  282. package/examples/nested/timelines/chapter-b.timeline.json +39 -0
  283. package/examples/nested/timelines/nested-reel.timeline.json +52 -0
  284. package/examples/static/README.md +17 -0
  285. package/examples/static/compositions/still.html +37 -0
  286. package/examples/static/fy.config.js +9 -0
  287. package/examples/static/timelines/still.timeline.json +13 -0
  288. package/examples/transitions/README.md +128 -0
  289. package/examples/transitions/assets/alpha.wav +0 -0
  290. package/examples/transitions/assets/beta.wav +0 -0
  291. package/examples/transitions/assets/gamma.wav +0 -0
  292. package/examples/transitions/compositions/alpha.html +56 -0
  293. package/examples/transitions/compositions/beta.html +56 -0
  294. package/examples/transitions/compositions/gamma.html +56 -0
  295. package/examples/transitions/fy.config.js +12 -0
  296. package/examples/transitions/timelines/audio-crossfade.timeline.json +44 -0
  297. package/examples/transitions/timelines/transitions.timeline.json +31 -0
  298. package/examples/video/README.md +4 -4
  299. package/examples/video/compositions/video-mp4.html +349 -357
  300. package/examples/video/compositions/video-overlay.html +337 -345
  301. package/examples/video/compositions/video.html +349 -357
  302. package/examples/webgl/README.md +1 -1
  303. package/examples/webgl/compositions/webgl.html +205 -213
  304. package/package.json +13 -5
  305. package/scaffolds/init/AGENTS.md +131 -0
  306. package/scaffolds/init/README.md +56 -0
  307. package/scaffolds/init/compositions/hello.html +79 -0
  308. package/scaffolds/init/fy.config.js +9 -0
  309. package/scaffolds/init/gitignore.template +1 -0
  310. package/scaffolds/init/package.json +11 -0
  311. package/scaffolds/init/props/hello.json +4 -0
  312. package/scaffolds/init/props/main.json +6 -0
  313. package/scaffolds/init/timelines/main.timeline.json +20 -0
  314. package/scaffolds/local-models/LOCAL-MODELS.md +77 -0
  315. package/scaffolds/local-models/fy.policy.json +5 -0
  316. package/scaffolds/local-models/opencode/opencode.jsonc +17 -0
  317. package/scaffolds/local-models/pi/APPEND_SYSTEM.md +21 -0
  318. package/scaffolds/local-models/pi/sandbox.json +15 -0
  319. package/scaffolds/local-models/pi/settings.json +15 -0
  320. package/skills/frameyard/README.md +1 -0
  321. package/skills/frameyard/SKILL.md +59 -59
  322. package/skills/frameyard/references/audio-assets.md +32 -3
  323. package/skills/frameyard/references/common-mistakes.md +81 -7
  324. package/skills/frameyard/references/controllers.md +5 -4
  325. package/skills/frameyard/references/deterministic-authoring.md +49 -37
  326. package/skills/frameyard/references/diagnostics.md +42 -4
  327. package/skills/frameyard/references/failure-recovery.md +56 -10
  328. package/skills/frameyard/references/fonts.md +132 -0
  329. package/skills/frameyard/references/json-results.md +165 -10
  330. package/skills/frameyard/references/quick-reference.md +222 -0
  331. package/skills/frameyard/references/timelines.md +109 -15
  332. /package/examples/demo/{video.config.js → fy.config.js} +0 -0
  333. /package/examples/video/{video.config.js → fy.config.js} +0 -0
  334. /package/examples/webgl/{video.config.js → fy.config.js} +0 -0
@@ -20,7 +20,7 @@ From the repository root:
20
20
  pnpm build
21
21
  node dist/cli.js check webgl --project examples/webgl
22
22
  node dist/cli.js still webgl --frame 210 --project examples/webgl --out webgl-still.png
23
- node dist/cli.js render webgl --project examples/webgl --props examples/webgl/props/webgl.json
23
+ FRAMEYARD_POLICY=full node dist/cli.js render webgl --project examples/webgl --props examples/webgl/props/webgl.json
24
24
  ```
25
25
 
26
26
  Start the interactive preview with:
@@ -1,215 +1,207 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <script type="application/json" data-fy="props">
6
- {
7
- "kicker": "Shader Study / 001",
8
- "title": "Liquid Light",
9
- "subtitle": "A frame-driven WebGL field, resolved in software so every captured pixel stays reproducible.",
10
- "accent": "#78f0c8"
11
- }
12
- </script>
13
- <style>
14
- @keyframes rise {
15
- from {
16
- opacity: 0;
17
- transform: translateY(44px);
18
- }
19
- to {
20
- opacity: 1;
21
- transform: translateY(0);
22
- }
23
- }
24
- @keyframes fade {
25
- from {
26
- opacity: 0;
27
- }
28
- to {
29
- opacity: 1;
30
- }
31
- }
32
- @keyframes draw {
33
- from {
34
- opacity: 0;
35
- transform: scaleX(0);
36
- }
37
- to {
38
- opacity: 1;
39
- transform: scaleX(1);
40
- }
41
- }
42
- html,
43
- body,
44
- fy-composition {
45
- background: #060a0e;
46
- }
47
- #scene {
48
- position: relative;
49
- width: 100%;
50
- height: 100%;
51
- overflow: hidden;
52
- background: #060a0e;
53
- color: #edf6f3;
54
- font-family: Inter, ui-sans-serif, system-ui, sans-serif;
55
- }
56
- #field {
57
- position: absolute;
58
- inset: 0;
59
- display: block;
60
- width: 100%;
61
- height: 100%;
62
- }
63
- #scrim {
64
- position: absolute;
65
- inset: 0;
66
- background: linear-gradient(
67
- 90deg,
68
- rgba(4, 8, 11, 0.94) 0%,
69
- rgba(4, 8, 11, 0.78) 34%,
70
- rgba(4, 8, 11, 0.18) 66%,
71
- rgba(4, 8, 11, 0) 100%
72
- );
73
- }
74
- #vignette {
75
- position: absolute;
76
- inset: 0;
77
- background: radial-gradient(ellipse at 60% 45%, transparent 34%, rgba(0, 0, 0, 0.46) 100%);
78
- pointer-events: none;
79
- }
80
- #kicker {
81
- position: absolute;
82
- top: 72px;
83
- left: 148px;
84
- display: flex;
85
- align-items: center;
86
- gap: 16px;
87
- font:
88
- 600 14px/1 ui-monospace,
89
- monospace;
90
- letter-spacing: 0.26em;
91
- text-transform: uppercase;
92
- color: var(--fy-prop-accent);
93
- animation: fade 1s linear both;
94
- }
95
- #kicker::before {
96
- content: "";
97
- width: 8px;
98
- height: 8px;
99
- background: var(--fy-prop-accent);
100
- box-shadow: 0 0 18px var(--fy-prop-accent);
101
- }
102
- #meta {
103
- position: absolute;
104
- top: 72px;
105
- right: 148px;
106
- text-align: right;
107
- font:
108
- 500 13px/1.9 ui-monospace,
109
- monospace;
110
- letter-spacing: 0.12em;
111
- color: #80908e;
112
- animation: fade 1s linear both;
113
- }
114
- #meta strong {
115
- color: #dce9e6;
116
- font-weight: 500;
117
- }
118
- #title {
119
- position: absolute;
120
- top: 292px;
121
- left: 148px;
122
- width: 780px;
123
- margin: 0;
124
- font-size: 168px;
125
- line-height: 0.88;
126
- letter-spacing: -0.062em;
127
- font-weight: 660;
128
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
129
- }
130
- #subtitle {
131
- position: absolute;
132
- top: 604px;
133
- left: 152px;
134
- width: 720px;
135
- margin: 0;
136
- font-size: 30px;
137
- line-height: 1.45;
138
- font-weight: 420;
139
- color: #96a7a3;
140
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
141
- }
142
- #footer {
143
- position: absolute;
144
- left: 148px;
145
- right: 148px;
146
- bottom: 64px;
147
- animation: fade 1s linear both;
148
- }
149
- #rule {
150
- position: relative;
151
- height: 1px;
152
- background: rgba(237, 246, 243, 0.14);
153
- transform-origin: left center;
154
- animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
155
- }
156
- #progress {
157
- position: absolute;
158
- inset: 0 auto 0 0;
159
- width: calc(var(--fy-progress) * 100%);
160
- background: var(--fy-prop-accent);
161
- box-shadow: 0 0 16px var(--fy-prop-accent);
162
- }
163
- #footer-row {
164
- display: flex;
165
- justify-content: space-between;
166
- align-items: baseline;
167
- gap: 24px;
168
- margin-top: 18px;
169
- font:
170
- 500 13px/1 ui-monospace,
171
- monospace;
172
- letter-spacing: 0.14em;
173
- color: #758581;
174
- animation: fade 1s linear both;
175
- }
176
- #footer-row strong {
177
- color: var(--fy-prop-accent);
178
- font-weight: 600;
179
- }
180
- #footer-meta {
181
- display: flex;
182
- gap: 28px;
183
- }
184
- </style>
185
- </head>
186
- <body>
187
- <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
188
- <div id="scene">
189
- <canvas id="field" width="1920" height="1080"></canvas>
190
- <div id="scrim"></div>
191
- <div id="vignette"></div>
192
- <div id="kicker" data-fy-text="kicker" data-fy-anim-start="0" data-fy-anim-duration="28"></div>
193
- <div id="meta" data-fy-anim-start="18" data-fy-anim-duration="34">
194
- <div><strong>WEBGL 1.0</strong> / SWIFTSHADER</div>
195
- <div>1920 × 1080 · 30 FPS</div>
196
- <div>12.00 S · 360 FRAMES</div>
197
- </div>
198
- <h1 id="title" data-fy-text="title" data-fy-anim-start="6" data-fy-anim-duration="56"></h1>
199
- <p id="subtitle" data-fy-text="subtitle" data-fy-anim-start="34" data-fy-anim-duration="52"></p>
200
- <div id="footer" data-fy-anim-start="28" data-fy-anim-duration="30">
201
- <div id="rule" data-fy-anim-start="38" data-fy-anim-duration="60">
202
- <div id="progress"></div>
203
- </div>
204
- <div id="footer-row" data-fy-anim-start="44" data-fy-anim-duration="30">
205
- <span>FRAME <strong id="frame">000</strong> / <span id="total">359</span></span>
206
- <span id="clock">0.00 S</span>
207
- <span id="footer-meta"><span>FRAME-SEEKED</span><span>NO RAF</span></span>
208
- </div>
209
- </div>
1
+ <script type="application/json" data-fy="props">
2
+ {
3
+ "kicker": "Shader Study / 001",
4
+ "title": "Liquid Light",
5
+ "subtitle": "A frame-driven WebGL field, resolved in software so every captured pixel stays reproducible.",
6
+ "accent": "#78f0c8"
7
+ }
8
+ </script>
9
+ <style>
10
+ @keyframes rise {
11
+ from {
12
+ opacity: 0;
13
+ transform: translateY(44px);
14
+ }
15
+ to {
16
+ opacity: 1;
17
+ transform: translateY(0);
18
+ }
19
+ }
20
+ @keyframes fade {
21
+ from {
22
+ opacity: 0;
23
+ }
24
+ to {
25
+ opacity: 1;
26
+ }
27
+ }
28
+ @keyframes draw {
29
+ from {
30
+ opacity: 0;
31
+ transform: scaleX(0);
32
+ }
33
+ to {
34
+ opacity: 1;
35
+ transform: scaleX(1);
36
+ }
37
+ }
38
+ html,
39
+ body,
40
+ fy-composition {
41
+ background: #060a0e;
42
+ }
43
+ #scene {
44
+ position: relative;
45
+ width: 100%;
46
+ height: 100%;
47
+ overflow: hidden;
48
+ background: #060a0e;
49
+ color: #edf6f3;
50
+ font-family: Inter, ui-sans-serif, system-ui, sans-serif;
51
+ }
52
+ #field {
53
+ position: absolute;
54
+ inset: 0;
55
+ display: block;
56
+ width: 100%;
57
+ height: 100%;
58
+ }
59
+ #scrim {
60
+ position: absolute;
61
+ inset: 0;
62
+ background: linear-gradient(
63
+ 90deg,
64
+ rgba(4, 8, 11, 0.94) 0%,
65
+ rgba(4, 8, 11, 0.78) 34%,
66
+ rgba(4, 8, 11, 0.18) 66%,
67
+ rgba(4, 8, 11, 0) 100%
68
+ );
69
+ }
70
+ #vignette {
71
+ position: absolute;
72
+ inset: 0;
73
+ background: radial-gradient(ellipse at 60% 45%, transparent 34%, rgba(0, 0, 0, 0.46) 100%);
74
+ pointer-events: none;
75
+ }
76
+ #kicker {
77
+ position: absolute;
78
+ top: 72px;
79
+ left: 148px;
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 16px;
83
+ font:
84
+ 600 14px/1 ui-monospace,
85
+ monospace;
86
+ letter-spacing: 0.26em;
87
+ text-transform: uppercase;
88
+ color: var(--fy-prop-accent);
89
+ animation: fade 1s linear both;
90
+ }
91
+ #kicker::before {
92
+ content: "";
93
+ width: 8px;
94
+ height: 8px;
95
+ background: var(--fy-prop-accent);
96
+ box-shadow: 0 0 18px var(--fy-prop-accent);
97
+ }
98
+ #meta {
99
+ position: absolute;
100
+ top: 72px;
101
+ right: 148px;
102
+ text-align: right;
103
+ font:
104
+ 500 13px/1.9 ui-monospace,
105
+ monospace;
106
+ letter-spacing: 0.12em;
107
+ color: #80908e;
108
+ animation: fade 1s linear both;
109
+ }
110
+ #meta strong {
111
+ color: #dce9e6;
112
+ font-weight: 500;
113
+ }
114
+ #title {
115
+ position: absolute;
116
+ top: 292px;
117
+ left: 148px;
118
+ width: 780px;
119
+ margin: 0;
120
+ font-size: 168px;
121
+ line-height: 0.88;
122
+ letter-spacing: -0.062em;
123
+ font-weight: 660;
124
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
125
+ }
126
+ #subtitle {
127
+ position: absolute;
128
+ top: 604px;
129
+ left: 152px;
130
+ width: 720px;
131
+ margin: 0;
132
+ font-size: 30px;
133
+ line-height: 1.45;
134
+ font-weight: 420;
135
+ color: #96a7a3;
136
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
137
+ }
138
+ #footer {
139
+ position: absolute;
140
+ left: 148px;
141
+ right: 148px;
142
+ bottom: 64px;
143
+ animation: fade 1s linear both;
144
+ }
145
+ #rule {
146
+ position: relative;
147
+ height: 1px;
148
+ background: rgba(237, 246, 243, 0.14);
149
+ transform-origin: left center;
150
+ animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
151
+ }
152
+ #progress {
153
+ position: absolute;
154
+ inset: 0 auto 0 0;
155
+ width: calc(var(--fy-progress) * 100%);
156
+ background: var(--fy-prop-accent);
157
+ box-shadow: 0 0 16px var(--fy-prop-accent);
158
+ }
159
+ #footer-row {
160
+ display: flex;
161
+ justify-content: space-between;
162
+ align-items: baseline;
163
+ gap: 24px;
164
+ margin-top: 18px;
165
+ font:
166
+ 500 13px/1 ui-monospace,
167
+ monospace;
168
+ letter-spacing: 0.14em;
169
+ color: #758581;
170
+ animation: fade 1s linear both;
171
+ }
172
+ #footer-row strong {
173
+ color: var(--fy-prop-accent);
174
+ font-weight: 600;
175
+ }
176
+ #footer-meta {
177
+ display: flex;
178
+ gap: 28px;
179
+ }
180
+ </style>
181
+ <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
182
+ <div id="scene">
183
+ <canvas id="field" width="1920" height="1080"></canvas>
184
+ <div id="scrim"></div>
185
+ <div id="vignette"></div>
186
+ <div id="kicker" data-fy-text="kicker" data-fy-anim-start="0" data-fy-anim-duration="28"></div>
187
+ <div id="meta" data-fy-anim-start="18" data-fy-anim-duration="34">
188
+ <div><strong>WEBGL 1.0</strong> / SWIFTSHADER</div>
189
+ <div>1920 × 1080 · 30 FPS</div>
190
+ <div>12.00 S · 360 FRAMES</div>
191
+ </div>
192
+ <h1 id="title" data-fy-text="title" data-fy-anim-start="6" data-fy-anim-duration="56"></h1>
193
+ <p id="subtitle" data-fy-text="subtitle" data-fy-anim-start="34" data-fy-anim-duration="52"></p>
194
+ <div id="footer" data-fy-anim-start="28" data-fy-anim-duration="30">
195
+ <div id="rule" data-fy-anim-start="38" data-fy-anim-duration="60">
196
+ <div id="progress"></div>
210
197
  </div>
211
- </fy-composition>
198
+ <div id="footer-row" data-fy-anim-start="44" data-fy-anim-duration="30">
199
+ <span>FRAME <strong id="frame">000</strong> / <span id="total">359</span></span>
200
+ <span id="clock">0.00 S</span>
201
+ <span id="footer-meta"><span>FRAME-SEEKED</span><span>NO RAF</span></span>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </fy-composition>
212
206
 
213
- <script type="text/fy-controller" src="./webgl.controller.js"></script>
214
- </body>
215
- </html>
207
+ <script type="text/fy-controller" src="./webgl.controller.js"></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frameyard",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Local-first, deterministic video composition framework for HTML, CSS, and JavaScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,6 +43,7 @@
43
43
  "dist",
44
44
  "examples",
45
45
  "docs",
46
+ "scaffolds",
46
47
  "skills",
47
48
  "README.md",
48
49
  "CHANGELOG.md",
@@ -71,16 +72,23 @@
71
72
  "lint": "oxlint",
72
73
  "format": "oxfmt",
73
74
  "format:check": "oxfmt --check",
74
- "check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm typecheck && pnpm typecheck:test && pnpm typecheck:browser",
75
+ "check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm build && pnpm typecheck:test && pnpm typecheck:browser",
75
76
  "compare-stills": "node scripts/compare-stills.mjs",
76
- "check": "pnpm check:static && pnpm test",
77
- "check:preview": "pnpm check:static && pnpm test:preview",
77
+ "check": "pnpm check:static && pnpm test:built && node scripts/check-stamp.mjs record",
78
+ "check:preview": "pnpm check:static && vitest run test/preview-runtime.test.ts test/timeline-preview.test.ts test/timeline-preview-runtime.test.ts",
79
+ "check:changelog": "node scripts/check-changelog.mjs",
78
80
  "test": "pnpm build && vitest run",
81
+ "test:built": "vitest run",
82
+ "test:integration": "pnpm build && vitest run --project integration",
83
+ "test:examples": "pnpm build && vitest run test/examples.test.ts",
79
84
  "test:preview": "pnpm build && vitest run test/preview-runtime.test.ts test/timeline-preview.test.ts test/timeline-preview-runtime.test.ts",
80
- "test:unit": "vitest run test/contracts.test.ts test/docs.test.ts test/encoders.test.ts test/random.test.ts test/release.test.ts test/scaffold.test.ts test/transcript.test.ts",
85
+ "test:unit": "vitest run --project unit",
81
86
  "bench": "pnpm build && node scripts/bench-render.mjs",
87
+ "bench:static": "pnpm build && node bench/static-composition.mjs",
88
+ "eval:local": "node evals/runner.mjs",
82
89
  "set-version": "node scripts/set-version.mjs",
83
90
  "verify-release": "node scripts/verify-release.mjs",
91
+ "verify-package": "node scripts/verify-package.mjs",
84
92
  "retag-release": "node scripts/retag-release.mjs"
85
93
  }
86
94
  }
@@ -0,0 +1,131 @@
1
+ # AGENTS.md
2
+
3
+ This project produces deterministic, parameterized video with Frameyard. This file is the local summary; the official skill has the full contract.
4
+
5
+ ## Project layout
6
+
7
+ - `fy.config.js` explicitly maps project IDs to sources. There is no directory scanning. It declares the project `format`, `compositions`, and `timelines`.
8
+ - `compositions/` contains HTML fragments; an optional controller lives beside the fragment as a `.controller.js` module.
9
+ - `timelines/` contains JSON timeline documents that assemble clips into one video.
10
+ - `props/hello.json` contains composition props; `props/main.json` contains timeline overrides keyed by composition clip ID.
11
+ - `out/` contains rendered files and is gitignored. `assets/` contains local media and generated assets.
12
+ - `package.json` pins the CLI in `dev`, `check`, `render`, and `still` scripts.
13
+
14
+ An animated composition declares `duration-in-frames`. A composition with neither `duration-in-frames` nor `fps` is static: it is captured once, renders to PNG, and cannot use animation, media, or transcripts. `width`, `height`, and `fps` inherit the project `format`; declare `width` and `height` together or not at all.
15
+
16
+ ## Composition skeleton
17
+
18
+ A composition is a partial HTML fragment, not a full document. Keep one top-level `<fy-composition>`; props, the controller, import maps, and styles are top-level siblings. Do not add `<!doctype>`, `<html>`, `<head>`, `<body>`, `<title>`, or `<meta>`.
19
+
20
+ ```html
21
+ <script type="application/json" data-fy="props">{ "title": "Hello" }</script>
22
+ <style>
23
+ html, body { margin: 0; width: 100%; height: 100%; }
24
+ </style>
25
+ <fy-composition width="1280" height="720" fps="30" duration-in-frames="150">
26
+ <h1 data-fy-text="title"></h1>
27
+ </fy-composition>
28
+ ```
29
+
30
+ ## Timeline skeleton
31
+
32
+ `timelines/main.timeline.json` assembles clips in the project format:
33
+
34
+ ```json
35
+ {
36
+ "version": 1,
37
+ "tracks": [{ "id": "v1", "kind": "video" }],
38
+ "clips": [
39
+ {
40
+ "id": "hello",
41
+ "track": "v1",
42
+ "source": { "kind": "composition", "id": "hello" },
43
+ "start": 0
44
+ }
45
+ ]
46
+ }
47
+ ```
48
+
49
+ - `source.kind` is `composition`, `timeline`, `video`, `image`, or `audio`. `start`, `sourceIn`, and `duration` are timeline frames. Image clips require a duration.
50
+ - A static composition clip needs an explicit `duration` and `sourceIn: 0`. It contributes no audio.
51
+ - Tracks composite bottom-to-top. Same-track overlap requires a timeline `transitions` entry; see the skill for transforms, keyframes, blends, and transitions.
52
+
53
+ ## Bindings and animation
54
+
55
+ - Bindings: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `<template data-fy-repeat>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables.
56
+ - Bindings apply once, before `mount`; controller-created DOM is not re-bound.
57
+ - CSS/WAAPI animations are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation in frames.
58
+ - Frame state on `:root`: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, `--fy-duration-frames`.
59
+
60
+ ## Controller
61
+
62
+ Exactly one optional controller is allowed, inline or external, as a top-level sibling. It may export `mount`, `update`, `dispose`, and `validateProps`; at least one is required. `mount` may be async; `update` is synchronous and seek-safe. The controller is the only executable code; other scripts and `on*` handlers are rejected. Extract large or reusable controllers to an external module.
63
+
64
+ ```html
65
+ <script type="text/fy-controller">
66
+ export default {
67
+ mount({ root, props }) {},
68
+ update({ frame, props }) {},
69
+ };
70
+ </script>
71
+ ```
72
+
73
+ ## Determinism
74
+
75
+ - A frame depends only on its integer `frame`, immutable `props`, and assets loaded before frame production.
76
+ - Use `random("stable:key")` from `frameyard/runtime`; never `Math.random()`, `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions.
77
+ - Load remote data, fonts, and images before frames. Network is blocked during capture; do not `fetch()` from `update()`.
78
+ - Vendor fonts with `npx frameyard font add --file`; do not rely on a remote Google Fonts link for a deterministic render. The server injects `@font-face` rules and the host waits for `document.fonts.ready`.
79
+ - Let the runtime seek embedded video; do not play, pause, or set `currentTime`. Redraw Canvas/WebGL from the current frame.
80
+ - Do not run model inference inside `mount()`, `update()`, or frame production.
81
+
82
+ ## Fonts
83
+
84
+ - `fy.fonts.json` records installed families, licenses, and SHA-256 values; commit it with `assets/fonts/`.
85
+ - Search Fontsource with `npx frameyard font search "<query>" --json`, then install a family with `npx frameyard font add <fontsource-id> --json`. Variable `wght` is the default; add `--static --weight 400,700` for static files.
86
+ - Use `npx frameyard font add --file <path> --family "<name>" --license <id> --license-file <path>` for a local or brand font. Use `--url` for an explicit HTTPS source with `--sha256`.
87
+ - Run `npx frameyard font list --json` and `npx frameyard font verify --json` after moving files. Do not edit manifest hashes by hand.
88
+ - `font add` requires `authoring` or `full`; `font remove`, `font add --force`, and an unlicensed `--url` import require `full`.
89
+
90
+ ## Media and transcripts
91
+
92
+ - Composition audio uses `<audio>` with `data-fy-start` (frames), `data-fy-trim` (seconds), optional `data-fy-duration` (seconds), and `data-fy-volume` (0-1). Timeline audio uses frames and audio tracks.
93
+ - Media and transcript `src` values resolve from the project root; controller `src` and ordinary HTML URLs resolve from the composition URL.
94
+ - Declare transcripts with `<fy-transcript src="assets/voice.transcript.json" words>`. `words` requires measured word timings; never interpolate segment timings.
95
+ - Use Frameyard's `tts`/`transcribe` wrappers; never call `qwen-tts`, `sherpa-onnx-offline-tts`, or `whisper-cli` directly. Do not overwrite generated assets without approval. Commit `fy.assets.json` when present; do not edit hashes.
96
+
97
+ ## Resource policy
98
+
99
+ A workspace can commit `fy.policy.json` to gate expensive operations. The nearest file from the working directory upward applies, independent of `fy.config.js`; `FRAMEYARD_POLICY` overrides it. `light` allows one still frame and blocks `check`, `tts`, `transcribe`, and `render`; `authoring` allows four still frames and `check` at low priority and blocks the three heavy operations; `full` allows everything up to the 32-frame still cap. `--profile shared-cpu` caps timeline workers at 2 and composition concurrency at 1. `tts`, `transcribe`, and `render` accept `--dry-run`; use it and report the handoff when blocked. The policy is a guardrail, not a security boundary.
100
+
101
+ ## Agent workflow
102
+
103
+ 1. Read `fy.config.js`, the timeline, the composition, its controller, and the props. Run `npx frameyard context --json` first.
104
+ 2. Author with native HTML, CSS, SVG, Canvas, WebGL, and an optional controller. Use `npx frameyard add --list --json` for templates and merge the returned registration object into `fy.config.js`.
105
+ 3. Lint the edit loop with `npx frameyard lint main --json`. It does not run `validateProps` or composition runtime.
106
+ 4. Run `npx frameyard check main --json` for the full gate when the policy allows it; read diagnostics and warnings.
107
+ 5. Inspect with `npx frameyard still main --frame <spec> --out <dir|file.png> --json`. The policy captures at most 32 unique frames in `full` (4 in `authoring`, 1 in `light`). Use `npx frameyard inspect`/`compare` for text facts; view the PNGs when possible.
108
+ 6. Render with `npx frameyard render main --props props/main.json [--workers <n>] [--scratch <dir>] --json`. If the policy blocks it, use `--dry-run` and report the handoff.
109
+ 7. Report sources, props, diagnostics, still paths, inspection status, output path, and failures. Never claim success when `check` fails, a still is missing, or a render exits nonzero.
110
+
111
+ ## Commands
112
+
113
+ ```sh
114
+ npx frameyard dev
115
+ npx frameyard context --json
116
+ npx frameyard doctor --json
117
+ npx frameyard add --list --json
118
+ npx frameyard lint main --json
119
+ npx frameyard check main --json
120
+ npx frameyard still main --frame 42 --out main-42.png --json
121
+ npx frameyard inspect main-42.png --json
122
+ npx frameyard render main --props props/main.json --workers 8 --scratch /path/to/scratch --json
123
+ ```
124
+
125
+ - Use `--json` for non-interactive commands. Parse stdout; keep stderr for progress and diagnostics.
126
+ - Use `--project <dir>` outside the project root. Outputs default to `out/<id>.mp4` or `out/<id>.png`.
127
+ - `--concurrency` is for animated compositions; `--workers`/`--scratch` are for timelines. `--container <mp4|mov|mkv|webm>`, `--codec <h264|hevc|vp9>`, and `--encoder <auto|software|nvenc|qsv>` select delivery; the extension must match.
128
+ - `--dry-run` validates and prints a handoff plan. `--profile shared-cpu` caps workers/concurrency and lowers priority.
129
+ - `still`/`render` replace outputs only with `--overwrite`; generated assets only with `--force`, and only with approval. The resource policy denies both outside `full`.
130
+
131
+ The official `frameyard` agent skill has the full contract. Install it with `npx skills add vladiantio/frameyard`. This project was scaffolded with frameyard {{VERSION}}; `npx frameyard` resolves the latest version, so run `npx frameyard@{{VERSION}}` when you need the scaffolding version.