loopwind 0.23.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 (497) 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 +4 -1
  32. package/dist/lib/renderer.js.map +1 -1
  33. package/dist/lib/utils.d.ts +13 -29
  34. package/dist/lib/utils.d.ts.map +1 -1
  35. package/dist/lib/utils.js +45 -155
  36. package/dist/lib/utils.js.map +1 -1
  37. package/dist/types/config.d.ts +0 -2
  38. package/dist/types/config.d.ts.map +1 -1
  39. package/dist/types/config.js.map +1 -1
  40. package/package.json +1 -15
  41. package/REGISTRY_SETUP.md +0 -363
  42. package/dist/default-templates/image/template.d.ts +0 -20
  43. package/dist/default-templates/image/template.d.ts.map +0 -1
  44. package/dist/default-templates/image/template.js +0 -18
  45. package/dist/default-templates/image/template.js.map +0 -1
  46. package/dist/default-templates/image/template.tsx +0 -20
  47. package/dist/default-templates/kitchen-sink/template.tsx +0 -64
  48. package/dist/default-templates/page/template.tsx +0 -37
  49. package/dist/default-templates/video/template.d.ts +0 -26
  50. package/dist/default-templates/video/template.d.ts.map +0 -1
  51. package/dist/default-templates/video/template.js +0 -33
  52. package/dist/default-templates/video/template.js.map +0 -1
  53. package/dist/default-templates/video/template.tsx +0 -37
  54. package/dist/default-templates/website/pages/home.tsx +0 -17
  55. package/dist/default-templates/website/parts/footer.tsx +0 -17
  56. package/dist/default-templates/website/parts/header.tsx +0 -17
  57. package/dist/default-templates/website/template.tsx +0 -17
  58. package/dist/default-templates/website-template/pages/home.tsx +0 -13
  59. package/dist/default-templates/website-template/parts/footer.tsx +0 -15
  60. package/dist/default-templates/website-template/parts/header.tsx +0 -15
  61. package/dist/default-templates/website-template/template.tsx +0 -32
  62. package/dist/lib/encode-worker.d.ts +0 -2
  63. package/dist/lib/encode-worker.d.ts.map +0 -1
  64. package/dist/lib/encode-worker.js +0 -29
  65. package/dist/lib/encode-worker.js.map +0 -1
  66. package/dist/lib/mjpeg-muxer.d.ts +0 -46
  67. package/dist/lib/mjpeg-muxer.d.ts.map +0 -1
  68. package/dist/lib/mjpeg-muxer.js +0 -513
  69. package/dist/lib/mjpeg-muxer.js.map +0 -1
  70. package/dist/lib/render-core.d.ts +0 -63
  71. package/dist/lib/render-core.d.ts.map +0 -1
  72. package/dist/lib/render-core.js +0 -65
  73. package/dist/lib/render-core.js.map +0 -1
  74. package/dist/lib/tailwind-browser.d.ts +0 -27
  75. package/dist/lib/tailwind-browser.d.ts.map +0 -1
  76. package/dist/lib/tailwind-browser.js +0 -853
  77. package/dist/lib/tailwind-browser.js.map +0 -1
  78. package/dist/lib/video-player.d.ts +0 -25
  79. package/dist/lib/video-player.d.ts.map +0 -1
  80. package/dist/lib/video-player.js +0 -392
  81. package/dist/lib/video-player.js.map +0 -1
  82. package/dist/sdk/compiler.d.ts +0 -94
  83. package/dist/sdk/compiler.d.ts.map +0 -1
  84. package/dist/sdk/compiler.js +0 -122
  85. package/dist/sdk/compiler.js.map +0 -1
  86. package/dist/sdk/edge/index.d.ts +0 -91
  87. package/dist/sdk/edge/index.d.ts.map +0 -1
  88. package/dist/sdk/edge/index.js +0 -187
  89. package/dist/sdk/edge/index.js.map +0 -1
  90. package/dist/sdk/index.d.ts +0 -62
  91. package/dist/sdk/index.d.ts.map +0 -1
  92. package/dist/sdk/index.js +0 -141
  93. package/dist/sdk/index.js.map +0 -1
  94. package/dist/sdk/preview.d.ts +0 -65
  95. package/dist/sdk/preview.d.ts.map +0 -1
  96. package/dist/sdk/preview.js +0 -262
  97. package/dist/sdk/preview.js.map +0 -1
  98. package/dist/sdk/template.d.ts +0 -162
  99. package/dist/sdk/template.d.ts.map +0 -1
  100. package/dist/sdk/template.js +0 -231
  101. package/dist/sdk/template.js.map +0 -1
  102. package/dist/sdk/workers/index.d.ts +0 -135
  103. package/dist/sdk/workers/index.d.ts.map +0 -1
  104. package/dist/sdk/workers/index.js +0 -271
  105. package/dist/sdk/workers/index.js.map +0 -1
  106. package/dist/sdk/workers/tailwind-config.d.ts +0 -48
  107. package/dist/sdk/workers/tailwind-config.d.ts.map +0 -1
  108. package/dist/sdk/workers/tailwind-config.js +0 -187
  109. package/dist/sdk/workers/tailwind-config.js.map +0 -1
  110. package/dist/sdk/workers/tailwind.d.ts +0 -9
  111. package/dist/sdk/workers/tailwind.d.ts.map +0 -1
  112. package/dist/sdk/workers/tailwind.js +0 -8
  113. package/dist/sdk/workers/tailwind.js.map +0 -1
  114. package/examples/code-editor-templates.ts +0 -173
  115. package/examples/nextjs-api/README.md +0 -180
  116. package/examples/nextjs-api/package.json +0 -21
  117. package/examples/nextjs-api/pages/api/intro-video.ts +0 -53
  118. package/examples/nextjs-api/pages/api/og-image.ts +0 -50
  119. package/examples/nextjs-template-import.ts +0 -58
  120. package/examples/sdk-video-preview.tsx +0 -120
  121. package/examples/template-compiler-workflow.ts +0 -251
  122. package/examples/visual-builder-templates.ts +0 -336
  123. package/render-examples-600x400.mjs +0 -161
  124. package/render-spring-variants-fixed.mjs +0 -60
  125. package/render-staggered-text.mjs +0 -56
  126. package/test-cloudflare-worker/README.md +0 -64
  127. package/test-cloudflare-worker/dist/README.md +0 -1
  128. package/test-cloudflare-worker/dist/index.js +0 -23743
  129. package/test-cloudflare-worker/dist/index.js.map +0 -8
  130. package/test-cloudflare-worker/package-lock.json +0 -1773
  131. package/test-cloudflare-worker/package.json +0 -25
  132. package/test-cloudflare-worker/test-sdk.mjs +0 -75
  133. package/test-cloudflare-worker/wrangler.toml +0 -14
  134. package/test-font-files.mjs +0 -72
  135. package/test-jsx-support.mjs +0 -146
  136. package/test-sdk-config.mjs +0 -454
  137. package/test-sdk-source-config.mjs +0 -427
  138. package/test-sdk-user-templates.mjs +0 -469
  139. package/test-static-debug.tsx +0 -19
  140. package/test-templates/TESTS.md +0 -63
  141. package/test-templates/config-test.mjs +0 -17
  142. package/test-templates/demo-intro-props.json +0 -4
  143. package/test-templates/run-tests.sh +0 -44
  144. package/test-templates/test-sdk.mjs +0 -139
  145. package/test-video-720p.mjs +0 -96
  146. package/test-video-breakdown.mjs +0 -98
  147. package/test-video-perf-1080.mjs +0 -67
  148. package/test-video-perf.mjs +0 -56
  149. package/test-video-props.json +0 -3
  150. package/test-worker-1080p.mjs +0 -103
  151. package/test-worker-viability.mjs +0 -140
  152. package/website/.astro/content.db +0 -0
  153. package/website/.astro/integrations/_inox-tools_astro-when/types.d.ts +0 -1
  154. package/website/.astro/integrations/astro_db/db.d.ts +0 -15
  155. package/website/.astro/settings.json +0 -5
  156. package/website/.astro/types.d.ts +0 -2
  157. package/website/DEPLOYMENT.md +0 -143
  158. package/website/OG_IMAGES.md +0 -142
  159. package/website/README.md +0 -158
  160. package/website/astro.config.mjs +0 -41
  161. package/website/dist/.gitkeep +0 -5
  162. package/website/dist/_astro/PlaygroundEditor.DzFavsm8.js +0 -26
  163. package/website/dist/_astro/VideoPreviewClient.BrajhYmh.js +0 -1
  164. package/website/dist/_astro/agents.CZXv4DCM.css +0 -1
  165. package/website/dist/_astro/client.BHSq4mdQ.js +0 -33
  166. package/website/dist/_astro/index.CTbGshLK.js +0 -9
  167. package/website/dist/_astro/jsx-runtime.BjG_zV1W.js +0 -9
  168. package/website/dist/_routes.json +0 -31
  169. package/website/dist/_worker.js/_@astrojs-ssr-adapter.mjs +0 -1033
  170. package/website/dist/_worker.js/_astro-internal_middleware.mjs +0 -40
  171. package/website/dist/_worker.js/chunks/Logo_Cud5QvBJ.mjs +0 -22
  172. package/website/dist/_worker.js/chunks/_@astro-renderers_-YVK7NHa.mjs +0 -15015
  173. package/website/dist/_worker.js/chunks/abap_BTmsHiP5.mjs +0 -1
  174. package/website/dist/_worker.js/chunks/actionscript-3_DmBelb1E.mjs +0 -1
  175. package/website/dist/_worker.js/chunks/ada_8-E0ahCN.mjs +0 -1
  176. package/website/dist/_worker.js/chunks/andromeeda_XI-CXx50.mjs +0 -1
  177. package/website/dist/_worker.js/chunks/angular-html_DKGh3gGH.mjs +0 -1
  178. package/website/dist/_worker.js/chunks/angular-ts_-qZGsJoA.mjs +0 -1
  179. package/website/dist/_worker.js/chunks/apache_ijTUt0Ee.mjs +0 -1
  180. package/website/dist/_worker.js/chunks/apex_agu1c6Sh.mjs +0 -1
  181. package/website/dist/_worker.js/chunks/apl_Bj2f7Art.mjs +0 -1
  182. package/website/dist/_worker.js/chunks/applescript_B_vXrOh3.mjs +0 -1
  183. package/website/dist/_worker.js/chunks/ara_DCEQ2rnh.mjs +0 -1
  184. package/website/dist/_worker.js/chunks/asciidoc_CGN_EkYS.mjs +0 -1
  185. package/website/dist/_worker.js/chunks/asm_BBWZgnDp.mjs +0 -1
  186. package/website/dist/_worker.js/chunks/astro/assets-service_j52rQLzU.mjs +0 -721
  187. package/website/dist/_worker.js/chunks/astro/server_CsUrSZgd.mjs +0 -2512
  188. package/website/dist/_worker.js/chunks/astro-designed-error-pages_1ELXm5Tt.mjs +0 -542
  189. package/website/dist/_worker.js/chunks/astro_Dr_hht3h.mjs +0 -1
  190. package/website/dist/_worker.js/chunks/aurora-x_9GHG8nSq.mjs +0 -1
  191. package/website/dist/_worker.js/chunks/awk_DHRvhXot.mjs +0 -1
  192. package/website/dist/_worker.js/chunks/ayu-dark_CcvqmEHE.mjs +0 -1
  193. package/website/dist/_worker.js/chunks/ballerina_C7SdeSZb.mjs +0 -1
  194. package/website/dist/_worker.js/chunks/bat_Dv4A3u45.mjs +0 -1
  195. package/website/dist/_worker.js/chunks/beancount_BfPf9Luv.mjs +0 -1
  196. package/website/dist/_worker.js/chunks/berry_B8rfM3lL.mjs +0 -1
  197. package/website/dist/_worker.js/chunks/bibtex_TcjYgtJM.mjs +0 -1
  198. package/website/dist/_worker.js/chunks/bicep_CrlFWCdN.mjs +0 -1
  199. package/website/dist/_worker.js/chunks/blade_lanKVYID.mjs +0 -1
  200. package/website/dist/_worker.js/chunks/bsl_BhppzXMB.mjs +0 -1
  201. package/website/dist/_worker.js/chunks/c_6FBALJTK.mjs +0 -1
  202. package/website/dist/_worker.js/chunks/cadence_2txU9LVE.mjs +0 -1
  203. package/website/dist/_worker.js/chunks/cairo_BkrFAIlP.mjs +0 -1
  204. package/website/dist/_worker.js/chunks/catppuccin-frappe_CkEqIYhU.mjs +0 -1
  205. package/website/dist/_worker.js/chunks/catppuccin-latte_DG4Gx_-v.mjs +0 -1
  206. package/website/dist/_worker.js/chunks/catppuccin-macchiato_Cwi3vCXf.mjs +0 -1
  207. package/website/dist/_worker.js/chunks/catppuccin-mocha_L9_OPlFX.mjs +0 -1
  208. package/website/dist/_worker.js/chunks/clarity_BEAe4Ulu.mjs +0 -1
  209. package/website/dist/_worker.js/chunks/clojure_VnUX6p2g.mjs +0 -1
  210. package/website/dist/_worker.js/chunks/cmake_0-SGkZEj.mjs +0 -1
  211. package/website/dist/_worker.js/chunks/cobol_92M_KGaE.mjs +0 -1
  212. package/website/dist/_worker.js/chunks/codeowners_CzMwskBv.mjs +0 -1
  213. package/website/dist/_worker.js/chunks/codeql_DWJZNHv1.mjs +0 -1
  214. package/website/dist/_worker.js/chunks/coffee_CQjKU2fh.mjs +0 -1
  215. package/website/dist/_worker.js/chunks/common-lisp_BBLWDpS5.mjs +0 -1
  216. package/website/dist/_worker.js/chunks/coq_hedRFV3D.mjs +0 -1
  217. package/website/dist/_worker.js/chunks/cpp_DlS1i6Zs.mjs +0 -1
  218. package/website/dist/_worker.js/chunks/crystal_D6n65fKV.mjs +0 -1
  219. package/website/dist/_worker.js/chunks/csharp_C6FCVFzc.mjs +0 -1
  220. package/website/dist/_worker.js/chunks/css_C5uJEgmJ.mjs +0 -1
  221. package/website/dist/_worker.js/chunks/csv_CtMYuuJl.mjs +0 -1
  222. package/website/dist/_worker.js/chunks/cue_BsPexqx6.mjs +0 -1
  223. package/website/dist/_worker.js/chunks/cypher_apzf6OBi.mjs +0 -1
  224. package/website/dist/_worker.js/chunks/d_DcvIRcgm.mjs +0 -1
  225. package/website/dist/_worker.js/chunks/dark-plus_C01ONtzj.mjs +0 -1
  226. package/website/dist/_worker.js/chunks/dart_WkzM5WrV.mjs +0 -1
  227. package/website/dist/_worker.js/chunks/dax_DjXAO5V4.mjs +0 -1
  228. package/website/dist/_worker.js/chunks/desktop_C92LCxdc.mjs +0 -1
  229. package/website/dist/_worker.js/chunks/diff_CVwM_9XJ.mjs +0 -1
  230. package/website/dist/_worker.js/chunks/docker_DPzgJf6Z.mjs +0 -1
  231. package/website/dist/_worker.js/chunks/dotenv_D_vgANvA.mjs +0 -1
  232. package/website/dist/_worker.js/chunks/dracula-soft_CLnUBwFm.mjs +0 -1
  233. package/website/dist/_worker.js/chunks/dracula_lBVpb6Lb.mjs +0 -1
  234. package/website/dist/_worker.js/chunks/dream-maker_DTLbzd_J.mjs +0 -1
  235. package/website/dist/_worker.js/chunks/edge_i54JYm3_.mjs +0 -1
  236. package/website/dist/_worker.js/chunks/elixir_BJCIjTu4.mjs +0 -1
  237. package/website/dist/_worker.js/chunks/elm_BbXD39-_.mjs +0 -1
  238. package/website/dist/_worker.js/chunks/emacs-lisp_pxa5cXaN.mjs +0 -1
  239. package/website/dist/_worker.js/chunks/erb_Ccjijeee.mjs +0 -1
  240. package/website/dist/_worker.js/chunks/erlang_B2VM_hi7.mjs +0 -1
  241. package/website/dist/_worker.js/chunks/everforest-dark_BxvIPBim.mjs +0 -1
  242. package/website/dist/_worker.js/chunks/everforest-light_B7VoyaJM.mjs +0 -1
  243. package/website/dist/_worker.js/chunks/fennel_D-uo7X6c.mjs +0 -1
  244. package/website/dist/_worker.js/chunks/fish_BjePoK3m.mjs +0 -1
  245. package/website/dist/_worker.js/chunks/fluent_C8fgkzLX.mjs +0 -1
  246. package/website/dist/_worker.js/chunks/fortran-fixed-form_D1pu5zrc.mjs +0 -1
  247. package/website/dist/_worker.js/chunks/fortran-free-form_CSGOhJD6.mjs +0 -1
  248. package/website/dist/_worker.js/chunks/fsharp_B0xy-A4Y.mjs +0 -1
  249. package/website/dist/_worker.js/chunks/gdresource_CWppjlHq.mjs +0 -1
  250. package/website/dist/_worker.js/chunks/gdscript_eQCHchcS.mjs +0 -1
  251. package/website/dist/_worker.js/chunks/gdshader_C4kxepX7.mjs +0 -1
  252. package/website/dist/_worker.js/chunks/genie_ACtQLcDW.mjs +0 -1
  253. package/website/dist/_worker.js/chunks/gherkin_BFp2uKUd.mjs +0 -1
  254. package/website/dist/_worker.js/chunks/git-commit_CLg9ZwMV.mjs +0 -1
  255. package/website/dist/_worker.js/chunks/git-rebase_DG8A80Nt.mjs +0 -1
  256. package/website/dist/_worker.js/chunks/github-dark-default_BI0EP2Kv.mjs +0 -1
  257. package/website/dist/_worker.js/chunks/github-dark-dimmed_a_NIC0Xb.mjs +0 -1
  258. package/website/dist/_worker.js/chunks/github-dark-high-contrast_jZGqT7hk.mjs +0 -1
  259. package/website/dist/_worker.js/chunks/github-dark_CHCDNd2O.mjs +0 -1
  260. package/website/dist/_worker.js/chunks/github-light-default_DRbOW5RG.mjs +0 -1
  261. package/website/dist/_worker.js/chunks/github-light-high-contrast_tn_kWutM.mjs +0 -1
  262. package/website/dist/_worker.js/chunks/github-light_D9brYzot.mjs +0 -1
  263. package/website/dist/_worker.js/chunks/gleam_Dmhu1oxW.mjs +0 -1
  264. package/website/dist/_worker.js/chunks/glimmer-js_BfZbXy8A.mjs +0 -1
  265. package/website/dist/_worker.js/chunks/glimmer-ts_B9QVICrD.mjs +0 -1
  266. package/website/dist/_worker.js/chunks/glsl_DD2PPwOs.mjs +0 -1
  267. package/website/dist/_worker.js/chunks/gnuplot_D2OYChUX.mjs +0 -1
  268. package/website/dist/_worker.js/chunks/go_DYGFTe3h.mjs +0 -1
  269. package/website/dist/_worker.js/chunks/graphql_B7XsT3nH.mjs +0 -1
  270. package/website/dist/_worker.js/chunks/groovy_BO12Uwkl.mjs +0 -1
  271. package/website/dist/_worker.js/chunks/hack_CB2_ztCP.mjs +0 -1
  272. package/website/dist/_worker.js/chunks/haml_CyfDcDD3.mjs +0 -1
  273. package/website/dist/_worker.js/chunks/handlebars_CfpxpWm2.mjs +0 -1
  274. package/website/dist/_worker.js/chunks/haskell_jUeC5uN5.mjs +0 -1
  275. package/website/dist/_worker.js/chunks/haxe_B6GxP1WB.mjs +0 -1
  276. package/website/dist/_worker.js/chunks/hcl_DwoHV2oh.mjs +0 -1
  277. package/website/dist/_worker.js/chunks/hjson_DV7cJRk4.mjs +0 -1
  278. package/website/dist/_worker.js/chunks/hlsl_BlFCscPI.mjs +0 -1
  279. package/website/dist/_worker.js/chunks/houston_COBFG1Mx.mjs +0 -1
  280. package/website/dist/_worker.js/chunks/html-derivative_C9pJ337h.mjs +0 -1
  281. package/website/dist/_worker.js/chunks/html_D1OkrZS5.mjs +0 -1
  282. package/website/dist/_worker.js/chunks/http_DIGXRqvJ.mjs +0 -1
  283. package/website/dist/_worker.js/chunks/hxml_DEwh9i-c.mjs +0 -1
  284. package/website/dist/_worker.js/chunks/hy_DDoIgW1K.mjs +0 -1
  285. package/website/dist/_worker.js/chunks/imba_B00zbHo4.mjs +0 -1
  286. package/website/dist/_worker.js/chunks/index_BDWR1Q-q.mjs +0 -1861
  287. package/website/dist/_worker.js/chunks/ini_D7XQA_p8.mjs +0 -1
  288. package/website/dist/_worker.js/chunks/java_B9wdFd8K.mjs +0 -1
  289. package/website/dist/_worker.js/chunks/javascript_CLsPGOON.mjs +0 -1
  290. package/website/dist/_worker.js/chunks/jinja_jarBCAN1.mjs +0 -1
  291. package/website/dist/_worker.js/chunks/jison_oGg3J708.mjs +0 -1
  292. package/website/dist/_worker.js/chunks/json5_DlZ1Kyaa.mjs +0 -1
  293. package/website/dist/_worker.js/chunks/json_DaYk_FMp.mjs +0 -1
  294. package/website/dist/_worker.js/chunks/jsonc_DlwgfSDs.mjs +0 -1
  295. package/website/dist/_worker.js/chunks/jsonl_BbCCVaZF.mjs +0 -1
  296. package/website/dist/_worker.js/chunks/jsonnet_Dt-G75xe.mjs +0 -1
  297. package/website/dist/_worker.js/chunks/jssm_BtKFTj2A.mjs +0 -1
  298. package/website/dist/_worker.js/chunks/jsx_DDx_xAZ8.mjs +0 -1
  299. package/website/dist/_worker.js/chunks/julia_CK0lv68l.mjs +0 -1
  300. package/website/dist/_worker.js/chunks/kanagawa-dragon_BldAK3Oo.mjs +0 -1
  301. package/website/dist/_worker.js/chunks/kanagawa-lotus_DVM8FX9_.mjs +0 -1
  302. package/website/dist/_worker.js/chunks/kanagawa-wave_Dpih0AKP.mjs +0 -1
  303. package/website/dist/_worker.js/chunks/kotlin_kWneB9V_.mjs +0 -1
  304. package/website/dist/_worker.js/chunks/kusto_BKVATd95.mjs +0 -1
  305. package/website/dist/_worker.js/chunks/laserwave_BqatxsVl.mjs +0 -1
  306. package/website/dist/_worker.js/chunks/latex_LVDcGBbc.mjs +0 -1
  307. package/website/dist/_worker.js/chunks/lean_W7qo-5M2.mjs +0 -1
  308. package/website/dist/_worker.js/chunks/less_DFNwJnBH.mjs +0 -1
  309. package/website/dist/_worker.js/chunks/light-plus_Dp0AoWsO.mjs +0 -1
  310. package/website/dist/_worker.js/chunks/liquid_D24qs0pc.mjs +0 -1
  311. package/website/dist/_worker.js/chunks/log_IPWMXriF.mjs +0 -1
  312. package/website/dist/_worker.js/chunks/logo_C6KaatrQ.mjs +0 -1
  313. package/website/dist/_worker.js/chunks/lua_CwnEf-T7.mjs +0 -1
  314. package/website/dist/_worker.js/chunks/luau_Br3-CXjS.mjs +0 -1
  315. package/website/dist/_worker.js/chunks/make_UBNG-kOo.mjs +0 -1
  316. package/website/dist/_worker.js/chunks/markdown_C7mhJFCm.mjs +0 -1
  317. package/website/dist/_worker.js/chunks/marko_4tchUvI7.mjs +0 -1
  318. package/website/dist/_worker.js/chunks/material-theme-darker_SKtaNEPn.mjs +0 -1
  319. package/website/dist/_worker.js/chunks/material-theme-lighter_zOX_DZCH.mjs +0 -1
  320. package/website/dist/_worker.js/chunks/material-theme-ocean_BN9WbhdC.mjs +0 -1
  321. package/website/dist/_worker.js/chunks/material-theme-palenight_DT_covjH.mjs +0 -1
  322. package/website/dist/_worker.js/chunks/material-theme_6RpeM3kc.mjs +0 -1
  323. package/website/dist/_worker.js/chunks/matlab_DCOXsPKR.mjs +0 -1
  324. package/website/dist/_worker.js/chunks/mdc_B9gb2UFP.mjs +0 -1
  325. package/website/dist/_worker.js/chunks/mdx_DGU7Nu9u.mjs +0 -1
  326. package/website/dist/_worker.js/chunks/mermaid_B69URzsZ.mjs +0 -1
  327. package/website/dist/_worker.js/chunks/min-dark_BgxifOMI.mjs +0 -1
  328. package/website/dist/_worker.js/chunks/min-light_BrPjXxUp.mjs +0 -1
  329. package/website/dist/_worker.js/chunks/mipsasm_9U-4_t7k.mjs +0 -1
  330. package/website/dist/_worker.js/chunks/mojo_B0wt7ug3.mjs +0 -1
  331. package/website/dist/_worker.js/chunks/monokai_B6Pxpoyi.mjs +0 -1
  332. package/website/dist/_worker.js/chunks/move_1eid4CyR.mjs +0 -1
  333. package/website/dist/_worker.js/chunks/narrat_Ds6-p5JZ.mjs +0 -1
  334. package/website/dist/_worker.js/chunks/nextflow_v2N1Qlqa.mjs +0 -1
  335. package/website/dist/_worker.js/chunks/nginx_Bp9Ab2NH.mjs +0 -1
  336. package/website/dist/_worker.js/chunks/night-owl_CdwOw_sc.mjs +0 -1
  337. package/website/dist/_worker.js/chunks/nim_BXGDUe53.mjs +0 -1
  338. package/website/dist/_worker.js/chunks/nix_CUig1nJH.mjs +0 -1
  339. package/website/dist/_worker.js/chunks/noop-middleware_B8fH5jha.mjs +0 -10
  340. package/website/dist/_worker.js/chunks/nord_SPoG1iae.mjs +0 -1
  341. package/website/dist/_worker.js/chunks/nushell_DJw1Lca8.mjs +0 -1
  342. package/website/dist/_worker.js/chunks/objective-c_Bktzl_CO.mjs +0 -1
  343. package/website/dist/_worker.js/chunks/objective-cpp_CP4DWdDp.mjs +0 -1
  344. package/website/dist/_worker.js/chunks/ocaml_CeEAs7bZ.mjs +0 -1
  345. package/website/dist/_worker.js/chunks/one-dark-pro_-hIwCNMi.mjs +0 -1
  346. package/website/dist/_worker.js/chunks/one-light_DSmYvJ05.mjs +0 -1
  347. package/website/dist/_worker.js/chunks/pascal_C-S_Ms_o.mjs +0 -1
  348. package/website/dist/_worker.js/chunks/perl_CKamvo15.mjs +0 -1
  349. package/website/dist/_worker.js/chunks/php_BlmcX_F3.mjs +0 -1
  350. package/website/dist/_worker.js/chunks/plastic_Ryt8tVoA.mjs +0 -1
  351. package/website/dist/_worker.js/chunks/plsql_Cb3v7cBj.mjs +0 -1
  352. package/website/dist/_worker.js/chunks/po_DZbdNRlo.mjs +0 -1
  353. package/website/dist/_worker.js/chunks/poimandres_bYmE3_5d.mjs +0 -1
  354. package/website/dist/_worker.js/chunks/polar_pJkMGwoW.mjs +0 -1
  355. package/website/dist/_worker.js/chunks/postcss_BAXSOKgk.mjs +0 -1
  356. package/website/dist/_worker.js/chunks/powerquery_oITMGN4x.mjs +0 -1
  357. package/website/dist/_worker.js/chunks/powershell_6306-xIF.mjs +0 -1
  358. package/website/dist/_worker.js/chunks/prisma_DSDxnZGz.mjs +0 -1
  359. package/website/dist/_worker.js/chunks/prolog_CxG7tjZR.mjs +0 -1
  360. package/website/dist/_worker.js/chunks/proto_CS9ByXm1.mjs +0 -1
  361. package/website/dist/_worker.js/chunks/pug_BMtLJo6U.mjs +0 -1
  362. package/website/dist/_worker.js/chunks/puppet_BfeeSzee.mjs +0 -1
  363. package/website/dist/_worker.js/chunks/purescript_BFfueNaH.mjs +0 -1
  364. package/website/dist/_worker.js/chunks/python_Cc4Faapv.mjs +0 -1
  365. package/website/dist/_worker.js/chunks/qml_C1CTJTK8.mjs +0 -1
  366. package/website/dist/_worker.js/chunks/qmldir_nG1KaqKR.mjs +0 -1
  367. package/website/dist/_worker.js/chunks/qss_Cncxk263.mjs +0 -1
  368. package/website/dist/_worker.js/chunks/r_ChR54Ihi.mjs +0 -1
  369. package/website/dist/_worker.js/chunks/racket_BDrhptDs.mjs +0 -1
  370. package/website/dist/_worker.js/chunks/raku_07OUHa0P.mjs +0 -1
  371. package/website/dist/_worker.js/chunks/razor_DIP3INLa.mjs +0 -1
  372. package/website/dist/_worker.js/chunks/red_DOPXfj-6.mjs +0 -1
  373. package/website/dist/_worker.js/chunks/reg_B64SwEDj.mjs +0 -1
  374. package/website/dist/_worker.js/chunks/regexp_ButFGoB5.mjs +0 -1
  375. package/website/dist/_worker.js/chunks/rel_BWJAWqZD.mjs +0 -1
  376. package/website/dist/_worker.js/chunks/riscv_79gXlbsF.mjs +0 -1
  377. package/website/dist/_worker.js/chunks/rose-pine-dawn_DHIjVGd3.mjs +0 -1
  378. package/website/dist/_worker.js/chunks/rose-pine-moon_t86aEbs0.mjs +0 -1
  379. package/website/dist/_worker.js/chunks/rose-pine_BHgrcDCs.mjs +0 -1
  380. package/website/dist/_worker.js/chunks/rst_D3F4Fcpj.mjs +0 -1
  381. package/website/dist/_worker.js/chunks/ruby_Cs7vM9iv.mjs +0 -1
  382. package/website/dist/_worker.js/chunks/rust_DpyRVatH.mjs +0 -1
  383. package/website/dist/_worker.js/chunks/sas_DW45xZXN.mjs +0 -1
  384. package/website/dist/_worker.js/chunks/sass_C6SiMwN_.mjs +0 -1
  385. package/website/dist/_worker.js/chunks/scala_DlZOjNZk.mjs +0 -1
  386. package/website/dist/_worker.js/chunks/scheme_D2ezSJXu.mjs +0 -1
  387. package/website/dist/_worker.js/chunks/scss_DG5Spjqu.mjs +0 -1
  388. package/website/dist/_worker.js/chunks/sdbl_ZCYaj4VN.mjs +0 -1
  389. package/website/dist/_worker.js/chunks/shaderlab_CAcRkg1_.mjs +0 -1
  390. package/website/dist/_worker.js/chunks/shellscript_BWwhkDVh.mjs +0 -1
  391. package/website/dist/_worker.js/chunks/shellsession_BfEA3juK.mjs +0 -1
  392. package/website/dist/_worker.js/chunks/slack-dark_CL3lSpCc.mjs +0 -1
  393. package/website/dist/_worker.js/chunks/slack-ochin_DdZKOQVh.mjs +0 -1
  394. package/website/dist/_worker.js/chunks/smalltalk_DgilzSui.mjs +0 -1
  395. package/website/dist/_worker.js/chunks/snazzy-light_eJU08Pz_.mjs +0 -1
  396. package/website/dist/_worker.js/chunks/solarized-dark_Dg_YQywx.mjs +0 -1
  397. package/website/dist/_worker.js/chunks/solarized-light_BnIsrA6p.mjs +0 -1
  398. package/website/dist/_worker.js/chunks/solidity_DkseH8pQ.mjs +0 -1
  399. package/website/dist/_worker.js/chunks/soy_DU7bOYoG.mjs +0 -1
  400. package/website/dist/_worker.js/chunks/sparql_BuI1DBDH.mjs +0 -1
  401. package/website/dist/_worker.js/chunks/splunk_B8Ha9Pkg.mjs +0 -1
  402. package/website/dist/_worker.js/chunks/sql_BniHwea5.mjs +0 -1
  403. package/website/dist/_worker.js/chunks/ssh-config_CkE1GuVe.mjs +0 -1
  404. package/website/dist/_worker.js/chunks/stata_Dtqpbd_l.mjs +0 -1
  405. package/website/dist/_worker.js/chunks/stylus_CXTtglzO.mjs +0 -1
  406. package/website/dist/_worker.js/chunks/svelte_BjWYcUCN.mjs +0 -1
  407. package/website/dist/_worker.js/chunks/swift_BzHql_rM.mjs +0 -1
  408. package/website/dist/_worker.js/chunks/synthwave-84_DLRNhxNA.mjs +0 -1
  409. package/website/dist/_worker.js/chunks/system-verilog_ChyInPph.mjs +0 -1
  410. package/website/dist/_worker.js/chunks/systemd_Bi9Qa2qD.mjs +0 -1
  411. package/website/dist/_worker.js/chunks/talonscript_B3sH_Y-V.mjs +0 -1
  412. package/website/dist/_worker.js/chunks/tasl_BJ5yipRs.mjs +0 -1
  413. package/website/dist/_worker.js/chunks/tcl_CoJQjNoP.mjs +0 -1
  414. package/website/dist/_worker.js/chunks/templ_CrU7Ffil.mjs +0 -1
  415. package/website/dist/_worker.js/chunks/terraform_DT9JSFpC.mjs +0 -1
  416. package/website/dist/_worker.js/chunks/tex_5PKu2yA0.mjs +0 -1
  417. package/website/dist/_worker.js/chunks/tokyo-night_Buo8OK7-.mjs +0 -1
  418. package/website/dist/_worker.js/chunks/toml_CPuXX3oc.mjs +0 -1
  419. package/website/dist/_worker.js/chunks/ts-tags_D0M_1VSH.mjs +0 -1
  420. package/website/dist/_worker.js/chunks/tsv_CuivVNot.mjs +0 -1
  421. package/website/dist/_worker.js/chunks/tsx_MkuGr8MY.mjs +0 -1
  422. package/website/dist/_worker.js/chunks/turtle_BqgEPK7f.mjs +0 -1
  423. package/website/dist/_worker.js/chunks/twig_r1G9rpYJ.mjs +0 -1
  424. package/website/dist/_worker.js/chunks/typescript_Au5buqzM.mjs +0 -1
  425. package/website/dist/_worker.js/chunks/typespec_47rhBK_z.mjs +0 -1
  426. package/website/dist/_worker.js/chunks/typst_BAtuQLh-.mjs +0 -1
  427. package/website/dist/_worker.js/chunks/v_BIvWImHg.mjs +0 -1
  428. package/website/dist/_worker.js/chunks/vala_DYEacj30.mjs +0 -1
  429. package/website/dist/_worker.js/chunks/vb_CikQuqGJ.mjs +0 -1
  430. package/website/dist/_worker.js/chunks/verilog_BQRENwI-.mjs +0 -1
  431. package/website/dist/_worker.js/chunks/vesper_DA0kvTmj.mjs +0 -1
  432. package/website/dist/_worker.js/chunks/vhdl_DHscJIyg.mjs +0 -1
  433. package/website/dist/_worker.js/chunks/viml_F2pvMwvG.mjs +0 -1
  434. package/website/dist/_worker.js/chunks/vitesse-black_D9tjNzd0.mjs +0 -1
  435. package/website/dist/_worker.js/chunks/vitesse-dark_Bnm5d0hd.mjs +0 -1
  436. package/website/dist/_worker.js/chunks/vitesse-light_CHwbyjNR.mjs +0 -1
  437. package/website/dist/_worker.js/chunks/vue-html_DyYtbbMK.mjs +0 -1
  438. package/website/dist/_worker.js/chunks/vue_DofN6juy.mjs +0 -1
  439. package/website/dist/_worker.js/chunks/vyper_CiR0m-OV.mjs +0 -1
  440. package/website/dist/_worker.js/chunks/wasm_CwIGgRGf.mjs +0 -1
  441. package/website/dist/_worker.js/chunks/wasm_jKWhg0J0.mjs +0 -1
  442. package/website/dist/_worker.js/chunks/wenyan_DKvVZKXW.mjs +0 -1
  443. package/website/dist/_worker.js/chunks/wgsl_BOWZY7yw.mjs +0 -1
  444. package/website/dist/_worker.js/chunks/wikitext_CXDhhHPy.mjs +0 -1
  445. package/website/dist/_worker.js/chunks/wolfram_ChkmGnW0.mjs +0 -1
  446. package/website/dist/_worker.js/chunks/xml_DXH3hHIu.mjs +0 -1
  447. package/website/dist/_worker.js/chunks/xsl_DuP2mFjg.mjs +0 -1
  448. package/website/dist/_worker.js/chunks/yaml_IGiEkTge.mjs +0 -1
  449. package/website/dist/_worker.js/chunks/zenscript_59iXGyNw.mjs +0 -1
  450. package/website/dist/_worker.js/chunks/zig_DKzb0zdT.mjs +0 -1
  451. package/website/dist/_worker.js/index.js +0 -61
  452. package/website/dist/_worker.js/manifest_Bk6136-u.mjs +0 -98
  453. package/website/dist/_worker.js/pages/_image.astro.mjs +0 -24
  454. package/website/dist/_worker.js/pages/agents.astro.mjs +0 -1
  455. package/website/dist/_worker.js/pages/animation.astro.mjs +0 -1
  456. package/website/dist/_worker.js/pages/api/playground/render.astro.mjs +0 -25562
  457. package/website/dist/_worker.js/pages/api/playground/templates.astro.mjs +0 -92
  458. package/website/dist/_worker.js/pages/api/raw-markdown/_---path_.astro.mjs +0 -44
  459. package/website/dist/_worker.js/pages/config.astro.mjs +0 -1
  460. package/website/dist/_worker.js/pages/fonts.astro.mjs +0 -1
  461. package/website/dist/_worker.js/pages/getting-started.astro.mjs +0 -1
  462. package/website/dist/_worker.js/pages/helpers.astro.mjs +0 -1
  463. package/website/dist/_worker.js/pages/images.astro.mjs +0 -1
  464. package/website/dist/_worker.js/pages/index.astro.mjs +0 -1
  465. package/website/dist/_worker.js/pages/llm.txt.astro.mjs +0 -1
  466. package/website/dist/_worker.js/pages/playground/_example_.astro.mjs +0 -95
  467. package/website/dist/_worker.js/pages/playground.astro.mjs +0 -1
  468. package/website/dist/_worker.js/pages/preview.astro.mjs +0 -1
  469. package/website/dist/_worker.js/pages/sdk.astro.mjs +0 -1
  470. package/website/dist/_worker.js/pages/sitemap.xml.astro.mjs +0 -1
  471. package/website/dist/_worker.js/pages/styling.astro.mjs +0 -1
  472. package/website/dist/_worker.js/pages/templates.astro.mjs +0 -1
  473. package/website/dist/_worker.js/pages/video.astro.mjs +0 -1
  474. package/website/dist/_worker.js/renderers.mjs +0 -2
  475. package/website/dist/agents/index.html +0 -54
  476. package/website/dist/animation/index.html +0 -1505
  477. package/website/dist/config/index.html +0 -186
  478. package/website/dist/fonts/index.html +0 -200
  479. package/website/dist/getting-started/index.html +0 -109
  480. package/website/dist/helpers/index.html +0 -350
  481. package/website/dist/images/index.html +0 -337
  482. package/website/dist/index.html +0 -141
  483. package/website/dist/llm.txt +0 -3626
  484. package/website/dist/playground/index.html +0 -6
  485. package/website/dist/preview/index.html +0 -113
  486. package/website/dist/robots.txt +0 -40
  487. package/website/dist/sdk/index.html +0 -1588
  488. package/website/dist/sitemap.xml +0 -76
  489. package/website/dist/styling/index.html +0 -368
  490. package/website/dist/templates/index.html +0 -76
  491. package/website/dist/video/index.html +0 -354
  492. package/website/package-lock.json +0 -8099
  493. package/website/package.json +0 -43
  494. package/website/public/.gitkeep +0 -5
  495. package/website/public/robots.txt +0 -40
  496. package/website/templates/og-image.tsx +0 -60
  497. package/website/wrangler.toml +0 -9
