loopwind 0.18.0 → 0.19.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 (39) hide show
  1. package/REGISTRY_SETUP.md +1 -55
  2. package/dist/commands/add.d.ts.map +1 -1
  3. package/dist/commands/add.js +0 -3
  4. package/dist/commands/add.js.map +1 -1
  5. package/dist/lib/installer.d.ts +0 -8
  6. package/dist/lib/installer.d.ts.map +1 -1
  7. package/dist/lib/installer.js +1 -48
  8. package/dist/lib/installer.js.map +1 -1
  9. package/dist/sdk/compiler.d.ts +94 -0
  10. package/dist/sdk/compiler.d.ts.map +1 -0
  11. package/dist/sdk/compiler.js +122 -0
  12. package/dist/sdk/compiler.js.map +1 -0
  13. package/dist/sdk/index.d.ts +1 -1
  14. package/dist/sdk/index.d.ts.map +1 -1
  15. package/dist/sdk/index.js +1 -1
  16. package/dist/sdk/index.js.map +1 -1
  17. package/dist/sdk/template.d.ts +30 -43
  18. package/dist/sdk/template.d.ts.map +1 -1
  19. package/dist/sdk/template.js +52 -73
  20. package/dist/sdk/template.js.map +1 -1
  21. package/examples/template-compiler-workflow.ts +251 -0
  22. package/output/sdk-static.jpg +0 -0
  23. package/package.json +7 -1
  24. package/test-jsx-support.mjs +146 -0
  25. package/test-sdk-source-config.mjs +427 -0
  26. package/test-templates/config-test.mjs +17 -0
  27. package/website/astro.config.mjs +10 -0
  28. package/website/dist/.gitkeep +1 -0
  29. package/website/dist/_worker.js/index.js +1 -1
  30. package/website/dist/_worker.js/{manifest_BAAoOzaU.mjs → manifest_CT_D-YDe.mjs} +1 -1
  31. package/website/dist/llm.txt +1 -1
  32. package/website/dist/sdk/index.html +405 -102
  33. package/website/dist/sitemap.xml +12 -12
  34. package/website/package-lock.json +1077 -17
  35. package/website/package.json +5 -1
  36. package/website/public/.gitkeep +1 -0
  37. package/website/deploy.sh +0 -19
  38. package/website/public/.assetsignore +0 -3
  39. package/website/wrangler.toml +0 -12
@@ -5,26 +5,56 @@
5
5
  <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>
6
6
  <span class="line"></span></code></pre>
7
7
  <h2 id="quick-start">Quick Start</h2>
8
- <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>
9
- <span class="line"></span>
10
- <span class="line"><span style="color:#6A737D">// Define template programmatically</span></span>
11
- <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>
8
+ <p>Create a template file in <code>_loopwind/templates/</code> or <code>templates/</code>:</p>
9
+ <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>
10
+ <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>
12
11
  <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
12
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;OpenGraph image&#39;</span><span style="color:#E1E4E8">,</span></span>
13
+ <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>
13
14
  <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>
14
- <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:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
15
- <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 bg-gradient-to-br from-blue-600 to-purple-700 p-12&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
16
- <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>
17
- <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 text-white/80&#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>
18
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
19
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
15
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
16
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
17
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;string?&#39;</span><span style="color:#E1E4E8">,</span></span>
18
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
19
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
20
+ <span class="line"></span>
21
+ <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>
22
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
23
+ <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>
24
+ <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>
25
+ <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>
26
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
27
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
28
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
29
+ <span class="line"></span></code></pre>
30
+ <p>Use it in your API route:</p>
31
+ <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"> { defineTemplateFromFile, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
32
+ <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>
33
+ <span class="line"></span>
34
+ <span class="line"><span style="color:#6A737D">// Load template from file</span></span>
35
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
36
+ <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
37
+ <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
38
+ <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
39
+ <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
40
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
41
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
20
42
  <span class="line"><span style="color:#E1E4E8">});</span></span>
21
43
  <span class="line"></span>
22
44
  <span class="line"><span style="color:#6A737D">// Render to buffer</span></span>
23
- <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>
45
+ <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>
24
46
  <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Hello World&#39;</span><span style="color:#E1E4E8">,</span></span>
