loopwind 0.22.0 → 0.24.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 (486) hide show
  1. package/README.md +13 -117
  2. package/dist/cli.js +16 -10
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +6 -10
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/agent.d.ts +8 -0
  8. package/dist/commands/agent.d.ts.map +1 -0
  9. package/dist/commands/agent.js +101 -0
  10. package/dist/commands/agent.js.map +1 -0
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +17 -7
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/render.d.ts.map +1 -1
  15. package/dist/commands/render.js +17 -19
  16. package/dist/commands/render.js.map +1 -1
  17. package/dist/default-templates/AGENTS.md +10 -24
  18. package/dist/lib/config.d.ts +2 -3
  19. package/dist/lib/config.d.ts.map +1 -1
  20. package/dist/lib/config.js +4 -9
  21. package/dist/lib/config.js.map +1 -1
  22. package/dist/lib/constants.d.ts +1 -3
  23. package/dist/lib/constants.d.ts.map +1 -1
  24. package/dist/lib/constants.js +2 -4
  25. package/dist/lib/constants.js.map +1 -1
  26. package/dist/lib/installer.d.ts +2 -0
  27. package/dist/lib/installer.d.ts.map +1 -1
  28. package/dist/lib/installer.js +6 -7
  29. package/dist/lib/installer.js.map +1 -1
  30. package/dist/lib/renderer.d.ts.map +1 -1
  31. package/dist/lib/renderer.js +14 -8
  32. package/dist/lib/renderer.js.map +1 -1
  33. package/dist/lib/resvg-init.d.ts +15 -0
  34. package/dist/lib/resvg-init.d.ts.map +1 -0
  35. package/dist/lib/resvg-init.js +55 -0
  36. package/dist/lib/resvg-init.js.map +1 -0
  37. package/dist/lib/tailwind/colors.d.ts +8 -0
  38. package/dist/lib/tailwind/colors.d.ts.map +1 -0
  39. package/dist/lib/tailwind/colors.js +102 -0
  40. package/dist/lib/tailwind/colors.js.map +1 -0
  41. package/dist/lib/tailwind/index.d.ts +10 -0
  42. package/dist/lib/tailwind/index.d.ts.map +1 -0
  43. package/dist/lib/tailwind/index.js +9 -0
  44. package/dist/lib/tailwind/index.js.map +1 -0
  45. package/dist/lib/tailwind/resolvers.d.ts +28 -0
  46. package/dist/lib/tailwind/resolvers.d.ts.map +1 -0
  47. package/dist/lib/tailwind/resolvers.js +94 -0
  48. package/dist/lib/tailwind/resolvers.js.map +1 -0
  49. package/dist/lib/tailwind/types.d.ts +29 -0
  50. package/dist/lib/tailwind/types.d.ts.map +1 -0
  51. package/dist/lib/tailwind/types.js +8 -0
  52. package/dist/lib/tailwind/types.js.map +1 -0
  53. package/dist/lib/tailwind-config-loader.d.ts +8 -45
  54. package/dist/lib/tailwind-config-loader.d.ts.map +1 -1
  55. package/dist/lib/tailwind-config-loader.js +6 -429
  56. package/dist/lib/tailwind-config-loader.js.map +1 -1
  57. package/dist/lib/tailwind.d.ts +1 -1
  58. package/dist/lib/tailwind.d.ts.map +1 -1
  59. package/dist/lib/tailwind.js +1 -1
  60. package/dist/lib/tailwind.js.map +1 -1
  61. package/dist/lib/utils.d.ts +13 -29
  62. package/dist/lib/utils.d.ts.map +1 -1
  63. package/dist/lib/utils.js +45 -155
  64. package/dist/lib/utils.js.map +1 -1
  65. package/dist/lib/video-renderer.d.ts.map +1 -1
  66. package/dist/lib/video-renderer.js +6 -5
  67. package/dist/lib/video-renderer.js.map +1 -1
  68. package/dist/types/config.d.ts +0 -2
  69. package/dist/types/config.d.ts.map +1 -1
  70. package/dist/types/config.js.map +1 -1
  71. package/package.json +2 -12
  72. package/REGISTRY_SETUP.md +0 -363
  73. package/_dsgn/templates/dashed-stroke-test/template.tsx +0 -73
  74. package/_dsgn/templates/path-follow-test/template.tsx +0 -176
  75. package/_dsgn/templates/path-simple-test/template.tsx +0 -98
  76. package/_dsgn/templates/stroke-dash-test/meta.json +0 -12
  77. package/_dsgn/templates/stroke-dash-test/template.tsx +0 -53
  78. package/dist/default-templates/image/template.d.ts +0 -20
  79. package/dist/default-templates/image/template.d.ts.map +0 -1
  80. package/dist/default-templates/image/template.js +0 -18
  81. package/dist/default-templates/image/template.js.map +0 -1
  82. package/dist/default-templates/image/template.tsx +0 -20
  83. package/dist/default-templates/kitchen-sink/template.tsx +0 -64
  84. package/dist/default-templates/page/template.tsx +0 -37
  85. package/dist/default-templates/video/template.d.ts +0 -26
  86. package/dist/default-templates/video/template.d.ts.map +0 -1
  87. package/dist/default-templates/video/template.js +0 -33
  88. package/dist/default-templates/video/template.js.map +0 -1
  89. package/dist/default-templates/video/template.tsx +0 -37
  90. package/dist/default-templates/website/pages/home.tsx +0 -17
  91. package/dist/default-templates/website/parts/footer.tsx +0 -17
  92. package/dist/default-templates/website/parts/header.tsx +0 -17
  93. package/dist/default-templates/website/template.tsx +0 -17
  94. package/dist/default-templates/website-template/pages/home.tsx +0 -13
  95. package/dist/default-templates/website-template/parts/footer.tsx +0 -15
  96. package/dist/default-templates/website-template/parts/header.tsx +0 -15
  97. package/dist/default-templates/website-template/template.tsx +0 -32
  98. package/dist/lib/encode-worker.d.ts +0 -2
  99. package/dist/lib/encode-worker.d.ts.map +0 -1
  100. package/dist/lib/encode-worker.js +0 -29
  101. package/dist/lib/encode-worker.js.map +0 -1
  102. package/dist/lib/mjpeg-muxer.d.ts +0 -46
  103. package/dist/lib/mjpeg-muxer.d.ts.map +0 -1
  104. package/dist/lib/mjpeg-muxer.js +0 -513
  105. package/dist/lib/mjpeg-muxer.js.map +0 -1
  106. package/dist/lib/tailwind-browser.d.ts +0 -27
  107. package/dist/lib/tailwind-browser.d.ts.map +0 -1
  108. package/dist/lib/tailwind-browser.js +0 -853
  109. package/dist/lib/tailwind-browser.js.map +0 -1
  110. package/dist/lib/video-player.d.ts +0 -25
  111. package/dist/lib/video-player.d.ts.map +0 -1
  112. package/dist/lib/video-player.js +0 -392
  113. package/dist/lib/video-player.js.map +0 -1
  114. package/dist/sdk/compiler.d.ts +0 -94
  115. package/dist/sdk/compiler.d.ts.map +0 -1
  116. package/dist/sdk/compiler.js +0 -122
  117. package/dist/sdk/compiler.js.map +0 -1
  118. package/dist/sdk/index.d.ts +0 -62
  119. package/dist/sdk/index.d.ts.map +0 -1
  120. package/dist/sdk/index.js +0 -141
  121. package/dist/sdk/index.js.map +0 -1
  122. package/dist/sdk/preview.d.ts +0 -65
  123. package/dist/sdk/preview.d.ts.map +0 -1
  124. package/dist/sdk/preview.js +0 -262
  125. package/dist/sdk/preview.js.map +0 -1
  126. package/dist/sdk/template.d.ts +0 -162
  127. package/dist/sdk/template.d.ts.map +0 -1
  128. package/dist/sdk/template.js +0 -231
  129. package/dist/sdk/template.js.map +0 -1
  130. package/examples/code-editor-templates.ts +0 -173
  131. package/examples/nextjs-api/README.md +0 -180
  132. package/examples/nextjs-api/package.json +0 -21
  133. package/examples/nextjs-api/pages/api/intro-video.ts +0 -53
  134. package/examples/nextjs-api/pages/api/og-image.ts +0 -50
  135. package/examples/nextjs-template-import.ts +0 -58
  136. package/examples/sdk-video-preview.tsx +0 -120
  137. package/examples/template-compiler-workflow.ts +0 -251
  138. package/examples/visual-builder-templates.ts +0 -336
  139. package/render-examples-600x400.mjs +0 -161
  140. package/render-spring-variants-fixed.mjs +0 -60
  141. package/render-staggered-text.mjs +0 -56
  142. package/test-font-files.mjs +0 -72
  143. package/test-jsx-support.mjs +0 -146
  144. package/test-sdk-config.mjs +0 -454
  145. package/test-sdk-source-config.mjs +0 -427
  146. package/test-sdk-user-templates.mjs +0 -469
  147. package/test-static-debug.tsx +0 -19
  148. package/test-templates/TESTS.md +0 -63
  149. package/test-templates/config-test.mjs +0 -17
  150. package/test-templates/demo-intro-props.json +0 -4
  151. package/test-templates/run-tests.sh +0 -44
  152. package/test-templates/test-sdk.mjs +0 -139
  153. package/test-video-props.json +0 -3
  154. package/website/.astro/content.db +0 -0
  155. package/website/.astro/integrations/_inox-tools_astro-when/types.d.ts +0 -1
  156. package/website/.astro/integrations/astro_db/db.d.ts +0 -15
  157. package/website/.astro/settings.json +0 -5
  158. package/website/.astro/types.d.ts +0 -2
  159. package/website/DEPLOYMENT.md +0 -143
  160. package/website/OG_IMAGES.md +0 -142
  161. package/website/README.md +0 -158
  162. package/website/astro.config.mjs +0 -46
  163. package/website/dist/.gitkeep +0 -5
  164. package/website/dist/_astro/agents.Yx-L_igG.css +0 -1
  165. package/website/dist/_routes.json +0 -30
  166. package/website/dist/_worker.js/_@astrojs-ssr-adapter.mjs +0 -1033
  167. package/website/dist/_worker.js/_astro-internal_middleware.mjs +0 -40
  168. package/website/dist/_worker.js/chunks/abap_BTmsHiP5.mjs +0 -1
  169. package/website/dist/_worker.js/chunks/actionscript-3_DmBelb1E.mjs +0 -1
  170. package/website/dist/_worker.js/chunks/ada_8-E0ahCN.mjs +0 -1
  171. package/website/dist/_worker.js/chunks/andromeeda_XI-CXx50.mjs +0 -1
  172. package/website/dist/_worker.js/chunks/angular-html_DKGh3gGH.mjs +0 -1
  173. package/website/dist/_worker.js/chunks/angular-ts_-qZGsJoA.mjs +0 -1
  174. package/website/dist/_worker.js/chunks/apache_ijTUt0Ee.mjs +0 -1
  175. package/website/dist/_worker.js/chunks/apex_agu1c6Sh.mjs +0 -1
  176. package/website/dist/_worker.js/chunks/apl_Bj2f7Art.mjs +0 -1
  177. package/website/dist/_worker.js/chunks/applescript_B_vXrOh3.mjs +0 -1
  178. package/website/dist/_worker.js/chunks/ara_DCEQ2rnh.mjs +0 -1
  179. package/website/dist/_worker.js/chunks/asciidoc_CGN_EkYS.mjs +0 -1
  180. package/website/dist/_worker.js/chunks/asm_BBWZgnDp.mjs +0 -1
  181. package/website/dist/_worker.js/chunks/astro/assets-service_j52rQLzU.mjs +0 -721
  182. package/website/dist/_worker.js/chunks/astro/server_Y5_QHO8v.mjs +0 -2401
  183. package/website/dist/_worker.js/chunks/astro-designed-error-pages_BNTLO-TA.mjs +0 -542
  184. package/website/dist/_worker.js/chunks/astro_Dr_hht3h.mjs +0 -1
  185. package/website/dist/_worker.js/chunks/aurora-x_9GHG8nSq.mjs +0 -1
  186. package/website/dist/_worker.js/chunks/awk_DHRvhXot.mjs +0 -1
  187. package/website/dist/_worker.js/chunks/ayu-dark_CcvqmEHE.mjs +0 -1
  188. package/website/dist/_worker.js/chunks/ballerina_C7SdeSZb.mjs +0 -1
  189. package/website/dist/_worker.js/chunks/bat_Dv4A3u45.mjs +0 -1
  190. package/website/dist/_worker.js/chunks/beancount_BfPf9Luv.mjs +0 -1
  191. package/website/dist/_worker.js/chunks/berry_B8rfM3lL.mjs +0 -1
  192. package/website/dist/_worker.js/chunks/bibtex_TcjYgtJM.mjs +0 -1
  193. package/website/dist/_worker.js/chunks/bicep_CrlFWCdN.mjs +0 -1
  194. package/website/dist/_worker.js/chunks/blade_lanKVYID.mjs +0 -1
  195. package/website/dist/_worker.js/chunks/bsl_BhppzXMB.mjs +0 -1
  196. package/website/dist/_worker.js/chunks/c_6FBALJTK.mjs +0 -1
  197. package/website/dist/_worker.js/chunks/cadence_2txU9LVE.mjs +0 -1
  198. package/website/dist/_worker.js/chunks/cairo_BkrFAIlP.mjs +0 -1
  199. package/website/dist/_worker.js/chunks/catppuccin-frappe_CkEqIYhU.mjs +0 -1
  200. package/website/dist/_worker.js/chunks/catppuccin-latte_DG4Gx_-v.mjs +0 -1
  201. package/website/dist/_worker.js/chunks/catppuccin-macchiato_Cwi3vCXf.mjs +0 -1
  202. package/website/dist/_worker.js/chunks/catppuccin-mocha_L9_OPlFX.mjs +0 -1
  203. package/website/dist/_worker.js/chunks/clarity_BEAe4Ulu.mjs +0 -1
  204. package/website/dist/_worker.js/chunks/clojure_VnUX6p2g.mjs +0 -1
  205. package/website/dist/_worker.js/chunks/cmake_0-SGkZEj.mjs +0 -1
  206. package/website/dist/_worker.js/chunks/cobol_92M_KGaE.mjs +0 -1
  207. package/website/dist/_worker.js/chunks/codeowners_CzMwskBv.mjs +0 -1
  208. package/website/dist/_worker.js/chunks/codeql_DWJZNHv1.mjs +0 -1
  209. package/website/dist/_worker.js/chunks/coffee_CQjKU2fh.mjs +0 -1
  210. package/website/dist/_worker.js/chunks/common-lisp_BBLWDpS5.mjs +0 -1
  211. package/website/dist/_worker.js/chunks/coq_hedRFV3D.mjs +0 -1
  212. package/website/dist/_worker.js/chunks/cpp_DlS1i6Zs.mjs +0 -1
  213. package/website/dist/_worker.js/chunks/crystal_D6n65fKV.mjs +0 -1
  214. package/website/dist/_worker.js/chunks/csharp_C6FCVFzc.mjs +0 -1
  215. package/website/dist/_worker.js/chunks/css_C5uJEgmJ.mjs +0 -1
  216. package/website/dist/_worker.js/chunks/csv_CtMYuuJl.mjs +0 -1
  217. package/website/dist/_worker.js/chunks/cue_BsPexqx6.mjs +0 -1
  218. package/website/dist/_worker.js/chunks/cypher_apzf6OBi.mjs +0 -1
  219. package/website/dist/_worker.js/chunks/d_DcvIRcgm.mjs +0 -1
  220. package/website/dist/_worker.js/chunks/dark-plus_C01ONtzj.mjs +0 -1
  221. package/website/dist/_worker.js/chunks/dart_WkzM5WrV.mjs +0 -1
  222. package/website/dist/_worker.js/chunks/dax_DjXAO5V4.mjs +0 -1
  223. package/website/dist/_worker.js/chunks/desktop_C92LCxdc.mjs +0 -1
  224. package/website/dist/_worker.js/chunks/diff_CVwM_9XJ.mjs +0 -1
  225. package/website/dist/_worker.js/chunks/docker_DPzgJf6Z.mjs +0 -1
  226. package/website/dist/_worker.js/chunks/dotenv_D_vgANvA.mjs +0 -1
  227. package/website/dist/_worker.js/chunks/dracula-soft_CLnUBwFm.mjs +0 -1
  228. package/website/dist/_worker.js/chunks/dracula_lBVpb6Lb.mjs +0 -1
  229. package/website/dist/_worker.js/chunks/dream-maker_DTLbzd_J.mjs +0 -1
  230. package/website/dist/_worker.js/chunks/edge_i54JYm3_.mjs +0 -1
  231. package/website/dist/_worker.js/chunks/elixir_BJCIjTu4.mjs +0 -1
  232. package/website/dist/_worker.js/chunks/elm_BbXD39-_.mjs +0 -1
  233. package/website/dist/_worker.js/chunks/emacs-lisp_pxa5cXaN.mjs +0 -1
  234. package/website/dist/_worker.js/chunks/erb_Ccjijeee.mjs +0 -1
  235. package/website/dist/_worker.js/chunks/erlang_B2VM_hi7.mjs +0 -1
  236. package/website/dist/_worker.js/chunks/everforest-dark_BxvIPBim.mjs +0 -1
  237. package/website/dist/_worker.js/chunks/everforest-light_B7VoyaJM.mjs +0 -1
  238. package/website/dist/_worker.js/chunks/fennel_D-uo7X6c.mjs +0 -1
  239. package/website/dist/_worker.js/chunks/fish_BjePoK3m.mjs +0 -1
  240. package/website/dist/_worker.js/chunks/fluent_C8fgkzLX.mjs +0 -1
  241. package/website/dist/_worker.js/chunks/fortran-fixed-form_D1pu5zrc.mjs +0 -1
  242. package/website/dist/_worker.js/chunks/fortran-free-form_CSGOhJD6.mjs +0 -1
  243. package/website/dist/_worker.js/chunks/fsharp_B0xy-A4Y.mjs +0 -1
  244. package/website/dist/_worker.js/chunks/gdresource_CWppjlHq.mjs +0 -1
  245. package/website/dist/_worker.js/chunks/gdscript_eQCHchcS.mjs +0 -1
  246. package/website/dist/_worker.js/chunks/gdshader_C4kxepX7.mjs +0 -1
  247. package/website/dist/_worker.js/chunks/genie_ACtQLcDW.mjs +0 -1
  248. package/website/dist/_worker.js/chunks/gherkin_BFp2uKUd.mjs +0 -1
  249. package/website/dist/_worker.js/chunks/git-commit_CLg9ZwMV.mjs +0 -1
  250. package/website/dist/_worker.js/chunks/git-rebase_DG8A80Nt.mjs +0 -1
  251. package/website/dist/_worker.js/chunks/github-dark-default_BI0EP2Kv.mjs +0 -1
  252. package/website/dist/_worker.js/chunks/github-dark-dimmed_a_NIC0Xb.mjs +0 -1
  253. package/website/dist/_worker.js/chunks/github-dark-high-contrast_jZGqT7hk.mjs +0 -1
  254. package/website/dist/_worker.js/chunks/github-dark_CHCDNd2O.mjs +0 -1
  255. package/website/dist/_worker.js/chunks/github-light-default_DRbOW5RG.mjs +0 -1
  256. package/website/dist/_worker.js/chunks/github-light-high-contrast_tn_kWutM.mjs +0 -1
  257. package/website/dist/_worker.js/chunks/github-light_D9brYzot.mjs +0 -1
  258. package/website/dist/_worker.js/chunks/gleam_Dmhu1oxW.mjs +0 -1
  259. package/website/dist/_worker.js/chunks/glimmer-js_BfZbXy8A.mjs +0 -1
  260. package/website/dist/_worker.js/chunks/glimmer-ts_B9QVICrD.mjs +0 -1
  261. package/website/dist/_worker.js/chunks/glsl_DD2PPwOs.mjs +0 -1
  262. package/website/dist/_worker.js/chunks/gnuplot_D2OYChUX.mjs +0 -1
  263. package/website/dist/_worker.js/chunks/go_DYGFTe3h.mjs +0 -1
  264. package/website/dist/_worker.js/chunks/graphql_B7XsT3nH.mjs +0 -1
  265. package/website/dist/_worker.js/chunks/groovy_BO12Uwkl.mjs +0 -1
  266. package/website/dist/_worker.js/chunks/hack_CB2_ztCP.mjs +0 -1
  267. package/website/dist/_worker.js/chunks/haml_CyfDcDD3.mjs +0 -1
  268. package/website/dist/_worker.js/chunks/handlebars_CfpxpWm2.mjs +0 -1
  269. package/website/dist/_worker.js/chunks/haskell_jUeC5uN5.mjs +0 -1
  270. package/website/dist/_worker.js/chunks/haxe_B6GxP1WB.mjs +0 -1
  271. package/website/dist/_worker.js/chunks/hcl_DwoHV2oh.mjs +0 -1
  272. package/website/dist/_worker.js/chunks/hjson_DV7cJRk4.mjs +0 -1
  273. package/website/dist/_worker.js/chunks/hlsl_BlFCscPI.mjs +0 -1
  274. package/website/dist/_worker.js/chunks/houston_COBFG1Mx.mjs +0 -1
  275. package/website/dist/_worker.js/chunks/html-derivative_C9pJ337h.mjs +0 -1
  276. package/website/dist/_worker.js/chunks/html_D1OkrZS5.mjs +0 -1
  277. package/website/dist/_worker.js/chunks/http_DIGXRqvJ.mjs +0 -1
  278. package/website/dist/_worker.js/chunks/hxml_DEwh9i-c.mjs +0 -1
  279. package/website/dist/_worker.js/chunks/hy_DDoIgW1K.mjs +0 -1
  280. package/website/dist/_worker.js/chunks/imba_B00zbHo4.mjs +0 -1
  281. package/website/dist/_worker.js/chunks/index_C1UTDwYg.mjs +0 -1861
  282. package/website/dist/_worker.js/chunks/ini_D7XQA_p8.mjs +0 -1
  283. package/website/dist/_worker.js/chunks/java_B9wdFd8K.mjs +0 -1
  284. package/website/dist/_worker.js/chunks/javascript_CLsPGOON.mjs +0 -1
  285. package/website/dist/_worker.js/chunks/jinja_jarBCAN1.mjs +0 -1
  286. package/website/dist/_worker.js/chunks/jison_oGg3J708.mjs +0 -1
  287. package/website/dist/_worker.js/chunks/json5_DlZ1Kyaa.mjs +0 -1
  288. package/website/dist/_worker.js/chunks/json_DaYk_FMp.mjs +0 -1
  289. package/website/dist/_worker.js/chunks/jsonc_DlwgfSDs.mjs +0 -1
  290. package/website/dist/_worker.js/chunks/jsonl_BbCCVaZF.mjs +0 -1
  291. package/website/dist/_worker.js/chunks/jsonnet_Dt-G75xe.mjs +0 -1
  292. package/website/dist/_worker.js/chunks/jssm_BtKFTj2A.mjs +0 -1
  293. package/website/dist/_worker.js/chunks/jsx_DDx_xAZ8.mjs +0 -1
  294. package/website/dist/_worker.js/chunks/julia_CK0lv68l.mjs +0 -1
  295. package/website/dist/_worker.js/chunks/kanagawa-dragon_BldAK3Oo.mjs +0 -1
  296. package/website/dist/_worker.js/chunks/kanagawa-lotus_DVM8FX9_.mjs +0 -1
  297. package/website/dist/_worker.js/chunks/kanagawa-wave_Dpih0AKP.mjs +0 -1
  298. package/website/dist/_worker.js/chunks/kotlin_kWneB9V_.mjs +0 -1
  299. package/website/dist/_worker.js/chunks/kusto_BKVATd95.mjs +0 -1
  300. package/website/dist/_worker.js/chunks/laserwave_BqatxsVl.mjs +0 -1
  301. package/website/dist/_worker.js/chunks/latex_LVDcGBbc.mjs +0 -1
  302. package/website/dist/_worker.js/chunks/lean_W7qo-5M2.mjs +0 -1
  303. package/website/dist/_worker.js/chunks/less_DFNwJnBH.mjs +0 -1
  304. package/website/dist/_worker.js/chunks/light-plus_Dp0AoWsO.mjs +0 -1
  305. package/website/dist/_worker.js/chunks/liquid_D24qs0pc.mjs +0 -1
  306. package/website/dist/_worker.js/chunks/log_IPWMXriF.mjs +0 -1
  307. package/website/dist/_worker.js/chunks/logo_C6KaatrQ.mjs +0 -1
  308. package/website/dist/_worker.js/chunks/lua_CwnEf-T7.mjs +0 -1
  309. package/website/dist/_worker.js/chunks/luau_Br3-CXjS.mjs +0 -1
  310. package/website/dist/_worker.js/chunks/make_UBNG-kOo.mjs +0 -1
  311. package/website/dist/_worker.js/chunks/markdown_C7mhJFCm.mjs +0 -1
  312. package/website/dist/_worker.js/chunks/marko_4tchUvI7.mjs +0 -1
  313. package/website/dist/_worker.js/chunks/material-theme-darker_SKtaNEPn.mjs +0 -1
  314. package/website/dist/_worker.js/chunks/material-theme-lighter_zOX_DZCH.mjs +0 -1
  315. package/website/dist/_worker.js/chunks/material-theme-ocean_BN9WbhdC.mjs +0 -1
  316. package/website/dist/_worker.js/chunks/material-theme-palenight_DT_covjH.mjs +0 -1
  317. package/website/dist/_worker.js/chunks/material-theme_6RpeM3kc.mjs +0 -1
  318. package/website/dist/_worker.js/chunks/matlab_DCOXsPKR.mjs +0 -1
  319. package/website/dist/_worker.js/chunks/mdc_B9gb2UFP.mjs +0 -1
  320. package/website/dist/_worker.js/chunks/mdx_DGU7Nu9u.mjs +0 -1
  321. package/website/dist/_worker.js/chunks/mermaid_B69URzsZ.mjs +0 -1
  322. package/website/dist/_worker.js/chunks/min-dark_BgxifOMI.mjs +0 -1
  323. package/website/dist/_worker.js/chunks/min-light_BrPjXxUp.mjs +0 -1
  324. package/website/dist/_worker.js/chunks/mipsasm_9U-4_t7k.mjs +0 -1
  325. package/website/dist/_worker.js/chunks/mojo_B0wt7ug3.mjs +0 -1
  326. package/website/dist/_worker.js/chunks/monokai_B6Pxpoyi.mjs +0 -1
  327. package/website/dist/_worker.js/chunks/move_1eid4CyR.mjs +0 -1
  328. package/website/dist/_worker.js/chunks/narrat_Ds6-p5JZ.mjs +0 -1
  329. package/website/dist/_worker.js/chunks/nextflow_v2N1Qlqa.mjs +0 -1
  330. package/website/dist/_worker.js/chunks/nginx_Bp9Ab2NH.mjs +0 -1
  331. package/website/dist/_worker.js/chunks/night-owl_CdwOw_sc.mjs +0 -1
  332. package/website/dist/_worker.js/chunks/nim_BXGDUe53.mjs +0 -1
  333. package/website/dist/_worker.js/chunks/nix_CUig1nJH.mjs +0 -1
  334. package/website/dist/_worker.js/chunks/noop-middleware_DlWGj5t5.mjs +0 -10
  335. package/website/dist/_worker.js/chunks/nord_SPoG1iae.mjs +0 -1
  336. package/website/dist/_worker.js/chunks/nushell_DJw1Lca8.mjs +0 -1
  337. package/website/dist/_worker.js/chunks/objective-c_Bktzl_CO.mjs +0 -1
  338. package/website/dist/_worker.js/chunks/objective-cpp_CP4DWdDp.mjs +0 -1
  339. package/website/dist/_worker.js/chunks/ocaml_CeEAs7bZ.mjs +0 -1
  340. package/website/dist/_worker.js/chunks/one-dark-pro_-hIwCNMi.mjs +0 -1
  341. package/website/dist/_worker.js/chunks/one-light_DSmYvJ05.mjs +0 -1
  342. package/website/dist/_worker.js/chunks/pascal_C-S_Ms_o.mjs +0 -1
  343. package/website/dist/_worker.js/chunks/perl_CKamvo15.mjs +0 -1
  344. package/website/dist/_worker.js/chunks/php_BlmcX_F3.mjs +0 -1
  345. package/website/dist/_worker.js/chunks/plastic_Ryt8tVoA.mjs +0 -1
  346. package/website/dist/_worker.js/chunks/plsql_Cb3v7cBj.mjs +0 -1
  347. package/website/dist/_worker.js/chunks/po_DZbdNRlo.mjs +0 -1
  348. package/website/dist/_worker.js/chunks/poimandres_bYmE3_5d.mjs +0 -1
  349. package/website/dist/_worker.js/chunks/polar_pJkMGwoW.mjs +0 -1
  350. package/website/dist/_worker.js/chunks/postcss_BAXSOKgk.mjs +0 -1
  351. package/website/dist/_worker.js/chunks/powerquery_oITMGN4x.mjs +0 -1
  352. package/website/dist/_worker.js/chunks/powershell_6306-xIF.mjs +0 -1
  353. package/website/dist/_worker.js/chunks/prisma_DSDxnZGz.mjs +0 -1
  354. package/website/dist/_worker.js/chunks/prolog_CxG7tjZR.mjs +0 -1
  355. package/website/dist/_worker.js/chunks/proto_CS9ByXm1.mjs +0 -1
  356. package/website/dist/_worker.js/chunks/pug_BMtLJo6U.mjs +0 -1
  357. package/website/dist/_worker.js/chunks/puppet_BfeeSzee.mjs +0 -1
  358. package/website/dist/_worker.js/chunks/purescript_BFfueNaH.mjs +0 -1
  359. package/website/dist/_worker.js/chunks/python_Cc4Faapv.mjs +0 -1
  360. package/website/dist/_worker.js/chunks/qml_C1CTJTK8.mjs +0 -1
  361. package/website/dist/_worker.js/chunks/qmldir_nG1KaqKR.mjs +0 -1
  362. package/website/dist/_worker.js/chunks/qss_Cncxk263.mjs +0 -1
  363. package/website/dist/_worker.js/chunks/r_ChR54Ihi.mjs +0 -1
  364. package/website/dist/_worker.js/chunks/racket_BDrhptDs.mjs +0 -1
  365. package/website/dist/_worker.js/chunks/raku_07OUHa0P.mjs +0 -1
  366. package/website/dist/_worker.js/chunks/razor_DIP3INLa.mjs +0 -1
  367. package/website/dist/_worker.js/chunks/red_DOPXfj-6.mjs +0 -1
  368. package/website/dist/_worker.js/chunks/reg_B64SwEDj.mjs +0 -1
  369. package/website/dist/_worker.js/chunks/regexp_ButFGoB5.mjs +0 -1
  370. package/website/dist/_worker.js/chunks/rel_BWJAWqZD.mjs +0 -1
  371. package/website/dist/_worker.js/chunks/riscv_79gXlbsF.mjs +0 -1
  372. package/website/dist/_worker.js/chunks/rose-pine-dawn_DHIjVGd3.mjs +0 -1
  373. package/website/dist/_worker.js/chunks/rose-pine-moon_t86aEbs0.mjs +0 -1
  374. package/website/dist/_worker.js/chunks/rose-pine_BHgrcDCs.mjs +0 -1
  375. package/website/dist/_worker.js/chunks/rst_D3F4Fcpj.mjs +0 -1
  376. package/website/dist/_worker.js/chunks/ruby_Cs7vM9iv.mjs +0 -1
  377. package/website/dist/_worker.js/chunks/rust_DpyRVatH.mjs +0 -1
  378. package/website/dist/_worker.js/chunks/sas_DW45xZXN.mjs +0 -1
  379. package/website/dist/_worker.js/chunks/sass_C6SiMwN_.mjs +0 -1
  380. package/website/dist/_worker.js/chunks/scala_DlZOjNZk.mjs +0 -1
  381. package/website/dist/_worker.js/chunks/scheme_D2ezSJXu.mjs +0 -1
  382. package/website/dist/_worker.js/chunks/scss_DG5Spjqu.mjs +0 -1
  383. package/website/dist/_worker.js/chunks/sdbl_ZCYaj4VN.mjs +0 -1
  384. package/website/dist/_worker.js/chunks/shaderlab_CAcRkg1_.mjs +0 -1
  385. package/website/dist/_worker.js/chunks/shellscript_BWwhkDVh.mjs +0 -1
  386. package/website/dist/_worker.js/chunks/shellsession_BfEA3juK.mjs +0 -1
  387. package/website/dist/_worker.js/chunks/slack-dark_CL3lSpCc.mjs +0 -1
  388. package/website/dist/_worker.js/chunks/slack-ochin_DdZKOQVh.mjs +0 -1
  389. package/website/dist/_worker.js/chunks/smalltalk_DgilzSui.mjs +0 -1
  390. package/website/dist/_worker.js/chunks/snazzy-light_eJU08Pz_.mjs +0 -1
  391. package/website/dist/_worker.js/chunks/solarized-dark_Dg_YQywx.mjs +0 -1
  392. package/website/dist/_worker.js/chunks/solarized-light_BnIsrA6p.mjs +0 -1
  393. package/website/dist/_worker.js/chunks/solidity_DkseH8pQ.mjs +0 -1
  394. package/website/dist/_worker.js/chunks/soy_DU7bOYoG.mjs +0 -1
  395. package/website/dist/_worker.js/chunks/sparql_BuI1DBDH.mjs +0 -1
  396. package/website/dist/_worker.js/chunks/splunk_B8Ha9Pkg.mjs +0 -1
  397. package/website/dist/_worker.js/chunks/sql_BniHwea5.mjs +0 -1
  398. package/website/dist/_worker.js/chunks/ssh-config_CkE1GuVe.mjs +0 -1
  399. package/website/dist/_worker.js/chunks/stata_Dtqpbd_l.mjs +0 -1
  400. package/website/dist/_worker.js/chunks/stylus_CXTtglzO.mjs +0 -1
  401. package/website/dist/_worker.js/chunks/svelte_BjWYcUCN.mjs +0 -1
  402. package/website/dist/_worker.js/chunks/swift_BzHql_rM.mjs +0 -1
  403. package/website/dist/_worker.js/chunks/synthwave-84_DLRNhxNA.mjs +0 -1
  404. package/website/dist/_worker.js/chunks/system-verilog_ChyInPph.mjs +0 -1
  405. package/website/dist/_worker.js/chunks/systemd_Bi9Qa2qD.mjs +0 -1
  406. package/website/dist/_worker.js/chunks/talonscript_B3sH_Y-V.mjs +0 -1
  407. package/website/dist/_worker.js/chunks/tasl_BJ5yipRs.mjs +0 -1
  408. package/website/dist/_worker.js/chunks/tcl_CoJQjNoP.mjs +0 -1
  409. package/website/dist/_worker.js/chunks/templ_CrU7Ffil.mjs +0 -1
  410. package/website/dist/_worker.js/chunks/terraform_DT9JSFpC.mjs +0 -1
  411. package/website/dist/_worker.js/chunks/tex_5PKu2yA0.mjs +0 -1
  412. package/website/dist/_worker.js/chunks/tokyo-night_Buo8OK7-.mjs +0 -1
  413. package/website/dist/_worker.js/chunks/toml_CPuXX3oc.mjs +0 -1
  414. package/website/dist/_worker.js/chunks/ts-tags_D0M_1VSH.mjs +0 -1
  415. package/website/dist/_worker.js/chunks/tsv_CuivVNot.mjs +0 -1
  416. package/website/dist/_worker.js/chunks/tsx_MkuGr8MY.mjs +0 -1
  417. package/website/dist/_worker.js/chunks/turtle_BqgEPK7f.mjs +0 -1
  418. package/website/dist/_worker.js/chunks/twig_r1G9rpYJ.mjs +0 -1
  419. package/website/dist/_worker.js/chunks/typescript_Au5buqzM.mjs +0 -1
  420. package/website/dist/_worker.js/chunks/typespec_47rhBK_z.mjs +0 -1
  421. package/website/dist/_worker.js/chunks/typst_BAtuQLh-.mjs +0 -1
  422. package/website/dist/_worker.js/chunks/v_BIvWImHg.mjs +0 -1
  423. package/website/dist/_worker.js/chunks/vala_DYEacj30.mjs +0 -1
  424. package/website/dist/_worker.js/chunks/vb_CikQuqGJ.mjs +0 -1
  425. package/website/dist/_worker.js/chunks/verilog_BQRENwI-.mjs +0 -1
  426. package/website/dist/_worker.js/chunks/vesper_DA0kvTmj.mjs +0 -1
  427. package/website/dist/_worker.js/chunks/vhdl_DHscJIyg.mjs +0 -1
  428. package/website/dist/_worker.js/chunks/viml_F2pvMwvG.mjs +0 -1
  429. package/website/dist/_worker.js/chunks/vitesse-black_D9tjNzd0.mjs +0 -1
  430. package/website/dist/_worker.js/chunks/vitesse-dark_Bnm5d0hd.mjs +0 -1
  431. package/website/dist/_worker.js/chunks/vitesse-light_CHwbyjNR.mjs +0 -1
  432. package/website/dist/_worker.js/chunks/vue-html_DyYtbbMK.mjs +0 -1
  433. package/website/dist/_worker.js/chunks/vue_DofN6juy.mjs +0 -1
  434. package/website/dist/_worker.js/chunks/vyper_CiR0m-OV.mjs +0 -1
  435. package/website/dist/_worker.js/chunks/wasm_CwIGgRGf.mjs +0 -1
  436. package/website/dist/_worker.js/chunks/wasm_jKWhg0J0.mjs +0 -1
  437. package/website/dist/_worker.js/chunks/wenyan_DKvVZKXW.mjs +0 -1
  438. package/website/dist/_worker.js/chunks/wgsl_BOWZY7yw.mjs +0 -1
  439. package/website/dist/_worker.js/chunks/wikitext_CXDhhHPy.mjs +0 -1
  440. package/website/dist/_worker.js/chunks/wolfram_ChkmGnW0.mjs +0 -1
  441. package/website/dist/_worker.js/chunks/xml_DXH3hHIu.mjs +0 -1
  442. package/website/dist/_worker.js/chunks/xsl_DuP2mFjg.mjs +0 -1
  443. package/website/dist/_worker.js/chunks/yaml_IGiEkTge.mjs +0 -1
  444. package/website/dist/_worker.js/chunks/zenscript_59iXGyNw.mjs +0 -1
  445. package/website/dist/_worker.js/chunks/zig_DKzb0zdT.mjs +0 -1
  446. package/website/dist/_worker.js/index.js +0 -53
  447. package/website/dist/_worker.js/manifest_CT_D-YDe.mjs +0 -98
  448. package/website/dist/_worker.js/pages/_image.astro.mjs +0 -24
  449. package/website/dist/_worker.js/pages/agents.astro.mjs +0 -1
  450. package/website/dist/_worker.js/pages/animation.astro.mjs +0 -1
  451. package/website/dist/_worker.js/pages/api/raw-markdown/_---path_.astro.mjs +0 -44
  452. package/website/dist/_worker.js/pages/config.astro.mjs +0 -1
  453. package/website/dist/_worker.js/pages/fonts.astro.mjs +0 -1
  454. package/website/dist/_worker.js/pages/getting-started.astro.mjs +0 -1
  455. package/website/dist/_worker.js/pages/helpers.astro.mjs +0 -1
  456. package/website/dist/_worker.js/pages/images.astro.mjs +0 -1
  457. package/website/dist/_worker.js/pages/index.astro.mjs +0 -1
  458. package/website/dist/_worker.js/pages/llm.txt.astro.mjs +0 -1
  459. package/website/dist/_worker.js/pages/preview.astro.mjs +0 -1
  460. package/website/dist/_worker.js/pages/sdk.astro.mjs +0 -1
  461. package/website/dist/_worker.js/pages/sitemap.xml.astro.mjs +0 -1
  462. package/website/dist/_worker.js/pages/styling.astro.mjs +0 -1
  463. package/website/dist/_worker.js/pages/templates.astro.mjs +0 -1
  464. package/website/dist/_worker.js/pages/video.astro.mjs +0 -1
  465. package/website/dist/_worker.js/renderers.mjs +0 -57
  466. package/website/dist/agents/index.html +0 -52
  467. package/website/dist/animation/index.html +0 -879
  468. package/website/dist/config/index.html +0 -184
  469. package/website/dist/fonts/index.html +0 -198
  470. package/website/dist/getting-started/index.html +0 -107
  471. package/website/dist/helpers/index.html +0 -164
  472. package/website/dist/images/index.html +0 -335
  473. package/website/dist/index.html +0 -140
  474. package/website/dist/llm.txt +0 -2776
  475. package/website/dist/preview/index.html +0 -111
  476. package/website/dist/robots.txt +0 -40
  477. package/website/dist/sdk/index.html +0 -1076
  478. package/website/dist/sitemap.xml +0 -76
  479. package/website/dist/styling/index.html +0 -366
  480. package/website/dist/templates/index.html +0 -74
  481. package/website/dist/video/index.html +0 -319
  482. package/website/package-lock.json +0 -8089
  483. package/website/package.json +0 -41
  484. package/website/public/.gitkeep +0 -5
  485. package/website/public/robots.txt +0 -40
  486. package/website/templates/og-image.tsx +0 -60
