framefields 2.5.3 → 2.6.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 (484) hide show
  1. package/README.md +11 -0
  2. package/dist/audio/index.d.mts +4 -4
  3. package/dist/audio/index.mjs +10 -10
  4. package/dist/{audio-2ew5y0Bl.d.mts → audio-B5mdR8vv.d.mts} +3 -3
  5. package/dist/{audio-2ew5y0Bl.d.mts.map → audio-B5mdR8vv.d.mts.map} +1 -1
  6. package/dist/{audio-DrUskzwZ.mjs → audio-QBA2VogL.mjs} +3 -3
  7. package/dist/{audio-DrUskzwZ.mjs.map → audio-QBA2VogL.mjs.map} +1 -1
  8. package/dist/cli.mjs +866 -16
  9. package/dist/cli.mjs.map +1 -1
  10. package/dist/components/index.d.mts +11 -11
  11. package/dist/components/index.mjs +45 -45
  12. package/dist/dist-BPOi5xb_.mjs.map +1 -1
  13. package/dist/{dist-Bf0H4lp1.mjs → dist-CDwWQYg3.mjs} +12 -4
  14. package/dist/dist-CDwWQYg3.mjs.map +1 -0
  15. package/dist/{dist-DG65RDhc.mjs → dist-nHCqVk-e.mjs} +6 -7
  16. package/dist/dist-nHCqVk-e.mjs.map +1 -0
  17. package/dist/effects/index.d.mts +4 -4
  18. package/dist/effects/index.mjs +35 -35
  19. package/dist/{effects-Z-brb-n7.mjs → effects-C-6I4iYW.mjs} +2 -2
  20. package/dist/{effects-Z-brb-n7.mjs.map → effects-C-6I4iYW.mjs.map} +1 -1
  21. package/dist/fonts/index.d.mts +1 -1
  22. package/dist/fonts/index.mjs +2 -2
  23. package/dist/{fonts-DqYgk_1a.mjs → fonts-BieW3VWV.mjs} +7 -3
  24. package/dist/fonts-BieW3VWV.mjs.map +1 -0
  25. package/dist/{gpu-CevnCkZC.mjs → gpu-Bb4WmYYH.mjs} +2 -2
  26. package/dist/{gpu-CevnCkZC.mjs.map → gpu-Bb4WmYYH.mjs.map} +1 -1
  27. package/dist/{index-D1C735Wp.d.mts → index-3Vdef2KE.d.mts} +3 -3
  28. package/dist/{index-D1C735Wp.d.mts.map → index-3Vdef2KE.d.mts.map} +1 -1
  29. package/dist/{index-COQTaniR.d.mts → index-B4o6F1JC.d.mts} +2 -2
  30. package/dist/{index-COQTaniR.d.mts.map → index-B4o6F1JC.d.mts.map} +1 -1
  31. package/dist/{index-KqW22NHL.d.mts → index-BhzlG_1Q.d.mts} +2 -2
  32. package/dist/{index-KqW22NHL.d.mts.map → index-BhzlG_1Q.d.mts.map} +1 -1
  33. package/dist/{index-DSGFMpwL.d.mts → index-D-Gnyffj.d.mts} +4 -4
  34. package/dist/{index-DSGFMpwL.d.mts.map → index-D-Gnyffj.d.mts.map} +1 -1
  35. package/dist/{index-tCKz9Tku.d.mts → index-E1IrPAm4.d.mts} +825 -399
  36. package/dist/index-E1IrPAm4.d.mts.map +1 -0
  37. package/dist/{index-CalU-mpZ.d.mts → index-j5LGVVbm.d.mts} +3 -1
  38. package/dist/index-j5LGVVbm.d.mts.map +1 -0
  39. package/dist/{index-CfMylY3O.d.mts → index-o8LxWdwz.d.mts} +3 -3
  40. package/dist/{index-CfMylY3O.d.mts.map → index-o8LxWdwz.d.mts.map} +1 -1
  41. package/dist/{index-CZK6CFLy.d.mts → index-ocZc12ZS.d.mts} +17 -17
  42. package/dist/index-ocZc12ZS.d.mts.map +1 -0
  43. package/dist/index.d.mts +12 -12
  44. package/dist/index.mjs +52 -52
  45. package/dist/preview/index.d.mts +3 -3
  46. package/dist/preview/index.mjs +10 -10
  47. package/dist/{preview-DVw2jLDh.mjs → preview-D6jS38Di.mjs} +2 -2
  48. package/dist/{preview-DVw2jLDh.mjs.map → preview-D6jS38Di.mjs.map} +1 -1
  49. package/dist/preview-engine/audio.mjs +1 -1
  50. package/dist/preview-engine/{chunk-MCFYUKAJ.mjs → chunk-2LQCQ3OT.mjs} +8 -8
  51. package/dist/preview-engine/{chunk-JMOJKT5W.mjs → chunk-2YGR5YCN.mjs} +1 -1
  52. package/dist/preview-engine/{chunk-LNJYHSYX.mjs → chunk-3KHD67Q3.mjs} +1 -1
  53. package/dist/preview-engine/chunk-3SR6BI4B.mjs +1 -0
  54. package/dist/preview-engine/{chunk-NXKXOSE6.mjs → chunk-4INGTMZ5.mjs} +1 -1
  55. package/dist/preview-engine/{chunk-T4VP45AP.mjs → chunk-4JX5LBE2.mjs} +1 -1
  56. package/dist/preview-engine/{chunk-7JYMNGNK.mjs → chunk-5NT7WWJR.mjs} +1 -1
  57. package/dist/preview-engine/{chunk-NB55FW7D.mjs → chunk-6PMATELR.mjs} +1 -1
  58. package/dist/preview-engine/{chunk-45XGWIOY.mjs → chunk-AKT4YQWL.mjs} +1 -1
  59. package/dist/preview-engine/{chunk-RNFYEZLL.mjs → chunk-ALSOO22Q.mjs} +1 -1
  60. package/dist/preview-engine/{chunk-5FTCPTOX.mjs → chunk-AYTP6OGO.mjs} +8 -8
  61. package/dist/preview-engine/{chunk-3AYICEZ5.mjs → chunk-B7J72YSP.mjs} +1 -1
  62. package/dist/preview-engine/{chunk-I3ZTDIVM.mjs → chunk-BBHRXV75.mjs} +1 -1
  63. package/dist/preview-engine/{chunk-NHQR76WL.mjs → chunk-DDFWN7DI.mjs} +1 -1
  64. package/dist/preview-engine/{chunk-ZVUTFXDR.mjs → chunk-DOCFOSA6.mjs} +1 -1
  65. package/dist/preview-engine/{chunk-YJ24W73B.mjs → chunk-FZLKPBKS.mjs} +1 -1
  66. package/dist/preview-engine/{chunk-EU7CFSB5.mjs → chunk-GQIRV466.mjs} +1 -1
  67. package/dist/preview-engine/{chunk-DWHQSNSJ.mjs → chunk-IIOHF7MH.mjs} +1 -1
  68. package/dist/preview-engine/{chunk-WUMVCQYI.mjs → chunk-J24VHZS6.mjs} +1 -1
  69. package/dist/preview-engine/{chunk-2WOCQNZX.mjs → chunk-JD4TBC6Z.mjs} +8 -8
  70. package/dist/preview-engine/{chunk-X6BJLPW6.mjs → chunk-JTT3YX7W.mjs} +1 -1
  71. package/dist/preview-engine/{chunk-XJ4CQIZZ.mjs → chunk-KWTTANYN.mjs} +1 -1
  72. package/dist/preview-engine/{chunk-QCUGWPYP.mjs → chunk-LN3YAWUD.mjs} +1 -1
  73. package/dist/preview-engine/{chunk-M6ZKK6ZU.mjs → chunk-LYSU6S4O.mjs} +1 -1
  74. package/dist/preview-engine/{chunk-TE2QAKKF.mjs → chunk-MJCK5FLZ.mjs} +1 -1
  75. package/dist/preview-engine/chunk-MLAIEIUF.mjs +1974 -0
  76. package/dist/preview-engine/{chunk-24H3QVYB.mjs → chunk-MMVFX42U.mjs} +1 -1
  77. package/dist/preview-engine/{chunk-25T3M6MI.mjs → chunk-MUOJMF7M.mjs} +1 -1
  78. package/dist/preview-engine/{chunk-B555EGEU.mjs → chunk-NQZRPPUU.mjs} +1 -1
  79. package/dist/preview-engine/{chunk-VTE3IISK.mjs → chunk-OXXRKNHW.mjs} +1 -1
  80. package/dist/preview-engine/{chunk-DUEY7AXY.mjs → chunk-PJMX2GFY.mjs} +1 -1
  81. package/dist/preview-engine/{chunk-MA4YVA2E.mjs → chunk-Q7JN4EHX.mjs} +1 -1
  82. package/dist/preview-engine/{chunk-UXCTWFKK.mjs → chunk-QPBTLJ5W.mjs} +1 -1
  83. package/dist/preview-engine/{chunk-LQVACZDE.mjs → chunk-QRYCB6E3.mjs} +1 -1
  84. package/dist/preview-engine/{chunk-2QQQTZHS.mjs → chunk-RDEQ4LQU.mjs} +1 -1
  85. package/dist/preview-engine/{chunk-KKBJ7X3T.mjs → chunk-RF62TV3G.mjs} +1 -1
  86. package/dist/preview-engine/{chunk-CE6CG5BH.mjs → chunk-RHXZXWJ4.mjs} +1 -1
  87. package/dist/preview-engine/{chunk-BNO5JAEZ.mjs → chunk-RKNV5N7D.mjs} +1 -1
  88. package/dist/preview-engine/{chunk-PXWCGXTF.mjs → chunk-SP22JWN2.mjs} +1 -1
  89. package/dist/preview-engine/{chunk-NYEDWUFS.mjs → chunk-SQ52PBF3.mjs} +1 -1
  90. package/dist/preview-engine/{chunk-QYIKOOFI.mjs → chunk-SVE3XWO7.mjs} +1 -1
  91. package/dist/preview-engine/{chunk-QJYAIQYU.mjs → chunk-SXNCNG4L.mjs} +1 -1
  92. package/dist/preview-engine/{chunk-VH5V54RA.mjs → chunk-TPFDAOW7.mjs} +1 -1
  93. package/dist/preview-engine/{chunk-ENS4KUJV.mjs → chunk-UEQIO6AH.mjs} +1 -1
  94. package/dist/preview-engine/{chunk-XYRBFLFF.mjs → chunk-ULPLHKM2.mjs} +1 -1
  95. package/dist/preview-engine/{chunk-MCR7XMNQ.mjs → chunk-ULVX4VYT.mjs} +1 -1
  96. package/dist/preview-engine/{chunk-ZAIVXGSS.mjs → chunk-UQD2UE6I.mjs} +1 -1
  97. package/dist/preview-engine/{chunk-QP24X77K.mjs → chunk-VY6ST4QX.mjs} +1 -1
  98. package/dist/preview-engine/{chunk-7BUKVFWB.mjs → chunk-W2EZFV4Q.mjs} +1 -1
  99. package/dist/preview-engine/chunk-W3I7LKDV.mjs +5 -0
  100. package/dist/preview-engine/{chunk-P734GD7D.mjs → chunk-WDWHOXSA.mjs} +1 -1
  101. package/dist/preview-engine/{chunk-2RZ6S4SW.mjs → chunk-YEPYVASB.mjs} +1 -1
  102. package/dist/preview-engine/chunk-ZMMNN77R.mjs +3 -0
  103. package/dist/preview-engine/components.mjs +1 -1
  104. package/dist/preview-engine/effects.mjs +1 -1
  105. package/dist/preview-engine/fonts.mjs +1 -1
  106. package/dist/preview-engine/framefields.mjs +1 -1
  107. package/dist/preview-engine/player.mjs +1 -1
  108. package/dist/preview-engine/renderers-4CRXZJ5G.mjs +1 -0
  109. package/dist/preview-engine/renderers-4G3UOPA7.mjs +228 -0
  110. package/dist/preview-engine/{renderers-WPBTAU2E.mjs → renderers-4RVSLTCX.mjs} +1 -1
  111. package/dist/preview-engine/{renderers-W5CTR4B5.mjs → renderers-56TGEFDK.mjs} +1 -1
  112. package/dist/preview-engine/renderers-5QF4OLFQ.mjs +1 -0
  113. package/dist/preview-engine/renderers-5SOW6OVE.mjs +1 -0
  114. package/dist/preview-engine/renderers-6M2HPY2J.mjs +1 -0
  115. package/dist/preview-engine/{renderers-DZZFIYYA.mjs → renderers-7GMW52G3.mjs} +1 -1
  116. package/dist/preview-engine/renderers-7HT5NW7W.mjs +1 -0
  117. package/dist/preview-engine/{renderers-KV3TTCO6.mjs → renderers-7SW2AHM2.mjs} +1 -1
  118. package/dist/preview-engine/{renderers-BNXTSIXJ.mjs → renderers-ART3EGSZ.mjs} +1 -1
  119. package/dist/preview-engine/{renderers-6NFRKHNO.mjs → renderers-ATTGUTYY.mjs} +1 -1
  120. package/dist/preview-engine/renderers-AYWCRBQC.mjs +1 -0
  121. package/dist/preview-engine/renderers-BA6ZCPI5.mjs +1 -0
  122. package/dist/preview-engine/{renderers-AKZAAGR7.mjs → renderers-BAS7RXTC.mjs} +1 -1
  123. package/dist/preview-engine/{renderers-CLLR5MOQ.mjs → renderers-BWRNCXR6.mjs} +1 -1
  124. package/dist/preview-engine/{renderers-N7T34SCI.mjs → renderers-C3YXNKNM.mjs} +1 -1
  125. package/dist/preview-engine/renderers-CZTCW4FC.mjs +1 -0
  126. package/dist/preview-engine/{renderers-C7SJ3M6S.mjs → renderers-D2XEQ2YY.mjs} +1 -1
  127. package/dist/preview-engine/renderers-DFIJYBW2.mjs +1 -0
  128. package/dist/preview-engine/{renderers-DHKB5LI6.mjs → renderers-EJGF3PYQ.mjs} +1 -1
  129. package/dist/preview-engine/renderers-EJN6M6DO.mjs +1 -0
  130. package/dist/preview-engine/{renderers-MKEXNBCW.mjs → renderers-EK3HQUUF.mjs} +1 -1
  131. package/dist/preview-engine/{renderers-O56IOD7J.mjs → renderers-EQDINUMT.mjs} +1 -1
  132. package/dist/preview-engine/{renderers-VYK2BJID.mjs → renderers-FC7G7KU7.mjs} +1 -1
  133. package/dist/preview-engine/{renderers-BQLYS4TM.mjs → renderers-FD3ZT73R.mjs} +1 -1
  134. package/dist/preview-engine/{renderers-TASYN62K.mjs → renderers-FYI6QNUH.mjs} +1 -1
  135. package/dist/preview-engine/renderers-G34C5J27.mjs +1 -0
  136. package/dist/preview-engine/{renderers-ROSSJ7Y2.mjs → renderers-GHP7SL5M.mjs} +1 -1
  137. package/dist/preview-engine/renderers-GKVTNDVH.mjs +1 -0
  138. package/dist/preview-engine/{renderers-MPCYLXXU.mjs → renderers-GY4R467B.mjs} +1 -1
  139. package/dist/preview-engine/{renderers-TAMDNTEQ.mjs → renderers-H2UI7YEO.mjs} +1 -1
  140. package/dist/preview-engine/{renderers-6PVGRTXV.mjs → renderers-HBIBRZD7.mjs} +1 -1
  141. package/dist/preview-engine/{renderers-GF77N2KM.mjs → renderers-HMNCIEU3.mjs} +1 -1
  142. package/dist/preview-engine/{renderers-SIBP54WK.mjs → renderers-HZBYQKIQ.mjs} +1 -1
  143. package/dist/preview-engine/renderers-IK7ZJJKJ.mjs +1 -0
  144. package/dist/preview-engine/renderers-ISASPH6T.mjs +1 -0
  145. package/dist/preview-engine/renderers-IW5MWDBI.mjs +1 -0
  146. package/dist/preview-engine/renderers-K2PI4WJS.mjs +1 -0
  147. package/dist/preview-engine/renderers-KESHVFL2.mjs +1 -0
  148. package/dist/preview-engine/renderers-KTZZVAB2.mjs +1 -0
  149. package/dist/preview-engine/renderers-KY5CJEOV.mjs +1 -0
  150. package/dist/preview-engine/{renderers-MS4HG3M4.mjs → renderers-LTV5RFDJ.mjs} +1 -1
  151. package/dist/preview-engine/{renderers-CEJP6Q7F.mjs → renderers-LV6LLULR.mjs} +1 -1
  152. package/dist/preview-engine/renderers-M5W6SUQD.mjs +1 -0
  153. package/dist/preview-engine/{renderers-H3ZM2TOA.mjs → renderers-MEM3REMI.mjs} +1 -1
  154. package/dist/preview-engine/{renderers-EJATUIUP.mjs → renderers-N36R42RL.mjs} +1 -1
  155. package/dist/preview-engine/{renderers-UVABXXQF.mjs → renderers-NTKSN32T.mjs} +1 -1
  156. package/dist/preview-engine/renderers-O5XMWFPY.mjs +1 -0
  157. package/dist/preview-engine/{renderers-QO2RHF7F.mjs → renderers-P2FTCDZ7.mjs} +1 -1
  158. package/dist/preview-engine/{renderers-L7EITUCD.mjs → renderers-PYDOSDEY.mjs} +1 -1
  159. package/dist/preview-engine/renderers-QJA35PKC.mjs +1 -0
  160. package/dist/preview-engine/{renderers-TWZHTKW7.mjs → renderers-RDHHVXRE.mjs} +1 -1
  161. package/dist/preview-engine/{renderers-RRJ2DX2W.mjs → renderers-SY435YU3.mjs} +1 -1
  162. package/dist/preview-engine/renderers-TJIFTQHV.mjs +1 -0
  163. package/dist/preview-engine/renderers-TN4XN436.mjs +1 -0
  164. package/dist/preview-engine/renderers-UDYLCYCB.mjs +1 -0
  165. package/dist/preview-engine/renderers-WZ7SFIBZ.mjs +1 -0
  166. package/dist/preview-engine/renderers-X6QNDC6R.mjs +1 -0
  167. package/dist/preview-engine/renderers-X6RNTJOV.mjs +1 -0
  168. package/dist/preview-engine/{renderers-QPFZBLDI.mjs → renderers-Y3N7236P.mjs} +1 -1
  169. package/dist/preview-engine/{renderers-XSPU2BIQ.mjs → renderers-YBTWN76S.mjs} +1 -1
  170. package/dist/preview-engine/{renderers-T62TXGVO.mjs → renderers-YK74777T.mjs} +1 -1
  171. package/dist/preview-engine/renderers-YP3ASXIJ.mjs +1 -0
  172. package/dist/preview-engine/renderers-YVJAG3PD.mjs +1 -0
  173. package/dist/preview-engine/{renderers-4RFB2LG2.mjs → renderers-ZAMSS7QN.mjs} +1 -1
  174. package/dist/preview-engine/{renderers-FOHW7RWH.mjs → renderers-ZDMDDWLN.mjs} +1 -1
  175. package/dist/preview-engine/renderers-ZPPZNMKJ.mjs +1 -0
  176. package/dist/preview-engine/signals.mjs +1 -1
  177. package/dist/{program-4mAVgg3c.mjs → program-Bf4yHbHC.mjs} +19 -3
  178. package/dist/program-Bf4yHbHC.mjs.map +1 -0
  179. package/dist/registry/annotate.json +1 -1
  180. package/dist/registry/app-frame.json +1 -1
  181. package/dist/registry/backdrop.json +3 -3
  182. package/dist/registry/before-after.json +1 -1
  183. package/dist/registry/callout.json +1 -1
  184. package/dist/registry/captions.json +7 -1
  185. package/dist/registry/card-stack.json +5 -4
  186. package/dist/registry/chat-thread.json +10 -3
  187. package/dist/registry/code-block.json +8 -2
  188. package/dist/registry/confetti.json +1 -1
  189. package/dist/registry/device-frame.json +2 -1
  190. package/dist/registry/focus-zoom.json +1 -1
  191. package/dist/registry/index.json +3 -2
  192. package/dist/registry/logo-reveal.json +1 -1
  193. package/dist/registry/lower-third.json +1 -1
  194. package/dist/registry/meter.json +1 -1
  195. package/dist/registry/mouse-pointer.json +1 -1
  196. package/dist/registry/selection.json +3 -3
  197. package/dist/registry/spotlight.json +1 -1
  198. package/dist/registry/terminal.json +7 -1
  199. package/dist/registry/text-reveal.json +2 -1
  200. package/dist/registry/title-card.json +1 -1
  201. package/dist/registry/toasts.json +1 -1
  202. package/dist/registry/touch-point.json +4 -4
  203. package/dist/registry/waveform.json +3 -3
  204. package/dist/registry/word-cycle.json +2 -2
  205. package/dist/renderer/index.d.mts +2 -2
  206. package/dist/renderer/index.mjs +2 -2
  207. package/dist/renderer-4FQmduPw.mjs +5 -0
  208. package/dist/{renderer-Cjw6lyaK.mjs → renderer-4v6XYAiX.mjs} +4 -4
  209. package/dist/{renderer-Cjw6lyaK.mjs.map → renderer-4v6XYAiX.mjs.map} +1 -1
  210. package/dist/{renderer-CXa6SWXy.mjs → renderer-79mO0b9Y.mjs} +4 -4
  211. package/dist/{renderer-CXa6SWXy.mjs.map → renderer-79mO0b9Y.mjs.map} +1 -1
  212. package/dist/{renderer-wEXBnlr1.mjs → renderer-AOZWHGT3.mjs} +4 -4
  213. package/dist/{renderer-wEXBnlr1.mjs.map → renderer-AOZWHGT3.mjs.map} +1 -1
  214. package/dist/{renderer-CjcrhBB2.mjs → renderer-B1Ox12RF.mjs} +3 -3
  215. package/dist/{renderer-CjcrhBB2.mjs.map → renderer-B1Ox12RF.mjs.map} +1 -1
  216. package/dist/{renderer-B9m7xIPo.mjs → renderer-B6vr-B0t.mjs} +4 -4
  217. package/dist/{renderer-B9m7xIPo.mjs.map → renderer-B6vr-B0t.mjs.map} +1 -1
  218. package/dist/renderer-BBCpRuYU.mjs +5 -0
  219. package/dist/renderer-BBTo_jLO.mjs +5 -0
  220. package/dist/{renderer-CgMnZYTM.mjs → renderer-BDCS7k5X.mjs} +4 -4
  221. package/dist/{renderer-CgMnZYTM.mjs.map → renderer-BDCS7k5X.mjs.map} +1 -1
  222. package/dist/{renderer-BjxceNBj.mjs → renderer-BFdgbqgW.mjs} +4 -4
  223. package/dist/{renderer-BjxceNBj.mjs.map → renderer-BFdgbqgW.mjs.map} +1 -1
  224. package/dist/{renderer-C0P8ZRiC.mjs → renderer-BHgYRSu8.mjs} +3 -3
  225. package/dist/{renderer-C0P8ZRiC.mjs.map → renderer-BHgYRSu8.mjs.map} +1 -1
  226. package/dist/{renderer-CTvPNRMH.mjs → renderer-BKssznKn.mjs} +3 -3
  227. package/dist/{renderer-CTvPNRMH.mjs.map → renderer-BKssznKn.mjs.map} +1 -1
  228. package/dist/{renderer-B9iVE2GG.mjs → renderer-BL9vkn6m.mjs} +4 -4
  229. package/dist/{renderer-B9iVE2GG.mjs.map → renderer-BL9vkn6m.mjs.map} +1 -1
  230. package/dist/renderer-BLg66ZgG.mjs +5 -0
  231. package/dist/{renderer-D22g6ION.mjs → renderer-BQFxoNzi.mjs} +4 -4
  232. package/dist/{renderer-D22g6ION.mjs.map → renderer-BQFxoNzi.mjs.map} +1 -1
  233. package/dist/{renderer-Datqywo_.mjs → renderer-BQdn_qGp.mjs} +4 -4
  234. package/dist/{renderer-Datqywo_.mjs.map → renderer-BQdn_qGp.mjs.map} +1 -1
  235. package/dist/{renderer-T2dd9a2L.mjs → renderer-BR35Xp9n.mjs} +4 -4
  236. package/dist/{renderer-T2dd9a2L.mjs.map → renderer-BR35Xp9n.mjs.map} +1 -1
  237. package/dist/renderer-BScS8AzG.mjs +5 -0
  238. package/dist/{renderer-BcSSAzc4.mjs → renderer-BTjveb1Y.mjs} +4 -4
  239. package/dist/{renderer-BcSSAzc4.mjs.map → renderer-BTjveb1Y.mjs.map} +1 -1
  240. package/dist/renderer-Bb1cS5mJ.mjs +5 -0
  241. package/dist/{renderer-4P-DCjk2.mjs → renderer-Bbkf5EIi.mjs} +4 -4
  242. package/dist/{renderer-4P-DCjk2.mjs.map → renderer-Bbkf5EIi.mjs.map} +1 -1
  243. package/dist/{renderer-C9r0-KY4.mjs → renderer-BgmO1Dzw.mjs} +11 -2
  244. package/dist/{renderer-C9r0-KY4.mjs.map → renderer-BgmO1Dzw.mjs.map} +1 -1
  245. package/dist/{renderer-khVVYoVF.mjs → renderer-BmgyvIC3.mjs} +4 -4
  246. package/dist/{renderer-khVVYoVF.mjs.map → renderer-BmgyvIC3.mjs.map} +1 -1
  247. package/dist/{renderer-CvYxxS_X.mjs → renderer-Bpve7M6S.mjs} +73 -73
  248. package/dist/{renderer-CvYxxS_X.mjs.map → renderer-Bpve7M6S.mjs.map} +1 -1
  249. package/dist/renderer-Bpwd9zEY.mjs +5 -0
  250. package/dist/renderer-BsewLDHZ.mjs +3 -0
  251. package/dist/{renderer-C4f_X1Qc.mjs → renderer-BsnhAgrB.mjs} +3 -3
  252. package/dist/{renderer-C4f_X1Qc.mjs.map → renderer-BsnhAgrB.mjs.map} +1 -1
  253. package/dist/{renderer-BIt-At3v.mjs → renderer-By5xTRqB.mjs} +4 -4
  254. package/dist/{renderer-BIt-At3v.mjs.map → renderer-By5xTRqB.mjs.map} +1 -1
  255. package/dist/{renderer-vc8t_z-y.mjs → renderer-BzEQXAdl.mjs} +3 -3
  256. package/dist/{renderer-vc8t_z-y.mjs.map → renderer-BzEQXAdl.mjs.map} +1 -1
  257. package/dist/renderer-Bzyd9xi5.mjs +5 -0
  258. package/dist/{renderer-CE9N4EEI.d.mts → renderer-C-dcd14F.d.mts} +2 -2
  259. package/dist/{renderer-CE9N4EEI.d.mts.map → renderer-C-dcd14F.d.mts.map} +1 -1
  260. package/dist/{renderer-RiWi7y5C.mjs → renderer-C64g87yU.mjs} +4 -4
  261. package/dist/{renderer-RiWi7y5C.mjs.map → renderer-C64g87yU.mjs.map} +1 -1
  262. package/dist/{renderer-De7gno9X.mjs → renderer-C6Wb1TZT.mjs} +4 -4
  263. package/dist/{renderer-De7gno9X.mjs.map → renderer-C6Wb1TZT.mjs.map} +1 -1
  264. package/dist/{renderer-CT_3Fegr.mjs → renderer-CHPLHbAs.mjs} +4 -4
  265. package/dist/{renderer-CT_3Fegr.mjs.map → renderer-CHPLHbAs.mjs.map} +1 -1
  266. package/dist/{renderer-CkJXq4bD.mjs → renderer-CJRu7lLH.mjs} +3 -3
  267. package/dist/{renderer-CkJXq4bD.mjs.map → renderer-CJRu7lLH.mjs.map} +1 -1
  268. package/dist/{renderer-BwRLXhxJ.mjs → renderer-CMWUtfwn.mjs} +3 -3
  269. package/dist/{renderer-BwRLXhxJ.mjs.map → renderer-CMWUtfwn.mjs.map} +1 -1
  270. package/dist/{renderer-C4pS7aAI.mjs → renderer-CRkgyk08.mjs} +4 -4
  271. package/dist/{renderer-C4pS7aAI.mjs.map → renderer-CRkgyk08.mjs.map} +1 -1
  272. package/dist/renderer-CXVssx1k.mjs +5 -0
  273. package/dist/{renderer-5-9DPis6.mjs → renderer-CY0BT6ok.mjs} +3 -3
  274. package/dist/{renderer-5-9DPis6.mjs.map → renderer-CY0BT6ok.mjs.map} +1 -1
  275. package/dist/renderer-CYFiGLls.mjs +5 -0
  276. package/dist/{renderer-BVtQzjgT.mjs → renderer-CYLP8sD6.mjs} +3 -3
  277. package/dist/{renderer-BVtQzjgT.mjs.map → renderer-CYLP8sD6.mjs.map} +1 -1
  278. package/dist/{renderer-DPVEs5ME.mjs → renderer-CY_1u6zi.mjs} +4 -4
  279. package/dist/{renderer-DPVEs5ME.mjs.map → renderer-CY_1u6zi.mjs.map} +1 -1
  280. package/dist/renderer-C__BvvtP.mjs +5 -0
  281. package/dist/renderer-Ca3YZsoT.mjs +5 -0
  282. package/dist/{renderer-BpBhvMi1.mjs → renderer-CcSBjtRa.mjs} +2 -2
  283. package/dist/{renderer-nLPfvY3-.mjs → renderer-CdxNwGeb.mjs} +2 -2
  284. package/dist/{renderer-nLPfvY3-.mjs.map → renderer-CdxNwGeb.mjs.map} +1 -1
  285. package/dist/{renderer-CJ2-jEy7.mjs → renderer-CgdplRri.mjs} +3 -3
  286. package/dist/{renderer-CJ2-jEy7.mjs.map → renderer-CgdplRri.mjs.map} +1 -1
  287. package/dist/{renderer-MQPiZQXN.mjs → renderer-Cn9tU_s5.mjs} +3 -3
  288. package/dist/{renderer-MQPiZQXN.mjs.map → renderer-Cn9tU_s5.mjs.map} +1 -1
  289. package/dist/renderer-CnM5iFj9.mjs +5 -0
  290. package/dist/{renderer-Cz5SOnJr.mjs → renderer-Cqp_TUl_.mjs} +3 -3
  291. package/dist/{renderer-Cz5SOnJr.mjs.map → renderer-Cqp_TUl_.mjs.map} +1 -1
  292. package/dist/{renderer-BKayVZhL.mjs → renderer-CsOLHKFx.mjs} +3 -3
  293. package/dist/{renderer-BKayVZhL.mjs.map → renderer-CsOLHKFx.mjs.map} +1 -1
  294. package/dist/{renderer-BWqQxZNk.mjs → renderer-D01RT3Ov.mjs} +2 -2
  295. package/dist/renderer-D1nZrvQx.mjs +5 -0
  296. package/dist/renderer-D6XjVQME.mjs +5 -0
  297. package/dist/{renderer-9gjn790a.mjs → renderer-DAONCf_3.mjs} +4 -4
  298. package/dist/{renderer-9gjn790a.mjs.map → renderer-DAONCf_3.mjs.map} +1 -1
  299. package/dist/{renderer-D0iSbNza.mjs → renderer-DAfWLGfy.mjs} +4 -4
  300. package/dist/{renderer-D0iSbNza.mjs.map → renderer-DAfWLGfy.mjs.map} +1 -1
  301. package/dist/{renderer-Bk7vVfTu.mjs → renderer-DEeKkljI.mjs} +3 -3
  302. package/dist/{renderer-Bk7vVfTu.mjs.map → renderer-DEeKkljI.mjs.map} +1 -1
  303. package/dist/{renderer-B9AZ6R1U.mjs → renderer-DFYXjAIT.mjs} +172 -8
  304. package/dist/renderer-DFYXjAIT.mjs.map +1 -0
  305. package/dist/renderer-DIuMnMF0.mjs +5 -0
  306. package/dist/{renderer-DnT62IDK.mjs → renderer-DJHuPNJx.mjs} +3 -3
  307. package/dist/{renderer-DnT62IDK.mjs.map → renderer-DJHuPNJx.mjs.map} +1 -1
  308. package/dist/{renderer-B9yv3R1y.mjs → renderer-DJTXhVNh.mjs} +3 -3
  309. package/dist/{renderer-B9yv3R1y.mjs.map → renderer-DJTXhVNh.mjs.map} +1 -1
  310. package/dist/{renderer-DPzair18.mjs → renderer-DNEFYrQ5.mjs} +2 -2
  311. package/dist/renderer-DUvvM8EF.mjs +5 -0
  312. package/dist/renderer-DWogyuMO.mjs +5 -0
  313. package/dist/renderer-DWsj_LJR.mjs +5 -0
  314. package/dist/{renderer-BBA-iCi-.mjs → renderer-DYJHFHF4.mjs} +34 -9
  315. package/dist/renderer-DYJHFHF4.mjs.map +1 -0
  316. package/dist/{renderer-Dkt1oh9H.mjs → renderer-DYR4gFEM.mjs} +4 -4
  317. package/dist/{renderer-Dkt1oh9H.mjs.map → renderer-DYR4gFEM.mjs.map} +1 -1
  318. package/dist/renderer-DYun0DJl.mjs +5 -0
  319. package/dist/renderer-D_6YqD-Z.mjs +5 -0
  320. package/dist/{renderer-DTXAa2pL.mjs → renderer-D_UPFbei.mjs} +4 -4
  321. package/dist/{renderer-DTXAa2pL.mjs.map → renderer-D_UPFbei.mjs.map} +1 -1
  322. package/dist/renderer-Db9LP86x.mjs +5 -0
  323. package/dist/{renderer-Y5JmTL8J.mjs → renderer-DgRJG_l-.mjs} +3 -3
  324. package/dist/{renderer-Y5JmTL8J.mjs.map → renderer-DgRJG_l-.mjs.map} +1 -1
  325. package/dist/renderer-DhHiC87m.mjs +5 -0
  326. package/dist/{renderer-C8-NmotX.mjs → renderer-DiD3r9tx.mjs} +3 -3
  327. package/dist/{renderer-C8-NmotX.mjs.map → renderer-DiD3r9tx.mjs.map} +1 -1
  328. package/dist/{renderer-_vcT__tW.mjs → renderer-Dj-y-BtT.mjs} +4 -4
  329. package/dist/{renderer-_vcT__tW.mjs.map → renderer-Dj-y-BtT.mjs.map} +1 -1
  330. package/dist/renderer-Dj5jGAkR.mjs +5 -0
  331. package/dist/{renderer-NL2IZShC.mjs → renderer-DlA7kzIs.mjs} +4 -4
  332. package/dist/{renderer-NL2IZShC.mjs.map → renderer-DlA7kzIs.mjs.map} +1 -1
  333. package/dist/renderer-DlPUQ7RF.mjs +5 -0
  334. package/dist/renderer-DpIPdgn8.mjs +5 -0
  335. package/dist/{renderer-wIsJb2eO.mjs → renderer-Duorbrb8.mjs} +2 -2
  336. package/dist/{renderer-wIsJb2eO.mjs.map → renderer-Duorbrb8.mjs.map} +1 -1
  337. package/dist/renderer-Dv0C9CEt.mjs +6 -0
  338. package/dist/{renderer-C7HIJcJK.mjs → renderer-EwSSuJXj.mjs} +4 -4
  339. package/dist/{renderer-C7HIJcJK.mjs.map → renderer-EwSSuJXj.mjs.map} +1 -1
  340. package/dist/renderer-Iw8fDL-p.mjs +5 -0
  341. package/dist/{renderer-CuwVuVKG.mjs → renderer-JJibsJfb.mjs} +3 -3
  342. package/dist/{renderer-CuwVuVKG.mjs.map → renderer-JJibsJfb.mjs.map} +1 -1
  343. package/dist/{renderer-B8PNlKEM.mjs → renderer-KppAWfmJ.mjs} +4 -4
  344. package/dist/{renderer-B8PNlKEM.mjs.map → renderer-KppAWfmJ.mjs.map} +1 -1
  345. package/dist/{renderer-Cm9fEQsw.mjs → renderer-LW8MfGjd.mjs} +3 -3
  346. package/dist/{renderer-Cm9fEQsw.mjs.map → renderer-LW8MfGjd.mjs.map} +1 -1
  347. package/dist/{renderer-Cb5uDZm0.mjs → renderer-PMjV8x65.mjs} +3 -3
  348. package/dist/{renderer-Cb5uDZm0.mjs.map → renderer-PMjV8x65.mjs.map} +1 -1
  349. package/dist/renderer-SLI69Mju.mjs +5 -0
  350. package/dist/{renderer-CGKKjJ5Q.mjs → renderer-UArFa4y9.mjs} +3 -3
  351. package/dist/{renderer-CGKKjJ5Q.mjs.map → renderer-UArFa4y9.mjs.map} +1 -1
  352. package/dist/{renderer-Bh0dwc8n.mjs → renderer-VwKmjuc6.mjs} +4 -4
  353. package/dist/{renderer-Bh0dwc8n.mjs.map → renderer-VwKmjuc6.mjs.map} +1 -1
  354. package/dist/{renderer-92IAZzBU.d.mts → renderer-WTvo3wo9.d.mts} +18 -4
  355. package/dist/renderer-WTvo3wo9.d.mts.map +1 -0
  356. package/dist/renderer-WyaqnVMz.mjs +3 -0
  357. package/dist/renderer-ZVtbb3-P.mjs +5 -0
  358. package/dist/{renderer-C5h2TSjH.mjs → renderer-bb3puodg.mjs} +3 -3
  359. package/dist/{renderer-C5h2TSjH.mjs.map → renderer-bb3puodg.mjs.map} +1 -1
  360. package/dist/{renderer-CWnhRMOq.mjs → renderer-eyvY4Lq4.mjs} +4 -4
  361. package/dist/{renderer-CWnhRMOq.mjs.map → renderer-eyvY4Lq4.mjs.map} +1 -1
  362. package/dist/renderer-gHNCu7H5.mjs +5 -0
  363. package/dist/renderer-gxDNmbtU.mjs +5 -0
  364. package/dist/renderer-hkK4DNRX.mjs +5 -0
  365. package/dist/{renderer-CxOaqnho.mjs → renderer-ij448rvt.mjs} +3 -3
  366. package/dist/{renderer-CxOaqnho.mjs.map → renderer-ij448rvt.mjs.map} +1 -1
  367. package/dist/{renderer-CxkUI9s_.mjs → renderer-j7aUfyeR.mjs} +4 -4
  368. package/dist/{renderer-CxkUI9s_.mjs.map → renderer-j7aUfyeR.mjs.map} +1 -1
  369. package/dist/{renderer-PNIgmWft.mjs → renderer-kFtcuY9J.mjs} +2 -2
  370. package/dist/{renderer-PNIgmWft.mjs.map → renderer-kFtcuY9J.mjs.map} +1 -1
  371. package/dist/{renderer-zdpn1Hkk.mjs → renderer-kOlfYLtu.mjs} +3 -3
  372. package/dist/{renderer-zdpn1Hkk.mjs.map → renderer-kOlfYLtu.mjs.map} +1 -1
  373. package/dist/{renderer-CPl1wOrj.mjs → renderer-l1qJE4z-.mjs} +3 -3
  374. package/dist/{renderer-CPl1wOrj.mjs.map → renderer-l1qJE4z-.mjs.map} +1 -1
  375. package/dist/{renderer-BIYJKdwA.mjs → renderer-ml2LHmRb.mjs} +3 -3
  376. package/dist/{renderer-BIYJKdwA.mjs.map → renderer-ml2LHmRb.mjs.map} +1 -1
  377. package/dist/{renderer-BmX2CmyO.mjs → renderer-mu2mYo5w.mjs} +4 -4
  378. package/dist/{renderer-BmX2CmyO.mjs.map → renderer-mu2mYo5w.mjs.map} +1 -1
  379. package/dist/{renderer-D-FwaV1R.mjs → renderer-ovkctV5a.mjs} +2 -2
  380. package/dist/renderer-oy6SGDRz.mjs +5 -0
  381. package/dist/{renderer-Du8z4UrN.mjs → renderer-qQmoO5Hk.mjs} +3 -3
  382. package/dist/{renderer-Du8z4UrN.mjs.map → renderer-qQmoO5Hk.mjs.map} +1 -1
  383. package/dist/renderer-rq7UIcuH.mjs +5 -0
  384. package/dist/{renderer-Bt_j2Zi6.mjs → renderer-rv-AKqCl.mjs} +3 -3
  385. package/dist/{renderer-Bt_j2Zi6.mjs.map → renderer-rv-AKqCl.mjs.map} +1 -1
  386. package/dist/renderer-xkEJahws.mjs +5 -0
  387. package/dist/{renderer-C1sFg34Y.mjs → renderer-yF40Pb-n.mjs} +4 -4
  388. package/dist/{renderer-C1sFg34Y.mjs.map → renderer-yF40Pb-n.mjs.map} +1 -1
  389. package/dist/{renderer-Ddg3dJmu.mjs → renderer-yFHP9Zj2.mjs} +4 -4
  390. package/dist/{renderer-Ddg3dJmu.mjs.map → renderer-yFHP9Zj2.mjs.map} +1 -1
  391. package/dist/{renderer-DYdll3xW.mjs → renderer-zOK5DYYn.mjs} +4 -4
  392. package/dist/{renderer-DYdll3xW.mjs.map → renderer-zOK5DYYn.mjs.map} +1 -1
  393. package/dist/{renderers-CP8CFWDs-BbLkcJTN.mjs → renderers-CP8CFWDs-DPLX5EJs.mjs} +3 -3
  394. package/dist/{renderers-CP8CFWDs-BbLkcJTN.mjs.map → renderers-CP8CFWDs-DPLX5EJs.mjs.map} +1 -1
  395. package/dist/signals/index.d.mts +5 -5
  396. package/dist/signals/index.mjs +5 -5
  397. package/dist/{src-WAnxhDYD.mjs → src-BnkrNj2W.mjs} +613 -118
  398. package/dist/src-BnkrNj2W.mjs.map +1 -0
  399. package/package.json +59 -58
  400. package/dist/dist-Bf0H4lp1.mjs.map +0 -1
  401. package/dist/dist-DG65RDhc.mjs.map +0 -1
  402. package/dist/fonts-DqYgk_1a.mjs.map +0 -1
  403. package/dist/index-CZK6CFLy.d.mts.map +0 -1
  404. package/dist/index-CalU-mpZ.d.mts.map +0 -1
  405. package/dist/index-tCKz9Tku.d.mts.map +0 -1
  406. package/dist/preview-engine/chunk-FAEWI3LF.mjs +0 -5
  407. package/dist/preview-engine/chunk-I6E3AKLS.mjs +0 -1
  408. package/dist/preview-engine/chunk-PR37RCNO.mjs +0 -1974
  409. package/dist/preview-engine/chunk-YSXMXD3F.mjs +0 -3
  410. package/dist/preview-engine/renderers-2S5ZWL2J.mjs +0 -1
  411. package/dist/preview-engine/renderers-37CN7O4S.mjs +0 -1
  412. package/dist/preview-engine/renderers-3UGG2IMP.mjs +0 -1
  413. package/dist/preview-engine/renderers-7MPBAHYA.mjs +0 -1
  414. package/dist/preview-engine/renderers-AFTYMS3H.mjs +0 -1
  415. package/dist/preview-engine/renderers-AYEWAW7F.mjs +0 -1
  416. package/dist/preview-engine/renderers-BILNWTUC.mjs +0 -228
  417. package/dist/preview-engine/renderers-DUF2CTX6.mjs +0 -1
  418. package/dist/preview-engine/renderers-EIJFIY6L.mjs +0 -1
  419. package/dist/preview-engine/renderers-EOMMSENQ.mjs +0 -1
  420. package/dist/preview-engine/renderers-JXBZPQKP.mjs +0 -1
  421. package/dist/preview-engine/renderers-LD3COS4X.mjs +0 -1
  422. package/dist/preview-engine/renderers-MCTBJEL7.mjs +0 -1
  423. package/dist/preview-engine/renderers-OZGEXW2X.mjs +0 -1
  424. package/dist/preview-engine/renderers-PSEDMUBV.mjs +0 -1
  425. package/dist/preview-engine/renderers-PVUROEGR.mjs +0 -1
  426. package/dist/preview-engine/renderers-QBVVTQV6.mjs +0 -1
  427. package/dist/preview-engine/renderers-R4U2TK6P.mjs +0 -1
  428. package/dist/preview-engine/renderers-RBLNDAO4.mjs +0 -1
  429. package/dist/preview-engine/renderers-RM25AXWZ.mjs +0 -1
  430. package/dist/preview-engine/renderers-RYRFCEYT.mjs +0 -1
  431. package/dist/preview-engine/renderers-SKT7AGGE.mjs +0 -1
  432. package/dist/preview-engine/renderers-SRU6QXLN.mjs +0 -1
  433. package/dist/preview-engine/renderers-TBP2EKJ4.mjs +0 -1
  434. package/dist/preview-engine/renderers-UQHKFGDX.mjs +0 -1
  435. package/dist/preview-engine/renderers-WDV7ENJA.mjs +0 -1
  436. package/dist/preview-engine/renderers-WNBDSSJ3.mjs +0 -1
  437. package/dist/preview-engine/renderers-WTC2RNGU.mjs +0 -1
  438. package/dist/preview-engine/renderers-XVUOO7B6.mjs +0 -1
  439. package/dist/preview-engine/renderers-YHOQUEHD.mjs +0 -1
  440. package/dist/preview-engine/renderers-YPFM5UJO.mjs +0 -1
  441. package/dist/preview-engine/renderers-YW3LBALW.mjs +0 -1
  442. package/dist/program-4mAVgg3c.mjs.map +0 -1
  443. package/dist/renderer-1ymN1FIT.mjs +0 -5
  444. package/dist/renderer-92IAZzBU.d.mts.map +0 -1
  445. package/dist/renderer-B-43EuKb.mjs +0 -5
  446. package/dist/renderer-B-v5ZboU.mjs +0 -5
  447. package/dist/renderer-B9AZ6R1U.mjs.map +0 -1
  448. package/dist/renderer-BBA-iCi-.mjs.map +0 -1
  449. package/dist/renderer-BKF8Sfbg.mjs +0 -3
  450. package/dist/renderer-BTB8ifgH.mjs +0 -5
  451. package/dist/renderer-BYxzR6Hp.mjs +0 -5
  452. package/dist/renderer-BcGhO_Mw.mjs +0 -5
  453. package/dist/renderer-Bk3RgiFg.mjs +0 -5
  454. package/dist/renderer-BvItCxkw.mjs +0 -5
  455. package/dist/renderer-BvcqncI2.mjs +0 -3
  456. package/dist/renderer-CCnfPHqk.mjs +0 -5
  457. package/dist/renderer-CIoWQNaA.mjs +0 -5
  458. package/dist/renderer-CMwQtIIq.mjs +0 -5
  459. package/dist/renderer-CN8kRB90.mjs +0 -6
  460. package/dist/renderer-CPKBDif1.mjs +0 -5
  461. package/dist/renderer-CamsHxEP.mjs +0 -5
  462. package/dist/renderer-CeQormOH.mjs +0 -5
  463. package/dist/renderer-Cex4eMtv.mjs +0 -5
  464. package/dist/renderer-CjyTrNlr.mjs +0 -5
  465. package/dist/renderer-Clv9TO6C.mjs +0 -5
  466. package/dist/renderer-D4LnJk-M.mjs +0 -5
  467. package/dist/renderer-D5OfQ8Ut.mjs +0 -5
  468. package/dist/renderer-DBYssktC.mjs +0 -5
  469. package/dist/renderer-DC-Aj3me.mjs +0 -5
  470. package/dist/renderer-DG97_Aoc.mjs +0 -5
  471. package/dist/renderer-DS4UGjcS.mjs +0 -5
  472. package/dist/renderer-DdG6ibtM.mjs +0 -5
  473. package/dist/renderer-DhZi4f3t.mjs +0 -5
  474. package/dist/renderer-MgLekf6d.mjs +0 -5
  475. package/dist/renderer-Qvs-1lWh.mjs +0 -5
  476. package/dist/renderer-bN7dOErt.mjs +0 -5
  477. package/dist/renderer-jWKVmReU.mjs +0 -5
  478. package/dist/renderer-lk-At57G.mjs +0 -5
  479. package/dist/renderer-qi8Slwfo.mjs +0 -5
  480. package/dist/renderer-qxOH4JH1.mjs +0 -5
  481. package/dist/renderer-t30NpmIl.mjs +0 -5
  482. package/dist/renderer-wKW4HWRx.mjs +0 -5
  483. package/dist/renderer-ydjPm8bp.mjs +0 -5
  484. package/dist/src-WAnxhDYD.mjs.map +0 -1