25
47
  <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>
26
48
  <span class="line"><span style="color:#E1E4E8">});</span></span>
27
49
  <span class="line"></span></code></pre>
50
+ <p><strong>Benefits:</strong></p>
51
+ <ul>
52
+ <li>✅ Reuse templates between CLI and SDK</li>
53
+ <li>✅ Keep templates in separate files (clean organization)</li>
54
+ <li>✅ Standard loopwind format</li>
55
+ <li>✅ Full JSX support with <code>&lt;div&gt;</code>, <code>&lt;h1&gt;</code>, etc.</li>
56
+ <li>✅ Works with any bundler (Next.js, Vite, Remix, etc.)</li>
57
+ </ul>
28
58
  <h2 id="sdk-vs-cli">SDK vs CLI</h2>
29
59
  <p>The SDK is designed to be <strong>completely stateless</strong> and <strong>self-contained</strong> - perfect for serverless environments like Vercel, Netlify, or AWS Lambda.</p>
30
60
  <p><strong>Key differences:</strong></p>
@@ -68,7 +98,26 @@
68
98
  </ol>
69
99
  <p><strong>Priority:</strong> Options config overrides template config.</p>
70
100
  <h3 id="basic-example">Basic Example</h3>
71
- <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>
101
+ <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>
102
+ <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>
103
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
104
+ <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>
105
+ <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>
106
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
107
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
108
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
109
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
110
+ <span class="line"></span>
111
+ <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>
112
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
113
+ <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>
114
+ <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>
115
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
116
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
117
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
118
+ <span class="line"></span></code></pre>
119
+ <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"> { defineTemplateFromFile, 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>
120
+ <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>
72
121
  <span class="line"></span>
73
122
  <span class="line"><span style="color:#6A737D">// Define your style config</span></span>
74
123
  <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>
@@ -82,16 +131,9 @@
82
131
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
83
132
  <span class="line"><span style="color:#E1E4E8">};</span></span>
84
133
  <span class="line"></span>
85
- <span class="line"><span style="color:#6A737D">// Option 1: Pass config to template definition</span></span>
86
- <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>
87
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
88
- <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>
134
+ <span class="line"><span style="color:#6A737D">// Option 1: Pass config when defining template</span></span>
135
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
89
136
  <span class="line"><span style="color:#E1E4E8"> config, </span><span style="color:#6A737D">// Fixed theme for this template</span></span>
90
- <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>
91
- <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-background&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
92
- <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-primary&#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>
93
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
94
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
95
137
  <span class="line"><span style="color:#E1E4E8">});</span></span>
96
138
  <span class="line"></span>
97
139
  <span class="line"><span style="color:#6A737D">// Option 2: Pass config at render time</span></span>
@@ -105,24 +147,39 @@
105
147
  <span class="line"></span></code></pre>
106
148
  <h3 id="dynamic-theming">Dynamic Theming</h3>
107
149
  <p>Perfect for multi-tenant apps or user-customizable themes:</p>
108
- <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"> brandTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
150
+ <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>
151
+ <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>
109
152
  <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;branded-og&#39;</span><span style="color:#E1E4E8">,</span></span>
153
+ <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>
110
154
  <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>
111
- <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>
112
- <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 bg-background p-12 justify-between&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
113
- <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 gap-4&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
114
- <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;w-16 h-16 rounded-full bg-brand&#39;</span><span style="color:#E1E4E8">)} </span><span style="color:#F97583">/&gt;</span></span>
115
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
116
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#B392F0">div</span><span style="color:#E1E4E8">&gt;</span></span>
117
- <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-7xl font-bold text-foreground mb-4&#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>
118
- <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-3xl text-muted-foreground&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{subtitle}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
119
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
120
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
121
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
122
- <span class="line"><span style="color:#E1E4E8">});</span></span>
155
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
156
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
157
+ <span class="line"><span style="color:#E1E4E8"> subtitle: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
158
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
159
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
160
+ <span class="line"></span>
161
+ <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>
162
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
163
+ <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>
164
+ <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>
165
+ <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>
166
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
167
+ <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
168
+ <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>
169
+ <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>
170
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
171
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
172
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
173
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
174
+ <span class="line"></span></code></pre>
175
+ <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"> { defineTemplateFromFile, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
176
+ <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>
177
+ <span class="line"></span>
178
+ <span class="line"><span style="color:#6A737D">// Load template</span></span>
179
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(brandTemplate);</span></span>
123
180
  <span class="line"></span>