@@ -1,2776 +0,0 @@
1
- # loopwind - Complete Documentation for LLMs
2
-
3
- This is a comprehensive, single-file documentation for loopwind, optimized for LLM consumption.
4
- Generated: 2025-11-20T11:24:11.500Z
5
-
6
- ---
7
-
8
-
9
-
10
- ================================================================================
11
- FILE: index.mdx
12
- ================================================================================
13
-
14
- import CodeVideoDemo from '../components/CodeVideoDemo.astro';
15
-
16
- export const videoCode = `// _loopwind/templates/video-intro.tsx
17
- export const meta = {
18
- name: 'video-intro',
19
- type: 'video',
20
- size: { width: 1200, height: 676 },
21
- video: { fps: 30, duration: 3 },
22
- props: {
23
- title: 'string',
24
- subtitle: 'string'
25
- }
26
- };
27
-
28
- export default function VideoIntro({ tw, title, subtitle }) {
29
- return (
30
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 gap-4')}>
31
- <div style={tw('relative flex flex-row items-center gap-2 ease-out')}>
32
- {/* loop-ping/500: scale up + fade out every 500ms (radar effect) */}
33
- <div style={tw('w-3 h-3 bg-white rounded-full loop-ping/500')} />
34
- <div style={tw('text-white')}>Label</div>
35
- </div>
36
- {/* enter-fade-in/0/1200: fade in starting at 0ms, lasting 1200ms */}
37
- <h1 style={tw('text-7xl font-bold text-white enter-fade-in/0/1200')}>
38
- {title}
39
- </h1>
40
- {/* enter-fade-in/300/1200: fade in starting at 300ms, lasting 1200ms */}
41
- <p style={tw('text-2xl text-white/80 ease-out enter-fade-in/300/1200')}>
42
- {subtitle}
43
- </p>
44
- </div>
45
- );
46
- }
47
- `;
48
-
49
- export const imageCode = `// _loopwind/templates/og-image.tsx
50
- export const meta = {
51
- name: 'og-image',
52
- type: 'image',
53
- size: { width: 1200, height: 630 },
54
- props: {
55
- title: 'string',
56
- description: 'string'
57
- }
58
- };
59
-
60
- export default function OGImage({ tw, title, description }) {
61
- return (
62
- <div style={tw('flex w-full h-full bg-card p-16')}>
63
- <div style={tw('flex flex-col justify-between flex-1')}>
64
- <div style={tw('text-sm text-muted-foreground')}>
65
- loopwind.dev
66
- </div>
67
-
68
- <div>
69
- <h1 style={tw('text-6xl font-bold text-foreground mb-6')}>
70
- {title}
71
- </h1>
72
- <p style={tw('text-2xl text-muted-foreground')}>
73
- {description}
74
- </p>
75
- </div>
76
-
77
- <div style={tw('flex items-center gap-3')}>
78
- <span style={tw('text-5xl')}>↫</span>
79
- <span style={tw('text-3xl font-semibold bg-linear-to-r from-brand-from to-brand-to bg-clip-text text-transparent')}>
80
- loopwind
81
- </span>
82
- </div>
83
- </div>
84
- </div>
85
- );
86
- }
87
- `;
88
-
89
- ## Generate Images and Videos with Code
90
-
91
- Write React components, render them as images or videos. No design tools needed.
92
-
93
- <CodeVideoDemo
94
- code={videoCode}
95
- videoSrc="/demo-intro.mp4"
96
- title="Video Templates"
97
- />
98
-
99
- **Render it in one command:**
100
-
101
- ```bash
102
- loopwind render video-intro '{"title":"Welcome!","subtitle":"Built with loopwind"}'
103
- ```
104
-
105
- **Perfect for:** Social media intros, animated logos, product demos, tutorial overlays.
106
-
107
- ---
108
-
109
- ## Image Templates
110
-
111
- Generate Open Graph images, Twitter cards, and more:
112
-
113
- ```tsx
114
- {imageCode}
115
- ```
116
-
117
- **Render it:**
118
-
119
- ```bash
120
- loopwind render og-image '{"title":"Hello World","description":"My awesome post"}'
121
- ```
122
-
123
- **Output:** `_loopwind/outputs/og-image.png` (1200×630px)
124
-
125
- ---
126
-
127
- ## Why loopwind?
128
-
129
- ### 🎨 Beautiful by Default
130
-
131
- Built on shadcn/ui design system with semantic colors (`text-primary`, `bg-card`) and Tailwind CSS utilities. Your templates look professional out of the box.
132
-
133
- ### ⚡ Blazing Fast Rendering
134
-
135
- - **Serverless-ready**: Pure JavaScript rendering with Satori - works on Vercel, Netlify, Cloudflare Workers
136
- - **WASM-powered video**: MP4 encoding that's 12x faster than traditional approaches
137
- - **No dependencies**: No FFmpeg, no Chrome, no Docker. Just JavaScript.
138
-
139
- ### 📦 Template Marketplace (coming soon)
140
-
141
- Install templates like UI components. Browse, add, and customize in seconds:
142
-
143
- ```bash
144
- loopwind add banner-hero
145
- loopwind add video-intro
146
- loopwind add product-card
147
- ```
148
-
149
- ### 🤖 Built for AI Agents
150
-
151
- Machine-readable metadata and simple CLI make it perfect for automation:
152
-
153
- ```bash
154
- # Generate social media content programmatically
155
- loopwind render og-image '{"title":"'$TITLE'","description":"'$DESC'"}'
156
- ```
157
-
158
- ### 🚀 Framework Agnostic
159
-
160
- Works everywhere: Node.js, Bun, Deno, Python, PHP, Go. If it can run a CLI command, it can use loopwind.
161
-
162
- ---
163
-
164
- ## Key Features
165
-
166
- - ✨ **Template-based workflow** - Install and customize design templates
167
- - 🎬 **Animation classes** - `enter-fade-in/0/500`, `loop-spin/1000` - no manual calculations
168
- - 📱 **Built-in helpers** - QR codes, image embedding, video backgrounds, template composition
169
- - ✅ **Smart validation** - Automatic prop and template validation with helpful errors
170
- - 🎨 **Full Tailwind support** - Including opacity modifiers (`bg-primary/50`)
171
- - 🔧 **Zero config** - Works out of the box, customize when you need to
172
- - 📝 **Type-safe** - Full TypeScript support with prop validation
173
- - 🌐 **Edge-ready** - Runs anywhere JavaScript runs
174
-
175
- ---
176
-
177
- ## Use Cases
178
-
179
- **Marketing Automation**
180
- - Generate personalized email headers
181
- - Create dynamic social media posts
182
- - Automate Open Graph images
183
-
184
- **Content Creation**
185
- - Produce video intros and outros
186
- - Generate thumbnail variations
187
- - Create branded graphics at scale
188
-
189
- **Developer Tools**
190
- - Build dynamic README badges
191
- - Generate documentation images
192
- - Create automated reports
193
-
194
- **E-commerce**
195
- - Product card generation
196
- - Sale announcement videos
197
- - Dynamic pricing graphics
198
-
199
- ---
200
-
201
- ## Get Started
202
-
203
- ```bash
204
- npm install -g loopwind
205
- loopwind init
206
- loopwind add video-intro
207
- loopwind render video-intro '{"title":"Hello World"}'
208
- ```
209
-
210
- [View Full Documentation](/getting-started) · [Browse Templates](/templates) · [See Examples](/images)
211
-
212
- ---
213
-
214
- ## Learn More
215
-
216
- - [Getting Started Guide](/getting-started) - Installation and first steps
217
- - [Templates](/templates) - Create and customize templates
218
- - [Images](/images) - Static image generation
219
- - [Video](/video) - Animated video rendering
220
- - [Styling](/styling) - Tailwind & shadcn/ui integration
221
- - [SDK](/sdk) - Programmatic usage
222
- - [AI Agents](/agents) - Integration guide for LLMs
223
-
224
-
225
-
226
-
227
-
228
- ================================================================================
229
- FILE: templates.mdx
230
- ================================================================================
231
-
232
- # Installing Templates
233
-
234
- ## Overview
235
- loopwind supports installing templates from multiple sources:
236
-
237
- 1. **Official templates**
238
- 2. **Direct URLs**
239
-
240
- ## 1. Default templates
241
-
242
- ```bash
243
- loopwind add image-template
244
- loopwind add video-template
245
- ```
246
-
247
- ## 2. Direct URLs
248
- Install a template from any publicly accessible URL:
249
-
250
- ```bash
251
- loopwind add https://example.com/templates/my-template.json
252
- loopwind add https://cdn.example.com/templates/awesome-banner.json
253
- ```
254
-
255
- **Requirements:**
256
- - URL must return JSON in the registry template format
257
- - Must be publicly accessible (no authentication)
258
-
259
- **Example template JSON:**
260
-
261
- ```json
262
- {
263
- "name": "my-template",
264
- "version": "1.0.0",
265
- "description": "My custom template",
266
- "files": [
267
- {
268
- "path": "template.tsx",
269
- "content": "export const meta = {...};\n\nexport default function..."
270
- }
271
- ]
272
- }
273
- ```
274
-
275
- ## 3. Local Filesystem
276
-
277
- Install templates from your local filesystem:
278
-
279
- ```bash
280
- # Relative path
281
- loopwind add ./my-templates/banner-hero
282
- loopwind add ../shared-templates/product-card
283
-
284
- # Absolute path
285
- loopwind add /Users/you/templates/social-card
286
- ```
287
-
288
- **Use cases:**
289
- - Development and testing
290
- - Private templates
291
- - Shared team templates (monorepo)
292
- - Before publishing to registry
293
-
294
- ## Template Installation Directory
295
-
296
- Templates are installed to `_loopwind/templates/<template-name>/` by default.
297
-
298
- Customize this in `_loopwind/loopwind.json`:
299
-
300
- ```json title="_loopwind/loopwind.json"
301
- {
302
- "templatesDir": "my-custom-templates"
303
- }
304
- ```
305
-
306
- ## Benefits of Local Installation
307
-
308
- - **Version Control**: Templates are part of your project
309
- - **Offline**: Works without internet once installed
310
- - **Team Sharing**: Everyone on the team has the same templates
311
- - **Customization**: Fork and modify templates per project
312
- - **Consistency**: Different projects can use different template versions
313
-
314
- ## Next Steps
315
-
316
- - [Learn about Helpers](/helpers)
317
- - [Styling with Tailwind & shadcn/ui](/styling)
318
- - [Custom Fonts](/fonts)
319
- - [Image Rendering](/images)
320
- - [Video Rendering](/video)
321
-
322
-
323
-
324
-
325
-
326
- ================================================================================
327
- FILE: images.mdx
328
- ================================================================================
329
-
330
- # Image Rendering
331
-
332
- Generate beautiful images from React components using loopwind's powerful image rendering engine powered by Satori.
333
-
334
- ## Quick Start
335
-
336
- ```bash
337
- # Render an image template with inline props
338
- loopwind render banner-hero '{"title":"Hello World","subtitle":"Welcome"}'
339
-
340
- # Custom output name
341
- loopwind render banner-hero '{"title":"Hello"}' --out custom-name.png
342
-
343
- # Different format
344
- loopwind render banner-hero '{"title":"Hello"}' --format jpeg --quality 95
345
-
346
- # Or use a props file
347
- loopwind render banner-hero props.json
348
- ```
349
-
350
- ## Image Template Structure
351
-
352
- ### Basic Template
353
-
354
- ```tsx
355
- // banner-hero.tsx
356
- export const meta = {
357
- name: "banner-hero",
358
- type: "image",
359
- description: "Hero banner with gradient background",
360
- size: { width: 1600, height: 900 },
361
- props: { title: "string", subtitle: "string" }
362
- };
363
-
364
- export default function BannerHero({ title, subtitle, tw }) {
365
- return (
366
- <div style={tw('flex flex-col justify-center items-center w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
367
- <h1 style={tw('text-7xl font-bold text-white mb-4')}>
368
- {title}
369
- </h1>
370
- <p style={tw('text-2xl text-white/80')}>
371
- {subtitle}
372
- </p>
373
- </div>
374
- );
375
- }
376
- ```
377
-
378
- ### Props File
379
-
380
- ```json
381
- {
382
- "title": "Welcome to loopwind",
383
- "subtitle": "Generate beautiful images from React"
384
- }
385
- ```
386
-
387
- ## Output Formats
388
-
389
- ### PNG (Default)
390
-
391
- ```bash
392
- loopwind render my-template '{"title":"Hello"}'
393
- # Output: _loopwind/outputs/my-template.png
394
- ```
395
-
396
- **Best for:**
397
- - Designs with transparency
398
- - High-quality graphics
399
- - Sharp text and logos
400
- - Web graphics
401
-
402
- ### JPEG
403
-
404
- ```bash
405
- loopwind render my-template '{"title":"Hello"}' --format jpeg --quality 92
406
- ```
407
-
408
- **Options:**
409
- - `--quality` - 1-100 (default: 92)
410
-
411
- **Best for:**
412
- - Photographs
413
- - Gradients
414
- - Smaller file sizes
415
- - Social media images
416
-
417
- ### SVG
418
-
419
- ```bash
420
- loopwind render my-template '{"title":"Hello"}' --format svg
421
- ```
422
-
423
- **Best for:**
424
- - Vector graphics
425
- - Scalable designs
426
- - Print-ready artwork
427
- - Logos and icons
428
-
429
- ## Embedding Images
430
-
431
- Use the `image()` helper to embed images in your templates. It supports two modes:
432
-
433
- 1. **Prop-based** - Load from a prop value: `image('logo')`
434
- 2. **Direct file** - Load from template directory: `image('icon.svg')`
435
-
436
- ### Prop-based Images
437
-
438
- Pass the prop name to load an image path from props:
439
-
440
- ```tsx
441
- export default function ProductCard({ tw, image, title, background }) {
442
- return (
443
- <div style={tw('relative w-full h-full')}>
444
- {/* Load image from 'background' prop */}
445
- <img
446
- src={image('background')}
447
- style={tw('absolute inset-0 w-full h-full object-cover')}
448
- />
449
-
450
- {/* Content overlay */}
451
- <div style={tw('relative z-10 p-12')}>
452
- <h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
453
- </div>
454
- </div>
455
- );
456
- }
457
- ```
458
-
459
- **Props:**
460
- ```json
461
- {
462
- "title": "New Product",
463
- "background": "./images/background.jpg"
464
- }
465
- ```
466
-
467
- ### Direct File Images
468
-
469
- Load images directly from your template directory by including the file extension:
470
-
471
- ```tsx
472
- export default function ChangelogItem({ tw, image, text }) {
473
- return (
474
- <div style={tw('flex items-center gap-4')}>
475
- {/* Load check.svg from template directory */}
476
- <img
477
- src={image('check.svg')}
478
- style={tw('w-6 h-6')}
479
- />
480
- <span style={tw('text-lg')}>{text}</span>
481
- </div>
482
- );
483
- }
484
- ```
485
-
486
- This loads `check.svg` from the same directory as your template file. You can also use subdirectories:
487
-
488
- ```tsx
489
- // Load from assets subdirectory
490
- <img src={image('assets/icons/star.svg')} />
491
-
492
- // Load from shared folder
493
- <img src={image('shared/logo.png')} />
494
- ```
495
-
496
- **Template directory structure:**
497
- ```
498
- _loopwind/templates/my-template/
499
- ├── template.tsx
500
- ├── check.svg ← image('check.svg')
501
- └── assets/
502
- └── icons/
503
- └── star.svg ← image('assets/icons/star.svg')
504
- ```
505
-
506
- ### URLs
507
-
508
- The `image()` helper also supports loading images from URLs:
509
-
510
- ```json
511
- {
512
- "background": "https://example.com/image.jpg"
513
- }
514
- ```
515
-
516
- ### Supported Image Formats
517
-
518
- - ✅ **JPEG** (`.jpg`, `.jpeg`)
519
- - ✅ **PNG** (`.png`)
520
- - ✅ **GIF** (`.gif`)
521
- - ✅ **WebP** (`.webp`)
522
- - ✅ **SVG** (`.svg`)
523
-
524
- ### Image Positioning
525
-
526
- ```tsx
527
- export default function ImageGrid({ tw, image, img1, img2, img3 }) {
528
- return (
529
- <div style={tw('grid grid-cols-3 gap-4 w-full h-full p-8 bg-gray-100')}>
530
- {/* Cover - fills entire area */}
531
- <img
532
- src={image(img1)}
533
- style={tw('w-full h-full object-cover rounded-lg')}
534
- />
535
-
536
- {/* Contain - fits within area */}
537
- <img
538
- src={image(img2)}
539
- style={tw('w-full h-full object-contain')}
540
- />
541
-
542
- {/* Fill - stretches to fill */}
543
- <img
544
- src={image(img3)}
545
- style={tw('w-full h-full object-fill')}
546
- />
547
- </div>
548
- );
549
- }
550
- ```
551
-
552
- ## Common Image Templates
553
-
554
- ### Open Graph / Social Cards
555
-
556
- ```tsx
557
- // og-image.tsx
558
- export default function OGImage({ tw, title, description, image, logo }) {
559
- return (
560
- <div style={tw('flex w-full h-full bg-white')}>
561
- {/* Left side - Content */}
562
- <div style={tw('flex-1 flex flex-col justify-between p-12')}>
563
- <img src={image(logo)} style={tw('h-12 w-auto')} />
564
-
565
- <div>
566
- <h1 style={tw('text-5xl font-bold text-gray-900 mb-4')}>
567
- {title}
568
- </h1>
569
- <p style={tw('text-xl text-gray-600')}>
570
- {description}
571
- </p>
572
- </div>
573
-
574
- <p style={tw('text-gray-400')}>yoursite.com</p>
575
- </div>
576
-
577
- {/* Right side - Image */}
578
- <div style={tw('flex-1')}>
579
- <img
580
- src={image(featuredImage)}
581
- style={tw('w-full h-full object-cover')}
582
- />
583
- </div>
584
- </div>
585
- );
586
- }
587
- ```
588
-
589
- **Size:** 1200x630 (standard OG image size)
590
-
591
- ### Product Cards
592
-
593
- ```tsx
594
- export default function ProductCard({ tw, image, product, price, badge }) {
595
- return (
596
- <div style={tw('flex flex-col w-full h-full bg-white rounded-2xl overflow-hidden')}>
597
- {/* Product image */}
598
- <div style={tw('relative h-2/3')}>
599
- <img
600
- src={image(product)}
601
- style={tw('w-full h-full object-cover')}
602
- />
603
- {badge && (
604
- <div style={tw('absolute top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-full text-sm font-bold')}>
605
- {badge}
606
- </div>
607
- )}
608
- </div>
609
-
610
- {/* Product info */}
611
- <div style={tw('flex-1 p-6 flex flex-col justify-between')}>
612
- <h2 style={tw('text-2xl font-bold text-gray-900')}>
613
- {title}
614
- </h2>
615
- <div style={tw('flex justify-between items-center')}>
616
- <span style={tw('text-3xl font-bold text-gray-900')}>
617
- ${price}
618
- </span>
619
- </div>
620
- </div>
621
- </div>
622
- );
623
- }
624
- ```
625
-
626
- ### Quote Cards
627
-
628
- ```tsx
629
- export default function QuoteCard({ tw, image, quote, author, avatar }) {
630
- return (
631
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-indigo-500 to-purple-600 p-16')}>
632
- {avatar && (
633
- <img
634
- src={image(avatar)}
635
- style={tw('w-24 h-24 rounded-full border-4 border-white mb-8')}
636
- />
637
- )}
638
-
639
- <blockquote style={tw('text-3xl text-white text-center font-light mb-8 max-w-3xl')}>
640
- "{quote}"
641
- </blockquote>
642
-
643
- <p style={tw('text-xl text-white/80')}>
644
- — {author}
645
- </p>
646
- </div>
647
- );
648
- }
649
- ```
650
-
651
- ## Performance Tips
652
-
653
- ### 1. Optimize Image Sizes
654
-
655
- Use appropriately sized images before embedding:
656
-
657
- ```bash
658
- # Resize large images before using them
659
- convert large-image.jpg -resize 1600x900 optimized.jpg
660
- ```
661
-
662
- ### 2. Use Web-Optimized Formats
663
-
664
- - **WebP** for modern browsers (smaller file sizes)
665
- - **JPEG** for photos with controlled quality
666
- - **PNG** only when transparency is needed
667
-
668
- ### 3. Batch Rendering
669
-
670
- Render multiple images at once:
671
-
672
- ```bash
673
- # Using a script with inline props
674
- loopwind render my-template '{"title":"Image 1"}'
675
- loopwind render my-template '{"title":"Image 2"}'
676
- loopwind render my-template '{"title":"Image 3"}'
677
-
678
- # Or loop through props files
679
- for props in props/*.json; do
680
- loopwind render my-template "$props"
681
- done
682
- ```
683
-
684
- ### 4. Cache Templates
685
-
686
- Templates are cached after first load for faster subsequent renders.
687
-
688
- ## Common Sizes
689
-
690
- ### Social Media
691
-
692
- - **Twitter/X Card**: 1200x675
693
- - **Facebook Post**: 1200x630
694
- - **Instagram Post**: 1080x1080
695
- - **LinkedIn Post**: 1200x627
696
- - **Pinterest Pin**: 1000x1500
697
-
698
- ### Web Graphics
699
-
700
- - **Hero Banner**: 1920x1080
701
- - **Blog Header**: 1600x900
702
- - **Thumbnail**: 640x360
703
- - **Avatar**: 400x400
704
-
705
- ### Print
706
-
707
- - **Flyer (A4)**: 2480x3508 (300 DPI)
708
- - **Business Card**: 1050x600 (300 DPI)
709
- - **Poster (A3)**: 3508x4961 (300 DPI)
710
-
711
- ## Troubleshooting
712
-
713
- ### Images Not Loading
714
-
715
- Check file paths are relative to the props file:
716
-
717
- ```json
718
- {
719
- "background": "./images/bg.jpg" // ✅ Relative path
720
- "background": "/images/bg.jpg" // ❌ Absolute path won't work
721
- }
722
- ```
723
-
724
- ### Blurry Text
725
-
726
- Ensure you're using appropriate dimensions. Satori renders at exact pixel dimensions.
727
-
728
- ## Next Steps
729
-
730
- - [Learn about Video Rendering](/video)
731
- - [QR Codes and Template Composition](/helpers)
732
- - [Styling with Tailwind & shadcn/ui](/styling)
733
- - [Using Custom Fonts](/fonts)
734
-
735
-
736
-
737
-
738
-
739
- ================================================================================
740
- FILE: video.mdx
741
- ================================================================================
742
-
743
- # Video Rendering
744
-
745
- Create animated videos programmatically using React components. Perfect for automated video generation, social media content, and dynamic animations.
746
-
747
- ## Quick Start
748
-
749
- ```bash
750
- # Render a video template with inline props
751
- loopwind render video-intro '{"title":"Welcome!"}' --out intro.mp4
752
-
753
- # With custom encoding
754
- loopwind render video-intro '{"title":"Welcome!"}' --preset ultrafast
755
-
756
- # Or use a props file
757
- loopwind render video-intro props.json --out intro.mp4
758
- ```
759
-
760
- ## Video Template Structure
761
-
762
- ### Basic Template
763
-
764
- ```tsx
765
- // video-intro.tsx
766
- export const meta = {
767
- name: "video-intro",
768
- type: "video",
769
- description: "Animated intro with bounce-in title",
770
- size: { width: 1920, height: 1080 },
771
- video: { fps: 30, duration: 3 },
772
- props: { title: "string" }
773
- };
774
-
775
- export default function VideoIntro({ tw, title }) {
776
- return (
777
- <div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
778
- {/* Bounce in from below at start */}
779
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/600')}>
780
- {title}
781
- </h1>
782
- </div>
783
- );
784
- }
785
- ```
786
-
787
- ### Props File
788
-
789
- ```json
790
- {
791
- "title": "Welcome!"
792
- }
793
- ```
794
-
795
- ## Animation
796
-
797
- loopwind provides **Tailwind-style animation classes** for creating smooth video animations without writing custom code.
798
-
799
- Use declarative animation classes with millisecond-based timing:
800
-
801
- ```tsx
802
- export default function AnimatedIntro({ tw, title, subtitle }) {
803
- return (
804
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
805
- {/* Bounce in from below: starts at 0ms, lasts 400ms */}
806
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400')}>
807
- {title}
808
- </h1>
809
-
810
- {/* Fade in with upward motion: starts at 300ms, lasts 400ms */}
811
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/300/400')}>
812
- {subtitle}
813
- </p>
814
- </div>
815
- );
816
- }
817
- ```
818
-
819
- ### Common Animation Patterns
820
-
821
- ```tsx
822
- // Fade animations
823
- <div style={tw('enter-fade-in/0/500')}>Fade in</div>
824
- <div style={tw('enter-fade-in-up/0/600')}>Fade in with slide</div>
825
-
826
- // Slide animations
827
- <div style={tw('enter-slide-left/0/500')}>Slide from left</div>
828
- <div style={tw('exit-slide-up/2500/500')}>Slide out up</div>
829
-
830
- // Bounce and scale
831
- <div style={tw('enter-bounce-in/0/500')}>Bouncy entrance</div>
832
- <div style={tw('enter-scale-in/0/400')}>Scale up</div>
833
-
834
- // Loop animations (continuous)
835
- <div style={tw('loop-float/1000')}>Continuous floating</div>
836
- <div style={tw('loop-pulse/800')}>Pulsing effect</div>
837
- ```
838
-
839
- ### Utility-Based Animations
840
-
841
- Animate any transform or opacity property directly:
842
-
843
- ```tsx
844
- // Translate, rotate, scale
845
- <div style={tw('enter-translate-x-5/0/500')}>Slide 20px from left</div>
846
- <div style={tw('enter-rotate-90/0/800')}>Rotate 90 degrees</div>
847
- <div style={tw('enter-scale-100/0/500')}>Scale from 0 to 100%</div>
848
-
849
- // Combine multiple animations
850
- <div style={tw('enter-translate-y-10/0/500 enter-rotate-45/0/500')}>
851
- Fly and spin
852
- </div>
853
- ```
854
-
855
- **See the full [Animation documentation](/animation)** for all animation classes, easing functions, staggered animations, and advanced patterns.
856
-
857
- ### Programmatic Animations
858
-
859
- For complex custom animations, use the `progress` prop directly:
860
-
861
- ```tsx
862
- export default function CustomAnimation({ progress, title, tw }) {
863
- const opacity = progress < 0.3 ? progress / 0.3 : 1;
864
- const scale = 0.8 + (progress * 0.2);
865
-
866
- return (
867
- <h1 style={{
868
- ...tw('text-8xl font-bold text-white'),
869
- opacity,
870
- transform: `scale(${scale})`
871
- }}>
872
- {title}
873
- </h1>
874
- );
875
- }
876
- ```
877
-
878
- Most use cases are better served by animation classes, but `progress` and `frame` props give you complete control when needed.
879
-
880
- ## FPS and Duration
881
-
882
- ### Common Frame Rates
883
-
884
- ```tsx
885
- // Film standard
886
- export const meta = {
887
- // ...
888
- video: { fps: 24, duration: 5 }
889
- };
890
- ```
891
-
892
- ```tsx
893
- // YouTube/web standard
894
- export const meta = {
895
- // ...
896
- video: { fps: 30, duration: 3 }
897
- };
898
- ```
899
-
900
- ```tsx
901
- // Smooth animations
902
- export const meta = {
903
- // ...
904
- video: { fps: 60, duration: 2 }
905
- };
906
- ```
907
-
908
- **Total frames = fps × duration**
909
- - 24fps × 5s = 120 frames
910
- - 30fps × 3s = 90 frames
911
- - 60fps × 2s = 120 frames
912
-
913
- ### Choosing FPS
914
-
915
- - **24fps**: Cinematic look, smaller files
916
- - **30fps**: Standard web video, good balance
917
- - **60fps**: Smooth animations, larger files
918
-
919
- ## Encoding Options
920
-
921
- ### Default (Balanced)
922
-
923
- ```bash
924
- loopwind render video-intro '{"title":"Welcome"}'
925
- ```
926
-
927
- Uses H.264 codec with good quality/size balance.
928
-
929
- ### Fast Encoding
930
-
931
- ```bash
932
- loopwind render video-intro '{"title":"Welcome"}' --preset ultrafast
933
- ```
934
-
935
- Faster encoding, slightly larger files. Good for previews.
936
-
937
- ### High Quality
938
-
939
- ```bash
940
- loopwind render video-intro '{"title":"Welcome"}' --preset slow
941
- ```
942
-
943
- Better compression, slower encoding. Good for final output.
944
-
945
- ## Performance
946
-
947
- ### Rendering Speed
948
-
949
- On an M1 Mac:
950
- - **30fps, 3s video** (90 frames): ~15-20 seconds
951
- - **60fps, 3s video** (180 frames): ~30-40 seconds
952
-
953
- Speed depends on:
954
- - Template complexity
955
- - Image/video embedding
956
- - CPU performance
957
- - Frame count
958
-
959
- ### Optimization Tips
960
-
961
- 1. **Lower FPS for previews**: Test at 15fps, render final at 30fps
962
- 2. **Shorter duration**: Preview with 1s, expand for final
963
- 3. **Simplify templates**: Complex layouts take longer
964
- 4. **Pre-optimize media**: Resize images/videos before embedding
965
-
966
- ### Memory Usage
967
-
968
- Video frames are cached in memory during rendering:
969
- - **1920x1080 @ 30fps for 3s**: ~500MB RAM
970
- - **1920x1080 @ 60fps for 5s**: ~1.7GB RAM
971
-
972
- For long videos, consider rendering in segments.
973
-
974
- ## Common Video Templates
975
-
976
- ### Loading Spinner
977
-
978
- ```tsx
979
- export default function LoadingVideo({ tw, title }) {
980
- return (
981
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900')}>
982
- {/* Spinner fades out at 60% (1800ms for 3s video) */}
983
- <div style={tw('exit-fade-out/1800/400')}>
984
- <div style={tw('w-32 h-32 border-8 border-blue-500 border-t-transparent rounded-full loop-spin/1000')} />
985
- </div>
986
-
987
- {/* Title fades in after spinner */}
988
- <h1 style={tw('text-6xl font-bold text-white mt-12 ease-out enter-fade-in-up/1800/400')}>
989
- {title}
990
- </h1>
991
- </div>
992
- );
993
- }
994
- ```
995
-
996
- ### Progress Bar
997
-
998
- ```tsx
999
- export default function ProgressVideo({ tw, title, subtitle }) {
1000
- return (
1001
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900 p-12')}>
1002
- {/* Title bounces in */}
1003
- <h2 style={tw('text-5xl font-bold text-white mb-12 ease-out enter-bounce-in/0/500')}>
1004
- {title}
1005
- </h2>
1006
-
1007
- {/* Progress bar container fades in */}
1008
- <div style={tw('w-full max-w-2xl h-4 bg-gray-700 rounded-full overflow-hidden ease-out enter-fade-in/300/400')}>
1009
- <div style={tw('h-full bg-gradient-to-r from-blue-500 to-green-500 w-full')} />
1010
- </div>
1011
-
1012
- {/* Subtitle fades in last */}
1013
- <p style={tw('text-2xl text-gray-400 mt-8 ease-out enter-fade-in/600/400')}>
1014
- {subtitle}
1015
- </p>
1016
- </div>
1017
- );
1018
- }
1019
- ```
1020
-
1021
- ### Countdown Timer
1022
-
1023
- ```tsx
1024
- export default function CountdownVideo({ tw, frame, message }) {
1025
- const fps = 30;
1026
- const secondsRemaining = Math.ceil((90 - frame) / fps); // 3s countdown
1027
-
1028
- return (
1029
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
1030
- {/* Number scales in and pulses continuously */}
1031
- <div style={tw('text-[200px] font-black text-white ease-out enter-scale-in/0/400 loop-pulse/1000')}>
1032
- {secondsRemaining}
1033
- </div>
1034
-
1035
- {/* Message fades in */}
1036
- <p style={tw('text-3xl text-gray-400 mt-8 ease-out enter-fade-in/400/500')}>
1037
- {message}
1038
- </p>
1039
- </div>
1040
- );
1041
- }
1042
- ```
1043
-
1044
- ## Output Formats
1045
-
1046
- Videos can be rendered in two formats:
1047
-
1048
- ### MP4 (Default)
1049
-
1050
- ```bash
1051
- # MP4 - H.264 codec, smaller file size, best quality
1052
- loopwind render video-intro '{"title":"Welcome"}' --out intro.mp4
1053
-
1054
- # With custom quality (lower CRF = better quality)
1055
- loopwind render video-intro '{"title":"Welcome"}' --crf 18
1056
- ```
1057
-
1058
- **Benefits:**
1059
- - Smaller file sizes
1060
- - Better color reproduction
1061
- - Universal playback support
1062
- - Best for social media, websites
1063
-
1064
- ### GIF
1065
-
1066
- ```bash
1067
- # GIF - animated, works everywhere
1068
- loopwind render video-intro '{"title":"Welcome"}' --format gif --out intro.gif
1069
-
1070
- # Or just use .gif extension
1071
- loopwind render video-intro '{"title":"Welcome"}' --out intro.gif
1072
- ```
1073
-
1074
- **Benefits:**
1075
- - Works in emails, GitHub READMEs, Slack
1076
- - No video player needed
1077
- - Auto-loops by default
1078
- - Great for short animations
1079
-
1080
- **Limitations:**
1081
- - Limited to 256 colors per frame
1082
- - Larger file sizes than MP4
1083
- - Some color banding with gradients
1084
-
1085
- **When to use GIF:**
1086
- - Short loops (< 5 seconds)
1087
- - Simple animations with solid colors
1088
- - Platforms that don't support video (email, GitHub)
1089
-
1090
- **When to use MP4:**
1091
- - Longer videos
1092
- - Complex gradients or photos
1093
- - Best quality and smallest size
1094
-
1095
- ## Video-Specific Props
1096
-
1097
- Every video template receives these additional props:
1098
-
1099
- ### `frame`
1100
- Current frame number (0 to totalFrames - 1)
1101
-
1102
- ```tsx
1103
- export default function MyVideo({ frame }) {
1104
- // Frame 0, 1, 2, 3, ... 89 (for 3s @ 30fps)
1105
- return <div>Frame: {frame}</div>;
1106
- }
1107
- ```
1108
-
1109
- ### `progress`
1110
- Animation progress from 0 to 1
1111
-
1112
- ```tsx
1113
- export default function MyVideo({ progress }) {
1114
- // 0.0 at start, 0.5 at middle, 1.0 at end
1115
- const x = progress * 100; // Move from 0 to 100
1116
-
1117
- return (
1118
- <div style={{ transform: `translateX(${x}px)` }}>
1119
- Moving element
1120
- </div>
1121
- );
1122
- }
1123
- ```
1124
-
1125
- ## Next Steps
1126
-
1127
- - [Animation - Complete animation guide](/animation)
1128
- - [Image Rendering](/images)
1129
- - [Helpers - QR Codes and Template Composition](/helpers)
1130
- - [Styling with Tailwind & shadcn/ui](/styling)
1131
- - [Custom Fonts in Videos](/fonts)
1132
-
1133
-
1134
-
1135
-
1136
-
1137
- ================================================================================
1138
- FILE: animation.mdx
1139
- ================================================================================
1140
-
1141
- # Animation
1142
-
1143
- loopwind provides **Tailwind-style animation classes** that work with time to create smooth video animations without writing custom code.
1144
-
1145
- > **Note:** Animation classes only work with **video templates** and **GIFs**. For static images, animations will have no effect since there's no time context.
1146
-
1147
- ## Quick Start
1148
-
1149
- ```tsx
1150
- export default function MyVideo({ tw, title, subtitle }) {
1151
- return (
1152
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
1153
- {/* Bounce in from below: starts at 0, lasts 400ms */}
1154
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400')}>
1155
- {title}
1156
- </h1>
1157
-
1158
- {/* Fade in with upward motion: starts at 300ms, lasts 400ms */}
1159
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/300/400')}>
1160
- {subtitle}
1161
- </p>
1162
-
1163
- {/* Continuous floating animation: repeats every 1s (1000ms) */}
1164
- <div style={tw('mt-8 text-4xl loop-float/1000')}>
1165
- ⬇️
1166
- </div>
1167
- </div>
1168
- );
1169
- }
1170
- ```
1171
-
1172
- ## Animation Format
1173
-
1174
- loopwind uses three types of animations with **millisecond timing**:
1175
-
1176
- | Type | Format | Description |
1177
- |------|--------|-------------|
1178
- | Enter | `enter-{type}/{start}/{duration}` | Animations that play when entering |
1179
- | Exit | `exit-{type}/{start}/{duration}` | Animations that play when exiting |
1180
- | Loop | `loop-{type}/{duration}` | Continuous looping animations |
1181
-
1182
- All timing values are in **milliseconds** (1000ms = 1 second).
1183
-
1184
- ## Utility-Based Animations
1185
-
1186
- In addition to predefined animations, loopwind supports **Tailwind utility-based animations** that let you animate any transform or opacity property directly:
1187
-
1188
- ```tsx
1189
- // Slide in 20px from the left
1190
- <div style={tw('enter-translate-x-5/0/1000')}>Content</div>
1191
-
1192
- // Rotate 90 degrees on entrance
1193
- <div style={tw('enter-rotate-90/0/500')}>Spinning</div>
1194
-
1195
- // Fade to 50% opacity in a loop
1196
- <div style={tw('loop-opacity-50/1000')}>Pulsing</div>
1197
-
1198
- // Scale down with negative value
1199
- <div style={tw('enter--scale-50/0/800')}>Shrinking</div>
1200
- ```
1201
-
1202
- ### Supported Utilities
1203
-
1204
- | Utility | Format | Description | Example |
1205
- |---------|--------|-------------|---------|
1206
- | **translate-x** | `enter-translate-x-{value}` | Translate horizontally | `enter-translate-x-5` = 20px<br/>`enter-translate-x-full` = 100%<br/>`enter-translate-x-[20px]` = 20px |
1207
- | **translate-y** | `enter-translate-y-{value}` | Translate vertically | `loop-translate-y-10` = 40px<br/>`enter-translate-y-1/2` = 50%<br/>`enter-translate-y-[5rem]` = 80px |
1208
- | **opacity** | `enter-opacity-{n}` | Set opacity (0-100) | `enter-opacity-50` = 50% |
1209
- | **scale** | `enter-scale-{n}` | Scale element (0-200) | `enter-scale-100` = 1.0x |
1210
- | **rotate** | `enter-rotate-{n}` | Rotate in degrees | `enter-rotate-45` = 45° |
1211
- | **skew-x** | `enter-skew-x-{n}` | Skew on X axis in degrees | `enter-skew-x-12` = 12° |
1212
- | **skew-y** | `enter-skew-y-{n}` | Skew on Y axis in degrees | `exit-skew-y-6` = 6° |
1213
-
1214
- **Translate value formats:**
1215
- - **Numeric**: `5` = 20px (Tailwind spacing scale: 1 unit = 4px)
1216
- - **Keywords**: `full` = 100%
1217
- - **Fractions**: `1/2` = 50%, `1/3` = 33.333%, `2/3` = 66.666%, etc.
1218
- - **Arbitrary values**: `[20px]`, `[5rem]`, `[10%]` (rem converts to px: 1rem = 16px)
1219
-
1220
- All utilities work with:
1221
- - **All prefixes**: `enter-`, `exit-`, `loop-`, `animate-`
1222
- - **Negative values**: Prefix with `-` (e.g., `-translate-x-5`, `-rotate-45`)
1223
- - **Timing syntax**: Add `/start/duration` (e.g., `enter-translate-x-5/0/800`)
1224
-
1225
- ### Translate Animations
1226
-
1227
- ```tsx
1228
- // Numeric (Tailwind spacing): 20px (5 * 4px)
1229
- <div style={tw('enter-translate-x-5/0/500')}>Content</div>
1230
-
1231
- // Keyword: Full width (100%)
1232
- <div style={tw('enter-translate-y-full/0/800')}>Dropping full height</div>
1233
-
1234
- // Fraction: Half width (50%)
1235
- <div style={tw('enter-translate-x-1/2/0/600')}>Slide in halfway</div>
1236
-
1237
- // Arbitrary values: Exact px or rem
1238
- <div style={tw('enter-translate-y-[20px]/0/500')}>Slide 20px</div>
1239
- <div style={tw('enter-translate-x-[5rem]/0/800')}>Slide 5rem (80px)</div>
1240
-
1241
- // Loop with fractions
1242
- <div style={tw('loop-translate-y-1/4/1000')}>Oscillate 25%</div>
1243
-
1244
- // Negative values
1245
- <div style={tw('exit--translate-y-8/2000/500')}>Rising</div>
1246
- ```
1247
-
1248
- ### Opacity Animations
1249
-
1250
- ```tsx
1251
- // Fade to 100% opacity
1252
- <div style={tw('enter-opacity-100/0/500')}>Fading In</div>
1253
-
1254
- // Fade to 50% opacity
1255
- <div style={tw('enter-opacity-50/0/800')}>Half Opacity</div>
1256
-
1257
- // Pulse between 50% and 100%
1258
- <div style={tw('loop-opacity-50/1000')}>Pulsing</div>
1259
-
1260
- // Fade out to 0%
1261
- <div style={tw('exit-opacity-0/2500/500')}>Vanishing</div>
1262
- ```
1263
-
1264
- ### Scale Animations
1265
-
1266
- ```tsx
1267
- // Scale from 0 to 100% (1.0x)
1268
- <div style={tw('enter-scale-100/0/500')}>Growing</div>
1269
-
1270
- // Scale to 150% (1.5x)
1271
- <div style={tw('enter-scale-150/0/800')}>Enlarging</div>
1272
-
1273
- // Pulse scale in a loop
1274
- <div style={tw('loop-scale-110/1000')}>Breathing</div>
1275
-
1276
- // Scale down to 50%
1277
- <div style={tw('exit-scale-50/2000/500')}>Shrinking</div>
1278
- ```
1279
-
1280
- ### Rotate Animations
1281
-
1282
- ```tsx
1283
- // Rotate 90 degrees
1284
- <div style={tw('enter-rotate-90/0/500')}>Quarter Turn</div>
1285
-
1286
- // Rotate 180 degrees
1287
- <div style={tw('enter-rotate-180/0/1000')}>Half Turn</div>
1288
-
1289
- // Continuous rotation in loop (360 degrees per cycle)
1290
- <div style={tw('loop-rotate-360/2000')}>Spinning</div>
1291
-
1292
- // Rotate backwards with negative value
1293
- <div style={tw('enter--rotate-45/0/500')}>Counter Rotation</div>
1294
- ```
1295
-
1296
- ### Skew Animations
1297
-
1298
- ```tsx
1299
- // Skew on X axis
1300
- <div style={tw('enter-skew-x-12/0/500')}>Slanted</div>
1301
-
1302
- // Skew on Y axis
1303
- <div style={tw('enter-skew-y-6/0/800')}>Tilted</div>
1304
-
1305
- // Oscillating skew in loop
1306
- <div style={tw('loop-skew-x-6/1000')}>Wobbling</div>
1307
-
1308
- // Negative skew
1309
- <div style={tw('exit--skew-x-12/2000/500')}>Reverse Slant</div>
1310
- ```
1311
-
1312
- ### Combining Utilities
1313
-
1314
- You can combine multiple utility animations on the same element:
1315
-
1316
- ```tsx
1317
- // Translate and rotate together
1318
- <div style={tw('enter-translate-y-10/0/500 enter-rotate-45/0/500')}>
1319
- Flying In
1320
- </div>
1321
-
1322
- // Fade and scale
1323
- <div style={tw('enter-opacity-100/0/800 enter-scale-100/0/800')}>
1324
- Appearing
1325
- </div>
1326
-
1327
- // Enter with translate, exit with rotation
1328
- <div style={tw('enter-translate-x-5/0/500 exit-rotate-180/2500/500')}>
1329
- Slide and Spin
1330
- </div>
1331
- ```
1332
-
1333
- ### Bracket Notation
1334
-
1335
- For more CSS-like syntax, you can use brackets with units:
1336
-
1337
- ```tsx
1338
- // Using bracket notation with seconds
1339
- <h1 style={tw('enter-slide-up/[0.6s]/[1.5s]')}>Hello</h1>
1340
-
1341
- // Using bracket notation with milliseconds
1342
- <h1 style={tw('enter-fade-in/[300ms]/[800ms]')}>World</h1>
1343
-
1344
- // Mix and match - plain numbers are milliseconds
1345
- <h1 style={tw('enter-bounce-in/0/[1.2s]')}>Mixed</h1>
1346
- ```
1347
-
1348
- ## Enter Animations
1349
-
1350
- Format: `enter-{type}/{startMs}/{durationMs}`
1351
-
1352
- - `startMs` - when the animation begins (milliseconds from start)
1353
- - `durationMs` - how long the animation lasts
1354
-
1355
- When values are omitted (`enter-fade-in`), it uses the full video duration.
1356
-
1357
- ### Fade Animations
1358
-
1359
- Simple opacity transitions with optional direction.
1360
-
1361
- ```tsx
1362
- // Fade in from 0ms to 500ms
1363
- <h1 style={tw('enter-fade-in/0/500')}>Hello</h1>
1364
-
1365
- // Fade in with upward motion
1366
- <h1 style={tw('enter-fade-in-up/0/600')}>Hello</h1>
1367
- ```
1368
-
1369
- | Class | Description |
1370
- |-------|-------------|
1371
- | `enter-fade-in/0/500` | Fade in (opacity 0 → 1) |
1372
- | `enter-fade-in-up/0/500` | Fade in + slide up (30px) |
1373
- | `enter-fade-in-down/0/500` | Fade in + slide down (30px) |
1374
- | `enter-fade-in-left/0/500` | Fade in + slide from left (30px) |
1375
- | `enter-fade-in-right/0/500` | Fade in + slide from right (30px) |
1376
-
1377
- ### Slide Animations
1378
-
1379
- Larger movement (100px) with fade.
1380
-
1381
- ```tsx
1382
- // Slide in from left: starts at 0, lasts 500ms
1383
- <div style={tw('enter-slide-left/0/500')}>Content</div>
1384
-
1385
- // Slide up from bottom: starts at 200ms, lasts 600ms
1386
- <div style={tw('enter-slide-up/200/600')}>Content</div>
1387
- ```
1388
-
1389
- | Class | Description |
1390
- |-------|-------------|
1391
- | `enter-slide-left/0/500` | Slide in from left (100px) |
1392
- | `enter-slide-right/0/500` | Slide in from right (100px) |
1393
- | `enter-slide-up/0/500` | Slide in from bottom (100px) |
1394
- | `enter-slide-down/0/500` | Slide in from top (100px) |
1395
-
1396
- ### Bounce Animations
1397
-
1398
- Playful entrance with overshoot effect.
1399
-
1400
- ```tsx
1401
- // Bounce in with scale overshoot
1402
- <h1 style={tw('enter-bounce-in/0/500')}>Bouncy!</h1>
1403
-
1404
- // Bounce in from below
1405
- <div style={tw('enter-bounce-in-up/0/600')}>Pop!</div>
1406
- ```
1407
-
1408
- | Class | Description |
1409
- |-------|-------------|
1410
- | `enter-bounce-in/0/500` | Bounce in with scale overshoot |
1411
- | `enter-bounce-in-up/0/500` | Bounce in from below |
1412
- | `enter-bounce-in-down/0/500` | Bounce in from above |
1413
- | `enter-bounce-in-left/0/500` | Bounce in from left |
1414
- | `enter-bounce-in-right/0/500` | Bounce in from right |
1415
-
1416
- ### Scale & Zoom Animations
1417
-
1418
- Size-based transitions.
1419
-
1420
- ```tsx
1421
- // Scale in from 50%
1422
- <div style={tw('enter-scale-in/0/500')}>Growing</div>
1423
-
1424
- // Zoom in from 0%
1425
- <div style={tw('enter-zoom-in/0/1000')}>Zooming</div>
1426
- ```
1427
-
1428
- | Class | Description |
1429
- |-------|-------------|
1430
- | `enter-scale-in/0/500` | Scale up from 50% to 100% |
1431
- | `enter-zoom-in/0/500` | Zoom in from 0% to 100% |
1432
-
1433
- ### Rotate & Flip Animations
1434
-
1435
- Rotation-based transitions.
1436
-
1437
- ```tsx
1438
- // Rotate in 180 degrees
1439
- <div style={tw('enter-rotate-in/0/500')}>Spinning</div>
1440
-
1441
- // 3D flip on X axis
1442
- <div style={tw('enter-flip-in-x/0/500')}>Flipping</div>
1443
- ```
1444
-
1445
- | Class | Description |
1446
- |-------|-------------|
1447
- | `enter-rotate-in/0/500` | Rotate in from -180° |
1448
- | `enter-flip-in-x/0/500` | 3D flip on horizontal axis |
1449
- | `enter-flip-in-y/0/500` | 3D flip on vertical axis |
1450
-
1451
- ## Exit Animations
1452
-
1453
- Format: `exit-{type}/{startMs}/{durationMs}`
1454
-
1455
- - `startMs` - when the exit animation begins
1456
- - `durationMs` - how long the exit animation lasts
1457
-
1458
- Exit animations use the same timing system but animate elements out.
1459
-
1460
- ```tsx
1461
- // Fade out starting at 2500ms, lasting 500ms (ends at 3000ms)
1462
- <h1 style={tw('exit-fade-out/2500/500')}>Goodbye</h1>
1463
-
1464
- // Combined enter and exit on same element
1465
- <h1 style={tw('enter-fade-in/0/500 exit-fade-out/2500/500')}>
1466
- Hello and Goodbye
1467
- </h1>
1468
- ```
1469
-
1470
- | Class | Description |
1471
- |-------|-------------|
1472
- | `exit-fade-out/2500/500` | Fade out (opacity 1 → 0) |
1473
- | `exit-fade-out-up/2500/500` | Fade out + slide up |
1474
- | `exit-fade-out-down/2500/500` | Fade out + slide down |
1475
- | `exit-fade-out-left/2500/500` | Fade out + slide left |
1476
- | `exit-fade-out-right/2500/500` | Fade out + slide right |
1477
- | `exit-slide-up/2500/500` | Slide out upward (100px) |
1478
- | `exit-slide-down/2500/500` | Slide out downward (100px) |
1479
- | `exit-slide-left/2500/500` | Slide out to left (100px) |
1480
- | `exit-slide-right/2500/500` | Slide out to right (100px) |
1481
- | `exit-scale-out/2500/500` | Scale out to 150% |
1482
- | `exit-zoom-out/2500/500` | Zoom out to 200% |
1483
- | `exit-rotate-out/2500/500` | Rotate out to 180° |
1484
- | `exit-bounce-out/2500/500` | Bounce out with scale |
1485
- | `exit-bounce-out-up/2500/500` | Bounce out upward |
1486
- | `exit-bounce-out-down/2500/500` | Bounce out downward |
1487
- | `exit-bounce-out-left/2500/500` | Bounce out to left |
1488
- | `exit-bounce-out-right/2500/500` | Bounce out to right |
1489
-
1490
- ## Loop Animations
1491
-
1492
- Format: `loop-{type}/{durationMs}`
1493
-
1494
- Loop animations repeat every `{durationMs}` milliseconds:
1495
- - `/1000` = 1 second loop
1496
- - `/500` = 0.5 second loop
1497
- - `/2000` = 2 second loop
1498
-
1499
- When duration is omitted (`loop-bounce`), it defaults to 1000ms (1 second).
1500
-
1501
- ```tsx
1502
- // Pulse opacity every 500ms
1503
- <div style={tw('loop-fade/500')}>Pulsing</div>
1504
-
1505
- // Bounce every 800ms
1506
- <div style={tw('loop-bounce/800')}>Bouncing</div>
1507
-
1508
- // Full rotation every 2000ms
1509
- <div style={tw('loop-spin/2000')}>Spinning</div>
1510
- ```
1511
-
1512
- | Class | Description |
1513
- |-------|-------------|
1514
- | `loop-fade/{ms}` | Opacity pulse (0.5 → 1 → 0.5) |
1515
- | `loop-bounce/{ms}` | Bounce up and down |
1516
- | `loop-spin/{ms}` | Full 360° rotation |
1517
- | `loop-ping/{ms}` | Scale up + fade out (radar effect) |
1518
- | `loop-wiggle/{ms}` | Side to side wiggle |
1519
- | `loop-float/{ms}` | Gentle up and down floating |
1520
- | `loop-pulse/{ms}` | Scale pulse (1.0 → 1.05 → 1.0) |
1521
- | `loop-shake/{ms}` | Shake side to side |
1522
-
1523
- ## Easing Functions
1524
-
1525
- Add an easing class **before** the animation class to control the timing curve.
1526
-
1527
- ```tsx
1528
- // Ease in (accelerate)
1529
- <h1 style={tw('ease-in enter-fade-in/0/1000')}>Accelerating</h1>
1530
-
1531
- // Ease out (decelerate) - default
1532
- <h1 style={tw('ease-out enter-fade-in/0/1000')}>Decelerating</h1>
1533
-
1534
- // Ease in-out (smooth)
1535
- <h1 style={tw('ease-in-out enter-fade-in/0/1000')}>Smooth</h1>
1536
-
1537
- // Strong cubic easing
1538
- <h1 style={tw('ease-out-cubic enter-bounce-in/0/500')}>Dramatic</h1>
1539
- ```
1540
-
1541
- | Class | Description | Best For |
1542
- |-------|-------------|----------|
1543
- | `linear` | Constant speed | Mechanical motion |
1544
- | `ease-in` | Slow start, fast end | Exit animations |
1545
- | `ease-out` | Fast start, slow end (default) | Enter animations |
1546
- | `ease-in-out` | Slow start and end | Subtle transitions |
1547
- | `ease-in-cubic` | Strong slow start | Dramatic exits |
1548
- | `ease-out-cubic` | Strong fast start | Impactful entrances |
1549
- | `ease-in-out-cubic` | Strong both ends | Emphasis animations |
1550
- | `ease-in-quart` | Very strong slow start | Powerful exits |
1551
- | `ease-out-quart` | Very strong fast start | Punchy entrances |
1552
- | `ease-in-out-quart` | Very strong both ends | Maximum drama |
1553
-
1554
- ## Combining Enter and Exit
1555
-
1556
- You can use both enter and exit animations on the same element:
1557
-
1558
- ```tsx
1559
- export default function EnterExit({ tw, title }) {
1560
- return (
1561
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1562
- {/* Fade in during first 500ms, fade out during last 500ms (assuming 3s video) */}
1563
- <h1 style={tw('text-8xl font-bold text-white enter-fade-in/0/500 exit-fade-out/2500/500')}>
1564
- {title}
1565
- </h1>
1566
- </div>
1567
- );
1568
- }
1569
- ```
1570
-
1571
- The opacities from multiple animations are **multiplied together**, so you get smooth transitions that combine properly.
1572
-
1573
- ## Staggered Animations
1574
-
1575
- Create sequenced animations by offsetting start times:
1576
-
1577
- ```tsx
1578
- export default function StaggeredList({ tw, items }) {
1579
- return (
1580
- <div style={tw('flex flex-col gap-4')}>
1581
- {/* First item: starts at 0ms, lasts 300ms */}
1582
- <div style={tw('ease-out enter-fade-in-left/0/300')}>
1583
- {items[0]}
1584
- </div>
1585
-
1586
- {/* Second item: starts at 100ms, lasts 300ms */}
1587
- <div style={tw('ease-out enter-fade-in-left/100/300')}>
1588
- {items[1]}
1589
- </div>
1590
-
1591
- {/* Third item: starts at 200ms, lasts 300ms */}
1592
- <div style={tw('ease-out enter-fade-in-left/200/300')}>
1593
- {items[2]}
1594
- </div>
1595
- </div>
1596
- );
1597
- }
1598
- ```
1599
-
1600
- ### Dynamic Staggering
1601
-
1602
- For dynamic lists, calculate the timing programmatically:
1603
-
1604
- ```tsx
1605
- export default function DynamicStagger({ tw, items }) {
1606
- return (
1607
- <div style={tw('flex flex-col gap-4')}>
1608
- {items.map((item, i) => {
1609
- const start = i * 100; // Each item starts 100ms later
1610
- const duration = 300; // Each animation lasts 300ms
1611
-
1612
- return (
1613
- <div
1614
- key={i}
1615
- style={tw(`ease-out enter-fade-in-up/${start}/${duration}`)}
1616
- >
1617
- {item}
1618
- </div>
1619
- );
1620
- })}
1621
- </div>
1622
- );
1623
- }
1624
- ```
1625
-
1626
- ## Common Patterns
1627
-
1628
- ### Intro Sequence
1629
-
1630
- ```tsx
1631
- export default function IntroVideo({ tw, title, subtitle, logo }) {
1632
- return (
1633
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
1634
- {/* Logo appears first */}
1635
- <img
1636
- src={logo}
1637
- style={tw('h-20 mb-8 ease-out enter-scale-in/0/300')}
1638
- />
1639
-
1640
- {/* Title bounces in */}
1641
- <h1 style={tw('text-7xl font-bold text-white ease-out enter-bounce-in-up/200/500')}>
1642
- {title}
1643
- </h1>
1644
-
1645
- {/* Subtitle fades in last */}
1646
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/400/700')}>
1647
- {subtitle}
1648
- </p>
1649
- </div>
1650
- );
1651
- }
1652
- ```
1653
-
1654
- ### Text Reveal
1655
-
1656
- ```tsx
1657
- export default function TextReveal({ tw, words }) {
1658
- return (
1659
- <div style={tw('flex flex-wrap gap-2 justify-center')}>
1660
- {words.split(' ').map((word, i) => (
1661
- <span
1662
- key={i}
1663
- style={tw(`text-4xl font-bold ease-out enter-fade-in-up/${i * 100}/200`)}
1664
- >
1665
- {word}
1666
- </span>
1667
- ))}
1668
- </div>
1669
- );
1670
- }
1671
- ```
1672
-
1673
- ### Looping Background Element
1674
-
1675
- ```tsx
1676
- export default function AnimatedBackground({ tw, children }) {
1677
- return (
1678
- <div style={tw('relative w-full h-full')}>
1679
- {/* Floating background circles */}
1680
- <div style={tw('absolute top-10 left-10 w-20 h-20 rounded-full bg-white/10 loop-float/2000')} />
1681
- <div style={tw('absolute bottom-20 right-20 w-32 h-32 rounded-full bg-white/10 loop-fade/1500')} />
1682
-
1683
- {/* Main content */}
1684
- <div style={tw('relative z-10')}>
1685
- {children}
1686
- </div>
1687
- </div>
1688
- );
1689
- }
1690
- ```
1691
-
1692
- ### Full Enter/Exit Animation
1693
-
1694
- ```tsx
1695
- export default function FullAnimation({ tw, title }) {
1696
- return (
1697
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1698
- {/* Enter: starts at 0, lasts 400ms. Exit: starts at 2600ms, lasts 400ms */}
1699
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400 exit-fade-out-up/2600/400')}>
1700
- {title}
1701
- </h1>
1702
- </div>
1703
- );
1704
- }
1705
- ```
1706
-
1707
- ## Programmatic Animations
1708
-
1709
- For complete control beyond animation classes, use `progress` and `frame` directly.
1710
-
1711
- ### Available Props
1712
-
1713
- | Prop | Type | Description |
1714
- |------|------|-------------|
1715
- | `progress` | `number` | 0 to 1 through the video (0% to 100%) |
1716
- | `frame` | `number` | Current frame number (0, 1, 2, ... totalFrames-1) |
1717
-
1718
- These are **only available in video templates**. Use them when animation classes aren't flexible enough.
1719
-
1720
- ### Using `frame`
1721
-
1722
- ```tsx
1723
- export default function FrameAnimation({ tw, frame, title }) {
1724
- // Color cycling using frame number
1725
- const hue = (frame * 5) % 360; // Cycle through colors
1726
-
1727
- // Pulsing based on frame
1728
- const fps = 30;
1729
- const pulse = Math.sin(frame / fps * Math.PI * 2) * 0.2 + 0.8; // 0.6 to 1.0
1730
-
1731
- return (
1732
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1733
- <h1 style={{
1734
- ...tw('text-8xl font-bold'),
1735
- color: `hsl(${hue}, 70%, 60%)`,
1736
- transform: `scale(${pulse})`
1737
- }}>
1738
- {title}
1739
- </h1>
1740
- </div>
1741
- );
1742
- }
1743
- ```
1744
-
1745
- ### Using `progress`
1746
-
1747
- ```tsx
1748
- export default function ProgressAnimation({ tw, progress, title }) {
1749
- // Custom fade based on progress
1750
- const opacity = progress < 0.3 ? progress / 0.3 : 1;
1751
-
1752
- // Custom scale based on progress
1753
- const scale = 0.8 + progress * 0.2; // 0.8 to 1.0
1754
-
1755
- return (
1756
- <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
1757
- <h1 style={{
1758
- ...tw('text-8xl font-bold text-white'),
1759
- opacity,
1760
- transform: `scale(${scale})`
1761
- }}>
1762
- {title}
1763
- </h1>
1764
- </div>
1765
- );
1766
- }
1767
- ```
1768
-
1769
- ### Custom Easing
1770
-
1771
- ```tsx
1772
- export default function CustomEasing({ tw, progress, title }) {
1773
- // Smoothstep easing
1774
- const eased = progress * progress * (3 - 2 * progress);
1775
-
1776
- // Elastic easing
1777
- const elastic = Math.pow(2, -10 * progress) * Math.sin((progress - 0.075) * (2 * Math.PI) / 0.3) + 1;
1778
-
1779
- return (
1780
- <div style={tw('flex items-center justify-center w-full h-full')}>
1781
- <h1 style={{
1782
- ...tw('text-8xl font-bold'),
1783
- opacity: eased,
1784
- transform: `translateY(${(1 - elastic) * 100}px)`
1785
- }}>
1786
- {title}
1787
- </h1>
1788
- </div>
1789
- );
1790
- }
1791
- ```
1792
-
1793
- ### When to Use Programmatic Animations
1794
-
1795
- Use `progress`/`frame` instead of animation classes when you need:
1796
- - **Custom easing functions** (elastic, spring, bounce with specific curves)
1797
- - **Color cycling or gradients** based on time
1798
- - **Mathematical animations** (sine waves, spirals, etc.)
1799
- - **Complex multi-property animations** that need precise coordination
1800
- - **Conditional logic** based on specific frame numbers
1801
-
1802
- For everything else, prefer animation classes - they're simpler and more maintainable.
1803
-
1804
- ## Performance Tips
1805
-
1806
- 1. **Use Tailwind classes** when possible - they're optimized for the renderer
1807
- 2. **Avoid too many nested animations** - each adds computation per frame
1808
- 3. **Use loop animations sparingly** - they're computed every frame
1809
- 4. **Prefer opacity and transform** - they're the most performant properties
1810
-
1811
- ## Next Steps
1812
-
1813
- - [Video Templates](/video) - Creating video templates
1814
- - [SDK](/sdk) - Programmatic rendering with animations
1815
- - [Helpers](/helpers) - QR codes, images, and more
1816
-
1817
-
1818
-
1819
-
1820
- ================================================================================
1821
- FILE: helpers.mdx
1822
- ================================================================================
1823
-
1824
- # Template Helpers
1825
-
1826
- Additional helpers for creating powerful, composable templates.
1827
-
1828
- ## Overview
1829
-
1830
- Beyond the basics, loopwind provides:
1831
- - `template()` - Compose templates together
1832
- - `qr()` - Generate QR codes on the fly
1833
- - `config` - Access user configuration
1834
-
1835
- For image embedding, see the [Images](/images) page.
1836
-
1837
- ## Template Composition
1838
-
1839
- Compose multiple templates together to create complex designs.
1840
-
1841
- ### Usage
1842
-
1843
- ```tsx
1844
- export default function CompositeCard({ tw, template, title, author, avatar }) {
1845
- return (
1846
- <div style={tw('w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
1847
- <div style={tw('bg-white rounded-2xl p-8 shadow-xl')}>
1848
- <h1 style={tw('text-4xl font-bold text-gray-900 mb-6')}>{title}</h1>
1849
-
1850
- {/* Embed another template */}
1851
- <div style={tw('mb-6')}>
1852
- {template('user-badge', {
1853
- name: author,
1854
- avatar: avatar
1855
- })}
1856
- </div>
1857
-
1858
- <p style={tw('text-gray-600')}>Published by {author}</p>
1859
- </div>
1860
- </div>
1861
- );
1862
- }
1863
- ```
1864
-
1865
- **How it works:**
1866
- 1. `template(name, props)` renders another installed template
1867
- 2. The embedded template is rendered at its specified size
1868
- 3. You can embed multiple templates in one design
1869
- 4. Templates can be nested (template within a template)
1870
-
1871
- ### Use Cases
1872
-
1873
- **1. Reusable components:**
1874
- ```tsx
1875
- // Create a logo template once, use it everywhere
1876
- <div>{template('company-logo', { variant: 'dark' })}</div>
1877
- ```
1878
-
1879
- **2. Complex layouts:**
1880
- ```tsx
1881
- // Combine multiple templates into one design
1882
- <div style={tw('grid grid-cols-2 gap-4')}>
1883
- {template('product-card', { product: product1 })}
1884
- {template('product-card', { product: product2 })}
1885
- </div>
1886
- ```
1887
-
1888
- **3. Dynamic content:**
1889
- ```tsx
1890
- // Render templates based on data
1891
- {users.map(user =>
1892
- template('user-avatar', { name: user.name, image: user.avatar })
1893
- )}
1894
- ```
1895
-
1896
- ### Best Practices
1897
-
1898
- 1. **Keep templates focused** - Each template should do one thing well
1899
- 2. **Pass minimal props** - Only pass what the embedded template needs
1900
- 3. **Document dependencies** - Note which templates are required in your README
1901
- 4. **Avoid deep nesting** - Too many nested templates can be hard to debug
1902
-
1903
- ## QR Codes
1904
-
1905
- Generate QR codes dynamically in your templates.
1906
-
1907
- ### Usage
1908
-
1909
- ```tsx
1910
- export default function QRCard({ tw, qr, title, url }) {
1911
- return (
1912
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-white p-10')}>
1913
- <h1 style={tw('text-4xl font-bold text-black mb-8')}>{title}</h1>
1914
-
1915
- {/* Generate QR code for the URL */}
1916
- <img src={qr(url)} style={tw('w-64 h-64')} />
1917
-
1918
- <p style={tw('text-gray-600 mt-4')}>{url}</p>
1919
- </div>
1920
- );
1921
- }
1922
- ```
1923
-
1924
- **Props format:**
1925
- ```json
1926
- {
1927
- "title": "Scan Me",
1928
- "url": "https://example.com"
1929
- }
1930
- ```
1931
-
1932
- ### QR Options
1933
-
1934
- You can customize QR code appearance:
1935
-
1936
- ```tsx
1937
- // Basic QR code
1938
- <img src={qr('https://example.com')} />
1939
-
1940
- // With error correction level
1941
- <img src={qr('https://example.com', { errorCorrectionLevel: 'H' })} />
1942
-
1943
- // With custom size
1944
- <img src={qr('https://example.com', { width: 512 })} />
1945
- ```
1946
-
1947
- **Error correction levels:**
1948
- - `L` - Low (~7% correction)
1949
- - `M` - Medium (~15% correction) - default
1950
- - `Q` - Quartile (~25% correction)
1951
- - `H` - High (~30% correction)
1952
-
1953
- ## User Configuration
1954
-
1955
- Access user settings from `_loopwind/loopwind.json` using the `config` prop:
1956
-
1957
- ```tsx
1958
- export default function BrandedTemplate({ tw, config, title }) {
1959
- // Access custom colors from loopwind.json
1960
- const primaryColor = config?.colors?.brand || '#6366f1';
1961
-
1962
- return (
1963
- <div style={tw('w-full h-full p-12')}>
1964
- <h1 style={{
1965
- ...tw('text-6xl font-bold'),
1966
- color: primaryColor
1967
- }}>
1968
- {title}
1969
- </h1>
1970
- </div>
1971
- );
1972
- }
1973
- ```
1974
-
1975
- **User's `_loopwind/loopwind.json`:**
1976
- ```json title="_loopwind/loopwind.json"
1977
- {
1978
- "colors": {
1979
- "brand": "#ff6b6b"
1980
- },
1981
- "fonts": {
1982
- "sans": ["Inter", "system-ui", "sans-serif"]
1983
- }
1984
- }
1985
- ```
1986
-
1987
- This allows templates to adapt to user preferences and brand guidelines.
1988
-
1989
- ## All Props Reference
1990
-
1991
- Every template receives these props:
1992
-
1993
- ```tsx
1994
- export default function MyTemplate({
1995
- // Core helpers
1996
- tw, // Tailwind class converter
1997
- qr, // QR code generator (this page)
1998
- template, // Template composer (this page)
1999
- config, // User config from loopwind.json (this page)
2000
-
2001
- // Media helpers (see dedicated pages)
2002
- image, // Image embedder → see /images
2003
-
2004
- // Video-specific (only in video templates)
2005
- frame, // Current frame number → see /video
2006
- progress, // Animation progress 0-1 → see /video
2007
-
2008
- // Your custom props
2009
- ...props // Any props from your props.json
2010
- }) {
2011
- // Your template code
2012
- }
2013
- ```
2014
-
2015
- ## Next Steps
2016
-
2017
- - [Image Rendering and Embedding](/images)
2018
- - [Video Animation](/video)
2019
- - [Styling with Tailwind & shadcn/ui](/styling)
2020
- - [Custom Fonts](/fonts)
2021
-
2022
-
2023
-
2024
-
2025
-
2026
- ================================================================================
2027
- FILE: styling.mdx
2028
- ================================================================================
2029
-
2030
- # Styling Templates
2031
-
2032
- Style your templates with Tailwind utility classes and shadcn/ui's beautiful design system.
2033
-
2034
- ## Quick Start
2035
-
2036
- ```tsx
2037
- export default function MyTemplate({ title, tw }) {
2038
- return (
2039
- <div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
2040
- <h1 style={tw('text-7xl font-bold text-white')}>
2041
- {title}
2042
- </h1>
2043
- </div>
2044
- );
2045
- }
2046
- ```
2047
-
2048
- ## The `tw()` Function
2049
-
2050
- Every template receives a `tw()` function that converts Tailwind classes to inline styles compatible with Satori:
2051
-
2052
- ```tsx
2053
- // Tailwind classes
2054
- tw('flex items-center justify-center p-8 bg-blue-500')
2055
-
2056
- // Converts to inline styles:
2057
- {
2058
- display: 'flex',
2059
- alignItems: 'center',
2060
- justifyContent: 'center',
2061
- padding: '2rem',
2062
- backgroundColor: '#3b82f6'
2063
- }
2064
- ```
2065
-
2066
- ### Basic Usage
2067
-
2068
- ```tsx
2069
- export default function Banner({ title, subtitle, tw }) {
2070
- return (
2071
- <div style={tw('w-full h-full p-12 bg-gray-50')}>
2072
- <h1 style={tw('text-6xl font-bold text-gray-900 mb-4')}>
2073
- {title}
2074
- </h1>
2075
- <p style={tw('text-2xl text-gray-600')}>
2076
- {subtitle}
2077
- </p>
2078
- </div>
2079
- );
2080
- }
2081
- ```
2082
-
2083
- ### Combining with Custom Styles
2084
-
2085
- Mix Tailwind classes with custom styles using the spread operator:
2086
-
2087
- ```tsx
2088
- export default function CustomGradient({ title, tw }) {
2089
- return (
2090
- <div
2091
- style={{
2092
- ...tw('flex flex-col items-center justify-center w-full h-full p-20'),
2093
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
2094
- }}
2095
- >
2096
- <h1 style={tw('text-8xl font-bold text-white')}>{title}</h1>
2097
- </div>
2098
- );
2099
- }
2100
- ```
2101
-
2102
- ## shadcn/ui Design System
2103
-
2104
- loopwind uses **shadcn/ui's design system** by default, providing semantic color tokens for beautiful, consistent designs.
2105
-
2106
- ### Default Color Palette
2107
-
2108
- All templates automatically have access to these semantic colors defined in `_loopwind/loopwind.json`:
2109
-
2110
- ```typescript
2111
- colors: {
2112
- // Primary colors
2113
- primary: '#18181b', // Main brand color
2114
- 'primary-foreground': '#fafafa',
2115
-
2116
- // Secondary colors
2117
- secondary: '#f4f4f5', // Subtle accents
2118
- 'secondary-foreground': '#18181b',
2119
-
2120
- // Background
2121
- background: '#ffffff', // Page background
2122
- foreground: '#09090b', // Main text color
2123
-
2124
- // Muted
2125
- muted: '#f4f4f5', // Subtle backgrounds
2126
- 'muted-foreground': '#71717a', // Muted text
2127
-
2128
- // Accent
2129
- accent: '#f4f4f5', // Highlight color
2130
- 'accent-foreground': '#18181b',
2131
-
2132
- // Destructive
2133
- destructive: '#ef4444', // Error/danger states
2134
- 'destructive-foreground': '#fafafa',
2135
-
2136
- // UI Elements
2137
- border: '#e4e4e7', // Border color
2138
- input: '#e4e4e7', // Input borders
2139
- ring: '#18181b', // Focus rings
2140
- card: '#ffffff', // Card background
2141
- 'card-foreground': '#09090b',
2142
- }
2143
- ```
2144
-
2145
- ### Using Semantic Colors
2146
-
2147
- ```tsx
2148
- export default function SemanticCard({ title, description, price, tw }) {
2149
- return (
2150
- <div style={tw('bg-card border border-border rounded-lg p-6')}>
2151
- <h2 style={tw('text-card-foreground text-2xl font-bold mb-2')}>
2152
- {title}
2153
- </h2>
2154
- <p style={tw('text-muted-foreground mb-4')}>
2155
- {description}
2156
- </p>
2157
- <div style={tw('text-primary text-3xl font-bold')}>
2158
- ${price}
2159
- </div>
2160
- </div>
2161
- );
2162
- }
2163
- ```
2164
-
2165
- ### Opacity Modifiers
2166
-
2167
- Use Tailwind's slash syntax for opacity with any color:
2168
-
2169
- ```tsx
2170
- export default function OpacityExample({ tw }) {
2171
- return (
2172
- <div style={tw('bg-primary/50')}> {/* 50% opacity */}
2173
- <p style={tw('text-muted-foreground/75')}> {/* 75% opacity */}
2174
- Subtle text
2175
- </p>
2176
- <div style={tw('border border-border/30')}> {/* 30% opacity */}
2177
- Faint border
2178
- </div>
2179
- </div>
2180
- );
2181
- }
2182
- ```
2183
-
2184
- **Supported syntax:**
2185
- - `bg-{color}/{opacity}` - Background with opacity
2186
- - `text-{color}/{opacity}` - Text with opacity
2187
- - `border-{color}/{opacity}` - Border with opacity
2188
-
2189
- ### Text Hierarchy
2190
-
2191
- ```tsx
2192
- // Primary text
2193
- tw('text-foreground')
2194
-
2195
- // Secondary/muted text
2196
- tw('text-muted-foreground')
2197
-
2198
- // Accent/brand text
2199
- tw('text-primary')
2200
-
2201
- // Destructive/error text
2202
- tw('text-destructive')
2203
- ```
2204
-
2205
- ### Backgrounds
2206
-
2207
- ```tsx
2208
- // Page background
2209
- tw('bg-background')
2210
-
2211
- // Card/elevated surfaces
2212
- tw('bg-card')
2213
-
2214
- // Subtle backgrounds
2215
- tw('bg-muted')
2216
-
2217
- // Accent backgrounds
2218
- tw('bg-accent')
2219
- ```
2220
-
2221
- ## Supported Tailwind Classes
2222
-
2223
- ### Layout
2224
-
2225
- - **Display**: `flex`, `inline-flex`, `block`, `inline-block`, `hidden`
2226
- - **Flex Direction**: `flex-row`, `flex-col`, `flex-row-reverse`, `flex-col-reverse`
2227
- - **Justify**: `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`
2228
- - **Align**: `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch`
2229
-
2230
- ### Spacing
2231
-
2232
- - **Padding**: `p-{n}`, `px-{n}`, `py-{n}`, `pt-{n}`, `pb-{n}`, `pl-{n}`, `pr-{n}`
2233
- - **Margin**: `m-{n}`, `mx-{n}`, `my-{n}`, `mt-{n}`, `mb-{n}`, `ml-{n}`, `mr-{n}`
2234
- - **Gap**: `gap-{n}`, `gap-x-{n}`, `gap-y-{n}`
2235
- - **Sizes**: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64
2236
-
2237
- Examples:
2238
- ```tsx
2239
- tw('p-4') // padding: 1rem
2240
- tw('px-8') // paddingLeft: 2rem, paddingRight: 2rem
2241
- tw('m-6') // margin: 1.5rem
2242
- tw('gap-4') // gap: 1rem
2243
- ```
2244
-
2245
- ### Sizing
2246
-
2247
- - **Width**: `w-{n}`, `w-full`, `w-screen`, `w-1/2`, `w-1/3`, `w-2/3`
2248
- - **Height**: `h-{n}`, `h-full`, `h-screen`
2249
-
2250
- Examples:
2251
- ```tsx
2252
- tw('w-full') // width: 100%
2253
- tw('h-64') // height: 16rem
2254
- tw('w-1/2') // width: 50%
2255
- ```
2256
-
2257
- ### Typography
2258
-
2259
- - **Font Size**: `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`, `text-7xl`, `text-8xl`, `text-9xl`
2260
- - **Font Weight**: `font-thin`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
2261
- - **Text Align**: `text-left`, `text-center`, `text-right`
2262
- - **Line Height**: `leading-none`, `leading-tight`, `leading-normal`, `leading-relaxed`, `leading-loose`
2263
-
2264
- ### Colors
2265
-
2266
- All standard Tailwind colors plus shadcn semantic colors:
2267
-
2268
- **Standard colors:**
2269
- - `text-{color}-{shade}`, `bg-{color}-{shade}`, `border-{color}-{shade}`
2270
- - Colors: `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `gray`, `indigo`, `teal`, `orange`
2271
- - Shades: `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
2272
-
2273
- **shadcn semantic colors:**
2274
- - `text-foreground`, `text-primary`, `text-muted-foreground`, `text-destructive`
2275
- - `bg-background`, `bg-card`, `bg-muted`, `bg-accent`, `bg-primary`
2276
- - `border-border`, `border-input`
2277
-
2278
- ```tsx
2279
- tw('text-blue-500') // Standard Tailwind color
2280
- tw('bg-purple-600') // Standard Tailwind color
2281
- tw('text-primary') // shadcn semantic color
2282
- tw('bg-card') // shadcn semantic color
2283
- ```
2284
-
2285
- ### Position & Layout
2286
-
2287
- - **Position**: `relative`, `absolute`, `fixed`, `sticky`
2288
- - **Inset**: `inset-0`, `top-0`, `bottom-0`, `left-0`, `right-0`
2289
- - **Z-Index**: `z-0`, `z-10`, `z-20`, `z-30`, `z-40`, `z-50`
2290
-
2291
- ### Borders
2292
-
2293
- - **Border Width**: `border`, `border-{n}`, `border-t`, `border-b`, `border-l`, `border-r`
2294
- - **Border Radius**: `rounded`, `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full`
2295
- - **Border Color**: `border-{color}-{shade}`, `border-border`, `border-input`
2296
-
2297
- ### Effects
2298
-
2299
- - **Shadow**: `shadow-sm`, `shadow`, `shadow-md`, `shadow-lg`, `shadow-xl`, `shadow-2xl`
2300
- - **Opacity**: `opacity-0`, `opacity-25`, `opacity-50`, `opacity-75`, `opacity-100`
2301
-
2302
- ### Filters
2303
-
2304
- - **Blur**: `blur-none`, `blur-sm`, `blur`, `blur-md`, `blur-lg`, `blur-xl`
2305
- - **Brightness**: `brightness-0`, `brightness-50`, `brightness-100`, `brightness-150`, `brightness-200`
2306
- - **Contrast**: `contrast-0`, `contrast-50`, `contrast-100`, `contrast-150`, `contrast-200`
2307
-
2308
- ## Gradients
2309
-
2310
- ### Linear Gradients
2311
-
2312
- ```tsx
2313
- // Gradient direction
2314
- tw('bg-gradient-to-r') // left to right
2315
- tw('bg-gradient-to-br') // top-left to bottom-right
2316
- tw('bg-gradient-to-t') // bottom to top
2317
-
2318
- // Gradient colors
2319
- tw('from-blue-500') // Start color
2320
- tw('via-purple-500') // Middle color
2321
- tw('to-pink-500') // End color
2322
-
2323
- // Complete gradient
2324
- tw('bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500')
2325
- ```
2326
-
2327
- ### Gradient Examples
2328
-
2329
- ```tsx
2330
- export default function GradientCard({ title, tw }) {
2331
- return (
2332
- <div style={tw('w-full h-full bg-gradient-to-br from-cyan-500 to-blue-600 p-12')}>
2333
- <h1 style={tw('text-white text-6xl font-bold')}>
2334
- {title}
2335
- </h1>
2336
- </div>
2337
- );
2338
- }
2339
- ```
2340
-
2341
- ## Custom Theme Colors
2342
-
2343
- You can override the default shadcn colors or add your own custom colors in `_loopwind/loopwind.json`:
2344
-
2345
- ```json title="_loopwind/loopwind.json"
2346
- {
2347
- "theme": {
2348
- "colors": {
2349
- "primary": "#3b82f6",
2350
- "primary-foreground": "#ffffff",
2351
- "accent": "#10b981",
2352
- "brand": "#ff6b6b"
2353
- }
2354
- }
2355
- }
2356
- ```
2357
-
2358
- Then use these custom colors in your templates:
2359
-
2360
- ```tsx
2361
- tw('text-brand') // Uses your custom brand color
2362
- tw('bg-primary') // Uses your custom primary color
2363
- tw('bg-accent') // Uses your custom accent color
2364
- ```
2365
-
2366
- ## Auto-Detection from tailwind.config.js
2367
-
2368
- loopwind automatically detects and loads your project's Tailwind configuration:
2369
-
2370
- ```
2371
- your-project/
2372
- ├── tailwind.config.js ← Automatically detected
2373
- └── _loopwind/
2374
- ├── loopwind.json
2375
- └── templates/
2376
- ```
2377
-
2378
- This includes:
2379
- - Custom colors
2380
- - Custom spacing values
2381
- - Custom fonts
2382
- - Theme extensions
2383
- - Custom utilities
2384
-
2385
- ## Complete Example
2386
-
2387
- ```tsx
2388
- export default function ModernCard({
2389
- tw,
2390
- image,
2391
- title,
2392
- description,
2393
- category,
2394
- author,
2395
- avatar
2396
- }) {
2397
- return (
2398
- <div style={tw('w-full h-full bg-card')}>
2399
- {/* Hero image */}
2400
- <div style={tw('relative h-2/3')}>
2401
- <img
2402
- src={image(hero)}
2403
- style={tw('w-full h-full object-cover')}
2404
- />
2405
- {/* Category badge */}
2406
- <div style={tw('absolute top-4 left-4 bg-primary/90 backdrop-blur px-4 py-2 rounded-full')}>
2407
- <span style={tw('text-sm font-semibold text-primary-foreground')}>
2408
- {category}
2409
- </span>
2410
- </div>
2411
- </div>
2412
-
2413
- {/* Content */}
2414
- <div style={tw('h-1/3 p-8 flex flex-col justify-between')}>
2415
- <div>
2416
- <h2 style={tw('text-3xl font-bold text-foreground mb-2')}>
2417
- {title}
2418
- </h2>
2419
- <p style={tw('text-muted-foreground line-clamp-2')}>
2420
- {description}
2421
- </p>
2422
- </div>
2423
-
2424
- {/* Author */}
2425
- <div style={tw('flex items-center gap-3')}>
2426
- <img
2427
- src={image(avatar)}
2428
- style={tw('w-10 h-10 rounded-full border-2 border-border')}
2429
- />
2430
- <span style={tw('text-sm text-muted-foreground')}>
2431
- {author}
2432
- </span>
2433
- </div>
2434
- </div>
2435
- </div>
2436
- );
2437
- }
2438
- ```
2439
-
2440
- ## Why This Approach?
2441
-
2442
- - **Semantic naming**: `text-primary` instead of `text-blue-600`
2443
- - **Consistency**: All templates use the same design language
2444
- - **Flexibility**: Easy to customize entire theme
2445
- - **Accessibility**: Pre-tested color contrasts
2446
- - **Modern**: Same system as shadcn/ui components
2447
- - **Familiar**: Standard Tailwind syntax
2448
-
2449
- ## Next Steps
2450
-
2451
- - [Custom Fonts](/fonts)
2452
- - [Built-in Helpers](/helpers)
2453
- - [Image Rendering](/images)
2454
- - [Video Rendering](/video)
2455
-
2456
-
2457
-
2458
-
2459
-
2460
- ================================================================================
2461
- FILE: fonts.mdx
2462
- ================================================================================
2463
-
2464
- # Font Handling in loopwind
2465
-
2466
- The recommended way to use fonts is through `loopwind.json` - configure fonts once, use everywhere.
2467
-
2468
- ## Using Fonts from loopwind.json (Recommended)
2469
-
2470
- Configure fonts in your `_loopwind/loopwind.json` and use Tailwind classes in templates.
2471
-
2472
- ### Simple Setup
2473
-
2474
- Define font families in `_loopwind/loopwind.json` without loading custom fonts (uses system fonts):
2475
-
2476
- ```json title="_loopwind/loopwind.json"
2477
- {
2478
- "fonts": {
2479
- "sans": ["Inter", "system-ui", "-apple-system", "sans-serif"],
2480
- "serif": ["Georgia", "serif"],
2481
- "mono": ["Courier New", "monospace"]
2482
- }
2483
- }
2484
- ```
2485
-
2486
- **Template usage:**
2487
- ```tsx
2488
- export default function({ title, tw }) {
2489
- return (
2490
- <div style={tw('w-full h-full')}>
2491
- {/* Uses fonts.sans from loopwind.json */}
2492
- <h1 style={tw('font-sans text-6xl font-bold')}>
2493
- {title}
2494
- </h1>
2495
-
2496
- {/* Uses fonts.mono from loopwind.json */}
2497
- <code style={tw('font-mono text-sm')}>
2498
- {code}
2499
- </code>
2500
- </div>
2501
- );
2502
- }
2503
- ```
2504
-
2505
- **Result:** Uses system fonts, falls back to Inter for rendering.
2506
-
2507
- ### Complete Setup (With Font Files)
2508
-
2509
- Load custom font files for brand-specific typography in `_loopwind/loopwind.json`:
2510
-
2511
- ```json title="_loopwind/loopwind.json"
2512
- {
2513
- "fonts": {
2514
- "sans": {
2515
- "family": ["Inter", "system-ui", "sans-serif"],
2516
- "files": [
2517
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
2518
- { "path": "./fonts/Inter-Bold.woff", "weight": 700 }
2519
- ]
2520
- },
2521
- "mono": {
2522
- "family": ["JetBrains Mono", "monospace"],
2523
- "files": [
2524
- { "path": "./fonts/JetBrainsMono-Regular.woff", "weight": 400 }
2525
- ]
2526
- }
2527
- }
2528
- }
2529
- ```
2530
-
2531
- **Project structure:**
2532
- ```
2533
- your-project/
2534
- ├── _loopwind/
2535
- │ ├── loopwind.json
2536
- │ └── templates/
2537
- └── fonts/
2538
- ├── Inter-Regular.woff
2539
- ├── Inter-Bold.woff
2540
- └── JetBrainsMono-Regular.woff
2541
- ```
2542
-
2543
- **Template usage (same as before):**
2544
- ```tsx
2545
- <h1 style={tw('font-sans font-bold')}>
2546
- {/* Uses Inter Bold from loopwind.json */}
2547
- {title}
2548
- </h1>
2549
- ```
2550
-
2551
- **Available classes:**
2552
- - `font-sans` - Uses `fonts.sans` from loopwind.json
2553
- - `font-serif` - Uses `fonts.serif` from loopwind.json
2554
- - `font-mono` - Uses `fonts.mono` from loopwind.json
2555
-
2556
- **Supported formats:**
2557
- - ✅ **WOFF** (`.woff`) - Recommended for best compatibility
2558
- - ✅ **TTF** (`.ttf`) - Also supported
2559
- - ✅ **OTF** (`.otf`) - Also supported
2560
- - ❌ **WOFF2** (`.woff2`) - Not supported by renderer
2561
-
2562
- ## Font Loading Priority
2563
-
2564
- loopwind loads fonts in this order:
2565
-
2566
- 1. **loopwind.json fonts** (if configured with `files`)
2567
- 2. **Bundled Inter fonts** (included with CLI)
2568
-
2569
- This ensures fonts work out of the box with no configuration.
2570
-
2571
- ## Default Fonts
2572
-
2573
- If no fonts are configured, loopwind uses **Inter** (Regular 400, Bold 700) which is bundled with the CLI. This means fonts work offline with no configuration required.
2574
-
2575
- ## Best Practices
2576
-
2577
- 1. ✅ **Use loopwind.json for project-wide fonts** - Configure once, use everywhere
2578
- 2. ✅ **Use font classes** - `tw('font-sans')` instead of `fontFamily: 'Inter'`
2579
- 3. ✅ **Include fallbacks** - Always add system fonts: `["Inter", "system-ui", "sans-serif"]`
2580
- 4. ✅ **Match names** - First font in `family` array is used as the loaded font name
2581
- 5. ✅ **Relative paths** - Font paths are relative to `loopwind.json` location
2582
-
2583
- ## Examples
2584
-
2585
- ### Minimal Setup (System Fonts)
2586
- ```json title="_loopwind/loopwind.json"
2587
- {
2588
- "fonts": {
2589
- "sans": ["Inter", "-apple-system", "sans-serif"]
2590
- }
2591
- }
2592
- ```
2593
- Uses system Inter if available, falls back to Noto Sans for rendering.
2594
-
2595
- ### Brand Fonts Setup
2596
- ```json title="_loopwind/loopwind.json"
2597
- {
2598
- "fonts": {
2599
- "sans": {
2600
- "family": ["Montserrat", "sans-serif"],
2601
- "files": [
2602
- { "path": "./fonts/Montserrat-Regular.woff", "weight": 400 },
2603
- { "path": "./fonts/Montserrat-Bold.woff", "weight": 700 }
2604
- ]
2605
- }
2606
- }
2607
- }
2608
- ```
2609
- Loads and uses Montserrat for all templates.
2610
-
2611
- ### Multi-Font Setup
2612
- ```json title="_loopwind/loopwind.json"
2613
- {
2614
- "fonts": {
2615
- "sans": {
2616
- "family": ["Inter", "sans-serif"],
2617
- "files": [
2618
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
2619
- { "path": "./fonts/Inter-Bold.woff", "weight": 700 }
2620
- ]
2621
- },
2622
- "serif": {
2623
- "family": ["Playfair Display", "serif"],
2624
- "files": [
2625
- { "path": "./fonts/Playfair-Regular.woff", "weight": 400 }
2626
- ]
2627
- },
2628
- "mono": {
2629
- "family": ["Fira Code", "monospace"],
2630
- "files": [
2631
- { "path": "./fonts/FiraCode-Regular.woff", "weight": 400 }
2632
- ]
2633
- }
2634
- }
2635
- }
2636
- ```
2637
- Loads different fonts for each style class.
2638
-
2639
- ### External Font URLs
2640
- Load fonts directly from CDNs without downloading files:
2641
-
2642
- ```json title="_loopwind/loopwind.json"
2643
- {
2644
- "fonts": {
2645
- "sans": {
2646
- "family": ["Inter", "sans-serif"],
2647
- "files": [
2648
- {
2649
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-400-normal.woff",
2650
- "weight": 400
2651
- },
2652
- {
2653
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
2654
- "weight": 700
2655
- }
2656
- ]
2657
- }
2658
- }
2659
- }
2660
- ```
2661
-
2662
- You can also mix local and external fonts:
2663
-
2664
- ```json title="_loopwind/loopwind.json"
2665
- {
2666
- "fonts": {
2667
- "sans": {
2668
- "family": ["Inter", "sans-serif"],
2669
- "files": [
2670
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
2671
- {
2672
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
2673
- "weight": 700
2674
- }
2675
- ]
2676
- }
2677
- }
2678
- }
2679
- ```
2680
-
2681
- **Note:** Use WOFF format (`.woff`) for best compatibility. WOFF2 is not supported by the underlying renderer.
2682
-
2683
- ## Performance
2684
-
2685
- - ✅ **Font caching** - Fonts load once and are cached for all renders
2686
- - ✅ **Video optimization** - 90-frame video loads fonts once, not 90 times
2687
- - ✅ **No CDN delays** - Local fonts load instantly
2688
-
2689
- ## Next Steps
2690
-
2691
- - [Styling with Tailwind & shadcn/ui](/styling)
2692
- - [Built-in Helpers](/helpers)
2693
- - [Image Rendering](/images)
2694
- - [Video Rendering](/video)
2695
-
2696
-
2697
-
2698
-
2699
- ================================================================================
2700
- FILE: agents.mdx
2701
- ================================================================================
2702
-
2703
- # AI Agents
2704
-
2705
- loopwind is designed to work with AI coding assistants. Simply add the `_loopwind` folder to context.
2706
-
2707
- ## Using with AI Code Agents
2708
-
2709
- In Cursor or Claude Code (Cline), type `@_loopwind` to add the folder to context, then ask:
2710
-
2711
- ```
2712
- Generate an Open Graph image for my blog post about React hooks
2713
- ```
2714
-
2715
- The AI will read the templates and generate the appropriate command.
2716
-
2717
- ## The AGENTS.md File
2718
-
2719
- When you run `loopwind init`, a `_loopwind/AGENTS.md` file is created with:
2720
- - Essential commands
2721
- - Template helpers reference
2722
- - Common workflows
2723
-
2724
- This gives AI agents everything they need to use loopwind effectively.
2725
-
2726
- ## Example Workflows
2727
-
2728
- ### Simple Image Generation
2729
-
2730
- **Prompt:**
2731
- ```
2732
- Create a social media card using the og-image template:
2733
- - Title: "10 React Tips"
2734
- - Description: "Learn advanced patterns"
2735
- ```
2736
-
2737
- **AI generates:**
2738
- ```bash
2739
- loopwind render og-image '{"title":"10 React Tips","description":"Learn advanced patterns"}'
2740
- ```
2741
-
2742
- **Output:** `_loopwind/outputs/og-image.png`
2743
-
2744
- ### Changelog Image
2745
-
2746
- **Prompt:**
2747
- ```
2748
- Create a changelog image for 3 recent changes:
2749
- - Added video rendering support
2750
- - Improved template validation
2751
- - Fixed font loading issues
2752
- ```
2753
-
2754
- **AI generates:**
2755
- ```bash
2756
- loopwind render changelog-card '{
2757
- "title":"Version 1.2.0",
2758
- "changes":[
2759
- "Added video rendering support",
2760
- "Improved template validation",
2761
- "Fixed font loading issues"
2762
- ]
2763
- }'
2764
- ```
2765
-
2766
- **Output:** `_loopwind/outputs/changelog-card.png`
2767
-
2768
- ## Next Steps
2769
-
2770
- - [Learn about Templates](/templates)
2771
- - [Image Rendering](/images)
2772
- - [Video Rendering](/video)
2773
- - [Built-in Helpers](/helpers)
2774
-
2775
-
2776
-