package/dist/cli.mjs CHANGED
@@ -1,17 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { dirname, join, relative, resolve } from "node:path";
3
+ import { basename, dirname, join, posix, relative, resolve, sep } from "node:path";
4
+ import { createHash } from "node:crypto";
4
5
  import { fileURLToPath } from "node:url";
6
+ import { spawn } from "node:child_process";
5
7
  import { parseArgs } from "node:util";
6
8
 
7
- //#region src/cli/add.ts
9
+ //#region src/cli/config.ts
8
10
  /**
9
- * `framefields add`: copy components' source into a project, the way a
10
- * shadcn-style registry does. The code becomes the project's own; it
11
- * imports only `framefields` and `framefields/components` (the toolkit),
12
- * plus any component it builds on, which is installed alongside it.
13
- *
14
- * Where they go is remembered in `framefields.json` at the project root.
11
+ * `framefields.json` at the project root: where installed components live,
12
+ * and which fonts `framefields add` / `framefields fonts add` installed.
15
13
  */
16
14
  const CONFIG_FILE = "framefields.json";
17
15
  /** The project's config, or the default it would get: `src/components` when there is a `src`. */
@@ -20,6 +18,7 @@ function readConfig(cwd) {
20
18
  if (existsSync(file)) {
21
19
  const config = JSON.parse(readFileSync(file, "utf8"));
22
20
  if (typeof config.components !== "string") throw new Error(`${CONFIG_FILE}: "components" must be a directory path`);
21
+ if (config.fonts !== void 0 && (typeof config.fonts !== "object" || Object.values(config.fonts).some((v) => typeof v !== "string"))) throw new Error(`${CONFIG_FILE}: "fonts" must map roles to font file paths, e.g. { "emoji": "assets/fonts/NotoColorEmoji.ttf" }`);
23
22
  return {
24
23
  config,
25
24
  saved: true
@@ -30,6 +29,236 @@ function readConfig(cwd) {
30
29
  saved: false
31
30
  };
32
31
  }
32
+ function writeConfig(cwd, config) {
33
+ writeFileSync(join(cwd, CONFIG_FILE), `${JSON.stringify(config, null, " ")}\n`);
34
+ }
35
+
36
+ //#endregion
37
+ //#region src/cli/fonts.ts
38
+ /**
39
+ * Fonts the CLI installs into a project: a catalog of pinned files (URL,
40
+ * size, SHA-256, license), the installer `framefields add` and
41
+ * `framefields fonts add` share, and the generated `fonts.ts` module that
42
+ * registers whatever is installed.
43
+ *
44
+ * framefields ships no fonts in its package (a color emoji font alone is
45
+ * ~10 MB); they're fetched when a project needs one, verified byte for byte,
46
+ * and saved with their license next to them.
47
+ */
48
+ const NOTO_EMOJI = "https://raw.githubusercontent.com/googlefonts/noto-emoji/v2.051";
49
+ /** google/fonts at one commit: Inter and JetBrains Mono. */
50
+ const GOOGLE_FONTS = "https://raw.githubusercontent.com/google/fonts/e7db6ca603fd4862010383ff17ffd6cc4ffac1c0";
51
+ /** Installable fonts by role. */
52
+ const FONTS = {
53
+ emoji: {
54
+ label: "Noto Color Emoji 2.051: color emoji in any text (🎉 ❤️ 👍)",
55
+ file: "NotoColorEmoji.ttf",
56
+ font: {
57
+ url: `${NOTO_EMOJI}/fonts/NotoColorEmoji.ttf`,
58
+ sha256: "72a635cb3d2f3524c51620cdde406b217204e8a6a06c6a096ff8ed4b5fd6e27b",
59
+ bytes: 10673480
60
+ },
61
+ license: {
62
+ id: "OFL-1.1",
63
+ file: "NotoColorEmoji-OFL.txt",
64
+ url: `${NOTO_EMOJI}/fonts/LICENSE`,
65
+ sha256: "6a73f9541c2de74158c0e7cf6b0a58ef774f5a780bf191f2d7ec9cc53efe2bf2",
66
+ bytes: 4301
67
+ },
68
+ register: { emoji: true }
69
+ },
70
+ sans: googleFont({
71
+ label: "Inter 4: the built-in themes' display and body face",
72
+ file: "Inter.ttf",
73
+ family: "Inter",
74
+ path: "ofl/inter/Inter%5Bopsz,wght%5D.ttf",
75
+ sha256: "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031",
76
+ bytes: 876576,
77
+ license: {
78
+ path: "ofl/inter/OFL.txt",
79
+ sha256: "5b9321a4298cfeb6b34354164a1c3afc3db114569984c502b9b35d988fd58c57",
80
+ bytes: 4377
81
+ }
82
+ }),
83
+ mono: googleFont({
84
+ label: "JetBrains Mono 2: the built-in themes' code and terminal face",
85
+ file: "JetBrainsMono.ttf",
86
+ family: "JetBrains Mono",
87
+ path: "ofl/jetbrainsmono/JetBrainsMono%5Bwght%5D.ttf",
88
+ sha256: "48715a42ec242c21e9f02692891e147d022299a52e48d5e413e1a942193ffeda",
89
+ bytes: 187208,
90
+ license: {
91
+ path: "ofl/jetbrainsmono/OFL.txt",
92
+ sha256: "b2fe5e8987594e9ffd1d2ca52a2f5d73eb8335243893c5d6254b5ad69269591d",
93
+ bytes: 4399
94
+ }
95
+ })
96
+ };
97
+ /** google/fonts, pinned to one commit. */
98
+ function googleFont(o) {
99
+ const at = (path$1) => `${GOOGLE_FONTS}/${path$1}`;
100
+ return {
101
+ label: o.label,
102
+ file: o.file,
103
+ font: {
104
+ url: at(o.path),
105
+ sha256: o.sha256,
106
+ bytes: o.bytes
107
+ },
108
+ license: {
109
+ url: at(o.license.path),
110
+ sha256: o.license.sha256,
111
+ bytes: o.license.bytes,
112
+ id: "OFL-1.1",
113
+ file: o.file.replace(/\.ttf$/, "-OFL.txt")
114
+ },
115
+ register: { family: o.family }
116
+ };
117
+ }
118
+ /** Where fonts go when `framefields.json` names no place: the setup skill's layout. */
119
+ const DEFAULT_FONTS_DIR = "assets/fonts";
120
+ const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
121
+ /** A project-relative path with forward slashes, as `framefields.json` keeps it. */
122
+ const projectPath = (cwd, path$1) => relative(cwd, path$1).split(sep).join(posix.sep);
123
+ /** Install the font for `role` into the project and record it in `framefields.json`. */
124
+ async function installFont(role, options) {
125
+ const catalog = options.catalog ?? FONTS;
126
+ const spec = catalog[role];
127
+ if (!spec) throw new Error(`Unknown font "${role}". Fonts framefields can install: ${Object.keys(catalog).join(", ")}.`);
128
+ const { cwd } = options;
129
+ const { config } = readConfig(cwd);
130
+ const configured = config.fonts?.[role];
131
+ if (configured && existsSync(resolve(cwd, configured))) return {
132
+ role,
133
+ path: configured,
134
+ status: "configured"
135
+ };
136
+ const target = resolve(cwd, options.dir ?? DEFAULT_FONTS_DIR, spec.file);
137
+ const path$1 = projectPath(cwd, target);
138
+ let status = "present";
139
+ const current = existsSync(target) ? readFileSync(target) : null;
140
+ if (!current || sha256(current) !== spec.font.sha256) {
141
+ if (current && !options.overwrite) throw new Error(`${path$1} exists but isn't the pinned ${spec.file}. Pass --overwrite to replace it, or point "fonts.${role}" in framefields.json at it to keep using yours.`);
142
+ options.onDownload?.(spec);
143
+ const get = options.fetch ?? fetch;
144
+ const [font, license] = await Promise.all([download(spec.font, get), download(spec.license, get)]);
145
+ mkdirSync(dirname(target), { recursive: true });
146
+ writeFileSync(target, font);
147
+ writeFileSync(join(dirname(target), spec.license.file), license);
148
+ status = "installed";
149
+ }
150
+ writeConfig(cwd, {
151
+ ...config,
152
+ fonts: {
153
+ ...config.fonts,
154
+ [role]: path$1
155
+ }
156
+ });
157
+ return {
158
+ role,
159
+ path: path$1,
160
+ status
161
+ };
162
+ }
163
+ async function download(file, get) {
164
+ let res;
165
+ try {
166
+ res = await get(file.url);
167
+ } catch (error) {
168
+ throw new Error(`Couldn't download ${file.url} (${error instanceof Error ? error.message : String(error)}). Check the connection, or download it yourself and point framefields.json "fonts" at it.`);
169
+ }
170
+ if (!res.ok) throw new Error(`${file.url}: ${res.status} ${res.statusText}`);
171
+ const bytes = new Uint8Array(await res.arrayBuffer());
172
+ const hash = sha256(bytes);
173
+ if (bytes.length !== file.bytes || hash !== file.sha256) throw new Error(`${file.url} isn't the pinned file (got ${bytes.length} bytes, sha256 ${hash}; expected ${file.bytes}, ${file.sha256}). Nothing was written.`);
174
+ return bytes;
175
+ }
176
+ /** First line of the generated module: files without it are the user's and left alone. */
177
+ const FONTS_MODULE_MARKER = "// Generated by framefields from framefields.json.";
178
+ /**
179
+ * Write `<components>/fonts.ts`, which registers every font in
180
+ * `framefields.json`. Call it once before building or rendering:
181
+ * `await registerFonts()`.
182
+ */
183
+ function writeFontsModule(cwd, componentsDir, catalog = FONTS) {
184
+ const { config } = readConfig(cwd);
185
+ const target = join(componentsDir, "fonts.ts");
186
+ const path$1 = projectPath(cwd, target);
187
+ const fonts$1 = Object.entries(config.fonts ?? {}).filter(([role]) => catalog[role]).sort(([a], [b]) => a.localeCompare(b)).map(([role, file]) => {
188
+ const from = relative(componentsDir, resolve(cwd, file)).split(sep).join(posix.sep);
189
+ const url = from.startsWith(".") ? from : `./${from}`;
190
+ return {
191
+ register: catalog[role].register,
192
+ url
193
+ };
194
+ });
195
+ const call = (r, source) => "family" in r ? `register({ family: ${JSON.stringify(r.family)}, source: ${source} })` : `registerEmojiFont(${source})`;
196
+ const calls = fonts$1.map((f) => `await FontManager.${call(f.register, `asset("${f.url}")`)};`);
197
+ const content = `${FONTS_MODULE_MARKER}
198
+ // \`framefields add\` and \`framefields fonts add\` rewrite it when fonts
199
+ // change; register any other fonts in your own code.
200
+ import { FontManager } from "framefields";
201
+
202
+ /** A path next to this file as a URL: works in Node and in the browser preview. */
203
+ const asset = (path: string) => new URL(path, import.meta.url).href;
204
+
205
+ /**
206
+ * Register the fonts this project installed with framefields. Call it
207
+ * once, before building any text or rendering:
208
+ *
209
+ * \`\`\`ts
210
+ * await registerFonts();
211
+ * \`\`\`
212
+ */
213
+ export async function registerFonts(): Promise<void> {
214
+ ${fonts$1.map((f) => "family" in f.register ? `\tawait FontManager.register({\n\t\tfamily: ${JSON.stringify(f.register.family)},\n\t\tsource: asset("${f.url}"),\n\t});` : `\tawait FontManager.registerEmojiFont(\n\t\tasset("${f.url}"),\n\t);`).join("\n")}
215
+ }
216
+ `;
217
+ if (existsSync(target)) {
218
+ const current = readFileSync(target, "utf8");
219
+ if (current === content) return {
220
+ path: path$1,
221
+ status: "unchanged",
222
+ calls
223
+ };
224
+ if (!current.startsWith(FONTS_MODULE_MARKER)) return {
225
+ path: path$1,
226
+ status: "skipped",
227
+ calls
228
+ };
229
+ }
230
+ mkdirSync(componentsDir, { recursive: true });
231
+ writeFileSync(target, content);
232
+ return {
233
+ path: path$1,
234
+ status: "written",
235
+ calls
236
+ };
237
+ }
238
+ /**
239
+ * A hint when fonts land in a Git repo whose `.gitattributes` doesn't send
240
+ * them to LFS (a 10 MB binary bloats every clone), or null.
241
+ */
242
+ function lfsHint(cwd, fontPath) {
243
+ if (!existsSync(join(cwd, ".git"))) return null;
244
+ const attrs = join(cwd, ".gitattributes");
245
+ const text = existsSync(attrs) ? readFileSync(attrs, "utf8") : "";
246
+ if (/\.ttf\b[^\n]*filter=lfs/.test(text)) return null;
247
+ return `Fonts are large binaries: track them with Git LFS, e.g. \`git lfs track "${posix.dirname(fontPath)}/**/*.ttf"\`.`;
248
+ }
249
+
250
+ //#endregion
251
+ //#region src/cli/add.ts
252
+ /**
253
+ * `framefields add`: copy components' source into a project, the way a
254
+ * shadcn-style registry does. The code becomes the project's own; it
255
+ * imports only `framefields` and `framefields/components` (the toolkit),
256
+ * plus any component it builds on, which is installed alongside it.
257
+ *
258
+ * Where they go is remembered in `framefields.json` at the project root.
259
+ * Components that draw with fonts beyond the theme's (emoji) get them
260
+ * installed too, plus a generated `fonts.ts` that registers them.
261
+ */
33
262
  /** Resolve `names` (and what they import) and write their files. */
34
263
  async function add(names, options) {
35
264
  const { cwd, registry } = options;
@@ -54,7 +283,9 @@ async function add(names, options) {
54
283
  written: [],
55
284
  unchanged: [],
56
285
  skipped: [],
57
- configWritten: false
286
+ configWritten: false,
287
+ fonts: [],
288
+ fontsSkipped: []
58
289
  };
59
290
  for (const item of order) for (const file of item.files) {
60
291
  const target = join(dir, file.path);
@@ -74,11 +305,499 @@ async function add(names, options) {
74
305
  report.written.push(shown);
75
306
  }
76
307
  if (!saved) {
77
- writeFileSync(join(cwd, CONFIG_FILE), `${JSON.stringify({ components: relative(cwd, dir) || "." }, null, " ")}\n`);
308
+ writeConfig(cwd, { components: relative(cwd, dir) || "." });
78
309
  report.configWritten = true;
79
310
  }
311
+ const needs = /* @__PURE__ */ new Map();
312
+ for (const item of order) for (const f of item.fonts ?? []) if (!needs.has(f.font)) needs.set(f.font, {
313
+ why: f.why,
314
+ by: item.name
315
+ });
316
+ for (const [role, need] of needs) {
317
+ if (options.fonts === false) {
318
+ report.fontsSkipped.push({
319
+ role,
320
+ ...need
321
+ });
322
+ continue;
323
+ }
324
+ const done = await installFont(role, {
325
+ cwd,
326
+ dir: options.fontsDir,
327
+ overwrite: options.overwrite,
328
+ fetch: options.fetch,
329
+ catalog: options.fontCatalog,
330
+ onDownload: options.onDownload
331
+ });
332
+ report.fonts.push({
333
+ ...done,
334
+ ...need
335
+ });
336
+ }
337
+ if (report.fonts.length > 0 || readConfig(cwd).config.fonts) report.fontsModule = writeFontsModule(cwd, dir, options.fontCatalog);
338
+ return report;
339
+ }
340
+
341
+ //#endregion
342
+ //#region src/cli/templates.ts
343
+ /**
344
+ * The files `framefields init` writes: the project layout the framefields
345
+ * setup skill describes. `film.ts` only builds; `render.ts` renders frames,
346
+ * a grid, the live preview or the video; `theme.ts` holds the numbers.
347
+ */
348
+ /**
349
+ * Packages with install scripts. framefields needs exactly one (skia-canvas
350
+ * downloads its native binary); the others work without theirs.
351
+ */
352
+ const ALLOWED_SCRIPTS = ["skia-canvas"];
353
+ const DENIED_SCRIPTS = [
354
+ "sharp",
355
+ "webgpu",
356
+ "onnxruntime-node",
357
+ "node-av",
358
+ "esbuild",
359
+ "protobufjs"
360
+ ];
361
+ /** Dev tools the scripts use, at versions the scaffold is tested with. */
362
+ const DEV_DEPENDENCIES = {
363
+ "@types/node": "^24.1.0",
364
+ tsx: "^4.20.3",
365
+ typescript: "^5.8.3"
366
+ };
367
+ const SCRIPTS = {
368
+ typecheck: "tsc --noEmit -p .",
369
+ frames: "tsx src/render.ts frames",
370
+ grid: "tsx src/render.ts grid",
371
+ preview: "tsx src/render.ts preview",
372
+ render: "tsx src/render.ts"
373
+ };
374
+ /**
375
+ * The `package.json` fields a framefields project needs. npm reads
376
+ * `allowScripts`, pnpm its `pnpm` block, bun `trustedDependencies`.
377
+ */
378
+ function packageFields(o) {
379
+ return {
380
+ type: "module",
381
+ ...o.packageManager && { packageManager: o.packageManager },
382
+ scripts: SCRIPTS,
383
+ dependencies: { framefields: `^${o.framefieldsVersion}` },
384
+ devDependencies: DEV_DEPENDENCIES,
385
+ allowScripts: Object.fromEntries([...ALLOWED_SCRIPTS.map((p) => [p, true]), ...DENIED_SCRIPTS.map((p) => [p, false])]),
386
+ pnpm: {
387
+ onlyBuiltDependencies: ALLOWED_SCRIPTS,
388
+ ignoredBuiltDependencies: DENIED_SCRIPTS
389
+ },
390
+ trustedDependencies: ALLOWED_SCRIPTS
391
+ };
392
+ }
393
+ const TSCONFIG = `${JSON.stringify({
394
+ compilerOptions: {
395
+ module: "NodeNext",
396
+ moduleResolution: "NodeNext",
397
+ target: "ES2022",
398
+ strict: true,
399
+ skipLibCheck: true,
400
+ noEmit: true,
401
+ types: ["node"]
402
+ },
403
+ include: ["src/**/*"]
404
+ }, null, " ")}\n`;
405
+ /** Lines `init` makes sure `.gitignore` has. */
406
+ const GITIGNORE = [
407
+ "node_modules/",
408
+ "output/",
409
+ "scratch/",
410
+ ".DS_Store",
411
+ ".env"
412
+ ];
413
+ /** Where the AI gateway's keys come from. */
414
+ const AI_DASHBOARD = "https://framefields.dev/dashboard";
415
+ /** Optional settings; `render.ts` loads `.env` when there is one. Commit this, not `.env`. */
416
+ const ENV_EXAMPLE = `# Copy to .env (git-ignored). Everything here is optional.
417
+
418
+ # framefields AI gateway: generate images, video, music, voice and 3D from code.
419
+ # Create a key at ${AI_DASHBOARD}
420
+ FRAMEFIELDS_API_KEY=
421
+ `;
422
+ /** Media, LUTs and fonts go to Git LFS; renders stay out of Git. */
423
+ const GITATTRIBUTES = [
424
+ "assets/**/*.mp4 filter=lfs diff=lfs merge=lfs -text",
425
+ "assets/**/*.mov filter=lfs diff=lfs merge=lfs -text",
426
+ "assets/**/*.webm filter=lfs diff=lfs merge=lfs -text",
427
+ "assets/**/*.mp3 filter=lfs diff=lfs merge=lfs -text",
428
+ "assets/**/*.wav filter=lfs diff=lfs merge=lfs -text",
429
+ "assets/**/*.cube filter=lfs diff=lfs merge=lfs -text",
430
+ "assets/**/*.lottie filter=lfs diff=lfs merge=lfs -text",
431
+ "assets/fonts/**/*.ttf filter=lfs diff=lfs merge=lfs -text",
432
+ "assets/fonts/**/*.otf filter=lfs diff=lfs merge=lfs -text"
433
+ ];
434
+ const THEME_TS = `import { registerFonts as registerInstalledFonts } from "./components/fonts.js";
435
+
436
+ export const W = 1920;
437
+ export const H = 1080;
438
+ export const FPS = 30;
439
+ export const INK = "#0E0D0C";
440
+ export const PAPER = "#EEE9E0";
441
+ /** Installed by \`framefields init\` (assets/fonts), registered below. */
442
+ export const SANS = "Inter";
443
+ export const MONO = "JetBrains Mono";
444
+
445
+ /**
446
+ * Register the film's fonts. Fonts framefields installed
447
+ * (\`npx framefields fonts add <role>\`) come from the generated
448
+ * components/fonts.ts; register any others here with \`FontManager.register\`.
449
+ */
450
+ export async function registerFonts(): Promise<void> {
451
+ await registerInstalledFonts();
452
+ }
453
+ `;
454
+ const FILM_TS = `import { Composition, Layer, LayerAnimation } from "framefields";
455
+ import { FPS, H, INK, PAPER, registerFonts, SANS, W } from "./theme.js";
456
+
457
+ const DURATION = 3 * FPS; // frames
458
+
459
+ /** Builds the film. Only builds: no rendering or writes, so tests, the preview and renders share it. */
460
+ export async function buildFilm(): Promise<Composition> {
461
+ // Fonts must be registered before any text layer is built.
462
+ await registerFonts();
463
+
464
+ const film = new Composition({
465
+ width: W,
466
+ height: H,
467
+ fps: FPS,
468
+ durationMs: (DURATION / FPS) * 1000,
469
+ backgroundColor: INK,
470
+ });
471
+
472
+ film.add(
473
+ Layer.text("Hello, framefields", {
474
+ id: "title",
475
+ position: "absolute",
476
+ x: 0,
477
+ y: H / 2 - 60,
478
+ width: W,
479
+ height: 120,
480
+ fontFamily: SANS,
481
+ fontSize: 96,
482
+ fontWeight: 700,
483
+ fill: PAPER,
484
+ align: "center",
485
+ }).animate(
486
+ LayerAnimation.create()
487
+ .fadeIn(0, 20, "power2.out")
488
+ .fromTo("y", H / 2 - 20, H / 2 - 60, {
489
+ start: 0,
490
+ end: 30,
491
+ ease: "expo.out",
492
+ }),
493
+ ),
494
+ );
495
+
496
+ return film;
497
+ }
498
+ `;
499
+ const RENDER_TS = `import { existsSync } from "node:fs";
500
+ import fs from "node:fs/promises";
501
+ import path from "node:path";
502
+ import { HeadlessMediaRenderer, startPreview } from "framefields";
503
+ import { buildFilm } from "./film.js";
504
+
505
+ // Optional settings such as FRAMEFIELDS_API_KEY (see .env.example).
506
+ const ENV = path.resolve(import.meta.dirname, "../.env");
507
+ if (existsSync(ENV)) process.loadEnvFile(ENV);
508
+
509
+ const OUT = path.resolve(import.meta.dirname, "../output");
510
+ // \`npm run frames -- 0 30\` needs the \`--\`; pnpm passes it through: drop it.
511
+ const [mode = "video", ...args] = process.argv
512
+ .slice(2)
513
+ .filter((a) => a !== "--");
514
+
515
+ if (mode === "preview") {
516
+ // The page runs film.ts itself and renders it with WebGPU in the browser.
517
+ const session = await startPreview({
518
+ entry: new URL("./film.ts", import.meta.url),
519
+ export: "buildFilm",
520
+ });
521
+ console.log(\`Preview at \${session.url}\`);
522
+ await session.closed; // until its tab closes or the next preview replaces it
523
+ process.exit(0);
524
+ }
525
+
526
+ const film = await buildFilm();
527
+ await fs.mkdir(OUT, { recursive: true });
528
+
529
+ if (mode === "frames") {
530
+ const renderer = new HeadlessMediaRenderer();
531
+ for (const frame of (args.length ? args : ["0"]).map(Number)) {
532
+ const png = await film.renderFrame({ frame, renderer });
533
+ const file = path.join(OUT, \`f\${String(frame).padStart(4, "0")}.png\`);
534
+ await fs.writeFile(file, png);
535
+ console.log(\`Wrote \${path.relative(process.cwd(), file)}\`);
536
+ }
537
+ } else if (mode === "grid") {
538
+ const png = await film.renderFrameGrid({
539
+ frames: args.length ? args.map(Number) : [0, 10, 20, 30, 45, 60, 75, 89],
540
+ columns: 4,
541
+ cellWidth: 480,
542
+ showLabels: true,
543
+ });
544
+ await fs.writeFile(path.join(OUT, "grid.png"), png);
545
+ console.log("Wrote output/grid.png");
546
+ } else {
547
+ const result = await film.renderVideo({
548
+ outputPath: path.join(OUT, "output.mp4"),
549
+ quality: "high",
550
+ });
551
+ await result.cleanup?.();
552
+ console.log(\`Exported \${result.filePath}\`);
553
+ }
554
+
555
+ // Native GPU and encoder threads keep Node alive; always exit explicitly.
556
+ process.exit(0);
557
+ `;
558
+ function readme(name, run) {
559
+ return `# ${name}
560
+
561
+ A film built with [framefields](https://www.npmjs.com/package/framefields).
562
+
563
+ \`\`\`bash
564
+ ${run("typecheck")}
565
+ ${run("frames")} -- 0 30 60 # PNGs in output/
566
+ ${run("grid")} # a contact sheet of the motion
567
+ ${run("preview")} # play it in the browser
568
+ ${run("render")} # output/output.mp4
569
+ \`\`\`
570
+
571
+ - \`src/film.ts\` builds the composition, \`src/theme.ts\` holds sizes, colors and fonts,
572
+ \`src/render.ts\` renders it.
573
+ - \`npx framefields list\` shows ready-made components; \`npx framefields add <name>\` copies
574
+ one into \`src/components/\`.
575
+ - Optional: the AI gateway generates images, video, music, voice and 3D from code. Copy
576
+ \`.env.example\` to \`.env\` and set \`FRAMEFIELDS_API_KEY\` (keys at ${AI_DASHBOARD}).
577
+ - Footage, audio and fonts live in \`assets/\` and are tracked with Git LFS
578
+ (\`git lfs install\` once per machine, \`git lfs pull\` after cloning).
579
+ `;
580
+ }
581
+
582
+ //#endregion
583
+ //#region src/cli/init.ts
584
+ /**
585
+ * `framefields init [dir]`: a new framefields project, or framefields added
586
+ * to an existing one. Writes the layout the setup skill describes
587
+ * (`package.json` with the install-script approvals framefields needs,
588
+ * `tsconfig.json`, `src/theme.ts` / `film.ts` / `render.ts`, `assets/`,
589
+ * LFS `.gitattributes`, `.gitignore`), installs the themes' fonts and keeps
590
+ * them registered in `src/components/fonts.ts`.
591
+ *
592
+ * It never overwrites: files that exist are left alone, and `package.json`,
593
+ * `.gitignore` and `.gitattributes` gain only what they're missing.
594
+ */
595
+ const PACKAGE_MANAGERS = [
596
+ "npm",
597
+ "pnpm",
598
+ "yarn",
599
+ "bun"
600
+ ];
601
+ /** The package manager in `npm_config_user_agent` (`pnpm/10.13.1 npm/? node/v22 …`). */
602
+ function detectPackageManager(userAgent) {
603
+ const m = userAgent?.match(/^(npm|pnpm|yarn|bun)\/(\d[\w.+-]*)/);
604
+ if (!m) return null;
605
+ return {
606
+ name: m[1],
607
+ version: m[2]
608
+ };
609
+ }
610
+ const runScriptFor = (pm) => (script) => pm === "npm" || pm === "bun" ? `${pm} run ${script}` : `${pm} ${script}`;
611
+ /** `My Film!` → `my-film`. */
612
+ function packageName(folder) {
613
+ return folder.toLowerCase().replace(/[^a-z0-9._~-]+/g, "-").replace(/^[-._]+|[-._]+$/g, "") || "framefields-film";
614
+ }
615
+ async function init(options) {
616
+ const root = resolve(options.cwd, options.dir ?? ".");
617
+ mkdirSync(root, { recursive: true });
618
+ const detected = detectPackageManager(options.userAgent);
619
+ const pm = options.packageManager ?? detected?.name ?? "npm";
620
+ const pmVersion = detected?.name === pm ? detected.version : void 0;
621
+ const run = options.run ?? spawnRunner;
622
+ const report = {
623
+ root,
624
+ name: "",
625
+ packageManager: pm,
626
+ written: [],
627
+ merged: [],
628
+ unchanged: [],
629
+ fonts: [],
630
+ fontsModule: {
631
+ path: "",
632
+ status: "unchanged",
633
+ calls: []
634
+ },
635
+ git: "skipped",
636
+ lfs: null,
637
+ installed: false,
638
+ runScript: runScriptFor(pm)
639
+ };
640
+ const fields = packageFields({
641
+ framefieldsVersion: options.framefieldsVersion ?? ownVersion(),
642
+ ...pmVersion && { packageManager: `${pm}@${pmVersion}` }
643
+ });
644
+ const pkgPath = join(root, "package.json");
645
+ if (existsSync(pkgPath)) {
646
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
647
+ if (pkg.type === "commonjs") throw new Error("package.json has \"type\": \"commonjs\". framefields projects are ES modules: set \"type\": \"module\" (and convert any require() files) first.");
648
+ report.name = typeof pkg.name === "string" ? pkg.name : packageName(basename(root));
649
+ record(report, "package.json", writeIfChanged(pkgPath, mergePackage(pkg, fields), pkg));
650
+ } else {
651
+ report.name = options.name ?? packageName(basename(root));
652
+ writeJson(pkgPath, {
653
+ name: report.name,
654
+ version: "0.0.0",
655
+ private: true,
656
+ ...fields
657
+ });
658
+ report.written.push("package.json");
659
+ }
660
+ const files = [
661
+ ["tsconfig.json", TSCONFIG],
662
+ ["src/theme.ts", THEME_TS],
663
+ ["src/film.ts", FILM_TS],
664
+ ["src/render.ts", RENDER_TS],
665
+ ["assets/footage/.gitkeep", ""],
666
+ [".env.example", ENV_EXAMPLE],
667
+ ["README.md", readme(report.name, report.runScript)]
668
+ ];
669
+ for (const [path$1, content] of files) {
670
+ const target = join(root, path$1);
671
+ if (existsSync(target)) report.unchanged.push(path$1);
672
+ else {
673
+ mkdirSync(dirname(target), { recursive: true });
674
+ writeFileSync(target, content);
675
+ report.written.push(path$1);
676
+ }
677
+ }
678
+ for (const [path$1, lines] of [[".gitignore", GITIGNORE], [".gitattributes", GITATTRIBUTES]]) record(report, path$1, appendLines(join(root, path$1), lines));
679
+ const { config, saved } = readConfig(root);
680
+ if (!saved) {
681
+ writeConfig(root, config);
682
+ report.written.push("framefields.json");
683
+ }
684
+ const roles = [...options.fonts === false ? [] : ["sans", "mono"], ...options.emoji ? ["emoji"] : []];
685
+ for (const role of roles) report.fonts.push(await installFont(role, {
686
+ cwd: root,
687
+ fetch: options.fetch,
688
+ catalog: options.fontCatalog,
689
+ onDownload: options.onDownload
690
+ }));
691
+ report.fontsModule = writeFontsModule(root, resolve(root, readConfig(root).config.components), options.fontCatalog);
692
+ if (options.git !== false) {
693
+ if (await run("git", ["rev-parse", "--is-inside-work-tree"], {
694
+ cwd: root,
695
+ quiet: true
696
+ }) === 0) report.git = "existing";
697
+ else if (await run("git", ["init", "-q"], {
698
+ cwd: root,
699
+ quiet: true
700
+ }) === 0) report.git = "initialized";
701
+ else report.git = "unavailable";
702
+ if (report.git !== "unavailable") report.lfs = await run("git", ["lfs", "version"], {
703
+ cwd: root,
704
+ quiet: true
705
+ }) === 0;
706
+ }
707
+ if (options.install) {
708
+ const code = await run(pm, ["install"], { cwd: root });
709
+ if (code !== 0) throw new Error(`\`${pm} install\` failed (exit ${code}). The project files are written; fix the error and run it again.`);
710
+ report.installed = true;
711
+ }
80
712
  return report;
81
713
  }
714
+ /** Add every field of `fields` the package lacks; existing values win. */
715
+ function mergePackage(pkg, fields) {
716
+ const out = { ...pkg };
717
+ const obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
718
+ const union = (a, b) => [...new Set([...Array.isArray(a) ? a : [], ...b])];
719
+ out.type ??= fields.type;
720
+ if (fields.packageManager) out.packageManager ??= fields.packageManager;
721
+ for (const key of [
722
+ "scripts",
723
+ "dependencies",
724
+ "devDependencies",
725
+ "allowScripts"
726
+ ]) {
727
+ const theirs = obj(pkg[key]);
728
+ const present = key === "dependencies" || key === "devDependencies" ? {
729
+ ...obj(pkg.dependencies),
730
+ ...obj(pkg.devDependencies)
731
+ } : theirs;
732
+ const missing = Object.entries(fields[key]).filter(([k]) => !(k in present));
733
+ if (missing.length > 0) out[key] = {
734
+ ...theirs,
735
+ ...Object.fromEntries(missing)
736
+ };
737
+ }
738
+ const pnpm = obj(pkg.pnpm);
739
+ out.pnpm = {
740
+ ...pnpm,
741
+ onlyBuiltDependencies: union(pnpm.onlyBuiltDependencies, fields.pnpm.onlyBuiltDependencies),
742
+ ignoredBuiltDependencies: union(pnpm.ignoredBuiltDependencies, fields.pnpm.ignoredBuiltDependencies)
743
+ };
744
+ out.trustedDependencies = union(pkg.trustedDependencies, fields.trustedDependencies);
745
+ return out;
746
+ }
747
+ function writeJson(path$1, value) {
748
+ writeFileSync(path$1, `${JSON.stringify(value, null, " ")}\n`);
749
+ }
750
+ function writeIfChanged(path$1, next, prev) {
751
+ if (JSON.stringify(next) === JSON.stringify(prev)) return false;
752
+ writeJson(path$1, next);
753
+ return true;
754
+ }
755
+ /** Create `path` with `lines` ("written"), or append the ones it lacks (true if any). */
756
+ function appendLines(path$1, lines) {
757
+ if (!existsSync(path$1)) {
758
+ writeFileSync(path$1, `${lines.join("\n")}\n`);
759
+ return "written";
760
+ }
761
+ const text = readFileSync(path$1, "utf8");
762
+ const have = new Set(text.split(/\r?\n/).map((l) => l.trim()));
763
+ const missing = lines.filter((l) => !have.has(l));
764
+ if (missing.length === 0) return false;
765
+ writeFileSync(path$1, `${text}${text.endsWith("\n") || text === "" ? "" : "\n"}${missing.join("\n")}\n`);
766
+ return true;
767
+ }
768
+ /** File it under written (new), merged (changed) or unchanged. */
769
+ function record(report, path$1, result) {
770
+ if (result === "written") report.written.push(path$1);
771
+ else if (result) report.merged.push(path$1);
772
+ else report.unchanged.push(path$1);
773
+ }
774
+ /** This package's version, from the nearest `package.json` named framefields. */
775
+ function ownVersion() {
776
+ let dir = dirname(fileURLToPath(import.meta.url));
777
+ for (;;) {
778
+ const file = join(dir, "package.json");
779
+ if (existsSync(file)) {
780
+ const pkg = JSON.parse(readFileSync(file, "utf8"));
781
+ if (pkg.name === "framefields" && pkg.version) return pkg.version;
782
+ }
783
+ const up = dirname(dir);
784
+ if (up === dir) throw new Error("Can't find framefields' own package.json");
785
+ dir = up;
786
+ }
787
+ }
788
+ const spawnRunner = (command, args, { cwd, quiet }) => new Promise((done) => {
789
+ const child = spawn(command, args, {
790
+ cwd,
791
+ stdio: quiet ? "ignore" : "inherit",
792
+ shell: process.platform === "win32"
793
+ });
794
+ child.on("error", () => done(127));
795
+ child.on("close", (code) => done(code ?? 1));
796
+ });
797
+ /** Where the project is, as the user would `cd` to it. */
798
+ function shownPath(cwd, root) {
799
+ return relative(cwd, root) || ".";
800
+ }
82
801
 
83
802
  //#endregion
84
803
  //#region src/cli/registry.ts
@@ -125,29 +844,55 @@ function openRegistry(source, cwd = process.cwd()) {
125
844
  /**
126
845
  * The `framefields` command.
127
846
  *
128
- * npx framefields add <name...> copy components into this project
129
- * npx framefields list the components you can add
847
+ * npx framefields init [dir] start a project (or add framefields to one)
848
+ * npx framefields add <name...> copy components into this project
849
+ * npx framefields list the components you can add
850
+ * npx framefields fonts add <role...> install fonts (emoji)
851
+ * npx framefields fonts list the fonts you can install
130
852
  */
131
853
  const HELP = `Usage: framefields <command> [options]
132
854
 
133
855
  Commands:
856
+ init [dir] Start a framefields project in dir (default: here), or add
857
+ framefields to the project there: package.json, tsconfig,
858
+ src/theme.ts, film.ts, render.ts, assets/, .gitignore,
859
+ LFS .gitattributes, and the themes' fonts. Never
860
+ overwrites; existing files only gain what they lack.
134
861
  add <name...> Copy components' source into this project (and the
135
862
  components they build on). The code is yours to edit.
136
863
  list List the components you can add.
864
+ fonts add <role...>
865
+ Install fonts (pinned, hash-checked, with their license)
866
+ and register them in the generated <components>/fonts.ts.
867
+ fonts list List the fonts you can install.
868
+
869
+ Options for init:
870
+ --name <name> Package name (default: the folder's name).
871
+ --pm <manager> npm, pnpm, yarn or bun (default: the one running this).
872
+ --install Install dependencies afterwards.
873
+ --emoji Also install the color emoji font.
874
+ --no-fonts Skip Inter and JetBrains Mono.
875
+ --no-git Don't run \`git init\`.
137
876
 
138
877
  Options for add:
139
878
  --all Add every component.
140
879
  --dir <path> Components directory (default: from framefields.json,
141
880
  else src/components or components). Saved the first time.
142
881
  --overwrite Replace files you have changed.
882
+ --no-fonts Don't install the fonts components ask for (emoji).
883
+
884
+ Options for add and fonts add:
885
+ --fonts-dir <path> Where downloaded fonts go (default assets/fonts).
143
886
 
144
- Options for both:
887
+ Options for add and list:
145
888
  --registry <src> A registry folder or http(s) URL instead of the one
146
889
  shipped with this version of framefields.
147
890
  -h, --help Show this help.
148
891
 
149
- Example:
892
+ Examples:
893
+ npx framefields init my-film --install
150
894
  npx framefields add mouse-pointer focus-zoom
895
+ npx framefields fonts add emoji
151
896
  `;
152
897
  async function main(argv) {
153
898
  const { values, positionals } = parseArgs({
@@ -157,6 +902,13 @@ async function main(argv) {
157
902
  all: { type: "boolean" },
158
903
  dir: { type: "string" },
159
904
  overwrite: { type: "boolean" },
905
+ "no-fonts": { type: "boolean" },
906
+ "fonts-dir": { type: "string" },
907
+ name: { type: "string" },
908
+ pm: { type: "string" },
909
+ install: { type: "boolean" },
910
+ emoji: { type: "boolean" },
911
+ "no-git": { type: "boolean" },
160
912
  registry: { type: "string" },
161
913
  help: {
162
914
  type: "boolean",
@@ -170,6 +922,31 @@ async function main(argv) {
170
922
  return command || values.help ? 0 : 1;
171
923
  }
172
924
  const cwd = process.cwd();
925
+ const log = (line) => process.stdout.write(`${line}\n`);
926
+ if (command === "fonts") return fonts(names, cwd, values, log);
927
+ if (command === "init") {
928
+ if (names.length > 1) {
929
+ process.stderr.write("init takes one folder: `framefields init my-film`.\n");
930
+ return 1;
931
+ }
932
+ if (values.pm && !PACKAGE_MANAGERS.includes(values.pm)) {
933
+ process.stderr.write(`--pm must be one of ${PACKAGE_MANAGERS.join(", ")}.\n`);
934
+ return 1;
935
+ }
936
+ logInit(await init({
937
+ cwd,
938
+ dir: names[0],
939
+ name: values.name,
940
+ packageManager: values.pm,
941
+ userAgent: process.env.npm_config_user_agent,
942
+ fonts: !values["no-fonts"],
943
+ emoji: values.emoji,
944
+ git: !values["no-git"],
945
+ install: values.install,
946
+ onDownload
947
+ }), cwd, log);
948
+ return 0;
949
+ }
173
950
  const registry = openRegistry(values.registry, cwd);
174
951
  if (command === "list") {
175
952
  const index = await registry.index();
@@ -190,13 +967,16 @@ async function main(argv) {
190
967
  cwd,
191
968
  registry,
192
969
  dir: values.dir,
193
- overwrite: values.overwrite
970
+ overwrite: values.overwrite,
971
+ fonts: !values["no-fonts"],
972
+ fontsDir: values["fonts-dir"],
973
+ onDownload
194
974
  });
195
- const log = (line) => process.stdout.write(`${line}\n`);
196
975
  for (const f of report.written) log(` wrote ${f}`);
197
976
  for (const f of report.unchanged) log(` unchanged ${f}`);
198
977
  for (const f of report.skipped) log(` skipped ${f} (yours differs; --overwrite replaces it)`);
199
978
  if (report.configWritten) log(" wrote framefields.json");
979
+ logFonts(report, cwd, log);
200
980
  const extra = report.components.filter((c) => !wanted.includes(c.name));
201
981
  if (extra.length > 0) log(`\nWith what they build on: ${extra.map((c) => c.name).join(", ")}.`);
202
982
  warnIfMissingFramefields(cwd, log);
@@ -204,6 +984,76 @@ async function main(argv) {
204
984
  if (first) log(`\nimport { ${first.title} } from "./${first.name}/index.js"; // from ${relative(cwd, report.dir) || "."}`);
205
985
  return 0;
206
986
  }
987
+ const onDownload = (spec) => process.stdout.write(` fetching ${spec.file} (${(spec.font.bytes / 1e6).toFixed(1)} MB, ${spec.license.id})\n`);
988
+ /** What `add` did about fonts, and how to use them. */
989
+ function logFonts(report, cwd, log) {
990
+ for (const f of report.fonts) log(` ${f.status === "installed" ? "font " : "font ok "} ${f.path} (${f.role}, for ${f.by}: ${f.why})`);
991
+ for (const f of report.fontsSkipped) log(` no font ${f.role} for ${f.by} (--no-fonts): ${f.why} Install later with \`framefields fonts add ${f.role}\`.`);
992
+ if (report.fontsModule) logFontsModule(report.fontsModule, log);
993
+ const installed = report.fonts.find((f) => f.status === "installed");
994
+ const hint = installed && lfsHint(cwd, installed.path);
995
+ if (hint) log(`\n${hint}`);
996
+ if (report.fonts.some((f) => f.status !== "configured") && report.fontsModule) log(`\nRegister them once before rendering: \`await registerFonts()\` from ${report.fontsModule.path.replace(/\.ts$/, ".js")}.`);
997
+ }
998
+ function logFontsModule(m, log) {
999
+ if (m.status === "written") log(` wrote ${m.path}`);
1000
+ else if (m.status === "unchanged") log(` unchanged ${m.path}`);
1001
+ else log(` skipped ${m.path} (not generated by framefields). Register the fonts yourself:\n${m.calls.map((c) => ` ${c}`).join("\n")}`);
1002
+ }
1003
+ /** `framefields fonts add <role...>` and `framefields fonts list`. */
1004
+ async function fonts([sub, ...roles], cwd, values, log) {
1005
+ if (sub === "list") {
1006
+ const { config: config$1 } = readConfig(cwd);
1007
+ for (const [role, spec] of Object.entries(FONTS)) log(`${role.padEnd(6)} ${spec.label}${config$1.fonts?.[role] ? ` (installed: ${config$1.fonts[role]})` : ""}`);
1008
+ return 0;
1009
+ }
1010
+ if (sub !== "add" || roles.length === 0) {
1011
+ process.stderr.write(`Usage: framefields fonts add <role...> (roles: ${Object.keys(FONTS).join(", ")})\n framefields fonts list\n`);
1012
+ return 1;
1013
+ }
1014
+ const unknown = roles.filter((r) => !FONTS[r]);
1015
+ if (unknown.length > 0) {
1016
+ process.stderr.write(`Unknown font ${unknown.map((r) => `"${r}"`).join(", ")}. Fonts framefields can install: ${Object.keys(FONTS).join(", ")}.\n`);
1017
+ return 1;
1018
+ }
1019
+ const { config, saved } = readConfig(cwd);
1020
+ const installs = [];
1021
+ for (const role of roles) installs.push(await installFont(role, {
1022
+ cwd,
1023
+ dir: values["fonts-dir"],
1024
+ overwrite: values.overwrite,
1025
+ onDownload
1026
+ }));
1027
+ for (const f of installs) log(` ${f.status === "installed" ? "font " : "font ok "} ${f.path} (${f.role})`);
1028
+ if (!saved) log(" wrote framefields.json");
1029
+ const module = writeFontsModule(cwd, resolve(cwd, values.dir ?? config.components));
1030
+ logFontsModule(module, log);
1031
+ const installed = installs.find((f) => f.status === "installed");
1032
+ const hint = installed && lfsHint(cwd, installed.path);
1033
+ if (hint) log(`\n${hint}`);
1034
+ log(`\nRegister them once before rendering: \`await registerFonts()\` from ${module.path.replace(/\.ts$/, ".js")}.`);
1035
+ return 0;
1036
+ }
1037
+ function logInit(report, cwd, log) {
1038
+ const at = shownPath(cwd, report.root);
1039
+ for (const f of report.written) log(` wrote ${f}`);
1040
+ for (const f of report.merged) log(` merged ${f} (added what it lacked)`);
1041
+ for (const f of report.unchanged) log(` unchanged ${f}`);
1042
+ for (const f of report.fonts) log(` ${f.status === "installed" ? "font " : "font ok "} ${f.path} (${f.role})`);
1043
+ logFontsModule(report.fontsModule, log);
1044
+ if (report.git === "initialized") log(" git initialized a repository");
1045
+ if (report.git === "unavailable") log(" git not found: skipped `git init`");
1046
+ if (report.lfs === false) log("\nGit LFS isn't installed. Footage and fonts are tracked with it: install it (https://git-lfs.com), then run `git lfs install` once.");
1047
+ else if (report.lfs) log("\nRun `git lfs install` once on this machine if you haven't yet.");
1048
+ const steps = [
1049
+ ...at === "." ? [] : [`cd ${/\s/.test(at) ? JSON.stringify(at) : at}`],
1050
+ ...report.installed ? [] : [`${report.packageManager} install`],
1051
+ `${report.runScript("frames")} -- 0 30 60`
1052
+ ];
1053
+ log(`\n${report.name} is ready. Next:\n${steps.map((s) => ` ${s}`).join("\n")}`);
1054
+ log("\nThen edit src/film.ts, and run `npx framefields list` for ready-made components.");
1055
+ log(`\nOptional: to generate images, video, music, voice and 3D with the framefields AI gateway, get a key at ${AI_DASHBOARD} and put it in .env as FRAMEFIELDS_API_KEY (see .env.example).`);
1056
+ }
207
1057
  /** Installed components import `framefields`; say so if the project lacks it. */
208
1058
  function warnIfMissingFramefields(cwd, log) {
209
1059
  try {