124
181
  <span class="line"><span style="color:#6A737D">// Render with different brand colors for each customer</span></span>
125
- <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">(brandTemplate,</span></span>
182
+ <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>
126
183
  <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>
127
184
  <span class="line"><span style="color:#E1E4E8"> {</span></span>
128
185
  <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
@@ -136,7 +193,7 @@
136
193
  <span class="line"><span style="color:#E1E4E8"> }</span></span>
137
194
  <span class="line"><span style="color:#E1E4E8">);</span></span>
138
195
  <span class="line"></span>
139
- <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">(brandTemplate,</span></span>
196
+ <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>
140
197
  <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>
141
198
  <span class="line"><span style="color:#E1E4E8"> {</span></span>
142
199
  <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
@@ -235,19 +292,30 @@
235
292
  <span class="line"><span style="color:#E1E4E8">});</span></span>
236
293
  <span class="line"></span></code></pre>
237
294
  <h2 id="core-functions">Core Functions</h2>
238
- <h3 id="definetemplate">defineTemplate()</h3>
239
- <p>Define a template programmatically without any file system dependencies:</p>
240
- <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">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>
241
- <span class="line"></span>
242
- <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>
243
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// Template name</span></span>
244
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// &#39;image&#39; or &#39;video&#39;</span></span>
295
+ <h3 id="definetemplatefromfile-recommended">defineTemplateFromFile() (Recommended)</h3>
296
+ <p>Load a template from a separate file (standard loopwind format):</p>
297
+ <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>
298
+ <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>
299
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">,</span></span>
300
+ <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>
245
301
  <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>
302
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
303
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
304
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
305
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
246
306
  <span class="line"></span>
247
- <span class="line"><span style="color:#6A737D"> // For videos only</span></span>
248
- <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>
307
+ <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>
308
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
309
+ <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>
310
+ <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>
311
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</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></code></pre>
315
+ <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"> { defineTemplateFromFile } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
316
+ <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>
249
317
  <span class="line"></span>
250
- <span class="line"><span style="color:#6A737D"> // Optional style config (colors, fonts, etc.)</span></span>
318
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(myTemplate, {</span></span>
251
319
  <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
252
320
  <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
253
321
  <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
@@ -257,8 +325,29 @@
257
325
  <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>
258
326
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
259
327
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
328
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
329
+ <span class="line"></span></code></pre>
330
+ <p><strong>Benefits:</strong></p>
331
+ <ul>
332
+ <li>✅ Reuse templates between CLI and SDK</li>
333
+ <li>✅ Clean separation of concerns</li>
334
+ <li>✅ Full JSX support with <code>&lt;div&gt;</code>, <code>&lt;h1&gt;</code>, etc.</li>
335
+ <li>✅ Standard loopwind format</li>
336
+ </ul>
337
+ <h3 id="definetemplate-alternative">defineTemplate() (Alternative)</h3>
338
+ <p>For inline templates without separate files:</p>
339
+ <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>
260
340
  <span class="line"></span>
261
- <span class="line"><span style="color:#6A737D"> // The render function</span></span>
341
+ <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>
342
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">,</span></span>
343
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#E1E4E8">,</span></span>
344
+ <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>
345
+ <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
346
+ <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
347
+ <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
348
+ <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
349
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
350
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
262
351
  <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>
263
352
  <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-background&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
264
353
  <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-primary&#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>
@@ -266,6 +355,12 @@
266
355
  <span class="line"><span style="color:#E1E4E8"> ),</span></span>
267
356
  <span class="line"><span style="color:#E1E4E8">});</span></span>
268
357
  <span class="line"></span></code></pre>