@@ -1,1588 +0,0 @@
1
- <!DOCTYPE html><html lang="en" data-astro-cid-mw7aashj> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="generator" content="Astro v4.16.19"><link rel="canonical" href="https://loopwind.dev/sdk/"><!-- Primary Meta Tags --><title></title><meta name="title"><meta name="description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta name="keywords" content="loopwind, image generation, video generation, React, Tailwind CSS, Satori, CLI tool, templates, shadcn/ui, AI agents, Cursor, automation, serverless, WASM, Open Graph, social media, marketing automation"><meta name="author" content="loopwind"><!-- Open Graph / Facebook --><meta property="og:type" content="website"><meta property="og:url" content="https://loopwind.dev/sdk/"><meta property="og:title"><meta property="og:description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta property="og:image" content="https://loopwind.dev/api/og/sdk"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta property="og:image:alt"><meta property="og:site_name" content="loopwind"><meta property="og:locale" content="en_US"><!-- Twitter --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://loopwind.dev/sdk/"><meta name="twitter:title"><meta name="twitter:description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta name="twitter:image" content="https://loopwind.dev/api/og/sdk"><meta name="twitter:image:alt"><meta name="twitter:creator" content="@loopwind"><meta name="twitter:site" content="@loopwind"><!-- Theme Color --><meta name="theme-color" content="#0a0a0a"><meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0a0a0a"><meta name="theme-color" media="(prefers-color-scheme: light)" content="#fafafa"><!-- Additional SEO --><meta name="robots" content="index, follow"><meta name="googlebot" content="index, follow"><meta name="language" content="English"><meta name="revisit-after" content="7 days"><meta name="rating" content="general"><!-- Mobile Web App --><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="loopwind"><!-- Structured Data (JSON-LD) --><script type="application/ld+json">{"@context":"https://schema.org","@type":"SoftwareApplication","name":"loopwind","applicationCategory":"DeveloperApplication","operatingSystem":"Cross-platform","description":"CLI-based Image & Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates.","url":"https://loopwind.dev/","author":{"@type":"Organization","name":"loopwind","url":"https://loopwind.dev/"},"offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"softwareVersion":"0.11.0","releaseNotes":"https://github.com/loopwind/loopwind/releases","screenshot":"https://loopwind.dev/api/og/sdk","featureList":["Image generation from React templates","Video generation with animations","Tailwind CSS support","shadcn/ui design system","CLI-based workflow","Serverless rendering","TypeScript support","AI Agent friendly"]}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","name":"loopwind","url":"https://loopwind.dev/","logo":"https://loopwind.dev/favicon.svg","sameAs":["https://github.com/loopwind/loopwind","https://www.npmjs.com/package/loopwind"]}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://loopwind.dev/"},{"@type":"ListItem","position":2,"name":"Sdk","item":"https://loopwind.dev/sdk"}]}</script><link rel="stylesheet" href="/_astro/agents.CZXv4DCM.css">
2
- <style>.hero[data-astro-cid-bbe6dxrz]{position:relative}html{scroll-behavior:smooth}.mobile-header[data-astro-cid-mw7aashj]{display:none}.toc-sidebar[data-astro-cid-mw7aashj]{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.1) transparent}.toc-sidebar[data-astro-cid-mw7aashj]::-webkit-scrollbar{width:4px}.toc-sidebar[data-astro-cid-mw7aashj]::-webkit-scrollbar-track{background:transparent}.toc-sidebar[data-astro-cid-mw7aashj]::-webkit-scrollbar-thumb{background-color:#ffffff1a;border-radius:2px}.toc-sidebar[data-astro-cid-mw7aashj]::-webkit-scrollbar-thumb:hover{background-color:#fff3}#copy-to-llm-btn[data-astro-cid-mw7aashj]{cursor:pointer}@media (max-width: 768px){.mobile-header[data-astro-cid-mw7aashj]{display:block}.desktop-sidebar[data-astro-cid-mw7aashj]{display:none}main[data-astro-cid-mw7aashj]{margin-left:0;margin-right:0!important;padding:5rem 1.5rem 1.5rem}.toc-sidebar[data-astro-cid-mw7aashj]{display:none}#copy-to-llm-btn[data-astro-cid-mw7aashj]{width:100%;justify-content:center}}@media (max-width: 1280px){.toc-sidebar[data-astro-cid-mw7aashj]{display:none!important}main[data-astro-cid-mw7aashj]{margin-right:0!important}}
3
- </style><script type="module">const p=document.getElementById("mobile-menu-button"),l=document.getElementById("mobile-menu"),g=document.querySelectorAll(".mobile-menu-link");p?.addEventListener("click",()=>{l?.classList.toggle("hidden")});l?.addEventListener("click",o=>{o.target===l&&l.classList.add("hidden")});g.forEach(o=>{o.addEventListener("click",()=>{l?.classList.add("hidden")})});function m(){const o=document.getElementById("toc-nav");if(!o){console.log("TOC: No toc-nav element found");return}const n=document.querySelector("article");if(!n){console.log("TOC: No article element found");return}const c=n.querySelectorAll("h2, h3");if(c.length===0){console.log("TOC: No headings found");return}console.log(`TOC: Found ${c.length} headings`);const a=document.createElement("ul");a.className="space-y-0 text-sm border-l border-border/50",c.forEach(e=>{const t=e.tagName==="H2"?2:3,r=e.id||e.textContent?.toLowerCase().replace(/[^\w]+/g,"-");!e.id&&r&&(e.id=r);const s=document.createElement("li"),d=document.createElement("a");d.href=`#${r}`,d.textContent=e.textContent,d.className=`toc-link block py-1 px-3 -ml-px border-l-2 border-transparent transition-all no-underline ${t===3?"pl-6 text-xs text-muted-foreground/70":"text-sm text-muted-foreground"} hover:text-foreground hover:border-muted-foreground/50`,s.appendChild(d),a.appendChild(s)}),o.appendChild(a);const i=new IntersectionObserver(e=>{e.forEach(t=>{const r=t.target.id,s=o.querySelector(`a[href="#${r}"]`);t.isIntersecting&&(document.querySelectorAll(".toc-link").forEach(d=>{d.classList.remove("text-foreground","border-accent","font-medium")}),s?.classList.add("text-foreground","border-accent","font-medium"))})},{rootMargin:"-100px 0px -66%",threshold:0});c.forEach(e=>{i.observe(e)})}document.addEventListener("DOMContentLoaded",m);document.addEventListener("astro:page-load",m);function u(){const o=document.getElementById("copy-to-llm-btn"),n=document.getElementById("copy-btn-text");if(!o||!n){console.log("Copy to LLM: Button not found on this page");return}console.log("Copy to LLM: Initialized"),o.addEventListener("click",async()=>{const c=window.location.pathname,a=c.replace(/^\//,"").replace(/\/$/,""),e=`/api/raw-markdown/${a===""?"index":a}`;console.log("Copy to LLM clicked: pathname=",c,"apiUrl=",e);try{n.textContent="Copying...";const t=await fetch(e);if(console.log("Response status:",t.status),!t.ok)throw new Error(`HTTP error! status: ${t.status}`);const r=await t.text();console.log("Got markdown, length:",r.length),await navigator.clipboard.writeText(r),n.textContent="Copied!",setTimeout(()=>{n.textContent="Copy to LLM"},2e3)}catch(t){console.error("Copy failed:",t),n.textContent="Failed",setTimeout(()=>{n.textContent="Copy to LLM"},2e3)}})}document.addEventListener("DOMContentLoaded",u);document.addEventListener("astro:page-load",u);
4
- </script></head> <body class="antialiased" data-astro-cid-mw7aashj> <div class="min-h-screen" data-astro-cid-mw7aashj> <!-- Mobile Header --> <header class="mobile-header fixed top-0 left-0 right-0 h-16 bg-card border-b border-border z-50 md:hidden" data-astro-cid-mw7aashj> <div class="flex items-center justify-between h-full px-4" data-astro-cid-mw7aashj> <a href="/" class="no-underline"> <h1 class="text-xl flex items-center gap-2 bg-linear-to-r from-brand-from to-brand-to bg-clip-text text-transparent"> <span class="text-2xl">➰</span> <span>loopwind</span> </h1> </a> <button id="mobile-menu-button" class="p-2 text-foreground hover:bg-accent rounded-md transition-colors" aria-label="Toggle menu" data-astro-cid-mw7aashj> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" data-astro-cid-mw7aashj> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" data-astro-cid-mw7aashj></path> </svg> </button> </div> </header> <!-- Mobile Menu Overlay --> <div id="mobile-menu" class="mobile-menu fixed inset-0 bg-background/80 backdrop-blur-sm z-40 md:hidden hidden" data-astro-cid-mw7aashj> <div class="fixed top-0 left-0 h-screen w-64 bg-card border-r border-border overflow-y-auto" data-astro-cid-mw7aashj> <div class="p-6 pt-20" data-astro-cid-mw7aashj> <nav data-astro-cid-mw7aashj> <ul class="space-y-1" data-astro-cid-mw7aashj> <li data-astro-cid-mw7aashj> <a href="/" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Home </a> </li><li data-astro-cid-mw7aashj> <a href="/getting-started" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Getting Started </a> </li><li data-astro-cid-mw7aashj> <a href="/templates" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Templates </a> </li><li data-astro-cid-mw7aashj> <a href="/images" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Images </a> </li><li data-astro-cid-mw7aashj> <a href="/video" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Video </a> </li><li data-astro-cid-mw7aashj> <a href="/preview" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Preview </a> </li><li data-astro-cid-mw7aashj> <a href="/animation" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Animation </a> </li><li data-astro-cid-mw7aashj> <a href="/helpers" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Helpers </a> </li><li data-astro-cid-mw7aashj> <a href="/styling" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Styling </a> </li><li data-astro-cid-mw7aashj> <a href="/fonts" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Fonts </a> </li><li data-astro-cid-mw7aashj> <a href="/config" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> loopwind.json </a> </li><li data-astro-cid-mw7aashj> <a href="/sdk" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> SDK </a> </li><li data-astro-cid-mw7aashj> <a href="/playground" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Playground </a> </li><li data-astro-cid-mw7aashj> <a href="/agents" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Use with AI Agents </a> </li><li data-astro-cid-mw7aashj> <a href="/llm.txt" class="mobile-menu-link block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> llm.txt ⇱ </a> </li> </ul> </nav> </div> </div> </div> <!-- Desktop Sidebar --> <aside class="desktop-sidebar fixed top-0 left-0 h-screen w-64 border-r border-border bg-card overflow-y-auto z-10" data-astro-cid-mw7aashj> <div class="p-6" data-astro-cid-mw7aashj> <div class="block mb-3" data-astro-cid-mw7aashj> <a href="/" class="no-underline"> <h1 class="text-3xl flex items-center gap-2 bg-linear-to-r from-brand-from to-brand-to bg-clip-text text-transparent"> <span class="text-4xl">➰</span> <span>loopwind</span> </h1> </a> </div> <nav data-astro-cid-mw7aashj> <ul class="space-y-0" data-astro-cid-mw7aashj> <li data-astro-cid-mw7aashj> <a href="/" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Home </a> </li><li data-astro-cid-mw7aashj> <a href="/getting-started" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Getting Started </a> </li><li data-astro-cid-mw7aashj> <a href="/templates" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Templates </a> </li><li data-astro-cid-mw7aashj> <a href="/images" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Images </a> </li><li data-astro-cid-mw7aashj> <a href="/video" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Video </a> </li><li data-astro-cid-mw7aashj> <a href="/preview" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Preview </a> </li><li data-astro-cid-mw7aashj> <a href="/animation" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Animation </a> </li><li data-astro-cid-mw7aashj> <a href="/helpers" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Helpers </a> </li><li data-astro-cid-mw7aashj> <a href="/styling" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Styling </a> </li><li data-astro-cid-mw7aashj> <a href="/fonts" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Fonts </a> </li><li data-astro-cid-mw7aashj> <a href="/config" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> loopwind.json </a> </li><li data-astro-cid-mw7aashj> <a href="/sdk" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> SDK </a> </li><li data-astro-cid-mw7aashj> <a href="/playground" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Playground </a> </li><li data-astro-cid-mw7aashj> <a href="/agents" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Use with AI Agents </a> </li><li data-astro-cid-mw7aashj> <a href="/llm.txt" class="block px-3 py-1.5 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> llm.txt ⇱ </a> </li> </ul> </nav> </div> </aside> <!-- Main content --> <main class="ml-64 mr-64" data-astro-cid-mw7aashj> <div class="p-12" data-astro-cid-mw7aashj> <button id="copy-to-llm-btn" class="mb-6 px-4 py-2 bg-accent hover:bg-accent/80 text-accent-foreground rounded-md text-sm font-medium transition-colors flex items-center gap-2" aria-label="Copy raw markdown to clipboard" data-astro-cid-mw7aashj> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" data-astro-cid-mw7aashj> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" data-astro-cid-mw7aashj></path> </svg> <span id="copy-btn-text" data-astro-cid-mw7aashj>Copy to LLM</span> </button> <article class="prose prose-invert max-w-4xl" data-astro-cid-mw7aashj> <h1 id="sdk">SDK</h1>
5
- <p>Use <strong>loopwind</strong> programmatically in your Next.js API routes, serverless functions, or Node.js applications. Perfect for building image/video generation APIs!</p>
6
- <h2 id="installation">Installation</h2>
7
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> loopwind</span></span>
8
- <span class="line"></span></code></pre>
9
- <h2 id="quick-start">Quick Start</h2>
10
- <p>Create a template file in <code>_loopwind/templates/</code> or <code>templates/</code>:</p>
11
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// _loopwind/templates/og-image/template.tsx</span></span>
12
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
13
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
14
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;OpenGraph image&#39;</span><span style="color:#E1E4E8">,</span></span>
15
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
16
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
17
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
18
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
19
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;string?&#39;</span><span style="color:#E1E4E8">,</span></span>
20
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
21
- <span class="line"><span style="color:#E1E4E8">};</span></span>
22
- <span class="line"></span>
23
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> OgImage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8"> }) {</span></span>
24
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
25
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
26
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
27
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">p</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl text-white/80&#39;</span><span style="color:#E1E4E8">)}&gt;{description}&lt;/</span><span style="color:#85E89D">p</span><span style="color:#E1E4E8">&gt;</span></span>
28
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
29
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
30
- <span class="line"><span style="color:#E1E4E8">}</span></span>
31
- <span class="line"></span></code></pre>
32
- <p>Use it in your API route:</p>
33
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
34
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> ogTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;./_loopwind/templates/og-image/template&#39;</span><span style="color:#E1E4E8">;</span></span>
35
- <span class="line"></span>
36
- <span class="line"><span style="color:#6A737D">// Load template from file</span></span>
37
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
38
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
39
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
40
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
41
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
42
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
43
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
44
- <span class="line"><span style="color:#E1E4E8">});</span></span>
45
- <span class="line"></span>
46
- <span class="line"><span style="color:#6A737D">// Render to buffer</span></span>
47
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, {</span></span>
48
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Hello World&#39;</span><span style="color:#E1E4E8">,</span></span>
49
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;Generated with loopwind SDK&#39;</span><span style="color:#E1E4E8">,</span></span>
50
- <span class="line"><span style="color:#E1E4E8">});</span></span>
51
- <span class="line"></span></code></pre>
52
- <h2 id="style-configuration">Style Configuration</h2>
53
- <p>The SDK accepts configuration through the <code>StyleConfig</code> type, which you can pass in two places:</p>
54
- <ol>
55
- <li><strong>In <code>defineTemplate()</code></strong> - For fixed theme per template</li>
56
- <li><strong>In <code>renderImage()</code>/<code>renderVideo()</code></strong> - For dynamic theming at render time</li>
57
- </ol>
58
- <p><strong>Priority:</strong> Options config overrides template config.</p>
59
- <h3 id="basic-example">Basic Example</h3>
60
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// templates/og-image.tsx</span></span>
61
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
62
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
63
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
64
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
65
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
66
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
67
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
68
- <span class="line"><span style="color:#E1E4E8">};</span></span>
69
- <span class="line"></span>
70
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> OgImage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) {</span></span>
71
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
72
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-background&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
73
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-primary&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
74
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
75
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
76
- <span class="line"><span style="color:#E1E4E8">}</span></span>
77
- <span class="line"></span></code></pre>
78
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage, </span><span style="color:#F97583">type</span><span style="color:#E1E4E8"> StyleConfig } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
79
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> ogTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;./templates/og-image&#39;</span><span style="color:#E1E4E8">;</span></span>
80
- <span class="line"></span>
81
- <span class="line"><span style="color:#6A737D">// Define your style config</span></span>
82
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> config</span><span style="color:#F97583">:</span><span style="color:#B392F0"> StyleConfig</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
83
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
84
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
85
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
86
- <span class="line"><span style="color:#E1E4E8"> text: </span><span style="color:#9ECBFF">&#39;#09090b&#39;</span><span style="color:#E1E4E8">,</span></span>
87
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
88
- <span class="line"><span style="color:#E1E4E8"> fonts: {</span></span>
89
- <span class="line"><span style="color:#E1E4E8"> sans: [</span><span style="color:#9ECBFF">&#39;Inter&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;system-ui&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;sans-serif&#39;</span><span style="color:#E1E4E8">],</span></span>
90
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
91
- <span class="line"><span style="color:#E1E4E8">};</span></span>
92
- <span class="line"></span>
93
- <span class="line"><span style="color:#6A737D">// Option 1: Pass config when defining template</span></span>
94
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
95
- <span class="line"><span style="color:#E1E4E8"> config, </span><span style="color:#6A737D">// Fixed theme for this template</span></span>
96
- <span class="line"><span style="color:#E1E4E8">});</span></span>
97
- <span class="line"></span>
98
- <span class="line"><span style="color:#6A737D">// Option 2: Pass config at render time</span></span>
99
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> }, {</span></span>
100
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
101
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
102
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#8b5cf6&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// Override template config</span></span>
103
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
104
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
105
- <span class="line"><span style="color:#E1E4E8">});</span></span>
106
- <span class="line"></span></code></pre>
107
- <h3 id="dynamic-theming">Dynamic Theming</h3>
108
- <p>Perfect for multi-tenant apps or user-customizable themes:</p>
109
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// templates/branded-og.tsx</span></span>
110
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
111
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;branded-og&#39;</span><span style="color:#E1E4E8">,</span></span>
112
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
113
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
114
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
115
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
116
- <span class="line"><span style="color:#E1E4E8"> subtitle: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
117
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
118
- <span class="line"><span style="color:#E1E4E8">};</span></span>
119
- <span class="line"></span>
120
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> BrandedOg</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8"> }) {</span></span>
121
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
122
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-background p-12 justify-between&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
123
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center gap-4&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
124
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-16 h-16 rounded-full bg-brand&#39;</span><span style="color:#E1E4E8">)} /&gt;</span></span>
125
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
126
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
127
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-7xl font-bold text-foreground mb-4&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
128
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">p</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-3xl text-muted-foreground&#39;</span><span style="color:#E1E4E8">)}&gt;{subtitle}&lt;/</span><span style="color:#85E89D">p</span><span style="color:#E1E4E8">&gt;</span></span>
129
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
130
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
131
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
132
- <span class="line"><span style="color:#E1E4E8">}</span></span>
133
- <span class="line"></span></code></pre>
134
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
135
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> brandTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;./templates/branded-og&#39;</span><span style="color:#E1E4E8">;</span></span>
136
- <span class="line"></span>
137
- <span class="line"><span style="color:#6A737D">// Load template</span></span>
138
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(brandTemplate);</span></span>
139
- <span class="line"></span>
140
- <span class="line"><span style="color:#6A737D">// Render with different brand colors for each customer</span></span>
141
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> customer1</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template,</span></span>
142
- <span class="line"><span style="color:#E1E4E8"> { title: </span><span style="color:#9ECBFF">&#39;Welcome&#39;</span><span style="color:#E1E4E8">, subtitle: </span><span style="color:#9ECBFF">&#39;Customer 1&#39;</span><span style="color:#E1E4E8"> },</span></span>
143
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
144
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
145
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
146
- <span class="line"><span style="color:#E1E4E8"> brand: </span><span style="color:#9ECBFF">&#39;#ef4444&#39;</span><span style="color:#E1E4E8">,</span></span>
147
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#fef2f2&#39;</span><span style="color:#E1E4E8">,</span></span>
148
- <span class="line"><span style="color:#E1E4E8"> foreground: </span><span style="color:#9ECBFF">&#39;#7f1d1d&#39;</span><span style="color:#E1E4E8">,</span></span>
149
- <span class="line"><span style="color:#9ECBFF"> &#39;muted-foreground&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#991b1b&#39;</span><span style="color:#E1E4E8">,</span></span>
150
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
151
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
152
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
153
- <span class="line"><span style="color:#E1E4E8">);</span></span>
154
- <span class="line"></span>
155
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> customer2</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template,</span></span>
156
- <span class="line"><span style="color:#E1E4E8"> { title: </span><span style="color:#9ECBFF">&#39;Welcome&#39;</span><span style="color:#E1E4E8">, subtitle: </span><span style="color:#9ECBFF">&#39;Customer 2&#39;</span><span style="color:#E1E4E8"> },</span></span>
157
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
158
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
159
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
160
- <span class="line"><span style="color:#E1E4E8"> brand: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
161
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#eff6ff&#39;</span><span style="color:#E1E4E8">,</span></span>
162
- <span class="line"><span style="color:#E1E4E8"> foreground: </span><span style="color:#9ECBFF">&#39;#1e3a8a&#39;</span><span style="color:#E1E4E8">,</span></span>
163
- <span class="line"><span style="color:#9ECBFF"> &#39;muted-foreground&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#1e40af&#39;</span><span style="color:#E1E4E8">,</span></span>
164
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
165
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
166
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
167
- <span class="line"><span style="color:#E1E4E8">);</span></span>
168
- <span class="line"></span></code></pre>
169
- <h3 id="full-styleconfig-example">Full StyleConfig Example</h3>
170
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { StyleConfig } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
171
- <span class="line"></span>
172
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> config</span><span style="color:#F97583">:</span><span style="color:#B392F0"> StyleConfig</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
173
- <span class="line"><span style="color:#6A737D"> // Theme colors (any custom color names)</span></span>
174
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
175
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#6366f1&#39;</span><span style="color:#E1E4E8">,</span></span>
176
- <span class="line"><span style="color:#9ECBFF"> &#39;primary-foreground&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
177
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
178
- <span class="line"><span style="color:#E1E4E8"> foreground: </span><span style="color:#9ECBFF">&#39;#0f172a&#39;</span><span style="color:#E1E4E8">,</span></span>
179
- <span class="line"><span style="color:#E1E4E8"> muted: </span><span style="color:#9ECBFF">&#39;#f1f5f9&#39;</span><span style="color:#E1E4E8">,</span></span>
180
- <span class="line"><span style="color:#9ECBFF"> &#39;muted-foreground&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#64748b&#39;</span><span style="color:#E1E4E8">,</span></span>
181
- <span class="line"><span style="color:#E1E4E8"> border: </span><span style="color:#9ECBFF">&#39;#e2e8f0&#39;</span><span style="color:#E1E4E8">,</span></span>
182
- <span class="line"><span style="color:#E1E4E8"> brand: </span><span style="color:#9ECBFF">&#39;#8b5cf6&#39;</span><span style="color:#E1E4E8">,</span></span>
183
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
184
- <span class="line"></span>
185
- <span class="line"><span style="color:#6A737D"> // Font families</span></span>
186
- <span class="line"><span style="color:#E1E4E8"> fonts: {</span></span>
187
- <span class="line"><span style="color:#E1E4E8"> sans: [</span><span style="color:#9ECBFF">&#39;Inter&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;sans-serif&#39;</span><span style="color:#E1E4E8">],</span></span>
188
- <span class="line"><span style="color:#E1E4E8"> serif: [</span><span style="color:#9ECBFF">&#39;Playfair Display&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;serif&#39;</span><span style="color:#E1E4E8">],</span></span>
189
- <span class="line"><span style="color:#E1E4E8"> mono: [</span><span style="color:#9ECBFF">&#39;JetBrains Mono&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;monospace&#39;</span><span style="color:#E1E4E8">],</span></span>
190
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
191
- <span class="line"></span>
192
- <span class="line"><span style="color:#6A737D"> // Custom tokens (optional)</span></span>
193
- <span class="line"><span style="color:#E1E4E8"> tokens: {</span></span>
194
- <span class="line"><span style="color:#E1E4E8"> borderRadius: {</span></span>
195
- <span class="line"><span style="color:#E1E4E8"> sm: </span><span style="color:#9ECBFF">&#39;0.25rem&#39;</span><span style="color:#E1E4E8">,</span></span>
196
- <span class="line"><span style="color:#E1E4E8"> md: </span><span style="color:#9ECBFF">&#39;0.375rem&#39;</span><span style="color:#E1E4E8">,</span></span>
197
- <span class="line"><span style="color:#E1E4E8"> lg: </span><span style="color:#9ECBFF">&#39;0.5rem&#39;</span><span style="color:#E1E4E8">,</span></span>
198
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
199
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
200
- <span class="line"><span style="color:#E1E4E8">};</span></span>
201
- <span class="line"></span></code></pre>
202
- <h3 id="font-files">Font Files</h3>
203
- <p>You can load custom fonts from local paths or external URLs:</p>
204
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> config</span><span style="color:#F97583">:</span><span style="color:#B392F0"> StyleConfig</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
205
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
206
- <span class="line"><span style="color:#E1E4E8"> text: </span><span style="color:#9ECBFF">&#39;#000000&#39;</span><span style="color:#E1E4E8">,</span></span>
207
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
208
- <span class="line"><span style="color:#E1E4E8"> fonts: {</span></span>
209
- <span class="line"><span style="color:#E1E4E8"> sans: {</span></span>
210
- <span class="line"><span style="color:#E1E4E8"> family: [</span><span style="color:#9ECBFF">&#39;Inter&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;sans-serif&#39;</span><span style="color:#E1E4E8">],</span></span>
211
- <span class="line"><span style="color:#E1E4E8"> files: [</span></span>
212
- <span class="line"><span style="color:#6A737D"> // Local font file (relative to your project)</span></span>
213
- <span class="line"><span style="color:#E1E4E8"> { path: </span><span style="color:#9ECBFF">&#39;./fonts/Inter-Regular.woff&#39;</span><span style="color:#E1E4E8">, weight: </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8"> },</span></span>
214
- <span class="line"><span style="color:#6A737D"> // External URL</span></span>
215
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
216
- <span class="line"><span style="color:#E1E4E8"> path: </span><span style="color:#9ECBFF">&#39;https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff&#39;</span><span style="color:#E1E4E8">,</span></span>
217
- <span class="line"><span style="color:#E1E4E8"> weight: </span><span style="color:#79B8FF">700</span></span>
218
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
219
- <span class="line"><span style="color:#E1E4E8"> ]</span></span>
220
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
221
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
222
- <span class="line"><span style="color:#E1E4E8">};</span></span>
223
- <span class="line"></span>
224
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
225
- <span class="line"><span style="color:#E1E4E8"> config,</span></span>
226
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
227
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;font-sans&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
228
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;font-bold&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">Bold uses weight </span><span style="color:#79B8FF">700</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
229
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;font-normal&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">Normal uses weight </span><span style="color:#79B8FF">400</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
230
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
231
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
232
- <span class="line"><span style="color:#E1E4E8">});</span></span>
233
- <span class="line"></span></code></pre>
234
- <p><strong>Supported formats:</strong></p>
235
- <ul>
236
- <li>✅ WOFF (<code>.woff</code>)</li>
237
- <li>✅ TTF (<code>.ttf</code>)</li>
238
- <li>✅ OTF (<code>.otf</code>)</li>
239
- <li>❌ WOFF2 (not supported)</li>
240
- </ul>
241
- <h3 id="hardcoded-colors">Hardcoded Colors</h3>
242
- <p>You can hardcode colors directly in your template using Tailwind arbitrary values:</p>
243
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// templates/og-image.tsx</span></span>
244
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
245
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
246
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
247
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
248
- <span class="line"><span style="color:#E1E4E8"> props: { title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8"> },</span></span>
249
- <span class="line"><span style="color:#E1E4E8">};</span></span>
250
- <span class="line"></span>
251
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> OgImage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) {</span></span>
252
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
253
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;bg-[#3B82F6] text-white p-12&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
254
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
255
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
256
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
257
- <span class="line"><span style="color:#E1E4E8">}</span></span>
258
- <span class="line"></span></code></pre>
259
- <h2 id="core-functions">Core Functions</h2>
260
- <h3 id="definetemplate">defineTemplate()</h3>
261
- <p>Load a template from a separate file (standard loopwind format):</p>
262
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// _loopwind/templates/my-template/template.tsx</span></span>
263
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
264
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">,</span></span>
265
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
266
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
267
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
268
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
269
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
270
- <span class="line"><span style="color:#E1E4E8">};</span></span>
271
- <span class="line"></span>
272
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> MyTemplate</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) {</span></span>
273
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
274
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-background&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
275
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-primary&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
276
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
277
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
278
- <span class="line"><span style="color:#E1E4E8">}</span></span>
279
- <span class="line"></span></code></pre>
280
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
281
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> myTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;./_loopwind/templates/my-template/template&#39;</span><span style="color:#E1E4E8">;</span></span>
282
- <span class="line"></span>
283
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(myTemplate, {</span></span>
284
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
285
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
286
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
287
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
288
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
289
- <span class="line"><span style="color:#E1E4E8"> fonts: {</span></span>
290
- <span class="line"><span style="color:#E1E4E8"> sans: [</span><span style="color:#9ECBFF">&#39;Inter&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;sans-serif&#39;</span><span style="color:#E1E4E8">],</span></span>
291
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
292
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
293
- <span class="line"><span style="color:#E1E4E8">});</span></span>
294
- <span class="line"></span></code></pre>
295
- <p><strong>Benefits:</strong></p>
296
- <ul>
297
- <li>✅ Reuse templates between CLI and SDK</li>
298
- <li>✅ Clean separation of concerns</li>
299
- <li>✅ Full JSX support with <code>&lt;div&gt;</code>, <code>&lt;h1&gt;</code>, etc.</li>
300
- <li>✅ Standard loopwind format</li>
301
- </ul>
302
- <h3 id="renderimage">renderImage()</h3>
303
- <p>Render an image template to a Buffer:</p>
304
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
305
- <span class="line"></span>
306
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> buffer</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, props, {</span></span>
307
- <span class="line"><span style="color:#E1E4E8"> format: </span><span style="color:#9ECBFF">&#39;png&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// &#39;png&#39;, &#39;jpeg&#39;, &#39;jpg&#39;, &#39;webp&#39;, &#39;svg&#39;</span></span>
308
- <span class="line"><span style="color:#E1E4E8"> quality: </span><span style="color:#79B8FF">92</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// JPEG/WebP quality (1-100)</span></span>
309
- <span class="line"><span style="color:#E1E4E8"> config: { </span><span style="color:#6A737D">// Optional: Override template config</span></span>
310
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
311
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#8b5cf6&#39;</span><span style="color:#E1E4E8">,</span></span>
312
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
313
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
314
- <span class="line"><span style="color:#E1E4E8">});</span></span>
315
- <span class="line"></span></code></pre>
316
- <p><strong>Supported formats:</strong></p>
317
- <ul>
318
- <li><code>png</code> - Best for graphics with transparency</li>
319
- <li><code>jpeg</code>/<code>jpg</code> - Smaller file size for photos</li>
320
- <li><code>webp</code> - Modern format, good compression</li>
321
- <li><code>svg</code> - Vector format, infinitely scalable</li>
322
- </ul>
323
- <h3 id="rendervideo">renderVideo()</h3>
324
- <p>Render a video template to an MP4 Buffer:</p>
325
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
326
- <span class="line"></span>
327
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> buffer</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(template, props, {</span></span>
328
- <span class="line"><span style="color:#E1E4E8"> quality: </span><span style="color:#79B8FF">23</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// H.264 quality (0-51, lower = better)</span></span>
329
- <span class="line"><span style="color:#B392F0"> onProgress</span><span style="color:#E1E4E8">: (</span><span style="color:#FFAB70">frame</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">total</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">phase</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
330
- <span class="line"><span style="color:#E1E4E8"> console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">`${</span><span style="color:#E1E4E8">phase</span><span style="color:#9ECBFF">}: ${</span><span style="color:#E1E4E8">frame</span><span style="color:#9ECBFF">}/${</span><span style="color:#E1E4E8">total</span><span style="color:#9ECBFF">}`</span><span style="color:#E1E4E8">);</span></span>
331
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
332
- <span class="line"><span style="color:#E1E4E8"> config: { </span><span style="color:#6A737D">// Optional: Override template config</span></span>
333
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
334
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#8b5cf6&#39;</span><span style="color:#E1E4E8">,</span></span>
335
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
336
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
337
- <span class="line"><span style="color:#E1E4E8">});</span></span>
338
- <span class="line"></span></code></pre>
339
- <h2 id="template-organization">Template Organization</h2>
340
- <h3 id="project-structure">Project Structure</h3>
341
- <p>Recommended structure for using templates in your project:</p>
342
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="plaintext"><code><span class="line"><span>my-project/</span></span>
343
- <span class="line"><span>├── _loopwind/</span></span>
344
- <span class="line"><span>│ ├── loopwind.json # Optional: shared config</span></span>
345
- <span class="line"><span>│ └── templates/</span></span>
346
- <span class="line"><span>│ ├── og-image/</span></span>
347
- <span class="line"><span>│ │ └── template.tsx # Image template</span></span>
348
- <span class="line"><span>│ └── intro-video/</span></span>
349
- <span class="line"><span>│ └── template.tsx # Video template</span></span>
350
- <span class="line"><span>├── pages/</span></span>
351
- <span class="line"><span>│ └── api/</span></span>
352
- <span class="line"><span>│ ├── og-image.ts # API route using og-image</span></span>
353
- <span class="line"><span>│ └── intro-video.ts # API route using intro-video</span></span>
354
- <span class="line"><span>└── package.json</span></span>
355
- <span class="line"><span></span></span></code></pre>
356
- <p>Or with a top-level <code>templates/</code> folder (like in Astro):</p>
357
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="plaintext"><code><span class="line"><span>my-project/</span></span>
358
- <span class="line"><span>├── templates/</span></span>
359
- <span class="line"><span>│ ├── og-image.tsx # Image template</span></span>
360
- <span class="line"><span>│ └── intro-video.tsx # Video template</span></span>
361
- <span class="line"><span>├── src/</span></span>
362
- <span class="line"><span>│ └── pages/</span></span>
363
- <span class="line"><span>│ └── api/</span></span>
364
- <span class="line"><span>│ └── og/</span></span>
365
- <span class="line"><span>│ └── [slug].ts # API route</span></span>
366
- <span class="line"><span>└── tsconfig.json # With path aliases</span></span>
367
- <span class="line"><span></span></span></code></pre>
368
- <h3 id="path-aliases-recommended">Path Aliases (Recommended)</h3>
369
- <p>Use TypeScript path aliases for cleaner imports:</p>
370
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="json"><code><span class="line"><span style="color:#6A737D">// tsconfig.json</span></span>
371
- <span class="line"><span style="color:#E1E4E8">{</span></span>
372
- <span class="line"><span style="color:#79B8FF"> &quot;compilerOptions&quot;</span><span style="color:#E1E4E8">: {</span></span>
373
- <span class="line"><span style="color:#79B8FF"> &quot;baseUrl&quot;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&quot;.&quot;</span><span style="color:#E1E4E8">,</span></span>
374
- <span class="line"><span style="color:#79B8FF"> &quot;paths&quot;</span><span style="color:#E1E4E8">: {</span></span>
375
- <span class="line"><span style="color:#79B8FF"> &quot;@templates/*&quot;</span><span style="color:#E1E4E8">: [</span><span style="color:#9ECBFF">&quot;_loopwind/templates/*&quot;</span><span style="color:#E1E4E8">],</span></span>
376
- <span class="line"><span style="color:#79B8FF"> &quot;@/*&quot;</span><span style="color:#E1E4E8">: [</span><span style="color:#9ECBFF">&quot;src/*&quot;</span><span style="color:#E1E4E8">]</span></span>
377
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
378
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
379
- <span class="line"><span style="color:#E1E4E8">}</span></span>
380
- <span class="line"></span></code></pre>
381
- <p>Then import with clean paths:</p>
382
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> ogTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;@templates/og-image/template&#39;</span><span style="color:#E1E4E8">;</span></span>
383
- <span class="line"></span></code></pre>
384
- <h3 id="sharing-templates">Sharing Templates</h3>
385
- <p>Templates are <strong>portable</strong> - copy the template folder to share between projects:</p>
386
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#6A737D"># Copy template to another project</span></span>
387
- <span class="line"><span style="color:#B392F0">cp</span><span style="color:#79B8FF"> -r</span><span style="color:#9ECBFF"> _loopwind/templates/og-image</span><span style="color:#9ECBFF"> ../other-project/_loopwind/templates/</span></span>
388
- <span class="line"></span>
389
- <span class="line"><span style="color:#6A737D"># Or publish as npm package</span></span>
390
- <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> publish</span><span style="color:#9ECBFF"> loopwind-templates</span></span>
391
- <span class="line"></span></code></pre>
392
- <h2 id="advanced-user-generated-templates">Advanced: User-Generated Templates</h2>
393
- <p>Build template editors where users create templates without writing code.</p>
394
- <h3 id="code-editor-jsx-pre-compilation">Code Editor (JSX Pre-compilation)</h3>
395
- <p>For code editors where users write JSX templates, use a two-step workflow:</p>
396
- <p><strong>Step 1: Admin Panel - Compile JSX</strong></p>
397
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// app/admin/templates/save/route.ts</span></span>
398
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { compileTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/compiler&#39;</span><span style="color:#E1E4E8">;</span></span>
399
- <span class="line"></span>
400
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> POST</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Request</span><span style="color:#E1E4E8">) {</span></span>
401
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">templateId</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">jsxCode</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> req.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">();</span></span>
402
- <span class="line"></span>
403
- <span class="line"><span style="color:#6A737D"> // User wrote JSX in Monaco Editor, CodeMirror, etc.</span></span>
404
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> jsxCode</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> `</span></span>
405
- <span class="line"><span style="color:#9ECBFF"> export const meta = {</span></span>
406
- <span class="line"><span style="color:#9ECBFF"> name: &#39;user-card&#39;,</span></span>
407
- <span class="line"><span style="color:#9ECBFF"> type: &#39;image&#39;,</span></span>
408
- <span class="line"><span style="color:#9ECBFF"> size: { width: 1200, height: 630 }</span></span>
409
- <span class="line"><span style="color:#9ECBFF"> };</span></span>
410
- <span class="line"></span>
411
- <span class="line"><span style="color:#9ECBFF"> export default ({ tw, title, description }) =&gt; (</span></span>
412
- <span class="line"><span style="color:#9ECBFF"> &lt;div style={tw(&#39;flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12&#39;)}&gt;</span></span>
413
- <span class="line"><span style="color:#9ECBFF"> &lt;h1 style={tw(&#39;text-6xl font-bold text-white&#39;)}&gt;{title}&lt;/h1&gt;</span></span>
414
- <span class="line"><span style="color:#9ECBFF"> &lt;p style={tw(&#39;text-2xl text-white/80&#39;)}&gt;{description}&lt;/p&gt;</span></span>
415
- <span class="line"><span style="color:#9ECBFF"> &lt;/div&gt;</span></span>
416
- <span class="line"><span style="color:#9ECBFF"> );</span></span>
417
- <span class="line"><span style="color:#9ECBFF"> `</span><span style="color:#E1E4E8">;</span></span>
418
- <span class="line"></span>
419
- <span class="line"><span style="color:#F97583"> try</span><span style="color:#E1E4E8"> {</span></span>
420
- <span class="line"><span style="color:#6A737D"> // Compile JSX to JavaScript</span></span>
421
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> compiled</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> compileTemplate</span><span style="color:#E1E4E8">(jsxCode);</span></span>
422
- <span class="line"></span>
423
- <span class="line"><span style="color:#6A737D"> // Save compiled version to database</span></span>
424
- <span class="line"><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> db.templates.</span><span style="color:#B392F0">update</span><span style="color:#E1E4E8">(templateId, {</span></span>
425
- <span class="line"><span style="color:#E1E4E8"> sourceCode: compiled,</span></span>
426
- <span class="line"><span style="color:#E1E4E8"> updatedAt: </span><span style="color:#F97583">new</span><span style="color:#B392F0"> Date</span><span style="color:#E1E4E8">()</span></span>
427
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
428
- <span class="line"></span>
429
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> Response.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({ success: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8"> });</span></span>
430
- <span class="line"><span style="color:#E1E4E8"> } </span><span style="color:#F97583">catch</span><span style="color:#E1E4E8"> (error) {</span></span>
431
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> Response.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({</span></span>
432
- <span class="line"><span style="color:#E1E4E8"> error: </span><span style="color:#9ECBFF">&#39;Compilation failed&#39;</span><span style="color:#E1E4E8">,</span></span>
433
- <span class="line"><span style="color:#E1E4E8"> message: error.message</span></span>
434
- <span class="line"><span style="color:#E1E4E8"> }, { status: </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8"> });</span></span>
435
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
436
- <span class="line"><span style="color:#E1E4E8">}</span></span>
437
- <span class="line"></span></code></pre>
438
- <p><strong>Step 2: Production API - Render Pre-compiled Templates</strong></p>
439
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// app/api/render/route.ts</span></span>
440
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplateFromSource, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
441
- <span class="line"></span>
442
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> POST</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Request</span><span style="color:#E1E4E8">) {</span></span>
443
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">templateId</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">props</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> req.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">();</span></span>
444
- <span class="line"></span>
445
- <span class="line"><span style="color:#6A737D"> // Load pre-compiled template from database</span></span>
446
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> db.templates.</span><span style="color:#B392F0">findById</span><span style="color:#E1E4E8">(templateId);</span></span>
447
- <span class="line"></span>
448
- <span class="line"><span style="color:#6A737D"> // No Babel needed! Already compiled</span></span>
449
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> templateDef</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromSource</span><span style="color:#E1E4E8">(template.sourceCode, {</span></span>
450
- <span class="line"><span style="color:#E1E4E8"> config: { colors: { primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8"> } }</span></span>
451
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
452
- <span class="line"></span>
453
- <span class="line"><span style="color:#6A737D"> // Render</span></span>
454
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(templateDef, props);</span></span>
455
- <span class="line"></span>
456
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(png, {</span></span>
457
- <span class="line"><span style="color:#E1E4E8"> headers: { </span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8"> }</span></span>
458
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
459
- <span class="line"><span style="color:#E1E4E8">}</span></span>
460
- <span class="line"></span></code></pre>
461
- <p><strong>✨ Benefits:</strong></p>
462
- <ul>
463
- <li>Users write natural JSX syntax (<code>&lt;div&gt;</code>, <code>&lt;h1&gt;</code>) in admin panel</li>
464
- <li>JSX compiled once at save time (not every render)</li>
465
- <li>Production bundle <strong>36 MB smaller</strong> (no @babel/standalone)</li>
466
- <li>Faster cold starts in Vercel/Lambda</li>
467
- <li>Syntax errors caught early (at save, not production)</li>
468
- </ul>
469
- <p><strong>⚠️ Security Warning:</strong></p>
470
- <p><code>defineTemplateFromSource()</code> uses <code>new Function()</code> to evaluate code strings. <strong>Only use with trusted sources!</strong> Never pass untrusted user input directly.</p>
471
- <p><strong>Safe use cases:</strong></p>
472
- <ul>
473
- <li>Admin-created templates stored in database</li>
474
- <li>Templates from authenticated, trusted users</li>
475
- <li>Code editor with validation/sandboxing</li>
476
- </ul>
477
- <p><strong>Template format:</strong></p>
478
- <ul>
479
- <li>Must export <code>meta</code> object with <code>name</code>, <code>size</code>, and optional <code>type</code>/<code>video</code></li>
480
- <li>Must have <code>export default</code> render function</li>
481
- <li>Supports JSX syntax (compile with <code>compileTemplate()</code> first)</li>
482
- <li>Props use curly braces: <code>style={tw(&#39;...&#39;)}</code>, <code>{title}</code></li>
483
- </ul>
484
- <h3 id="visual-builder-definetemplatefromschema">Visual Builder (defineTemplateFromSchema)</h3>
485
- <p>For drag-and-drop template builders with JSON schemas:</p>
486
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplateFromSchema, renderImage, </span><span style="color:#F97583">type</span><span style="color:#E1E4E8"> TemplateSchema } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
487
- <span class="line"></span>
488
- <span class="line"><span style="color:#6A737D">// Schema generated by visual builder UI</span></span>
489
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> schema</span><span style="color:#F97583">:</span><span style="color:#B392F0"> TemplateSchema</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
490
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;visual-card&#39;</span><span style="color:#E1E4E8">,</span></span>
491
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
492
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
493
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
494
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
495
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
496
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
497
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
498
- <span class="line"><span style="color:#E1E4E8"> elements: [</span></span>
499
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
500
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
501
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-background p-12 justify-center&#39;</span><span style="color:#E1E4E8">,</span></span>
502
- <span class="line"><span style="color:#E1E4E8"> children: [</span></span>
503
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
504
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">,</span></span>
505
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-primary mb-4&#39;</span><span style="color:#E1E4E8">,</span></span>
506
- <span class="line"><span style="color:#E1E4E8"> content: </span><span style="color:#9ECBFF">&#39;{{title}}&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// Variable interpolation</span></span>
507
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
508
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
509
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;p&#39;</span><span style="color:#E1E4E8">,</span></span>
510
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;text-2xl text-gray-600&#39;</span><span style="color:#E1E4E8">,</span></span>
511
- <span class="line"><span style="color:#E1E4E8"> content: </span><span style="color:#9ECBFF">&#39;{{description}}&#39;</span><span style="color:#E1E4E8">,</span></span>
512
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
513
- <span class="line"><span style="color:#E1E4E8"> ],</span></span>
514
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
515
- <span class="line"><span style="color:#E1E4E8"> ],</span></span>
516
- <span class="line"><span style="color:#E1E4E8">};</span></span>
517
- <span class="line"></span>
518
- <span class="line"><span style="color:#6A737D">// Create template from schema</span></span>
519
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromSchema</span><span style="color:#E1E4E8">(schema);</span></span>
520
- <span class="line"></span>
521
- <span class="line"><span style="color:#6A737D">// Render with props</span></span>
522
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, {</span></span>
523
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Built Visually&#39;</span><span style="color:#E1E4E8">,</span></span>
524
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;No code required!&#39;</span></span>
525
- <span class="line"><span style="color:#E1E4E8">});</span></span>
526
- <span class="line"></span></code></pre>
527
- <p><strong>Schema Features:</strong></p>
528
- <ul>
529
- <li><strong>Elements:</strong> Supports <code>div</code>, <code>span</code>, <code>h1</code>-<code>h6</code>, <code>p</code>, <code>img</code></li>
530
- <li><strong>Styling:</strong> Use <code>className</code> for Tailwind classes or <code>style</code> for inline styles</li>
531
- <li><strong>Variables:</strong> Use <code>{{variableName}}</code> in <code>content</code> or <code>src</code> for dynamic values</li>
532
- <li><strong>Nesting:</strong> Unlimited nested <code>children</code> arrays</li>
533
- <li><strong>Type-safe:</strong> Full TypeScript support with <code>TemplateSchema</code> type</li>
534
- </ul>
535
- <p><strong>Example with dynamic branding:</strong></p>
536
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> brandedSchema</span><span style="color:#F97583">:</span><span style="color:#B392F0"> TemplateSchema</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
537
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;branded&#39;</span><span style="color:#E1E4E8">,</span></span>
538
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
539
- <span class="line"><span style="color:#E1E4E8"> elements: [</span></span>
540
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
541
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
542
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;flex w-full h-full bg-brand-bg p-12&#39;</span><span style="color:#E1E4E8">,</span></span>
543
- <span class="line"><span style="color:#E1E4E8"> children: [</span></span>
544
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
545
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
546
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;w-16 h-16 rounded-full bg-brand-primary&#39;</span><span style="color:#E1E4E8">,</span></span>
547
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
548
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
549
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">,</span></span>
550
- <span class="line"><span style="color:#E1E4E8"> className: </span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-brand-text&#39;</span><span style="color:#E1E4E8">,</span></span>
551
- <span class="line"><span style="color:#E1E4E8"> content: </span><span style="color:#9ECBFF">&#39;{{companyName}}&#39;</span><span style="color:#E1E4E8">,</span></span>
552
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
553
- <span class="line"><span style="color:#E1E4E8"> ],</span></span>
554
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
555
- <span class="line"><span style="color:#E1E4E8"> ],</span></span>
556
- <span class="line"><span style="color:#E1E4E8">};</span></span>
557
- <span class="line"></span>
558
- <span class="line"><span style="color:#6A737D">// Render with different brands</span></span>
559
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> brand1</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(</span></span>
560
- <span class="line"><span style="color:#B392F0"> defineTemplateFromSchema</span><span style="color:#E1E4E8">({</span></span>
561
- <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">brandedSchema,</span></span>
562
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
563
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
564
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-primary&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
565
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-bg&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#eff6ff&#39;</span><span style="color:#E1E4E8">,</span></span>
566
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-text&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#1e3a8a&#39;</span><span style="color:#E1E4E8">,</span></span>
567
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
568
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
569
- <span class="line"><span style="color:#E1E4E8"> }),</span></span>
570
- <span class="line"><span style="color:#E1E4E8"> { companyName: </span><span style="color:#9ECBFF">&#39;TechCorp&#39;</span><span style="color:#E1E4E8"> }</span></span>
571
- <span class="line"><span style="color:#E1E4E8">);</span></span>
572
- <span class="line"></span>
573
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> brand2</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(</span></span>
574
- <span class="line"><span style="color:#B392F0"> defineTemplateFromSchema</span><span style="color:#E1E4E8">({</span></span>
575
- <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">brandedSchema,</span></span>
576
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
577
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
578
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-primary&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#8b5cf6&#39;</span><span style="color:#E1E4E8">,</span></span>
579
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-bg&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#faf5ff&#39;</span><span style="color:#E1E4E8">,</span></span>
580
- <span class="line"><span style="color:#9ECBFF"> &#39;brand-text&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#581c87&#39;</span><span style="color:#E1E4E8">,</span></span>
581
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
582
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
583
- <span class="line"><span style="color:#E1E4E8"> }),</span></span>
584
- <span class="line"><span style="color:#E1E4E8"> { companyName: </span><span style="color:#9ECBFF">&#39;DesignStudio&#39;</span><span style="color:#E1E4E8"> }</span></span>
585
- <span class="line"><span style="color:#E1E4E8">);</span></span>
586
- <span class="line"></span></code></pre>
587
- <p><strong>Benefits:</strong></p>
588
- <ul>
589
- <li>✅ <strong>Safe</strong> - No code execution, just JSON</li>
590
- <li>✅ <strong>User-friendly</strong> - Perfect for drag-and-drop builders</li>
591
- <li>✅ <strong>Dynamic</strong> - Variable interpolation with <code>{{}}</code> syntax</li>
592
- <li>✅ <strong>Flexible</strong> - Support images, styles, nesting</li>
593
- <li>✅ <strong>Type-safe</strong> - Full TypeScript support</li>
594
- </ul>
595
- <h2 id="nextjs-api-routes">Next.js API Routes</h2>
596
- <h3 id="image-generation">Image Generation</h3>
597
- <p>Create your template:</p>
598
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// _loopwind/templates/og-image/template.tsx</span></span>
599
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
600
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
601
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
602
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
603
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
604
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
605
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
606
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
607
- <span class="line"><span style="color:#E1E4E8">};</span></span>
608
- <span class="line"></span>
609
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> OgImage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8"> }) {</span></span>
610
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
611
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
612
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center gap-4&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
613
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-12 h-12 rounded-full bg-white/20&#39;</span><span style="color:#E1E4E8">)} /&gt;</span></span>
614
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">span</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-white/80 text-2xl&#39;</span><span style="color:#E1E4E8">)}&gt;yoursite.com&lt;/</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">&gt;</span></span>
615
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
616
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
617
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white mb-4&#39;</span><span style="color:#E1E4E8">)}&gt;{title}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
618
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">p</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl text-white/80&#39;</span><span style="color:#E1E4E8">)}&gt;{description}&lt;/</span><span style="color:#85E89D">p</span><span style="color:#E1E4E8">&gt;</span></span>
619
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
620
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
621
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
622
- <span class="line"><span style="color:#E1E4E8">}</span></span>
623
- <span class="line"></span></code></pre>
624
- <p>Use it in your API route:</p>
625
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// pages/api/og-image.ts</span></span>
626
- <span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { NextApiRequest, NextApiResponse } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;next&#39;</span><span style="color:#E1E4E8">;</span></span>
627
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
628
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> ogTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;../../_loopwind/templates/og-image/template&#39;</span><span style="color:#E1E4E8">;</span></span>
629
- <span class="line"></span>
630
- <span class="line"><span style="color:#6A737D">// Load template once at module level (cached)</span></span>
631
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(ogTemplate);</span></span>
632
- <span class="line"></span>
633
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiRequest</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiResponse</span><span style="color:#E1E4E8">) {</span></span>
634
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Welcome&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">description</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Generated with loopwind&#39;</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
635
- <span class="line"></span>
636
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, {</span></span>
637
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
638
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(description),</span></span>
639
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
640
- <span class="line"></span>
641
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8">);</span></span>
642
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Cache-Control&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;s-maxage=86400, stale-while-revalidate&#39;</span><span style="color:#E1E4E8">);</span></span>
643
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(png);</span></span>
644
- <span class="line"><span style="color:#E1E4E8">}</span></span>
645
- <span class="line"></span></code></pre>
646
- <p><strong>Usage:</strong></p>
647
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="plaintext"><code><span class="line"><span>https://yoursite.com/api/og-image?title=Hello&amp;description=World</span></span>
648
- <span class="line"><span></span></span></code></pre>
649
- <h3 id="video-generation">Video Generation</h3>
650
- <p>Create your template:</p>
651
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// _loopwind/templates/intro-video/template.tsx</span></span>
652
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> meta</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
653
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;intro-video&#39;</span><span style="color:#E1E4E8">,</span></span>
654
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span><span style="color:#E1E4E8">,</span></span>
655
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
656
- <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
657
- <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
658
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
659
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
660
- <span class="line"><span style="color:#E1E4E8">};</span></span>
661
- <span class="line"></span>
662
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> IntroVideo</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) {</span></span>
663
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
664
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-gradient-to-br from-black to-gray-900&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
665
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Fade in animation using utility classes */</span><span style="color:#E1E4E8">}</span></span>
666
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold text-white enter-fade-in/0/1500&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
667
- <span class="line"><span style="color:#E1E4E8"> {title}</span></span>
668
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
669
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
670
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
671
- <span class="line"><span style="color:#E1E4E8">}</span></span>
672
- <span class="line"></span></code></pre>
673
- <p>Use it in your API route:</p>
674
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// pages/api/intro-video.ts</span></span>
675
- <span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { NextApiRequest, NextApiResponse } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;next&#39;</span><span style="color:#E1E4E8">;</span></span>
676
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
677
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> introTemplate </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;../../_loopwind/templates/intro-video/template&#39;</span><span style="color:#E1E4E8">;</span></span>
678
- <span class="line"></span>
679
- <span class="line"><span style="color:#6A737D">// Load template once at module level (cached)</span></span>
680
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">(introTemplate);</span></span>
681
- <span class="line"></span>
682
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiRequest</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiResponse</span><span style="color:#E1E4E8">) {</span></span>
683
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Welcome!&#39;</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
684
- <span class="line"></span>
685
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> mp4</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(template, {</span></span>
686
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
687
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
688
- <span class="line"></span>
689
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;video/mp4&#39;</span><span style="color:#E1E4E8">);</span></span>
690
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Cache-Control&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;s-maxage=3600&#39;</span><span style="color:#E1E4E8">);</span></span>
691
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(mp4);</span></span>
692
- <span class="line"><span style="color:#E1E4E8">}</span></span>
693
- <span class="line"></span></code></pre>
694
- <h2 id="template-props">Template Props</h2>
695
- <p>Templates receive these props in their render function:</p>
696
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({</span></span>
697
- <span class="line"><span style="color:#E1E4E8"> tw, </span><span style="color:#6A737D">// Tailwind style function</span></span>
698
- <span class="line"><span style="color:#E1E4E8"> qr, </span><span style="color:#6A737D">// QR code generator</span></span>
699
- <span class="line"><span style="color:#E1E4E8"> image, </span><span style="color:#6A737D">// Image embedding helper</span></span>
700
- <span class="line"><span style="color:#E1E4E8"> template, </span><span style="color:#6A737D">// Template composition helper</span></span>
701
- <span class="line"><span style="color:#E1E4E8"> frame, </span><span style="color:#6A737D">// Current frame (video only)</span></span>
702
- <span class="line"><span style="color:#E1E4E8"> progress, </span><span style="color:#6A737D">// Progress 0-1 (video only)</span></span>
703
- <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">props </span><span style="color:#6A737D">// Your custom props</span></span>
704
- <span class="line"><span style="color:#E1E4E8">}) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* JSX */</span><span style="color:#E1E4E8">)</span></span>
705
- <span class="line"></span></code></pre>
706
- <h3 id="using-tw">Using tw()</h3>
707
- <p>The <code>tw</code> function converts Tailwind classes to inline styles:</p>
708
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
709
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col gap-4 p-8 bg-gradient-to-br from-blue-600 to-purple-700&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
710
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
711
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
712
- <span class="line"><span style="color:#E1E4E8">)</span></span>
713
- <span class="line"></span></code></pre>
714
- <h3 id="using-qr">Using qr()</h3>
715
- <p>Generate QR codes as data URIs:</p>
716
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">qr</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">url</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
717
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-white p-12&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
718
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#FFAB70">img</span></span>
719
- <span class="line"><span style="color:#E1E4E8"> src</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">qr</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">url</span><span style="color:#E1E4E8">, {</span></span>
720
- <span class="line"><span style="color:#FFAB70"> width</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">200</span><span style="color:#E1E4E8">,</span></span>
721
- <span class="line"><span style="color:#FFAB70"> margin</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">,</span></span>
722
- <span class="line"><span style="color:#FFAB70"> errorCorrectionLevel</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;M&#39;</span><span style="color:#E1E4E8">,</span></span>
723
- <span class="line"><span style="color:#FFAB70"> color</span><span style="color:#E1E4E8">: {</span></span>
724
- <span class="line"><span style="color:#FFAB70"> dark</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#000000&#39;</span><span style="color:#E1E4E8">,</span></span>
725
- <span class="line"><span style="color:#FFAB70"> light</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span></span>
726
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
727
- <span class="line"><span style="color:#E1E4E8"> })}</span></span>
728
- <span class="line"><span style="color:#E1E4E8"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-48 h-48&#39;</span><span style="color:#E1E4E8">)}</span></span>
729
- <span class="line"><span style="color:#F97583"> /&gt;</span></span>
730
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
731
- <span class="line"><span style="color:#E1E4E8">)</span></span>
732
- <span class="line"></span></code></pre>
733
- <p><strong>Options:</strong></p>
734
- <ul>
735
- <li><code>width</code> - QR code size in pixels (default: 200)</li>
736
- <li><code>margin</code> - Quiet zone margin (default: 4)</li>
737
- <li><code>errorCorrectionLevel</code> - <code>&#39;L&#39;</code>, <code>&#39;M&#39;</code>, <code>&#39;Q&#39;</code>, or <code>&#39;H&#39;</code> (default: ‘M’)</li>
738
- <li><code>color.dark</code> - Dark module color (default: ‘#000000’)</li>
739
- <li><code>color.light</code> - Light module color (default: ‘#ffffff’)</li>
740
- </ul>
741
- <h3 id="using-image">Using image()</h3>
742
- <p>Embed local images as data URIs:</p>
743
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">image</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
744
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
745
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Image path relative to template directory */</span><span style="color:#E1E4E8">}</span></span>
746
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#FFAB70">img</span></span>
747
- <span class="line"><span style="color:#E1E4E8"> src</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">image</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;logo.png&#39;</span><span style="color:#E1E4E8">)}</span></span>
748
- <span class="line"><span style="color:#E1E4E8"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-32 h-32&#39;</span><span style="color:#E1E4E8">)}</span></span>
749
- <span class="line"><span style="color:#F97583"> /&gt;</span></span>
750
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
751
- <span class="line"><span style="color:#E1E4E8">)</span></span>
752
- <span class="line"></span></code></pre>
753
- <p><strong>Note:</strong> Images must be in the same directory as your template file when using <code>defineTemplateFromFile()</code>, or embedded as data URIs when using <code>defineTemplate()</code>.</p>
754
- <h3 id="using-template">Using template()</h3>
755
- <p>Compose templates by embedding one template inside another:</p>
756
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// Card template</span></span>
757
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> cardTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
758
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;card&#39;</span><span style="color:#E1E4E8">,</span></span>
759
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">200</span><span style="color:#E1E4E8"> },</span></span>
760
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
761
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-blue-500&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
762
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h2 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl font-bold text-white&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h2</span><span style="color:#F97583">&gt;</span></span>
763
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
764
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
765
- <span class="line"><span style="color:#E1E4E8">});</span></span>
766
- <span class="line"></span>
767
- <span class="line"><span style="color:#6A737D">// Main template embedding the card</span></span>
768
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> mainTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
769
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;main&#39;</span><span style="color:#E1E4E8">,</span></span>
770
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
771
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">template</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
772
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col gap-4 w-full h-full bg-gray-100 p-8&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
773
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-4xl font-bold&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">My Cards</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
774
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Embed card template */</span><span style="color:#E1E4E8">}</span></span>
775
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#FFAB70">img</span></span>
776
- <span class="line"><span style="color:#E1E4E8"> src</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">template</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;card&#39;</span><span style="color:#E1E4E8">, { </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;Card 1&#39;</span><span style="color:#E1E4E8"> })}</span></span>
777
- <span class="line"><span style="color:#E1E4E8"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-96&#39;</span><span style="color:#E1E4E8">)}</span></span>
778
- <span class="line"><span style="color:#F97583"> /&gt;</span></span>
779
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
780
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
781
- <span class="line"><span style="color:#E1E4E8">});</span></span>
782
- <span class="line"></span></code></pre>
783
- <h3 id="video-animations">Video Animations</h3>
784
- <p>Use Tailwind-style animation classes for easy video animations! All timing values are in <strong>milliseconds</strong>.</p>
785
- <h4 id="enter-animations">Enter Animations</h4>
786
- <p>Format: <code>enter-{type}/{startMs}/{endMs}</code> with optional easing</p>
787
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
788
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
789
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Fade in from 0ms to 1500ms */</span><span style="color:#E1E4E8">}</span></span>
790
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold enter-fade-in/0/1500&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
791
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
792
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
793
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
794
- <span class="line"><span style="color:#E1E4E8">)</span></span>
795
- <span class="line"></span></code></pre>
796
- <p><strong>Available enter animations:</strong></p>
797
- <p><em>Fade:</em></p>
798
- <ul>
799
- <li><code>enter-fade-in/0/500</code> - Fade in</li>
800
- <li><code>enter-fade-in-up/0/500</code> - Fade in + slide up</li>
801
- <li><code>enter-fade-in-down/0/500</code> - Fade in + slide down</li>
802
- <li><code>enter-fade-in-left/0/500</code> - Fade in + slide from left</li>
803
- <li><code>enter-fade-in-right/0/500</code> - Fade in + slide from right</li>
804
- </ul>
805
- <p><em>Slide:</em></p>
806
- <ul>
807
- <li><code>enter-slide-left/0/500</code> - Slide in from left (100px)</li>
808
- <li><code>enter-slide-right/0/500</code> - Slide in from right (100px)</li>
809
- <li><code>enter-slide-up/0/500</code> - Slide in from bottom (100px)</li>
810
- <li><code>enter-slide-down/0/500</code> - Slide in from top (100px)</li>
811
- </ul>
812
- <p><em>Bounce:</em></p>
813
- <ul>
814
- <li><code>enter-bounce-in/0/500</code> - Bounce in with scale overshoot</li>
815
- <li><code>enter-bounce-in-up/0/500</code> - Bounce in from below</li>
816
- <li><code>enter-bounce-in-down/0/500</code> - Bounce in from above</li>
817
- <li><code>enter-bounce-in-left/0/500</code> - Bounce in from left</li>
818
- <li><code>enter-bounce-in-right/0/500</code> - Bounce in from right</li>
819
- </ul>
820
- <p><em>Scale &amp; Zoom:</em></p>
821
- <ul>
822
- <li><code>enter-scale-in/0/500</code> - Scale up from 50%</li>
823
- <li><code>enter-zoom-in/0/500</code> - Zoom in from 0%</li>
824
- </ul>
825
- <p><em>Rotate &amp; Flip:</em></p>
826
- <ul>
827
- <li><code>enter-rotate-in/0/500</code> - Rotate in (180°)</li>
828
- <li><code>enter-flip-in-x/0/500</code> - Flip in horizontally</li>
829
- <li><code>enter-flip-in-y/0/500</code> - Flip in vertically</li>
830
- </ul>
831
- <h4 id="exit-animations">Exit Animations</h4>
832
- <p>Format: <code>exit-{type}/{startMs}/{endMs}</code></p>
833
- <ul>
834
- <li><code>exit-fade-out/2500/3000</code> - Fade out</li>
835
- <li><code>exit-fade-out-up/2500/3000</code> - Fade out + slide up</li>
836
- <li><code>exit-slide-left/2500/3000</code> - Slide out to left</li>
837
- <li><code>exit-scale-out/2500/3000</code> - Scale out to 150%</li>
838
- <li><code>exit-zoom-out/2500/3000</code> - Zoom out to 200%</li>
839
- </ul>
840
- <p><strong>Easing modifiers:</strong></p>
841
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// Add easing before animation class</span></span>
842
- <span class="line"><span style="color:#F97583">&lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;ease-in-out enter-fade-in/0/1500&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
843
- <span class="line"></span></code></pre>
844
- <ul>
845
- <li><code>linear</code> - Linear</li>
846
- <li><code>ease-in</code> - Accelerate</li>
847
- <li><code>ease-out</code> - Decelerate (default)</li>
848
- <li><code>ease-in-out</code> - Accelerate then decelerate</li>
849
- <li><code>ease-in-cubic</code> - Strong accelerate</li>
850
- <li><code>ease-out-cubic</code> - Strong decelerate</li>
851
- <li><code>ease-in-out-cubic</code> - Strong ease in/out</li>
852
- </ul>
853
- <h4 id="loop-animations">Loop Animations</h4>
854
- <p>Format: <code>loop-{type}/{durationMs}</code></p>
855
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
856
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
857
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Pulse every 500ms */</span><span style="color:#E1E4E8">}</span></span>
858
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold loop-fade/500&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
859
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
860
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
861
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
862
- <span class="line"><span style="color:#E1E4E8">)</span></span>
863
- <span class="line"></span></code></pre>
864
- <p><strong>Available loop animations:</strong></p>
865
- <ul>
866
- <li><code>loop-fade/1000</code> - Opacity pulse</li>
867
- <li><code>loop-bounce/1000</code> - Bounce up and down</li>
868
- <li><code>loop-spin/1000</code> - Full rotation</li>
869
- <li><code>loop-ping/1000</code> - Scale up and fade out</li>
870
- <li><code>loop-wiggle/1000</code> - Side to side wiggle</li>
871
- <li><code>loop-float/1000</code> - Gentle floating</li>
872
- </ul>
873
- <h4 id="staggered-animations">Staggered Animations</h4>
874
- <p>Combine multiple animations with different timings:</p>
875
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
876
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
877
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Title slides in first */</span><span style="color:#E1E4E8">}</span></span>
878
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold ease-out enter-slide-up/0/900&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
879
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
880
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
881
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Subtitle slides in after */</span><span style="color:#E1E4E8">}</span></span>
882
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl mt-4 ease-out enter-slide-up/600/1500&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
883
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8">}</span></span>
884
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
885
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
886
- <span class="line"><span style="color:#E1E4E8">)</span></span>
887
- <span class="line"></span></code></pre>
888
- <h4 id="combined-enter-and-exit">Combined Enter and Exit</h4>
889
- <p>You can use both on the same element:</p>
890
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
891
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold enter-fade-in/0/500 exit-fade-out/2500/3000&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
892
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
893
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
894
- <span class="line"><span style="color:#E1E4E8">)</span></span>
895
- <span class="line"></span></code></pre>
896
- <h4 id="manual-animations">Manual Animations</h4>
897
- <p>You can still use <code>progress</code> and <code>frame</code> directly for custom animations:</p>
898
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">frame</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
899
- <span class="line"><span style="color:#6A737D"> // Custom easing</span></span>
900
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> opacity</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> Math.</span><span style="color:#B392F0">pow</span><span style="color:#E1E4E8">(progress, </span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">);</span></span>
901
- <span class="line"></span>
902
- <span class="line"><span style="color:#6A737D"> // Custom transform</span></span>
903
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> rotation</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> Math.</span><span style="color:#B392F0">sin</span><span style="color:#E1E4E8">(frame </span><span style="color:#F97583">*</span><span style="color:#79B8FF"> 0.1</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">*</span><span style="color:#79B8FF"> 10</span><span style="color:#E1E4E8">;</span></span>
904
- <span class="line"></span>
905
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
906
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
907
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{</span></span>
908
- <span class="line"><span style="color:#F97583"> ...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold&#39;</span><span style="color:#E1E4E8">),</span></span>
909
- <span class="line"><span style="color:#E1E4E8"> opacity,</span></span>
910
- <span class="line"><span style="color:#B392F0"> transform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">`rotate(${</span><span style="color:#E1E4E8">rotation</span><span style="color:#9ECBFF">}deg)`</span></span>
911
- <span class="line"><span style="color:#E1E4E8"> }}</span><span style="color:#F97583">&gt;</span></span>
912
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
913
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
914
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
915
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
916
- <span class="line"><span style="color:#E1E4E8">}</span></span>
917
- <span class="line"></span></code></pre>
918
- <h2 id="template-registry">Template Registry</h2>
919
- <p>Create a registry of reusable templates:</p>
920
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// lib/templates.ts</span></span>
921
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
922
- <span class="line"></span>
923
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> templates</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
924
- <span class="line"><span style="color:#E1E4E8"> ogImage: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
925
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
926
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
927
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
928
- <span class="line"><span style="color:#E1E4E8"> }),</span></span>
929
- <span class="line"></span>
930
- <span class="line"><span style="color:#E1E4E8"> twitterCard: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
931
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;twitter-card&#39;</span><span style="color:#E1E4E8">,</span></span>
932
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">600</span><span style="color:#E1E4E8"> },</span></span>
933
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
934
- <span class="line"><span style="color:#E1E4E8"> }),</span></span>
935
- <span class="line"></span>
936
- <span class="line"><span style="color:#E1E4E8"> videoIntro: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
937
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;video-intro&#39;</span><span style="color:#E1E4E8">,</span></span>
938
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
939
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
940
- <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
941
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
942
- <span class="line"><span style="color:#E1E4E8"> }),</span></span>
943
- <span class="line"><span style="color:#E1E4E8">};</span></span>
944
- <span class="line"></span>
945
- <span class="line"><span style="color:#6A737D">// pages/api/render/[template].ts</span></span>
946
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { templates } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;@/lib/templates&#39;</span><span style="color:#E1E4E8">;</span></span>
947
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
948
- <span class="line"></span>
949
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#E1E4E8">) {</span></span>
950
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">template</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">templateName</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
951
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> templates[templateName];</span></span>
952
- <span class="line"></span>
953
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">template) {</span></span>
954
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">status</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">404</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({ error: </span><span style="color:#9ECBFF">&#39;Template not found&#39;</span><span style="color:#E1E4E8"> });</span></span>
955
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
956
- <span class="line"></span>
957
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (template.type </span><span style="color:#F97583">===</span><span style="color:#9ECBFF"> &#39;video&#39;</span><span style="color:#E1E4E8">) {</span></span>
958
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> mp4</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(template, req.query);</span></span>
959
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;video/mp4&#39;</span><span style="color:#E1E4E8">);</span></span>
960
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(mp4);</span></span>
961
- <span class="line"><span style="color:#E1E4E8"> } </span><span style="color:#F97583">else</span><span style="color:#E1E4E8"> {</span></span>
962
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, req.query);</span></span>
963
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8">);</span></span>
964
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(png);</span></span>
965
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
966
- <span class="line"><span style="color:#E1E4E8">}</span></span>
967
- <span class="line"></span></code></pre>
968
- <h2 id="typescript-support">TypeScript Support</h2>
969
- <p>Define typed props for your templates:</p>
970
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">interface</span><span style="color:#B392F0"> OGImageProps</span><span style="color:#E1E4E8"> {</span></span>
971
- <span class="line"><span style="color:#FFAB70"> title</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
972
- <span class="line"><span style="color:#FFAB70"> description</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
973
- <span class="line"><span style="color:#FFAB70"> author</span><span style="color:#F97583">?:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
974
- <span class="line"><span style="color:#E1E4E8">}</span></span>
975
- <span class="line"></span>
976
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> ogTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">&lt;</span><span style="color:#B392F0">OGImageProps</span><span style="color:#E1E4E8">&gt;({</span></span>
977
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
978
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
979
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
980
- <span class="line"><span style="color:#6A737D"> // Props are fully typed!</span></span>
981
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
982
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
983
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{description}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
984
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> &amp;&amp; &lt;</span><span style="color:#FFAB70">span</span><span style="color:#FFAB70"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">By {author}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">span</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">}</span></span>
985
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
986
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
987
- <span class="line"><span style="color:#E1E4E8">});</span></span>
988
- <span class="line"></span>
989
- <span class="line"><span style="color:#6A737D">// TypeScript will enforce correct props</span></span>
990
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
991
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8">,</span></span>
992
- <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;World&#39;</span><span style="color:#E1E4E8">,</span></span>
993
- <span class="line"><span style="color:#6A737D"> // author is optional</span></span>
994
- <span class="line"><span style="color:#E1E4E8">});</span></span>
995
- <span class="line"></span></code></pre>
996
- <h2 id="deployment">Deployment</h2>
997
- <h3 id="vercel">Vercel</h3>
998
- <p>The SDK works out of the box on Vercel serverless functions:</p>
999
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> loopwind</span></span>
1000
- <span class="line"><span style="color:#B392F0">vercel</span><span style="color:#9ECBFF"> deploy</span></span>
1001
- <span class="line"></span></code></pre>
1002
- <h3 id="netlify">Netlify</h3>
1003
- <p>Works with Netlify Functions:</p>
1004
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// netlify/functions/og-image.ts</span></span>
1005
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1006
- <span class="line"></span>
1007
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#B392F0"> handler</span><span style="color:#F97583"> =</span><span style="color:#F97583"> async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">event</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
1008
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> event.queryStringParameters </span><span style="color:#F97583">||</span><span style="color:#E1E4E8"> {};</span></span>
1009
- <span class="line"></span>
1010
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title });</span></span>
1011
- <span class="line"></span>
1012
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> {</span></span>
1013
- <span class="line"><span style="color:#E1E4E8"> statusCode: </span><span style="color:#79B8FF">200</span><span style="color:#E1E4E8">,</span></span>
1014
- <span class="line"><span style="color:#E1E4E8"> headers: { </span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8"> },</span></span>
1015
- <span class="line"><span style="color:#E1E4E8"> body: png.</span><span style="color:#B392F0">toString</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;base64&#39;</span><span style="color:#E1E4E8">),</span></span>
1016
- <span class="line"><span style="color:#E1E4E8"> isBase64Encoded: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">,</span></span>
1017
- <span class="line"><span style="color:#E1E4E8"> };</span></span>
1018
- <span class="line"><span style="color:#E1E4E8">};</span></span>
1019
- <span class="line"></span></code></pre>
1020
- <h3 id="docker">Docker</h3>
1021
- <p>For self-hosted deployments:</p>
1022
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="dockerfile"><code><span class="line"><span style="color:#F97583">FROM</span><span style="color:#E1E4E8"> node:20-alpine</span></span>
1023
- <span class="line"><span style="color:#F97583">WORKDIR</span><span style="color:#E1E4E8"> /app</span></span>
1024
- <span class="line"><span style="color:#F97583">COPY</span><span style="color:#E1E4E8"> package*.json ./</span></span>
1025
- <span class="line"><span style="color:#F97583">RUN</span><span style="color:#E1E4E8"> npm install</span></span>
1026
- <span class="line"><span style="color:#F97583">COPY</span><span style="color:#E1E4E8"> . .</span></span>
1027
- <span class="line"><span style="color:#F97583">RUN</span><span style="color:#E1E4E8"> npm run build</span></span>
1028
- <span class="line"><span style="color:#F97583">CMD</span><span style="color:#E1E4E8"> [</span><span style="color:#9ECBFF">&quot;npm&quot;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&quot;start&quot;</span><span style="color:#E1E4E8">]</span></span>
1029
- <span class="line"></span></code></pre>
1030
- <h2 id="performance">Performance</h2>
1031
- <ul>
1032
- <li><strong>Images</strong>: ~50-100ms per image (PNG/JPEG/WebP)</li>
1033
- <li><strong>Videos</strong>: ~7s for 3-second Full HD video (1920×1080 @ 30fps)</li>
1034
- <li><strong>Pure JavaScript/WASM</strong>: No native dependencies, works everywhere</li>
1035
- </ul>
1036
- <h2 id="use-cases">Use Cases</h2>
1037
- <ul>
1038
- <li><strong>Dynamic OG images</strong> for blogs and marketing sites</li>
1039
- <li><strong>Programmatic videos</strong> for social media automation</li>
1040
- <li><strong>Email images</strong> generated on-the-fly</li>
1041
- <li><strong>User-generated content</strong> like certificates, cards, badges</li>
1042
- <li><strong>Batch processing</strong> of images or videos</li>
1043
- <li><strong>Image/video generation APIs</strong> that you can monetize</li>
1044
- </ul>
1045
- <h2 id="video-preview-component">Video Preview Component</h2>
1046
- <p>Preview video templates in your React applications before rendering. Perfect for template editors, preview interfaces, and documentation.</p>
1047
- <h3 id="previewvideo">previewVideo()</h3>
1048
- <p>Pre-render all video frames on the server or at build time:</p>
1049
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, previewVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1050
- <span class="line"></span>
1051
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1052
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;intro&#39;</span><span style="color:#E1E4E8">,</span></span>
1053
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
1054
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
1055
- <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
1056
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
1057
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-black&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
1058
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{ </span><span style="color:#F97583">...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold text-white&#39;</span><span style="color:#E1E4E8">), </span><span style="color:#B392F0">opacity</span><span style="color:#E1E4E8">: progress }}</span><span style="color:#F97583">&gt;</span></span>
1059
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
1060
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
1061
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
1062
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1063
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1064
- <span class="line"></span>
1065
- <span class="line"><span style="color:#6A737D">// Pre-render all frames</span></span>
1066
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> frames</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> previewVideo</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello World&#39;</span><span style="color:#E1E4E8"> });</span></span>
1067
- <span class="line"></span></code></pre>
1068
- <p><strong>Returns:</strong></p>
1069
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
1070
- <span class="line"><span style="color:#B392F0"> frames</span><span style="color:#E1E4E8">: string[], </span><span style="color:#6A737D">// Array of SVG strings or data URLs</span></span>
1071
- <span class="line"><span style="color:#B392F0"> meta</span><span style="color:#E1E4E8">: {</span></span>
1072
- <span class="line"><span style="color:#B392F0"> name</span><span style="color:#E1E4E8">: string,</span></span>
1073
- <span class="line"><span style="color:#B392F0"> width</span><span style="color:#E1E4E8">: number,</span></span>
1074
- <span class="line"><span style="color:#B392F0"> height</span><span style="color:#E1E4E8">: number,</span></span>
1075
- <span class="line"><span style="color:#B392F0"> fps</span><span style="color:#E1E4E8">: number,</span></span>
1076
- <span class="line"><span style="color:#B392F0"> duration</span><span style="color:#E1E4E8">: number,</span></span>
1077
- <span class="line"><span style="color:#B392F0"> totalFrames</span><span style="color:#E1E4E8">: number</span></span>
1078
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1079
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1080
- <span class="line"></span></code></pre>
1081
- <p><strong>Options:</strong></p>
1082
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> frames</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> previewVideo</span><span style="color:#E1E4E8">(template, props, {</span></span>
1083
- <span class="line"><span style="color:#E1E4E8"> format: </span><span style="color:#9ECBFF">&#39;svg&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// &#39;svg&#39; (default) or &#39;data-url&#39;</span></span>
1084
- <span class="line"><span style="color:#E1E4E8"> config: { </span><span style="color:#6A737D">// Optional: Override template config</span></span>
1085
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
1086
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span></span>
1087
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1088
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1089
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1090
- <span class="line"></span></code></pre>
1091
- <h3 id="videopreview-component">&lt;VideoPreview&gt; Component</h3>
1092
- <p>Browser-safe React component for displaying pre-rendered frames:</p>
1093
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { VideoPreview, </span><span style="color:#F97583">type</span><span style="color:#E1E4E8"> PreviewFrames } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1094
- <span class="line"></span>
1095
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> PreviewPage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">frames</span><span style="color:#E1E4E8"> }</span><span style="color:#F97583">:</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">frames</span><span style="color:#F97583">:</span><span style="color:#B392F0"> PreviewFrames</span><span style="color:#E1E4E8"> }) {</span></span>
1096
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
1097
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#79B8FF">VideoPreview</span></span>
1098
- <span class="line"><span style="color:#B392F0"> frames</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{frames}</span></span>
1099
- <span class="line"><span style="color:#B392F0"> autoPlay</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">}</span></span>
1100
- <span class="line"><span style="color:#B392F0"> loop</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">}</span></span>
1101
- <span class="line"><span style="color:#B392F0"> controls</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">}</span></span>
1102
- <span class="line"><span style="color:#B392F0"> onFrameChange</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{(</span><span style="color:#FFAB70">frame</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">`Frame: ${</span><span style="color:#E1E4E8">frame</span><span style="color:#9ECBFF">}`</span><span style="color:#E1E4E8">)}</span></span>
1103
- <span class="line"><span style="color:#E1E4E8"> /&gt;</span></span>
1104
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
1105
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1106
- <span class="line"></span></code></pre>
1107
- <p><strong>Props:</strong></p>
1108
- <ul>
1109
- <li><code>frames</code> - Pre-rendered frames from <code>previewVideo()</code></li>
1110
- <li><code>autoPlay</code> - Auto-play on mount (default: <code>true</code>)</li>
1111
- <li><code>loop</code> - Loop the video (default: <code>true</code>)</li>
1112
- <li><code>controls</code> - Show playback controls (default: <code>true</code>)</li>
1113
- <li><code>className</code> - CSS class for styling</li>
1114
- <li><code>style</code> - Inline styles</li>
1115
- <li><code>onFrameChange</code> - Callback when frame changes</li>
1116
- </ul>
1117
- <h3 id="nextjs-example">Next.js Example</h3>
1118
- <p><strong>Pre-render at build time:</strong></p>
1119
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// pages/templates/[id].tsx</span></span>
1120
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { previewVideo, VideoPreview, </span><span style="color:#F97583">type</span><span style="color:#E1E4E8"> PreviewFrames } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1121
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { templates } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;@/lib/templates&#39;</span><span style="color:#E1E4E8">;</span></span>
1122
- <span class="line"></span>
1123
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> getStaticProps</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">params</span><span style="color:#E1E4E8"> }) {</span></span>
1124
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> templates[params.id];</span></span>
1125
- <span class="line"></span>
1126
- <span class="line"><span style="color:#6A737D"> // Pre-render all frames on server</span></span>
1127
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> frames</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> previewVideo</span><span style="color:#E1E4E8">(template, {</span></span>
1128
- <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Preview&#39;</span></span>
1129
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1130
- <span class="line"></span>
1131
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> {</span></span>
1132
- <span class="line"><span style="color:#E1E4E8"> props: { frames } </span><span style="color:#6A737D">// Pass serialized frames to client</span></span>
1133
- <span class="line"><span style="color:#E1E4E8"> };</span></span>
1134
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1135
- <span class="line"></span>
1136
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> TemplatePage</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">frames</span><span style="color:#E1E4E8"> }</span><span style="color:#F97583">:</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">frames</span><span style="color:#F97583">:</span><span style="color:#B392F0"> PreviewFrames</span><span style="color:#E1E4E8"> }) {</span></span>
1137
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
1138
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;container mx-auto py-8&quot;</span><span style="color:#E1E4E8">&gt;</span></span>
1139
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;text-3xl font-bold mb-4&quot;</span><span style="color:#E1E4E8">&gt;{frames.meta.name}&lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
1140
- <span class="line"></span>
1141
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#79B8FF">VideoPreview</span></span>
1142
- <span class="line"><span style="color:#B392F0"> frames</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{frames}</span></span>
1143
- <span class="line"><span style="color:#B392F0"> autoPlay</span></span>
1144
- <span class="line"><span style="color:#B392F0"> loop</span></span>
1145
- <span class="line"><span style="color:#B392F0"> controls</span></span>
1146
- <span class="line"><span style="color:#E1E4E8"> /&gt;</span></span>
1147
- <span class="line"></span>
1148
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;mt-4 text-sm text-gray-600&quot;</span><span style="color:#E1E4E8">&gt;</span></span>
1149
- <span class="line"><span style="color:#E1E4E8"> {frames.meta.width}×{frames.meta.height} • {frames.meta.fps}fps • {frames.meta.duration}s</span></span>
1150
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1151
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1152
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
1153
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1154
- <span class="line"></span></code></pre>
1155
- <p><strong>On-demand with API route:</strong></p>
1156
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#6A737D">// pages/api/preview/[template].ts</span></span>
1157
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { previewVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1158
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { templates } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;@/lib/templates&#39;</span><span style="color:#E1E4E8">;</span></span>
1159
- <span class="line"></span>
1160
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#E1E4E8">) {</span></span>
1161
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">template</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">templateId</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
1162
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> props</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> req.body;</span></span>
1163
- <span class="line"></span>
1164
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> templates[templateId];</span></span>
1165
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">template) {</span></span>
1166
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">status</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">404</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({ error: </span><span style="color:#9ECBFF">&#39;Template not found&#39;</span><span style="color:#E1E4E8"> });</span></span>
1167
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1168
- <span class="line"></span>
1169
- <span class="line"><span style="color:#6A737D"> // Generate preview frames on-demand</span></span>
1170
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> frames</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> previewVideo</span><span style="color:#E1E4E8">(template, props, {</span></span>
1171
- <span class="line"><span style="color:#E1E4E8"> format: </span><span style="color:#9ECBFF">&#39;data-url&#39;</span><span style="color:#6A737D"> // Easier JSON serialization</span></span>
1172
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1173
- <span class="line"></span>
1174
- <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">(frames);</span></span>
1175
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1176
- <span class="line"></span>
1177
- <span class="line"><span style="color:#6A737D">// pages/preview.tsx</span></span>
1178
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { useState, useEffect } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;react&#39;</span><span style="color:#E1E4E8">;</span></span>
1179
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { VideoPreview, </span><span style="color:#F97583">type</span><span style="color:#E1E4E8"> PreviewFrames } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1180
- <span class="line"></span>
1181
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> PreviewPage</span><span style="color:#E1E4E8">() {</span></span>
1182
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">frames</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">setFrames</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#B392F0"> useState</span><span style="color:#E1E4E8">&lt;</span><span style="color:#B392F0">PreviewFrames</span><span style="color:#F97583"> |</span><span style="color:#79B8FF"> null</span><span style="color:#E1E4E8">&gt;(</span><span style="color:#79B8FF">null</span><span style="color:#E1E4E8">);</span></span>
1183
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">loading</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">setLoading</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#B392F0"> useState</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">);</span></span>
1184
- <span class="line"></span>
1185
- <span class="line"><span style="color:#B392F0"> useEffect</span><span style="color:#E1E4E8">(() </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
1186
- <span class="line"><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;/api/preview/intro&#39;</span><span style="color:#E1E4E8">, {</span></span>
1187
- <span class="line"><span style="color:#E1E4E8"> method: </span><span style="color:#9ECBFF">&#39;POST&#39;</span><span style="color:#E1E4E8">,</span></span>
1188
- <span class="line"><span style="color:#E1E4E8"> body: </span><span style="color:#79B8FF">JSON</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">stringify</span><span style="color:#E1E4E8">({ title: </span><span style="color:#9ECBFF">&#39;Dynamic Preview&#39;</span><span style="color:#E1E4E8"> })</span></span>
1189
- <span class="line"><span style="color:#E1E4E8"> })</span></span>
1190
- <span class="line"><span style="color:#E1E4E8"> .</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">res</span><span style="color:#F97583"> =&gt;</span><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">())</span></span>
1191
- <span class="line"><span style="color:#E1E4E8"> .</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(setFrames)</span></span>
1192
- <span class="line"><span style="color:#E1E4E8"> .</span><span style="color:#B392F0">finally</span><span style="color:#E1E4E8">(() </span><span style="color:#F97583">=&gt;</span><span style="color:#B392F0"> setLoading</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">false</span><span style="color:#E1E4E8">));</span></span>
1193
- <span class="line"><span style="color:#E1E4E8"> }, []);</span></span>
1194
- <span class="line"></span>
1195
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (loading) </span><span style="color:#F97583">return</span><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;Loading preview...&lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;;</span></span>
1196
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">frames) </span><span style="color:#F97583">return</span><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;Failed to load&lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;;</span></span>
1197
- <span class="line"></span>
1198
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> &lt;</span><span style="color:#79B8FF">VideoPreview</span><span style="color:#B392F0"> frames</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{frames} </span><span style="color:#B392F0">autoPlay</span><span style="color:#B392F0"> loop</span><span style="color:#E1E4E8"> /&gt;;</span></span>
1199
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1200
- <span class="line"></span></code></pre>
1201
- <h3 id="template-editor-example">Template Editor Example</h3>
1202
- <p>Build a visual template editor with live preview:</p>
1203
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="tsx"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { useState } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;react&#39;</span><span style="color:#E1E4E8">;</span></span>
1204
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, previewVideo, VideoPreview } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
1205
- <span class="line"></span>
1206
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> TemplateEditor</span><span style="color:#E1E4E8">() {</span></span>
1207
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">frames</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">setFrames</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#B392F0"> useState</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">null</span><span style="color:#E1E4E8">);</span></span>
1208
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> [</span><span style="color:#79B8FF">title</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">setTitle</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">=</span><span style="color:#B392F0"> useState</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Hello World&#39;</span><span style="color:#E1E4E8">);</span></span>
1209
- <span class="line"></span>
1210
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#B392F0"> handlePreview</span><span style="color:#F97583"> =</span><span style="color:#F97583"> async</span><span style="color:#E1E4E8"> () </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
1211
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1212
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;preview&#39;</span><span style="color:#E1E4E8">,</span></span>
1213
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
1214
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
1215
- <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8"> },</span></span>
1216
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
1217
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-gradient-to-r from-blue-500 to-purple-600&#39;</span><span style="color:#E1E4E8">)}&gt;</span></span>
1218
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{ </span><span style="color:#F97583">...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold text-white&#39;</span><span style="color:#E1E4E8">), opacity: progress }}&gt;</span></span>
1219
- <span class="line"><span style="color:#E1E4E8"> {title}</span></span>
1220
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
1221
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1222
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1223
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1224
- <span class="line"></span>
1225
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> frames</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> previewVideo</span><span style="color:#E1E4E8">(template, { title });</span></span>
1226
- <span class="line"><span style="color:#B392F0"> setFrames</span><span style="color:#E1E4E8">(frames);</span></span>
1227
- <span class="line"><span style="color:#E1E4E8"> };</span></span>
1228
- <span class="line"></span>
1229
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
1230
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;grid grid-cols-2 gap-4&quot;</span><span style="color:#E1E4E8">&gt;</span></span>
1231
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Editor */</span><span style="color:#E1E4E8">}</span></span>
1232
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1233
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">input</span></span>
1234
- <span class="line"><span style="color:#B392F0"> type</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;text&quot;</span></span>
1235
- <span class="line"><span style="color:#B392F0"> value</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{title}</span></span>
1236
- <span class="line"><span style="color:#B392F0"> onChange</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{(</span><span style="color:#FFAB70">e</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#B392F0"> setTitle</span><span style="color:#E1E4E8">(e.target.value)}</span></span>
1237
- <span class="line"><span style="color:#B392F0"> className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;border p-2 w-full mb-4&quot;</span></span>
1238
- <span class="line"><span style="color:#E1E4E8"> /&gt;</span></span>
1239
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> onClick</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{handlePreview} </span><span style="color:#B392F0">className</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">&quot;bg-blue-500 text-white px-4 py-2&quot;</span><span style="color:#E1E4E8">&gt;</span></span>
1240
- <span class="line"><span style="color:#E1E4E8"> Update Preview</span></span>
1241
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">&gt;</span></span>
1242
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1243
- <span class="line"></span>
1244
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Preview */</span><span style="color:#E1E4E8">}</span></span>
1245
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1246
- <span class="line"><span style="color:#E1E4E8"> {frames </span><span style="color:#F97583">?</span><span style="color:#E1E4E8"> (</span></span>
1247
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#79B8FF">VideoPreview</span><span style="color:#B392F0"> frames</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{frames} </span><span style="color:#B392F0">autoPlay</span><span style="color:#B392F0"> loop</span><span style="color:#E1E4E8"> /&gt;</span></span>
1248
- <span class="line"><span style="color:#E1E4E8"> ) </span><span style="color:#F97583">:</span><span style="color:#E1E4E8"> (</span></span>
1249
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;Click &quot;Update Preview&quot; to see your template&lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1250
- <span class="line"><span style="color:#E1E4E8"> )}</span></span>
1251
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1252
- <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
1253
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
1254
- <span class="line"><span style="color:#E1E4E8">}</span></span>
1255
- <span class="line"></span></code></pre>
1256
- <h3 id="benefits">Benefits</h3>
1257
- <ul>
1258
- <li>✅ <strong>Accurate</strong> - Preview matches final video exactly (same renderer)</li>
1259
- <li>✅ <strong>Fast</strong> - Pre-rendered frames load instantly</li>
1260
- <li>✅ <strong>Interactive</strong> - Scrub, play, pause, step through frames</li>
1261
- <li>✅ <strong>Browser-safe</strong> - No Node.js dependencies in React component</li>
1262
- <li>✅ <strong>Flexible</strong> - Server-side or build-time rendering</li>
1263
- <li>✅ <strong>Type-safe</strong> - Full TypeScript support</li>
1264
- </ul>
1265
- <h3 id="use-cases-1">Use Cases</h3>
1266
- <ul>
1267
- <li><strong>Template editors</strong> - Live preview while editing</li>
1268
- <li><strong>Documentation</strong> - Show template examples</li>
1269
- <li><strong>Admin panels</strong> - Preview before rendering final video</li>
1270
- <li><strong>Marketing sites</strong> - Showcase video templates</li>
1271
- <li><strong>Template galleries</strong> - Browse available templates</li>
1272
- </ul>
1273
- <h2 id="cloudflare-workers">Cloudflare Workers</h2>
1274
- <p>Use loopwind in Cloudflare Workers and other edge runtimes with the dedicated workers SDK.</p>
1275
- <h3 id="installation-1">Installation</h3>
1276
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> loopwind</span><span style="color:#9ECBFF"> @resvg/resvg-wasm</span></span>
1277
- <span class="line"></span></code></pre>
1278
- <h3 id="quick-start-1">Quick Start</h3>
1279
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage, renderSVG, defineTemplate, initWasm } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/workers&#39;</span><span style="color:#E1E4E8">;</span></span>
1280
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> React </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;react&#39;</span><span style="color:#E1E4E8">;</span></span>
1281
- <span class="line"></span>
1282
- <span class="line"><span style="color:#6A737D">// Define a template using React.createElement</span></span>
1283
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1284
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
1285
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
1286
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span></span>
1287
- <span class="line"><span style="color:#9ECBFF"> &#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
1288
- <span class="line"><span style="color:#E1E4E8"> { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-500 to-purple-600 p-12&#39;</span><span style="color:#E1E4E8">) },</span></span>
1289
- <span class="line"><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-7xl font-bold text-white text-center&#39;</span><span style="color:#E1E4E8">) }, title),</span></span>
1290
- <span class="line"><span style="color:#E1E4E8"> subtitle </span><span style="color:#F97583">&amp;&amp;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;p&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-3xl text-white mt-6&#39;</span><span style="color:#E1E4E8">) }, subtitle)</span></span>
1291
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1292
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1293
- <span class="line"></span>
1294
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#E1E4E8"> {</span></span>
1295
- <span class="line"><span style="color:#F97583"> async</span><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">request</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Request</span><span style="color:#E1E4E8">)</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Promise</span><span style="color:#E1E4E8">&lt;</span><span style="color:#B392F0">Response</span><span style="color:#E1E4E8">&gt; {</span></span>
1296
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> url</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> URL</span><span style="color:#E1E4E8">(request.url);</span></span>
1297
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> title</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> url.searchParams.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;title&#39;</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">||</span><span style="color:#9ECBFF"> &#39;Hello World&#39;</span><span style="color:#E1E4E8">;</span></span>
1298
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> format</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> url.searchParams.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;format&#39;</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">||</span><span style="color:#9ECBFF"> &#39;svg&#39;</span><span style="color:#E1E4E8">;</span></span>
1299
- <span class="line"></span>
1300
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (format </span><span style="color:#F97583">===</span><span style="color:#9ECBFF"> &#39;png&#39;</span><span style="color:#E1E4E8">) {</span></span>
1301
- <span class="line"><span style="color:#6A737D"> // Initialize WASM once (required for PNG)</span></span>
1302
- <span class="line"><span style="color:#F97583"> await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">();</span></span>
1303
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title }, { format: </span><span style="color:#9ECBFF">&#39;png&#39;</span><span style="color:#E1E4E8"> });</span></span>
1304
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(png, {</span></span>
1305
- <span class="line"><span style="color:#E1E4E8"> headers: { </span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8"> }</span></span>
1306
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1307
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1308
- <span class="line"></span>
1309
- <span class="line"><span style="color:#6A737D"> // SVG doesn&#39;t require WASM initialization</span></span>
1310
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> svg</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderSVG</span><span style="color:#E1E4E8">(template, { title });</span></span>
1311
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(svg, {</span></span>
1312
- <span class="line"><span style="color:#E1E4E8"> headers: { </span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/svg+xml&#39;</span><span style="color:#E1E4E8"> }</span></span>
1313
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1314
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1315
- <span class="line"><span style="color:#E1E4E8">};</span></span>
1316
- <span class="line"></span></code></pre>
1317
- <h3 id="key-differences-from-main-sdk">Key Differences from Main SDK</h3>
1318
- <p>The workers SDK (<code>loopwind/sdk/workers</code>) is designed for edge runtimes:</p>
1319
-
1320
-
1321
-
1322
-
1323
-
1324
-
1325
-
1326
-
1327
-
1328
-
1329
-
1330
-
1331
-
1332
-
1333
-
1334
-
1335
-
1336
-
1337
-
1338
-
1339
-
1340
-
1341
-
1342
-
1343
-
1344
-
1345
-
1346
-
1347
-
1348
-
1349
-
1350
-
1351
-
1352
-
1353
-
1354
-
1355
-
1356
-
1357
-
1358
-
1359
- <table><thead><tr><th>Feature</th><th>Main SDK (<code>loopwind/sdk</code>)</th><th>Workers SDK (<code>loopwind/sdk/workers</code>)</th></tr></thead><tbody><tr><td><strong>Runtime</strong></td><td>Node.js</td><td>Cloudflare Workers, Edge</td></tr><tr><td><strong>File System</strong></td><td>Uses temp files</td><td>All in-memory</td></tr><tr><td><strong>PNG Rendering</strong></td><td>@resvg/resvg-js (native)</td><td>@resvg/resvg-wasm</td></tr><tr><td><strong>Templates</strong></td><td>JSX or React.createElement</td><td>React.createElement only</td></tr><tr><td><strong>Fonts</strong></td><td>Local files or CDN</td><td>ArrayBuffer (from CDN, R2, or bundled)</td></tr><tr><td><strong>Video</strong></td><td>Full support</td><td>Not supported</td></tr></tbody></table>
1360
- <h3 id="api-reference">API Reference</h3>
1361
- <h4 id="initwasm">initWasm()</h4>
1362
- <p>Initialize WASM modules. Required before rendering PNG images.</p>
1363
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// Initialize with default CDN</span></span>
1364
- <span class="line"><span style="color:#F97583">await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">();</span></span>
1365
- <span class="line"></span>
1366
- <span class="line"><span style="color:#6A737D">// Or provide your own WASM binary</span></span>
1367
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> wasmBinary</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;/resvg.wasm&#39;</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">r</span><span style="color:#F97583"> =&gt;</span><span style="color:#E1E4E8"> r.</span><span style="color:#B392F0">arrayBuffer</span><span style="color:#E1E4E8">());</span></span>
1368
- <span class="line"><span style="color:#F97583">await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">(wasmBinary);</span></span>
1369
- <span class="line"></span></code></pre>
1370
- <h4 id="rendersvg">renderSVG()</h4>
1371
- <p>Render a template to SVG string. No WASM required.</p>
1372
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> svg</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderSVG</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> });</span></span>
1373
- <span class="line"><span style="color:#6A737D">// Returns: string (SVG markup)</span></span>
1374
- <span class="line"></span></code></pre>
1375
- <h4 id="renderimage-1">renderImage()</h4>
1376
- <p>Render a template to PNG. Requires <code>initWasm()</code> first.</p>
1377
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">();</span></span>
1378
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> }, {</span></span>
1379
- <span class="line"><span style="color:#E1E4E8"> format: </span><span style="color:#9ECBFF">&#39;png&#39;</span><span style="color:#E1E4E8">,</span></span>
1380
- <span class="line"><span style="color:#E1E4E8"> scale: </span><span style="color:#79B8FF">2</span><span style="color:#6A737D"> // 2x resolution (default: 2)</span></span>
1381
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1382
- <span class="line"><span style="color:#6A737D">// Returns: Uint8Array (PNG data)</span></span>
1383
- <span class="line"></span></code></pre>
1384
- <h4 id="definetemplate-1">defineTemplate()</h4>
1385
- <p>Define a template inline using React.createElement.</p>
1386
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1387
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">,</span></span>
1388
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
1389
- <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
1390
- <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
1391
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
1392
- <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span></span>
1393
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1394
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
1395
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span></span>
1396
- <span class="line"><span style="color:#9ECBFF"> &#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
1397
- <span class="line"><span style="color:#E1E4E8"> { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-background&#39;</span><span style="color:#E1E4E8">) },</span></span>
1398
- <span class="line"><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-primary&#39;</span><span style="color:#E1E4E8">) }, title)</span></span>
1399
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1400
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1401
- <span class="line"></span></code></pre>
1402
- <h4 id="loadfont">loadFont()</h4>
1403
- <p>Load a custom font from a URL. By default, the SDK fetches Inter fonts from CDN, but you can use your own fonts.</p>
1404
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { loadFont, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/workers&#39;</span><span style="color:#E1E4E8">;</span></span>
1405
- <span class="line"></span>
1406
- <span class="line"><span style="color:#6A737D">// Load custom fonts</span></span>
1407
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> fonts</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#79B8FF"> Promise</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">all</span><span style="color:#E1E4E8">([</span></span>
1408
- <span class="line"><span style="color:#B392F0"> loadFont</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://your-cdn.com/fonts/custom-400.woff&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;CustomFont&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8">),</span></span>
1409
- <span class="line"><span style="color:#B392F0"> loadFont</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://your-cdn.com/fonts/custom-700.woff&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;CustomFont&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">700</span><span style="color:#E1E4E8">),</span></span>
1410
- <span class="line"><span style="color:#E1E4E8">]);</span></span>
1411
- <span class="line"></span>
1412
- <span class="line"><span style="color:#6A737D">// Use custom fonts when rendering</span></span>
1413
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> }, { fonts });</span></span>
1414
- <span class="line"></span></code></pre>
1415
- <p>You can also load fonts from Cloudflare R2 or KV storage:</p>
1416
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// From R2 bucket</span></span>
1417
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> fontData</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> env.</span><span style="color:#79B8FF">FONTS_BUCKET</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;my-font.woff&#39;</span><span style="color:#E1E4E8">);</span></span>
1418
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> fonts</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> [{</span></span>
1419
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;MyFont&#39;</span><span style="color:#E1E4E8">,</span></span>
1420
- <span class="line"><span style="color:#E1E4E8"> data: </span><span style="color:#F97583">await</span><span style="color:#E1E4E8"> fontData.</span><span style="color:#B392F0">arrayBuffer</span><span style="color:#E1E4E8">(),</span></span>
1421
- <span class="line"><span style="color:#E1E4E8"> weight: </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8">,</span></span>
1422
- <span class="line"><span style="color:#E1E4E8"> style: </span><span style="color:#9ECBFF">&#39;normal&#39;</span></span>
1423
- <span class="line"><span style="color:#E1E4E8">}];</span></span>
1424
- <span class="line"></span></code></pre>
1425
- <h4 id="fetchimage">fetchImage()</h4>
1426
- <p>Fetch an image from URL and convert to data URI for use in templates. Satori requires images as data URIs, so you need to pre-fetch them before rendering.</p>
1427
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { fetchImage, renderImage, defineTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/workers&#39;</span><span style="color:#E1E4E8">;</span></span>
1428
- <span class="line"></span>
1429
- <span class="line"><span style="color:#6A737D">// Pre-fetch images before rendering</span></span>
1430
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> logo</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> fetchImage</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://example.com/logo.png&#39;</span><span style="color:#E1E4E8">);</span></span>
1431
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> background</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> fetchImage</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://example.com/bg.jpg&#39;</span><span style="color:#E1E4E8">);</span></span>
1432
- <span class="line"></span>
1433
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1434
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
1435
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
1436
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">logo</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">background</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span></span>
1437
- <span class="line"><span style="color:#9ECBFF"> &#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
1438
- <span class="line"><span style="color:#E1E4E8"> { style: { </span><span style="color:#F97583">...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">), backgroundImage: </span><span style="color:#9ECBFF">`url(${</span><span style="color:#E1E4E8">background</span><span style="color:#9ECBFF">})`</span><span style="color:#E1E4E8"> } },</span></span>
1439
- <span class="line"><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;img&#39;</span><span style="color:#E1E4E8">, { src: logo, width: </span><span style="color:#79B8FF">100</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">100</span><span style="color:#E1E4E8"> })</span></span>
1440
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1441
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1442
- <span class="line"></span>
1443
- <span class="line"><span style="color:#6A737D">// Pass pre-fetched images as props</span></span>
1444
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { logo, background });</span></span>
1445
- <span class="line"></span></code></pre>
1446
- <h3 id="wrangler-configuration">Wrangler Configuration</h3>
1447
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="toml"><code><span class="line"><span style="color:#6A737D"># wrangler.toml</span></span>
1448
- <span class="line"><span style="color:#E1E4E8">name = </span><span style="color:#9ECBFF">&quot;my-og-image-worker&quot;</span></span>
1449
- <span class="line"><span style="color:#E1E4E8">main = </span><span style="color:#9ECBFF">&quot;src/index.ts&quot;</span></span>
1450
- <span class="line"><span style="color:#E1E4E8">compatibility_date = </span><span style="color:#9ECBFF">&quot;2024-12-01&quot;</span></span>
1451
- <span class="line"></span>
1452
- <span class="line"><span style="color:#6A737D"># Required for satori and resvg-wasm dependencies</span></span>
1453
- <span class="line"><span style="color:#E1E4E8">compatibility_flags = [</span><span style="color:#9ECBFF">&quot;nodejs_compat_v2&quot;</span><span style="color:#E1E4E8">]</span></span>
1454
- <span class="line"></span></code></pre>
1455
- <h3 id="local-development-note">Local Development Note</h3>
1456
- <p>Local development with <code>wrangler dev</code> may have WASM limitations due to the local workerd runtime. For full testing:</p>
1457
- <ol>
1458
- <li><strong>Use SVG format locally</strong>: <code>?format=svg</code> works without WASM</li>
1459
- <li><strong>Test with Node.js</strong>: Create a test script to verify PNG rendering</li>
1460
- <li><strong>Deploy to production</strong>: Full PNG support works on Cloudflare’s production runtime</li>
1461
- </ol>
1462
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// test-sdk.mjs - Test locally with Node.js</span></span>
1463
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage, renderSVG, defineTemplate, initWasm } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/workers&#39;</span><span style="color:#E1E4E8">;</span></span>
1464
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> React </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;react&#39;</span><span style="color:#E1E4E8">;</span></span>
1465
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> fs </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;fs/promises&#39;</span><span style="color:#E1E4E8">;</span></span>
1466
- <span class="line"></span>
1467
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1468
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;test&#39;</span><span style="color:#E1E4E8">,</span></span>
1469
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
1470
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span></span>
1471
- <span class="line"><span style="color:#9ECBFF"> &#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
1472
- <span class="line"><span style="color:#E1E4E8"> { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-blue-500&#39;</span><span style="color:#E1E4E8">) },</span></span>
1473
- <span class="line"><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white&#39;</span><span style="color:#E1E4E8">) }, title)</span></span>
1474
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1475
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1476
- <span class="line"></span>
1477
- <span class="line"><span style="color:#6A737D">// Test SVG</span></span>
1478
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> svg</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderSVG</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> });</span></span>
1479
- <span class="line"><span style="color:#E1E4E8">console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;SVG length:&#39;</span><span style="color:#E1E4E8">, svg.</span><span style="color:#79B8FF">length</span><span style="color:#E1E4E8">);</span></span>
1480
- <span class="line"></span>
1481
- <span class="line"><span style="color:#6A737D">// Test PNG</span></span>
1482
- <span class="line"><span style="color:#F97583">await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">();</span></span>
1483
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> });</span></span>
1484
- <span class="line"><span style="color:#F97583">await</span><span style="color:#E1E4E8"> fs.</span><span style="color:#B392F0">writeFile</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;test.png&#39;</span><span style="color:#E1E4E8">, png);</span></span>
1485
- <span class="line"><span style="color:#E1E4E8">console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;PNG saved!&#39;</span><span style="color:#E1E4E8">);</span></span>
1486
- <span class="line"></span></code></pre>
1487
- <h3 id="custom-fonts">Custom Fonts</h3>
1488
- <p>Fonts are fetched from CDN by default (Inter). You can provide custom fonts:</p>
1489
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> customFonts</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> [</span></span>
1490
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
1491
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;Roboto&#39;</span><span style="color:#E1E4E8">,</span></span>
1492
- <span class="line"><span style="color:#E1E4E8"> data: </span><span style="color:#F97583">await</span><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://example.com/Roboto-Regular.woff&#39;</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">r</span><span style="color:#F97583"> =&gt;</span><span style="color:#E1E4E8"> r.</span><span style="color:#B392F0">arrayBuffer</span><span style="color:#E1E4E8">()),</span></span>
1493
- <span class="line"><span style="color:#E1E4E8"> weight: </span><span style="color:#79B8FF">400</span><span style="color:#E1E4E8">,</span></span>
1494
- <span class="line"><span style="color:#E1E4E8"> style: </span><span style="color:#9ECBFF">&#39;normal&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span></span>
1495
- <span class="line"><span style="color:#E1E4E8"> },</span></span>
1496
- <span class="line"><span style="color:#E1E4E8"> {</span></span>
1497
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;Roboto&#39;</span><span style="color:#E1E4E8">,</span></span>
1498
- <span class="line"><span style="color:#E1E4E8"> data: </span><span style="color:#F97583">await</span><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;https://example.com/Roboto-Bold.woff&#39;</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">r</span><span style="color:#F97583"> =&gt;</span><span style="color:#E1E4E8"> r.</span><span style="color:#B392F0">arrayBuffer</span><span style="color:#E1E4E8">()),</span></span>
1499
- <span class="line"><span style="color:#E1E4E8"> weight: </span><span style="color:#79B8FF">700</span><span style="color:#E1E4E8">,</span></span>
1500
- <span class="line"><span style="color:#E1E4E8"> style: </span><span style="color:#9ECBFF">&#39;normal&#39;</span><span style="color:#F97583"> as</span><span style="color:#F97583"> const</span></span>
1501
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1502
- <span class="line"><span style="color:#E1E4E8">];</span></span>
1503
- <span class="line"></span>
1504
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8"> }, {</span></span>
1505
- <span class="line"><span style="color:#E1E4E8"> fonts: customFonts</span></span>
1506
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1507
- <span class="line"></span></code></pre>
1508
- <p><strong>Supported font formats:</strong> WOFF, TTF, OTF (not WOFF2)</p>
1509
- <h3 id="full-cloudflare-worker-example">Full Cloudflare Worker Example</h3>
1510
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// src/index.ts</span></span>
1511
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage, renderSVG, defineTemplate, initWasm } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk/workers&#39;</span><span style="color:#E1E4E8">;</span></span>
1512
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> React </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;react&#39;</span><span style="color:#E1E4E8">;</span></span>
1513
- <span class="line"></span>
1514
- <span class="line"><span style="color:#F97583">let</span><span style="color:#E1E4E8"> wasmInitialized </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> false</span><span style="color:#E1E4E8">;</span></span>
1515
- <span class="line"></span>
1516
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> ogTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
1517
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
1518
- <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
1519
- <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span></span>
1520
- <span class="line"><span style="color:#9ECBFF"> &#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
1521
- <span class="line"><span style="color:#E1E4E8"> { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 p-12&#39;</span><span style="color:#E1E4E8">) },</span></span>
1522
- <span class="line"><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-7xl font-bold text-white text-center mb-4&#39;</span><span style="color:#E1E4E8">) }, title),</span></span>
1523
- <span class="line"><span style="color:#E1E4E8"> subtitle </span><span style="color:#F97583">&amp;&amp;</span><span style="color:#E1E4E8"> React.</span><span style="color:#B392F0">createElement</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;p&#39;</span><span style="color:#E1E4E8">, { style: </span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-3xl text-white/80&#39;</span><span style="color:#E1E4E8">) }, subtitle)</span></span>
1524
- <span class="line"><span style="color:#E1E4E8"> )</span></span>
1525
- <span class="line"><span style="color:#E1E4E8">});</span></span>
1526
- <span class="line"></span>
1527
- <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#E1E4E8"> {</span></span>
1528
- <span class="line"><span style="color:#F97583"> async</span><span style="color:#B392F0"> fetch</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">request</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Request</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">env</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> any</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">ctx</span><span style="color:#F97583">:</span><span style="color:#B392F0"> ExecutionContext</span><span style="color:#E1E4E8">)</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Promise</span><span style="color:#E1E4E8">&lt;</span><span style="color:#B392F0">Response</span><span style="color:#E1E4E8">&gt; {</span></span>
1529
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> url</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> URL</span><span style="color:#E1E4E8">(request.url);</span></span>
1530
- <span class="line"></span>
1531
- <span class="line"><span style="color:#6A737D"> // Health check</span></span>
1532
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (url.pathname </span><span style="color:#F97583">===</span><span style="color:#9ECBFF"> &#39;/health&#39;</span><span style="color:#E1E4E8">) {</span></span>
1533
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> Response.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({ status: </span><span style="color:#9ECBFF">&#39;ok&#39;</span><span style="color:#E1E4E8">, wasmInitialized });</span></span>
1534
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1535
- <span class="line"></span>
1536
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> title</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> url.searchParams.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;title&#39;</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">||</span><span style="color:#9ECBFF"> &#39;Hello from Loopwind!&#39;</span><span style="color:#E1E4E8">;</span></span>
1537
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> subtitle</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> url.searchParams.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;subtitle&#39;</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">||</span><span style="color:#9ECBFF"> &#39;&#39;</span><span style="color:#E1E4E8">;</span></span>
1538
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> format</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> url.searchParams.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;format&#39;</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">||</span><span style="color:#9ECBFF"> &#39;png&#39;</span><span style="color:#E1E4E8">;</span></span>
1539
- <span class="line"></span>
1540
- <span class="line"><span style="color:#F97583"> try</span><span style="color:#E1E4E8"> {</span></span>
1541
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (format </span><span style="color:#F97583">===</span><span style="color:#9ECBFF"> &#39;svg&#39;</span><span style="color:#E1E4E8">) {</span></span>
1542
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> svg</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderSVG</span><span style="color:#E1E4E8">(ogTemplate, { title, subtitle });</span></span>
1543
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(svg, {</span></span>
1544
- <span class="line"><span style="color:#E1E4E8"> headers: {</span></span>
1545
- <span class="line"><span style="color:#9ECBFF"> &#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/svg+xml&#39;</span><span style="color:#E1E4E8">,</span></span>
1546
- <span class="line"><span style="color:#9ECBFF"> &#39;Cache-Control&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;public, max-age=3600&#39;</span></span>
1547
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1548
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1549
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1550
- <span class="line"></span>
1551
- <span class="line"><span style="color:#6A737D"> // PNG requires WASM</span></span>
1552
- <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">wasmInitialized) {</span></span>
1553
- <span class="line"><span style="color:#F97583"> await</span><span style="color:#B392F0"> initWasm</span><span style="color:#E1E4E8">();</span></span>
1554
- <span class="line"><span style="color:#E1E4E8"> wasmInitialized </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> true</span><span style="color:#E1E4E8">;</span></span>
1555
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1556
- <span class="line"></span>
1557
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(ogTemplate, { title, subtitle }, { format: </span><span style="color:#9ECBFF">&#39;png&#39;</span><span style="color:#E1E4E8">, scale: </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8"> });</span></span>
1558
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(png, {</span></span>
1559
- <span class="line"><span style="color:#E1E4E8"> headers: {</span></span>
1560
- <span class="line"><span style="color:#9ECBFF"> &#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8">,</span></span>
1561
- <span class="line"><span style="color:#9ECBFF"> &#39;Cache-Control&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;public, max-age=3600&#39;</span></span>
1562
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1563
- <span class="line"><span style="color:#E1E4E8"> });</span></span>
1564
- <span class="line"><span style="color:#E1E4E8"> } </span><span style="color:#F97583">catch</span><span style="color:#E1E4E8"> (error) {</span></span>
1565
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">`Error: ${</span><span style="color:#E1E4E8">error</span><span style="color:#9ECBFF">}`</span><span style="color:#E1E4E8">, { status: </span><span style="color:#79B8FF">500</span><span style="color:#E1E4E8"> });</span></span>
1566
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1567
- <span class="line"><span style="color:#E1E4E8"> }</span></span>
1568
- <span class="line"><span style="color:#E1E4E8">};</span></span>
1569
- <span class="line"></span></code></pre>
1570
- <p><strong>Deploy:</strong></p>
1571
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npx</span><span style="color:#9ECBFF"> wrangler</span><span style="color:#9ECBFF"> deploy</span></span>
1572
- <span class="line"></span></code></pre>
1573
- <p><strong>Test:</strong></p>
1574
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="plaintext"><code><span class="line"><span>https://your-worker.workers.dev/?title=Hello&amp;format=png</span></span>
1575
- <span class="line"><span>https://your-worker.workers.dev/?title=Hello&amp;format=svg</span></span>
1576
- <span class="line"><span></span></span></code></pre>
1577
- <h2 id="example-project">Example Project</h2>
1578
- <p>See the full Next.js example at <a href="https://github.com/tomtev/loopwind/tree/main/examples/nextjs-api">examples/nextjs-api/</a></p>
1579
- <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">git</span><span style="color:#9ECBFF"> clone</span><span style="color:#9ECBFF"> https://github.com/tomtev/loopwind.git</span></span>
1580
- <span class="line"><span style="color:#79B8FF">cd</span><span style="color:#9ECBFF"> loopwind/examples/nextjs-api</span></span>
1581
- <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span></span>
1582
- <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> run</span><span style="color:#9ECBFF"> dev</span></span>
1583
- <span class="line"></span></code></pre>
1584
- <p>Then visit:</p>
1585
- <ul>
1586
- <li><code>http://localhost:3000/api/og-image?title=Hello</code></li>
1587
- <li><code>http://localhost:3000/api/intro-video?title=Welcome</code></li>
1588
- </ul> </article> </div> </main> <!-- Table of Contents Sidebar (Desktop only) --> <aside id="toc-sidebar" class="toc-sidebar fixed top-0 right-0 h-screen w-64 overflow-y-auto z-10" data-astro-cid-mw7aashj> <div class="px-6 py-8" data-astro-cid-mw7aashj> <h3 class="text-xs font-semibold text-muted-foreground mb-4 uppercase tracking-wider" data-astro-cid-mw7aashj>On this page</h3> <nav id="toc-nav" data-astro-cid-mw7aashj> <!-- TOC will be populated by JavaScript --> </nav> </div> </aside> </div> </body></html>