358
+ <p><strong>Use when:</strong></p>
359
+ <ul>
360
+ <li>Simple, one-off templates</li>
361
+ <li>No need to reuse in CLI</li>
362
+ <li>Keeping everything in one file</li>
363
+ </ul>
269
364
  <h3 id="renderimage">renderImage()</h3>
270
365
  <p>Render an image template to a Buffer:</p>
271
366
  <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>
@@ -303,63 +398,262 @@
303
398
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
304
399
  <span class="line"><span style="color:#E1E4E8">});</span></span>
305
400
  <span class="line"></span></code></pre>
306
- <h2 id="importing-template-files">Importing Template Files</h2>
307
- <p>You can reuse file-based templates from <code>_loopwind/templates/</code> in your SDK code! Perfect for sharing templates between CLI and API.</p>
308
- <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">// Import a template file</span></span>
309
- <span class="line"><span style="color:#F97583">import</span><span style="color:#79B8FF"> *</span><span style="color:#F97583"> as</span><span style="color:#E1E4E8"> videoIntro </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;./_loopwind/templates/video-intro/template&#39;</span><span style="color:#E1E4E8">;</span></span>
310
- <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplateFromFile, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
401
+ <h2 id="template-organization">Template Organization</h2>
402
+ <h3 id="project-structure">Project Structure</h3>
403
+ <p>Recommended structure for using templates in your project:</p>
404
+ <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>
405
+ <span class="line"><span>├── _loopwind/</span></span>
406
+ <span class="line"><span>│ ├── loopwind.json # Optional: shared config</span></span>
407
+ <span class="line"><span>│ └── templates/</span></span>
408
+ <span class="line"><span>│ ├── og-image/</span></span>
409
+ <span class="line"><span>│ │ └── template.tsx # Image template</span></span>
410
+ <span class="line"><span>│ └── intro-video/</span></span>
411
+ <span class="line"><span>│ └── template.tsx # Video template</span></span>
412
+ <span class="line"><span>├── pages/</span></span>
413
+ <span class="line"><span>│ └── api/</span></span>
414
+ <span class="line"><span>│ ├── og-image.ts # API route using og-image</span></span>
415
+ <span class="line"><span>│ └── intro-video.ts # API route using intro-video</span></span>
416
+ <span class="line"><span>└── package.json</span></span>
417
+ <span class="line"><span></span></span></code></pre>
418
+ <p>Or with a top-level <code>templates/</code> folder (like in Astro):</p>
419
+ <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>
420
+ <span class="line"><span>├── templates/</span></span>
421
+ <span class="line"><span>│ ├── og-image.tsx # Image template</span></span>
422
+ <span class="line"><span>│ └── intro-video.tsx # Video template</span></span>
423
+ <span class="line"><span>├── src/</span></span>
424
+ <span class="line"><span>│ └── pages/</span></span>
425
+ <span class="line"><span>│ └── api/</span></span>
426
+ <span class="line"><span>│ └── og/</span></span>
427
+ <span class="line"><span>│ └── [slug].ts # API route</span></span>
428
+ <span class="line"><span>└── tsconfig.json # With path aliases</span></span>
429
+ <span class="line"><span></span></span></code></pre>
430
+ <h3 id="path-aliases-recommended">Path Aliases (Recommended)</h3>
431
+ <p>Use TypeScript path aliases for cleaner imports:</p>
432
+ <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>
433
+ <span class="line"><span style="color:#E1E4E8">{</span></span>
434
+ <span class="line"><span style="color:#79B8FF"> &quot;compilerOptions&quot;</span><span style="color:#E1E4E8">: {</span></span>
435
+ <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>
436
+ <span class="line"><span style="color:#79B8FF"> &quot;paths&quot;</span><span style="color:#E1E4E8">: {</span></span>
437
+ <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>
438
+ <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>
439
+ <span class="line"><span style="color:#E1E4E8"> }</span></span>
440
+ <span class="line"><span style="color:#E1E4E8"> }</span></span>
441
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
442
+ <span class="line"></span></code></pre>
443
+ <p>Then import with clean paths:</p>
444
+ <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>
445
+ <span class="line"></span></code></pre>
446
+ <h3 id="sharing-templates">Sharing Templates</h3>
447
+ <p>Templates are <strong>portable</strong> - copy the template folder to share between projects:</p>
448
+ <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>
449
+ <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>
450
+ <span class="line"></span>
451
+ <span class="line"><span style="color:#6A737D"># Or publish as npm package</span></span>
452
+ <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> publish</span><span style="color:#9ECBFF"> loopwind-templates</span></span>
453
+ <span class="line"></span></code></pre>
454
+ <h2 id="user-generated-templates">User-Generated Templates</h2>
455
+ <p>Build template editors where users create templates without writing code!</p>
456
+ <h3 id="code-editor-definetemplatefromsource">Code Editor (defineTemplateFromSource)</h3>
457
+ <p>For code editors where users write template source as strings:</p>
458
+ <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"> { defineTemplateFromSource, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
459
+ <span class="line"></span>
460
+ <span class="line"><span style="color:#6A737D">// User writes template code in Monaco Editor, CodeMirror, etc.</span></span>
461
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> userCode</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> `</span></span>
462
+ <span class="line"><span style="color:#9ECBFF">export const meta = {</span></span>
463
+ <span class="line"><span style="color:#9ECBFF"> name: &#39;user-card&#39;,</span></span>
464
+ <span class="line"><span style="color:#9ECBFF"> type: &#39;image&#39;,</span></span>
465
+ <span class="line"><span style="color:#9ECBFF"> size: { width: 1200, height: 630 }</span></span>
466
+ <span class="line"><span style="color:#9ECBFF">};</span></span>
467
+ <span class="line"></span>
468
+ <span class="line"><span style="color:#9ECBFF">export default ({ tw, title, description }) =&gt; (</span></span>
469
+ <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>
470
+ <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>
471
+ <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>
472
+ <span class="line"><span style="color:#9ECBFF"> &lt;/div&gt;</span></span>
473
+ <span class="line"><span style="color:#9ECBFF">);</span></span>
474
+ <span class="line"><span style="color:#9ECBFF">`</span><span style="color:#E1E4E8">;</span></span>
475
+ <span class="line"></span>
476
+ <span class="line"><span style="color:#6A737D">// Create template from source string</span></span>
477
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromSource</span><span style="color:#E1E4E8">(userCode, {</span></span>
478
+ <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
479
+ <span class="line"><span style="color:#E1E4E8"> colors: { primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8"> }</span></span>
480
+ <span class="line"><span style="color:#E1E4E8"> }</span></span>
481
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
482
+ <span class="line"></span>
483
+ <span class="line"><span style="color:#6A737D">// Render</span></span>
484
+ <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>
485
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;User Created&#39;</span><span style="color:#E1E4E8">,</span></span>
486
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;From code editor&#39;</span></span>
487
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
488
+ <span class="line"></span></code></pre>
489
+ <p><strong>⚠️ Security Warning:</strong></p>
490
+ <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>
491
+ <p><strong>Safe use cases:</strong></p>
492
+ <ul>
493
+ <li>Admin-created templates stored in database</li>
494
+ <li>Templates from authenticated, trusted users</li>
495
+ <li>Code editor with validation/sandboxing</li>
496
+ </ul>
497
+ <p><strong>Template format:</strong></p>
498
+ <ul>
499
+ <li>Must export <code>meta</code> object with <code>name</code>, <code>size</code>, and optional <code>type</code>/<code>video</code></li>
500
+ <li>Must have <code>export default</code> render function</li>
501
+ <li><strong>Supports JSX syntax!</strong> Write normal React components with <code>&lt;div&gt;</code>, <code>&lt;h1&gt;</code>, etc.</li>
502
+ <li>Props use curly braces: <code>style={tw(&#39;...&#39;)}</code>, <code>{title}</code></li>
503
+ </ul>
504
+ <h3 id="visual-builder-definetemplatefromschema">Visual Builder (defineTemplateFromSchema)</h3>
505
+ <p>For drag-and-drop template builders with JSON schemas:</p>
506
+ <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>
311
507
  <span class="line"></span>
312
- <span class="line"><span style="color:#6A737D">// Create SDK template from file with optional config override</span></span>
313
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(videoIntro, {</span></span>
508
+ <span class="line"><span style="color:#6A737D">// Schema generated by visual builder UI</span></span>
509
+ <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>
510
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;visual-card&#39;</span><span style="color:#E1E4E8">,</span></span>
511
+ <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>
314
512
  <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
315
513
  <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
316
- <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#ff0000&#39;</span><span style="color:#E1E4E8">,</span></span>
514
+ <span class="line"><span style="color:#E1E4E8"> primary: </span><span style="color:#9ECBFF">&#39;#3b82f6&#39;</span><span style="color:#E1E4E8">,</span></span>
515
+ <span class="line"><span style="color:#E1E4E8"> background: </span><span style="color:#9ECBFF">&#39;#ffffff&#39;</span><span style="color:#E1E4E8">,</span></span>
317
516
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
318
517
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
319
- <span class="line"><span style="color:#E1E4E8">});</span></span>
518
+ <span class="line"><span style="color:#E1E4E8"> elements: [</span></span>
519
+ <span class="line"><span style="color:#E1E4E8"> {</span></span>
520
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
521
+ <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>
522
+ <span class="line"><span style="color:#E1E4E8"> children: [</span></span>
523
+ <span class="line"><span style="color:#E1E4E8"> {</span></span>
524
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">,</span></span>
525
+ <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>
526
+ <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>
527
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
528
+ <span class="line"><span style="color:#E1E4E8"> {</span></span>
529
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;p&#39;</span><span style="color:#E1E4E8">,</span></span>
530
+ <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>
531
+ <span class="line"><span style="color:#E1E4E8"> content: </span><span style="color:#9ECBFF">&#39;{{description}}&#39;</span><span style="color:#E1E4E8">,</span></span>
532
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
533
+ <span class="line"><span style="color:#E1E4E8"> ],</span></span>
534
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
535
+ <span class="line"><span style="color:#E1E4E8"> ],</span></span>
536
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
537
+ <span class="line"></span>
538
+ <span class="line"><span style="color:#6A737D">// Create template from schema</span></span>
539
+ <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>
320
540
  <span class="line"></span>
321
541
  <span class="line"><span style="color:#6A737D">// Render with props</span></span>
322
- <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>
323
- <span class="line"><span style="color:#E1E4E8"> version: </span><span style="color:#9ECBFF">&#39;2.0.0&#39;</span><span style="color:#E1E4E8">,</span></span>
324
- <span class="line"><span style="color:#E1E4E8"> changes: [</span><span style="color:#9ECBFF">&#39;New feature added&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;Bug fixes&#39;</span><span style="color:#E1E4E8">],</span></span>
542
+ <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>
543
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Built Visually&#39;</span><span style="color:#E1E4E8">,</span></span>
544
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;No code required!&#39;</span></span>
325
545
  <span class="line"><span style="color:#E1E4E8">});</span></span>
326
546
  <span class="line"></span></code></pre>
547
+ <p><strong>Schema Features:</strong></p>
548
+ <ul>
549
+ <li><strong>Elements:</strong> Supports <code>div</code>, <code>span</code>, <code>h1</code>-<code>h6</code>, <code>p</code>, <code>img</code></li>
550
+ <li><strong>Styling:</strong> Use <code>className</code> for Tailwind classes or <code>style</code> for inline styles</li>
551
+ <li><strong>Variables:</strong> Use <code>{{variableName}}</code> in <code>content</code> or <code>src</code> for dynamic values</li>
552
+ <li><strong>Nesting:</strong> Unlimited nested <code>children</code> arrays</li>
553
+ <li><strong>Type-safe:</strong> Full TypeScript support with <code>TemplateSchema</code> type</li>
554
+ </ul>
555
+ <p><strong>Example with dynamic branding:</strong></p>
556
+ <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>
557
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;branded&#39;</span><span style="color:#E1E4E8">,</span></span>
558
+ <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>
559
+ <span class="line"><span style="color:#E1E4E8"> elements: [</span></span>
560
+ <span class="line"><span style="color:#E1E4E8"> {</span></span>
561
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
562
+ <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>
563
+ <span class="line"><span style="color:#E1E4E8"> children: [</span></span>
564
+ <span class="line"><span style="color:#E1E4E8"> {</span></span>
565
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;div&#39;</span><span style="color:#E1E4E8">,</span></span>
566
+ <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>
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"> type: </span><span style="color:#9ECBFF">&#39;h1&#39;</span><span style="color:#E1E4E8">,</span></span>
570
+ <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>
571
+ <span class="line"><span style="color:#E1E4E8"> content: </span><span style="color:#9ECBFF">&#39;{{companyName}}&#39;</span><span style="color:#E1E4E8">,</span></span>
572
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
573
+ <span class="line"><span style="color:#E1E4E8"> ],</span></span>
574
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
575
+ <span class="line"><span style="color:#E1E4E8"> ],</span></span>
576
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
577
+ <span class="line"></span>
578
+ <span class="line"><span style="color:#6A737D">// Render with different brands</span></span>
579
+ <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>
580
+ <span class="line"><span style="color:#B392F0"> defineTemplateFromSchema</span><span style="color:#E1E4E8">({</span></span>
581
+ <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">brandedSchema,</span></span>
582
+ <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
583
+ <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
584
+ <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>
585
+ <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>
586
+ <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>
587
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
588
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
589
+ <span class="line"><span style="color:#E1E4E8"> }),</span></span>
590
+ <span class="line"><span style="color:#E1E4E8"> { companyName: </span><span style="color:#9ECBFF">&#39;TechCorp&#39;</span><span style="color:#E1E4E8"> }</span></span>
591
+ <span class="line"><span style="color:#E1E4E8">);</span></span>
592
+ <span class="line"></span>
593
+ <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>
594
+ <span class="line"><span style="color:#B392F0"> defineTemplateFromSchema</span><span style="color:#E1E4E8">({</span></span>
595
+ <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">brandedSchema,</span></span>
596
+ <span class="line"><span style="color:#E1E4E8"> config: {</span></span>
597
+ <span class="line"><span style="color:#E1E4E8"> colors: {</span></span>
598
+ <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>
599
+ <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>
600
+ <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>
601
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
602
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
603
+ <span class="line"><span style="color:#E1E4E8"> }),</span></span>
604
+ <span class="line"><span style="color:#E1E4E8"> { companyName: </span><span style="color:#9ECBFF">&#39;DesignStudio&#39;</span><span style="color:#E1E4E8"> }</span></span>
605
+ <span class="line"><span style="color:#E1E4E8">);</span></span>
606
+ <span class="line"></span></code></pre>
327
607
  <p><strong>Benefits:</strong></p>
328
608
  <ul>
329
- <li>✅ Reuse templates between CLI and SDK</li>
330
- <li>✅ Keep templates in separate files (better organization)</li>
331
- <li>✅ Override config at SDK level</li>
332
- <li>✅ Type-safe with TypeScript</li>
333
- <li>✅ Works with any bundler (Next.js, Vite, webpack, etc.)</li>
609
+ <li>✅ <strong>Safe</strong> - No code execution, just JSON</li>
610
+ <li>✅ <strong>User-friendly</strong> - Perfect for drag-and-drop builders</li>
611
+ <li>✅ <strong>Dynamic</strong> - Variable interpolation with <code>{{}}</code> syntax</li>
612
+ <li>✅ <strong>Flexible</strong> - Support images, styles, nesting</li>
613
+ <li>✅ <strong>Type-safe</strong> - Full TypeScript support</li>
334
614
  </ul>
335
- <p><strong>Note:</strong> Requires a bundler that handles TypeScript/TSX imports (Next.js, Vite, etc.)</p>
336
615
  <h2 id="nextjs-api-routes">Next.js API Routes</h2>
337
616
  <h3 id="image-generation">Image Generation</h3>
617
+ <p>Create your template:</p>
618
+ <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>
619
+ <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>
620
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
621
+ <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>
622
+ <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>
623
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
624
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
625
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
626
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
627
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
628
+ <span class="line"></span>
629
+ <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>
630
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
631
+ <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>
632
+ <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>
633
+ <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>
634
+ <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>
635
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
636
+ <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
637
+ <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>
638
+ <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>
639
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
640
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
641
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
642
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
643
+ <span class="line"></span></code></pre>
644
+ <p>Use it in your API route:</p>
338
645
  <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>
339
646
  <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>
340
- <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>
647
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplateFromFile, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
648
+ <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>
341
649
  <span class="line"></span>
342
- <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>
343
- <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
344
- <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>
345
- <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:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
346
- <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 bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
347
- <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 gap-4&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
348
- <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;w-12 h-12 rounded-full bg-white/20&#39;</span><span style="color:#E1E4E8">)} </span><span style="color:#F97583">/&gt;</span></span>
349
- <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">span 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-white/80 text-2xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">yoursite.com</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">span</span><span style="color:#F97583">&gt;</span></span>
350
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
351
- <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#B392F0">div</span><span style="color:#E1E4E8">&gt;</span></span>
352
- <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 mb-4&#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>
353
- <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 text-white/80&#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>
354
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
355
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
356
- <span class="line"><span style="color:#E1E4E8"> ),</span></span>
357
- <span class="line"><span style="color:#E1E4E8">});</span></span>
650
+ <span class="line"><span style="color:#6A737D">// Load template once at module level (cached)</span></span>
651
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(ogTemplate);</span></span>
358
652
  <span class="line"></span>
359
653
  <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>
360
654
  <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>
361
655
  <span class="line"></span>
362
- <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>
656
+ <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>
363
657
  <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
364
658
  <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(description),</span></span>
365
659
  <span class="line"><span style="color:#E1E4E8"> });</span></span>
@@ -373,33 +667,42 @@
373
667
  <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>
374
668
  <span class="line"><span></span></span></code></pre>
375
669
  <h3 id="video-generation">Video Generation</h3>
376
- <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>
377
- <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>
378
- <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>
379
- <span class="line"></span>
380
- <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> introTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
670
+ <p>Create your template:</p>
671
+ <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>
672
+ <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>
381
673
  <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;intro-video&#39;</span><span style="color:#E1E4E8">,</span></span>
382
- <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
674
+ <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>
383
675
  <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>
384
676
  <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>
385
- <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>
386
- <span class="line"><span style="color:#6A737D"> // Fade in animation</span></span>
387
- <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> opacity</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> progress </span><span style="color:#F97583">&lt;</span><span style="color:#79B8FF"> 0.5</span><span style="color:#F97583"> ?</span><span style="color:#E1E4E8"> progress </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 0.5</span><span style="color:#F97583"> :</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">;</span></span>
388
- <span class="line"></span>
389
- <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
390
- <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-gradient-to-br from-black to-gray-900&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
391
- <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">), opacity }}</span><span style="color:#F97583">&gt;</span></span>
392
- <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
393
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
394
- <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
395
- <span class="line"><span style="color:#E1E4E8"> );</span></span>
677
+ <span class="line"><span style="color:#E1E4E8"> props: {</span></span>
678
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;string&#39;</span><span style="color:#E1E4E8">,</span></span>
396
679
  <span class="line"><span style="color:#E1E4E8"> },</span></span>
397
- <span class="line"><span style="color:#E1E4E8">});</span></span>
680
+ <span class="line"><span style="color:#E1E4E8">};</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"> 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>
683
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
684
+ <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>
685
+ <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>
686
+ <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>
687
+ <span class="line"><span style="color:#E1E4E8"> {title}</span></span>
688
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">&gt;</span></span>
689
+ <span class="line"><span style="color:#E1E4E8"> &lt;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">&gt;</span></span>
690
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
691
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
692
+ <span class="line"></span></code></pre>
693
+ <p>Use it in your API route:</p>
694
+ <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>
695
+ <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>
696
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplateFromFile, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
697
+ <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>
698
+ <span class="line"></span>
699
+ <span class="line"><span style="color:#6A737D">// Load template once at module level (cached)</span></span>
700
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplateFromFile</span><span style="color:#E1E4E8">(introTemplate);</span></span>
398
701
  <span class="line"></span>
399
702
  <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>
400
703
  <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>
401
704
  <span class="line"></span>
402
- <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">(introTemplate, {</span></span>
705
+ <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>
403
706
  <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
404
707
  <span class="line"><span style="color:#E1E4E8"> });</span></span>
405
708
  <span class="line"></span>