loopwind 0.9.1

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 (245) hide show
  1. package/FONTS.md +156 -0
  2. package/HELPERS_DEMO.md +134 -0
  3. package/PROJECT_STRUCTURE.md +286 -0
  4. package/PUBLISHING.md +171 -0
  5. package/README.md +1020 -0
  6. package/REGISTRY_SETUP.md +427 -0
  7. package/SHADCN_INTEGRATION.md +269 -0
  8. package/TAILWIND.md +228 -0
  9. package/TEMPLATE_SOURCES.md +363 -0
  10. package/_dsgn/templates/banner-hero/banner-hero.tsx +57 -0
  11. package/_dsgn/templates/banner-hero/meta.json +14 -0
  12. package/_dsgn/templates/composite-card/meta.json +16 -0
  13. package/_dsgn/templates/composite-card/template.tsx +44 -0
  14. package/_dsgn/templates/image/meta.json +13 -0
  15. package/_dsgn/templates/image/template.tsx +28 -0
  16. package/_dsgn/templates/kitchen-sink/meta.json +13 -0
  17. package/_dsgn/templates/kitchen-sink/template.tsx +72 -0
  18. package/_dsgn/templates/qr-card/meta.json +14 -0
  19. package/_dsgn/templates/qr-card/template.tsx +39 -0
  20. package/_dsgn/templates/test-parent/child/meta.json +11 -0
  21. package/_dsgn/templates/test-parent/child/template.tsx +27 -0
  22. package/_dsgn/templates/test-parent/meta.json +12 -0
  23. package/_dsgn/templates/test-parent/template.tsx +30 -0
  24. package/_dsgn/templates/test-sibling/meta.json +11 -0
  25. package/_dsgn/templates/test-sibling/template.tsx +20 -0
  26. package/_dsgn/templates/video/.tmp/template-1763421345296.mjs +43 -0
  27. package/_dsgn/templates/video/.tmp/template-1763421362228.mjs +43 -0
  28. package/_dsgn/templates/video/.tmp/template-1763421377706.mjs +43 -0
  29. package/_dsgn/templates/video/meta.json +17 -0
  30. package/_dsgn/templates/video/template.tsx +48 -0
  31. package/dist/cli.d.ts +3 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +70 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/commands/add.d.ts +6 -0
  36. package/dist/commands/add.d.ts.map +1 -0
  37. package/dist/commands/add.js +86 -0
  38. package/dist/commands/add.js.map +1 -0
  39. package/dist/commands/default.d.ts +2 -0
  40. package/dist/commands/default.d.ts.map +1 -0
  41. package/dist/commands/default.js +69 -0
  42. package/dist/commands/default.js.map +1 -0
  43. package/dist/commands/init.d.ts +2 -0
  44. package/dist/commands/init.d.ts.map +1 -0
  45. package/dist/commands/init.js +75 -0
  46. package/dist/commands/init.js.map +1 -0
  47. package/dist/commands/list.d.ts +2 -0
  48. package/dist/commands/list.d.ts.map +1 -0
  49. package/dist/commands/list.js +83 -0
  50. package/dist/commands/list.js.map +1 -0
  51. package/dist/commands/preview.d.ts +3 -0
  52. package/dist/commands/preview.d.ts.map +1 -0
  53. package/dist/commands/preview.js +296 -0
  54. package/dist/commands/preview.js.map +1 -0
  55. package/dist/commands/render.d.ts +10 -0
  56. package/dist/commands/render.d.ts.map +1 -0
  57. package/dist/commands/render.js +204 -0
  58. package/dist/commands/render.js.map +1 -0
  59. package/dist/commands/validate.d.ts +2 -0
  60. package/dist/commands/validate.d.ts.map +1 -0
  61. package/dist/commands/validate.js +107 -0
  62. package/dist/commands/validate.js.map +1 -0
  63. package/dist/default-templates/AGENTS.md +229 -0
  64. package/dist/default-templates/image/meta.json +13 -0
  65. package/dist/default-templates/image/template.d.ts +20 -0
  66. package/dist/default-templates/image/template.d.ts.map +1 -0
  67. package/dist/default-templates/image/template.js +18 -0
  68. package/dist/default-templates/image/template.js.map +1 -0
  69. package/dist/default-templates/image/template.tsx +20 -0
  70. package/dist/default-templates/image-template/meta.json +13 -0
  71. package/dist/default-templates/image-template/template.tsx +19 -0
  72. package/dist/default-templates/kitchen-sink/meta.json +13 -0
  73. package/dist/default-templates/kitchen-sink/template.tsx +64 -0
  74. package/dist/default-templates/page/meta.json +17 -0
  75. package/dist/default-templates/page/template.tsx +37 -0
  76. package/dist/default-templates/video/meta.json +17 -0
  77. package/dist/default-templates/video/template.d.ts +26 -0
  78. package/dist/default-templates/video/template.d.ts.map +1 -0
  79. package/dist/default-templates/video/template.js +33 -0
  80. package/dist/default-templates/video/template.js.map +1 -0
  81. package/dist/default-templates/video/template.tsx +37 -0
  82. package/dist/default-templates/video-template/meta.json +17 -0
  83. package/dist/default-templates/video-template/template.tsx +36 -0
  84. package/dist/default-templates/website/meta.json +16 -0
  85. package/dist/default-templates/website/pages/home.tsx +17 -0
  86. package/dist/default-templates/website/parts/footer.tsx +17 -0
  87. package/dist/default-templates/website/parts/header.tsx +17 -0
  88. package/dist/default-templates/website/template.tsx +17 -0
  89. package/dist/default-templates/website-template/meta.json +16 -0
  90. package/dist/default-templates/website-template/pages/home.tsx +16 -0
  91. package/dist/default-templates/website-template/parts/footer.tsx +16 -0
  92. package/dist/default-templates/website-template/parts/header.tsx +16 -0
  93. package/dist/default-templates/website-template/template.tsx +16 -0
  94. package/dist/lib/config.d.ts +34 -0
  95. package/dist/lib/config.d.ts.map +1 -0
  96. package/dist/lib/config.js +248 -0
  97. package/dist/lib/config.js.map +1 -0
  98. package/dist/lib/constants.d.ts +7 -0
  99. package/dist/lib/constants.d.ts.map +1 -0
  100. package/dist/lib/constants.js +12 -0
  101. package/dist/lib/constants.js.map +1 -0
  102. package/dist/lib/helpers.d.ts +29 -0
  103. package/dist/lib/helpers.d.ts.map +1 -0
  104. package/dist/lib/helpers.js +159 -0
  105. package/dist/lib/helpers.js.map +1 -0
  106. package/dist/lib/installer.d.ts +51 -0
  107. package/dist/lib/installer.d.ts.map +1 -0
  108. package/dist/lib/installer.js +215 -0
  109. package/dist/lib/installer.js.map +1 -0
  110. package/dist/lib/renderer.d.ts +51 -0
  111. package/dist/lib/renderer.d.ts.map +1 -0
  112. package/dist/lib/renderer.js +524 -0
  113. package/dist/lib/renderer.js.map +1 -0
  114. package/dist/lib/tailwind-config-loader.d.ts +47 -0
  115. package/dist/lib/tailwind-config-loader.d.ts.map +1 -0
  116. package/dist/lib/tailwind-config-loader.js +432 -0
  117. package/dist/lib/tailwind-config-loader.js.map +1 -0
  118. package/dist/lib/tailwind-detector.d.ts +36 -0
  119. package/dist/lib/tailwind-detector.d.ts.map +1 -0
  120. package/dist/lib/tailwind-detector.js +156 -0
  121. package/dist/lib/tailwind-detector.js.map +1 -0
  122. package/dist/lib/tailwind.d.ts +8 -0
  123. package/dist/lib/tailwind.d.ts.map +1 -0
  124. package/dist/lib/tailwind.js +994 -0
  125. package/dist/lib/tailwind.js.map +1 -0
  126. package/dist/lib/template-validator.d.ts +22 -0
  127. package/dist/lib/template-validator.d.ts.map +1 -0
  128. package/dist/lib/template-validator.js +174 -0
  129. package/dist/lib/template-validator.js.map +1 -0
  130. package/dist/lib/utils.d.ts +44 -0
  131. package/dist/lib/utils.d.ts.map +1 -0
  132. package/dist/lib/utils.js +207 -0
  133. package/dist/lib/utils.js.map +1 -0
  134. package/dist/lib/version-check.d.ts +16 -0
  135. package/dist/lib/version-check.d.ts.map +1 -0
  136. package/dist/lib/version-check.js +88 -0
  137. package/dist/lib/version-check.js.map +1 -0
  138. package/dist/lib/video-renderer.d.ts +32 -0
  139. package/dist/lib/video-renderer.d.ts.map +1 -0
  140. package/dist/lib/video-renderer.js +226 -0
  141. package/dist/lib/video-renderer.js.map +1 -0
  142. package/dist/sdk/index.d.ts +58 -0
  143. package/dist/sdk/index.d.ts.map +1 -0
  144. package/dist/sdk/index.js +119 -0
  145. package/dist/sdk/index.js.map +1 -0
  146. package/dist/sdk/template.d.ts +40 -0
  147. package/dist/sdk/template.d.ts.map +1 -0
  148. package/dist/sdk/template.js +60 -0
  149. package/dist/sdk/template.js.map +1 -0
  150. package/dist/types/config.d.ts +62 -0
  151. package/dist/types/config.d.ts.map +1 -0
  152. package/dist/types/config.js +47 -0
  153. package/dist/types/config.js.map +1 -0
  154. package/dist/types/template.d.ts +79 -0
  155. package/dist/types/template.d.ts.map +1 -0
  156. package/dist/types/template.js +2 -0
  157. package/dist/types/template.js.map +1 -0
  158. package/examples/nextjs-api/README.md +180 -0
  159. package/examples/nextjs-api/package.json +21 -0
  160. package/examples/nextjs-api/pages/api/intro-video.ts +53 -0
  161. package/examples/nextjs-api/pages/api/og-image.ts +50 -0
  162. package/netlify.toml +13 -0
  163. package/package.json +84 -0
  164. package/patches/satori+0.18.3.patch +13 -0
  165. package/test-templates/TESTS.md +63 -0
  166. package/test-templates/_dsgn/templates/absolute-spin/meta.json +7 -0
  167. package/test-templates/_dsgn/templates/absolute-spin/template.tsx +16 -0
  168. package/test-templates/_dsgn/templates/animated-intro/.tmp/template-1763468771640.mjs +7 -0
  169. package/test-templates/_dsgn/templates/animated-intro/meta.json +10 -0
  170. package/test-templates/_dsgn/templates/animated-intro/template.tsx +23 -0
  171. package/test-templates/_dsgn/templates/centered-spin/.tmp/template-1763468525386.mjs +7 -0
  172. package/test-templates/_dsgn/templates/centered-spin/meta.json +7 -0
  173. package/test-templates/_dsgn/templates/centered-spin/template.tsx +11 -0
  174. package/test-templates/_dsgn/templates/composite/.tmp/template-1763468815645.mjs +7 -0
  175. package/test-templates/_dsgn/templates/composite/meta.json +9 -0
  176. package/test-templates/_dsgn/templates/composite/template.tsx +23 -0
  177. package/test-templates/_dsgn/templates/easing-test/.tmp/template-1763468824501.mjs +7 -0
  178. package/test-templates/_dsgn/templates/easing-test/meta.json +7 -0
  179. package/test-templates/_dsgn/templates/easing-test/template.tsx +47 -0
  180. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466364336.mjs +10 -0
  181. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466584319.mjs +10 -0
  182. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466667797.mjs +10 -0
  183. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466746504.mjs +10 -0
  184. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466930225.mjs +10 -0
  185. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467004552.mjs +10 -0
  186. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467060334.mjs +10 -0
  187. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467124493.mjs +10 -0
  188. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467174690.mjs +10 -0
  189. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467359134.mjs +10 -0
  190. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467451928.mjs +10 -0
  191. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467758275.mjs +10 -0
  192. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467985201.mjs +10 -0
  193. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468020563.mjs +10 -0
  194. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468090428.mjs +10 -0
  195. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468211036.mjs +10 -0
  196. package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468394057.mjs +10 -0
  197. package/test-templates/_dsgn/templates/minimal-spin/meta.json +7 -0
  198. package/test-templates/_dsgn/templates/minimal-spin/template.tsx +13 -0
  199. package/test-templates/_dsgn/templates/no-origin-spin/meta.json +7 -0
  200. package/test-templates/_dsgn/templates/no-origin-spin/template.tsx +10 -0
  201. package/test-templates/_dsgn/templates/opacity-test/meta.json +7 -0
  202. package/test-templates/_dsgn/templates/opacity-test/template.tsx +9 -0
  203. package/test-templates/_dsgn/templates/qr-code/.tmp/template-1763468758954.mjs +17 -0
  204. package/test-templates/_dsgn/templates/qr-code/.tmp/template-1763468815672.mjs +17 -0
  205. package/test-templates/_dsgn/templates/qr-code/meta.json +9 -0
  206. package/test-templates/_dsgn/templates/qr-code/template.tsx +20 -0
  207. package/test-templates/_dsgn/templates/rotation-abs-test/meta.json +7 -0
  208. package/test-templates/_dsgn/templates/rotation-abs-test/template.tsx +15 -0
  209. package/test-templates/_dsgn/templates/rotation-corner/meta.json +7 -0
  210. package/test-templates/_dsgn/templates/rotation-corner/template.tsx +12 -0
  211. package/test-templates/_dsgn/templates/rotation-test/meta.json +7 -0
  212. package/test-templates/_dsgn/templates/rotation-test/template.tsx +12 -0
  213. package/test-templates/_dsgn/templates/shake-test/meta.json +7 -0
  214. package/test-templates/_dsgn/templates/shake-test/template.tsx +12 -0
  215. package/test-templates/_dsgn/templates/static-image/.tmp/template-1763468746271.mjs +7 -0
  216. package/test-templates/_dsgn/templates/static-image/meta.json +9 -0
  217. package/test-templates/_dsgn/templates/static-image/template.tsx +19 -0
  218. package/test-templates/_dsgn/templates/translate-test/meta.json +7 -0
  219. package/test-templates/_dsgn/templates/translate-test/template.tsx +9 -0
  220. package/test-templates/_dsgn/templates/video-loops/.tmp/template-1763468793192.mjs +15 -0
  221. package/test-templates/_dsgn/templates/video-loops/meta.json +9 -0
  222. package/test-templates/_dsgn/templates/video-loops/template.tsx +39 -0
  223. package/test-templates/_dsgn/templates/wrapped-spin/meta.json +7 -0
  224. package/test-templates/_dsgn/templates/wrapped-spin/template.tsx +17 -0
  225. package/test-templates/compare-svgs.mjs +30 -0
  226. package/test-templates/convert-frames.mjs +15 -0
  227. package/test-templates/debug-rotation.mjs +25 -0
  228. package/test-templates/run-tests.sh +39 -0
  229. package/test-templates/test-sdk.mjs +115 -0
  230. package/website/.astro/settings.json +5 -0
  231. package/website/.astro/types.d.ts +1 -0
  232. package/website/README.md +112 -0
  233. package/website/astro.config.mjs +18 -0
  234. package/website/dist/_astro/fonts.DHdiHGBO.css +1 -0
  235. package/website/dist/fonts/index.html +193 -0
  236. package/website/dist/helpers/index.html +166 -0
  237. package/website/dist/images/index.html +314 -0
  238. package/website/dist/index.html +219 -0
  239. package/website/dist/llm.txt +2448 -0
  240. package/website/dist/styling/index.html +365 -0
  241. package/website/dist/templates/index.html +124 -0
  242. package/website/dist/video/index.html +636 -0
  243. package/website/package-lock.json +7606 -0
  244. package/website/package.json +23 -0
  245. package/website/public/robots.txt +5 -0
package/README.md ADDED
@@ -0,0 +1,1020 @@
1
+ # dsgn
2
+
3
+ > The Shadcn for design and marketing. A template-based CLI tool for generating images and videos using React + Tailwind CSS + Satori.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **shadcn/ui Design System**: Beautiful, semantic colors out of the box (`text-primary`, `bg-card`, etc.)
8
+ - ✨ **Template-based**: Install design templates like you install UI components
9
+ - 🖼️ **Serverless Image Rendering**: Pure JavaScript rendering with Satori - works on Vercel, Netlify, Cloudflare Workers
10
+ - 🎬 **Serverless Video Rendering**: WASM-based MP4 encoding - 12x faster than traditional approaches
11
+ - 🎨 **Tailwind CSS Support**: Style templates with Tailwind utility classes + opacity modifiers (`bg-primary/50`)
12
+ - 📱 **Built-in Helpers**: QR codes, image embedding, video backgrounds, template composition
13
+ - ✅ **Smart Validation**: Automatic prop and template validation with helpful error messages
14
+ - 🚀 **Framework-agnostic**: Works with Node, Bun, Deno, Laravel, Python, and more
15
+ - 🤖 **Agent-friendly**: Machine-readable metadata for LLMs
16
+ - 📦 **Easy installation**: `npx dsgn add template-name`
17
+ - 🌐 **Pure JavaScript/WASM**: No native dependencies, works everywhere
18
+
19
+ ## Quick Start
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ npm install -g dsgn
25
+ ```
26
+
27
+ Or use with npx:
28
+
29
+ ```bash
30
+ npx dsgn --help
31
+ ```
32
+
33
+ ### Install a Template
34
+
35
+ Templates can be installed from multiple sources:
36
+
37
+ #### 1. Official Registry (Coming Soon)
38
+
39
+ ```bash
40
+ dsgn add banner-hero
41
+ dsgn add og-image --registry https://custom-registry.com/r
42
+ ```
43
+
44
+ #### 2. GitHub Repositories
45
+
46
+ ```bash
47
+ # From a GitHub repo (looks for template.json in repo root)
48
+ dsgn add github:username/repo
49
+
50
+ # From a specific path in the repo
51
+ dsgn add github:username/repo/templates/banner-hero
52
+
53
+ # From an organization
54
+ dsgn add github:myorg/design-templates/social-media/og-image
55
+ ```
56
+
57
+ **How it works:**
58
+ - Converts to raw GitHub URLs
59
+ - Fetches `template.json` from the specified path
60
+ - Downloads all referenced files
61
+ - Uses `main` branch by default
62
+
63
+ #### 3. Direct URLs
64
+
65
+ ```bash
66
+ # Install from any publicly accessible URL
67
+ dsgn add https://example.com/templates/my-template.json
68
+ dsgn add https://cdn.example.com/templates/awesome-banner.json
69
+ ```
70
+
71
+ **Requirements:**
72
+ - URL must return JSON in the registry template format
73
+ - Must be publicly accessible (no authentication)
74
+
75
+ #### 4. Local Filesystem
76
+
77
+ ```bash
78
+ # Relative path
79
+ dsgn add ./my-templates/banner-hero
80
+ dsgn add ../shared-templates/product-card
81
+
82
+ # Absolute path
83
+ dsgn add /Users/you/templates/social-card
84
+ ```
85
+
86
+ **Use cases:**
87
+ - Development and testing
88
+ - Private templates
89
+ - Shared team templates (monorepo)
90
+ - Before publishing to registry
91
+
92
+ Templates are installed to: `_dsgn/templates/<template>/` (customizable in `dsgn.json`)
93
+
94
+ **Benefits:**
95
+ - Templates are local to your project (like npm packages)
96
+ - Different projects can use different template versions
97
+ - Version controlled with your project
98
+ - Easy to share within your team
99
+ - Works offline once installed
100
+
101
+ See `REGISTRY_SETUP.md` to create your own registry.
102
+
103
+ ### Render an Image
104
+
105
+ ```bash
106
+ dsgn render banner-hero \
107
+ --props '{"title":"Hello World","subtitle":"Welcome to dsgn"}'
108
+ ```
109
+
110
+ By default, images are saved to `dsgn/outputs/` in your current project.
111
+
112
+ You can specify a custom output path:
113
+
114
+ ```bash
115
+ dsgn render banner-hero \
116
+ --props '{"title":"Hello World","subtitle":"Welcome to dsgn"}' \
117
+ --out custom/path/banner.png
118
+ ```
119
+
120
+ Or use a props file:
121
+
122
+ ```bash
123
+ # props.json
124
+ {
125
+ "title": "Hello World",
126
+ "subtitle": "Welcome to dsgn"
127
+ }
128
+
129
+ dsgn render banner-hero --props props.json --out banner.png
130
+ ```
131
+
132
+ **Output formats:**
133
+
134
+ ```bash
135
+ # PNG (default) - best for photos and complex gradients
136
+ dsgn render banner-hero --props props.json --format png -o banner.png
137
+
138
+ # SVG - scalable vector, smaller file size, perfect for template composition
139
+ dsgn render banner-hero --props props.json --format svg -o banner.svg
140
+
141
+ # WebP - modern format, smaller than PNG with similar quality
142
+ dsgn render banner-hero --props props.json --format webp -o banner.webp
143
+
144
+ # JPEG - smallest file size, good for photos
145
+ dsgn render banner-hero --props props.json --format jpg -o banner.jpg
146
+ ```
147
+
148
+ **SVG benefits with template composition:**
149
+ - Smaller file size (typically 3-4x smaller than PNG)
150
+ - Infinitely scalable without quality loss
151
+ - Embedded templates remain as native SVG elements
152
+ - Can be edited in design tools (Figma, Illustrator)
153
+
154
+ ### List Installed Templates
155
+
156
+ ```bash
157
+ dsgn list
158
+ ```
159
+
160
+ ### Validate Templates
161
+
162
+ ```bash
163
+ dsgn validate
164
+ dsgn validate banner-hero
165
+ ```
166
+
167
+ ## SDK for Programmatic Use
168
+
169
+ Use **dsgn** programmatically in your Next.js API routes, serverless functions, or Node.js applications!
170
+
171
+ ### Installation
172
+
173
+ ```bash
174
+ npm install dsgn
175
+ ```
176
+
177
+ ### Quick Example
178
+
179
+ ```typescript
180
+ import { defineTemplate, renderImage } from 'dsgn/sdk';
181
+
182
+ // Define template programmatically
183
+ const ogTemplate = defineTemplate({
184
+ name: 'og-image',
185
+ size: { width: 1200, height: 630 },
186
+ render: ({ tw, title, description }) => (
187
+ <div style={tw('flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12')}>
188
+ <h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
189
+ <p style={tw('text-2xl text-white/80')}>{description}</p>
190
+ </div>
191
+ ),
192
+ });
193
+
194
+ // Render to buffer
195
+ const png = await renderImage(ogTemplate, {
196
+ title: 'Hello World',
197
+ description: 'Generated with dsgn SDK',
198
+ });
199
+
200
+ // Use in Next.js API route
201
+ export default async function handler(req, res) {
202
+ const png = await renderImage(ogTemplate, req.query);
203
+ res.setHeader('Content-Type', 'image/png');
204
+ res.send(png);
205
+ }
206
+ ```
207
+
208
+ ### Video Rendering
209
+
210
+ ```typescript
211
+ import { defineTemplate, renderVideo } from 'dsgn/sdk';
212
+
213
+ const introTemplate = defineTemplate({
214
+ name: 'intro-video',
215
+ type: 'video',
216
+ size: { width: 1920, height: 1080 },
217
+ video: { fps: 30, duration: 3 },
218
+ render: ({ tw, progress, title }) => {
219
+ const opacity = progress; // 0 to 1 animation
220
+
221
+ return (
222
+ <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
223
+ <h1 style={{ ...tw('text-8xl font-bold text-white'), opacity }}>
224
+ {title}
225
+ </h1>
226
+ </div>
227
+ );
228
+ },
229
+ });
230
+
231
+ const mp4 = await renderVideo(introTemplate, { title: 'Welcome!' });
232
+ ```
233
+
234
+ ### Key Features
235
+
236
+ - ✅ **No file system dependencies** - templates defined in code
237
+ - ✅ **Returns buffers** - integrate with any framework or storage
238
+ - ✅ **Serverless-first** - works on Vercel, Netlify, Cloudflare Workers
239
+ - ✅ **Full TypeScript support** - type-safe props and templates
240
+ - ✅ **Same Tailwind + shadcn/ui styling** - consistent with CLI
241
+
242
+ ### Use Cases
243
+
244
+ - 🖼️ **Dynamic OG images** for blogs and marketing sites
245
+ - 🎬 **Programmatic videos** for social media automation
246
+ - 📧 **Email images** generated on-the-fly
247
+ - 🎨 **User-generated content** like certificates, cards, badges
248
+ - 🔄 **Batch processing** of images or videos
249
+
250
+ ### Example: Next.js API Route
251
+
252
+ See the full example in [`examples/nextjs-api/`](examples/nextjs-api/)
253
+
254
+ ```typescript
255
+ // pages/api/og-image.ts
256
+ import { defineTemplate, renderImage } from 'dsgn/sdk';
257
+
258
+ const template = defineTemplate({
259
+ name: 'og-image',
260
+ size: { width: 1200, height: 630 },
261
+ render: ({ tw, title }) => (
262
+ <div style={tw('flex items-center justify-center w-full h-full bg-white')}>
263
+ <h1 style={tw('text-6xl font-bold')}>{title}</h1>
264
+ </div>
265
+ ),
266
+ });
267
+
268
+ export default async function handler(req, res) {
269
+ const png = await renderImage(template, req.query);
270
+ res.setHeader('Content-Type', 'image/png');
271
+ res.setHeader('Cache-Control', 's-maxage=86400');
272
+ res.send(png);
273
+ }
274
+ ```
275
+
276
+ **Usage:**
277
+ ```
278
+ https://yoursite.com/api/og-image?title=Hello+World
279
+ ```
280
+
281
+ ## shadcn/ui Design System
282
+
283
+ dsgn uses **shadcn/ui's design system** by default! All templates have access to semantic color tokens:
284
+
285
+ ```tsx
286
+ // Use shadcn colors in templates
287
+ <div style={tw('bg-card border rounded-xl p-16')}>
288
+ <h1 style={tw('text-foreground font-bold')}>Title</h1>
289
+ <p style={tw('text-muted-foreground')}>Subtitle</p>
290
+ </div>
291
+ ```
292
+
293
+ **Supported shadcn classes:**
294
+ - `text-primary`, `bg-secondary`, `text-muted-foreground`, `bg-card`, `border`, `bg-destructive`
295
+ - **Opacity modifiers**: `bg-primary/50`, `text-muted-foreground/75`, `border/30`
296
+ - **Dark mode ready**: Override colors in `dsgn.json`
297
+
298
+ See `SHADCN_INTEGRATION.md` for complete documentation.
299
+
300
+ ## Configuration (dsgn.json)
301
+
302
+ Templates automatically use your project's design tokens defined in `dsgn.json`. This allows generated images to match your brand colors and design system.
303
+
304
+ ### Initialize Config
305
+
306
+ ```bash
307
+ dsgn init
308
+ ```
309
+
310
+ This creates a `dsgn.json` file in your project:
311
+
312
+ ```json
313
+ {
314
+ "colors": {
315
+ "primary": "#667eea",
316
+ "secondary": "#764ba2",
317
+ "background": "#ffffff",
318
+ "foreground": "#000000",
319
+ "muted": "#f3f4f6",
320
+ "accent": "#3b82f6",
321
+ "destructive": "#ef4444",
322
+ "border": "#e5e7eb"
323
+ },
324
+ "fonts": {
325
+ "sans": ["Inter", "system-ui", "sans-serif"],
326
+ "mono": ["JetBrains Mono", "monospace"]
327
+ },
328
+ "tokens": {
329
+ "borderRadius": {
330
+ "sm": "0.25rem",
331
+ "md": "0.5rem",
332
+ "lg": "1rem",
333
+ "xl": "1.5rem"
334
+ }
335
+ },
336
+ "defaults": {
337
+ "width": 1200,
338
+ "height": 630
339
+ }
340
+ }
341
+ ```
342
+
343
+ ### Using Config in Templates
344
+
345
+ Templates receive the config via props:
346
+
347
+ ```tsx
348
+ export default function MyTemplate({ title, config }) {
349
+ const primaryColor = config?.colors?.primary || '#000';
350
+
351
+ return (
352
+ <div style={{ background: primaryColor }}>
353
+ {title}
354
+ </div>
355
+ );
356
+ }
357
+ ```
358
+
359
+ This ensures your generated images use the same colors as your Tailwind/Shadcn setup!
360
+
361
+ ## Using Tailwind Classes
362
+
363
+ Templates can use Tailwind utility classes for styling! The `tw()` function is automatically provided:
364
+
365
+ ```tsx
366
+ export default function MyTemplate({ title, tw }) {
367
+ return (
368
+ <div style={tw('flex items-center justify-center w-full h-full bg-primary text-white')}>
369
+ <h1 style={tw('text-6xl font-bold')}>
370
+ {title}
371
+ </h1>
372
+ </div>
373
+ );
374
+ }
375
+ ```
376
+
377
+ Combine with custom styles:
378
+
379
+ ```tsx
380
+ <div
381
+ style={{
382
+ ...tw('flex flex-col p-20 text-white'),
383
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
384
+ }}
385
+ >
386
+ <h1 style={tw('text-8xl font-bold')}>{title}</h1>
387
+ </div>
388
+ ```
389
+
390
+ ### Automatic Tailwind Config Support
391
+
392
+ **dsgn automatically detects and uses your `tailwind.config.js`!**
393
+
394
+ If you have a Tailwind project:
395
+
396
+ ```js
397
+ // tailwind.config.js
398
+ export default {
399
+ theme: {
400
+ extend: {
401
+ colors: {
402
+ primary: '#667eea',
403
+ brand: {
404
+ 500: '#0ea5e9',
405
+ 900: '#0c4a6e',
406
+ },
407
+ },
408
+ spacing: {
409
+ '72': '18rem',
410
+ },
411
+ },
412
+ },
413
+ };
414
+ ```
415
+
416
+ Your templates can use these values:
417
+
418
+ ```tsx
419
+ <div style={tw('bg-primary text-white p-72')}>
420
+ <div style={tw('bg-brand-500')}>Custom brand color!</div>
421
+ </div>
422
+ ```
423
+
424
+ **Config priority:**
425
+ 1. `tailwind.config.js` (if exists)
426
+ 2. `dsgn.json` (fallback)
427
+ 3. Default Tailwind values
428
+
429
+ **Supported classes:**
430
+ - Layout: `flex`, `flex-col`, `items-center`, `justify-between`, etc.
431
+ - Spacing: `p-4`, `px-8`, `m-6`, `gap-4`, etc.
432
+ - Typography: `text-6xl`, `font-bold`, `text-center`, etc.
433
+ - Colors: `bg-primary`, `text-brand-500`, etc. (from your config!)
434
+ - Effects: `opacity-90`, `rounded-xl`, etc.
435
+ - shadcn/ui colors: `bg-card`, `text-foreground`, `text-muted-foreground`
436
+ - Opacity modifiers: `bg-primary/50`, `text-muted-foreground/75`
437
+
438
+ **Note:** Tailwind v4 uses CSS variables instead of JS config. We're working on CSS parsing support. For now, use `dsgn.json` or `tailwind.config.js` (v3 format).
439
+
440
+ ## Fonts
441
+
442
+ Templates can use **default fonts** (from CDN) or **bundle custom fonts** for brand-specific typography.
443
+
444
+ ### Default Fonts
445
+
446
+ By default, templates use **Noto Sans** fetched from jsDelivr CDN. No setup required!
447
+
448
+ ### Custom Fonts
449
+
450
+ Templates can bundle their own fonts (WOFF, WOFF2, TTF, OTF):
451
+
452
+ ```
453
+ my-template/
454
+ ├── my-template.tsx
455
+ ├── meta.json
456
+ └── fonts/
457
+ ├── CustomFont-Regular.woff
458
+ └── CustomFont-Bold.woff
459
+ ```
460
+
461
+ **meta.json:**
462
+ ```json
463
+ {
464
+ "name": "my-template",
465
+ "fonts": [
466
+ {
467
+ "name": "Custom Font",
468
+ "path": "fonts/CustomFont-Bold.woff",
469
+ "weight": 700,
470
+ "style": "normal"
471
+ }
472
+ ]
473
+ }
474
+ ```
475
+
476
+ **Template usage:**
477
+ ```tsx
478
+ <h1 style={{ fontFamily: 'Custom Font', fontWeight: 700 }}>
479
+ Branded Typography
480
+ </h1>
481
+ ```
482
+
483
+ **Use cases:**
484
+ - Brand-specific fonts (company guidelines)
485
+ - Offline rendering (no CDN dependency)
486
+ - Special display typography
487
+
488
+ ## Template Helpers
489
+
490
+ Templates have access to built-in helper functions:
491
+
492
+ - **`tw(classes)`** - Convert Tailwind classes to inline styles
493
+ - **`qr(text, options?)`** - Generate QR codes from text/URLs
494
+ - **`image(propKey)`** - Embed images as data URIs
495
+ - **`video(propKey)`** - Embed video frames (for video templates)
496
+ - **`template(name, props)`** - Embed other templates
497
+ - **`config`** - Access dsgn.json configuration
498
+
499
+ ### QR Code Helper
500
+
501
+ Generate QR codes from any text or URL using the `qr()` helper:
502
+
503
+ ```tsx
504
+ export default function Template({ title, url, qr, tw }) {
505
+ return (
506
+ <div style={tw('flex flex-col items-center p-16')}>
507
+ <h1 style={tw('text-5xl font-bold mb-8')}>{title}</h1>
508
+
509
+ {/* QR code auto-generated from url prop */}
510
+ <img src={qr(url)} width={200} height={200} alt="QR Code" />
511
+
512
+ <p style={tw('text-xl mt-4')}>{url}</p>
513
+ </div>
514
+ );
515
+ }
516
+ ```
517
+
518
+ **Usage:**
519
+ ```bash
520
+ dsgn render qr-card '{
521
+ "title": "Visit Our Website",
522
+ "url": "https://dsgncli.com"
523
+ }'
524
+ ```
525
+
526
+ **Customization:**
527
+ ```tsx
528
+ // Custom size and error correction
529
+ <img src={qr(url, {
530
+ width: 300,
531
+ margin: 2,
532
+ errorCorrectionLevel: 'H', // L, M, Q, H (higher = more error correction)
533
+ color: {
534
+ dark: '#000000',
535
+ light: '#FFFFFF'
536
+ }
537
+ })} />
538
+ ```
539
+
540
+ **How it works:**
541
+ - All string props are automatically pre-generated as QR codes
542
+ - Call `qr()` with any prop value (url, text, etc.)
543
+ - Returns a data URI that can be used directly in `<img src={...} />`
544
+ - Cached per render for performance
545
+
546
+ **Use cases:**
547
+ - Event tickets with QR codes
548
+ - Business cards with contact info
549
+ - Marketing materials with campaign URLs
550
+ - Product labels with tracking codes
551
+
552
+ ### Image Helper
553
+
554
+ Embed images (jpg, png, gif, webp, svg) as data URIs using the `image()` helper:
555
+
556
+ ```tsx
557
+ export default function Banner({ background, tw, image }) {
558
+ return (
559
+ <div style={tw('relative w-full h-full')}>
560
+ <img src={image('background')} style={tw('absolute inset-0 w-full h-full object-cover')} />
561
+ <h1 style={tw('relative text-6xl font-bold text-white')}>Hello World</h1>
562
+ </div>
563
+ );
564
+ }
565
+ ```
566
+
567
+ **Props format:**
568
+ ```json
569
+ {
570
+ "background": "./path/to/image.jpg"
571
+ }
572
+ ```
573
+
574
+ **Usage:**
575
+ ```bash
576
+ dsgn render banner '{
577
+ "background": "./my-background.jpg"
578
+ }'
579
+ ```
580
+
581
+ **How it works:**
582
+ - The renderer auto-detects props ending in image extensions (.jpg, .png, .gif, .webp, .svg)
583
+ - Images are pre-loaded and converted to data URIs
584
+ - Call `image('propKey')` to get the data URI for that prop
585
+ - Works with any image format
586
+
587
+ **Use cases:**
588
+ - Background images in banners
589
+ - Logos and brand elements
590
+ - Product photos in cards
591
+ - Avatar images
592
+
593
+ ### Video Helper
594
+
595
+ For video templates, embed video frames synchronized to the current animation frame using the `video()` helper:
596
+
597
+ ```tsx
598
+ export default function VideoOverlay({ background, title, tw, video, frame, progress }) {
599
+ const opacity = progress < 0.2 ? progress / 0.2 : 1;
600
+
601
+ return (
602
+ <div style={tw('relative w-full h-full')}>
603
+ {/* Background video - auto-syncs to current frame */}
604
+ <img
605
+ src={video('background')}
606
+ style={tw('absolute inset-0 w-full h-full object-cover')}
607
+ />
608
+
609
+ {/* Animated overlay */}
610
+ <h1
611
+ style={{
612
+ ...tw('relative text-8xl font-bold text-white'),
613
+ opacity
614
+ }}
615
+ >
616
+ {title}
617
+ </h1>
618
+ </div>
619
+ );
620
+ }
621
+ ```
622
+
623
+ **Props format:**
624
+ ```json
625
+ {
626
+ "title": "My Video",
627
+ "background": "./my-video.mp4"
628
+ }
629
+ ```
630
+
631
+ **Usage:**
632
+ ```bash
633
+ dsgn render video-overlay props.json --out output.mp4
634
+ ```
635
+
636
+ **How it works:**
637
+ 1. First render detects video props (auto-detects .mp4, .mov, etc.)
638
+ 2. Extracts all frames at template's FPS
639
+ 3. Returns frame matching current template frame number
640
+ 4. Frames cached in memory for fast access
641
+
642
+ **Use cases:**
643
+ - Video backgrounds with text overlays
644
+ - Adding titles/captions to existing videos
645
+ - Combining multiple video sources
646
+ - Dynamic watermarking
647
+
648
+ ### Template Composition Helper
649
+
650
+ Embed one template inside another using the `template()` helper. This is perfect for creating composite designs where smaller templates are reused as building blocks.
651
+
652
+ **Just call template() - no declaration needed:**
653
+ ```tsx
654
+ export default function Template({ title, bannerTitle, bannerSubtitle, template, tw }) {
655
+ return (
656
+ <div style={tw('w-full h-full flex flex-col bg-slate-900 p-12')}>
657
+ <div style={tw('bg-white rounded-3xl p-10 flex flex-col')}>
658
+ <h1 style={tw('text-5xl font-bold mb-8')}>{title}</h1>
659
+
660
+ {/* Embed any template - auto-loaded on first render */}
661
+ <div style={tw('rounded-xl overflow-hidden flex')}>
662
+ {template('image', { title: bannerTitle, subtitle: bannerSubtitle })}
663
+ </div>
664
+ </div>
665
+ </div>
666
+ );
667
+ }
668
+ ```
669
+
670
+ **Usage:**
671
+ ```bash
672
+ dsgn render composite-card '{
673
+ "title": "Product Launch",
674
+ "bannerTitle": "New Release",
675
+ "bannerSubtitle": "Coming Soon"
676
+ }'
677
+ ```
678
+
679
+ **How it works:**
680
+ - **No declaration needed** - just call `template('any-template-name')`
681
+ - First render pass detects which templates are needed
682
+ - All templates load in parallel automatically
683
+ - Second render pass completes with loaded templates
684
+ - Each template uses its own `meta.json` for sizing
685
+ - Returns JSX directly (not a data URI)
686
+ - Satori renders everything as a single SVG tree
687
+
688
+ **Pass props directly:**
689
+ ```tsx
690
+ {template('image', { title: 'Custom Title', subtitle: 'Custom Subtitle' })}
691
+ {template('qr-card', { url: 'https://example.com', title: 'Scan Me' })}
692
+ ```
693
+
694
+ **Use cases:**
695
+ - Composite social media graphics with reusable headers/footers
696
+ - Multi-panel infographics
697
+ - Email templates with consistent branding elements
698
+ - Product cards with embedded badges or labels
699
+ - Presentations with reusable slide components
700
+
701
+ ## Template Structure
702
+
703
+ Templates are TSX files with metadata. Each template can be either an **image** or a **video** template (not both).
704
+
705
+ ### Image Template
706
+
707
+ ```tsx
708
+ export const meta = {
709
+ name: "banner-hero",
710
+ description: "Hero banner with shadcn design",
711
+ type: "image", // Optional, defaults to "image"
712
+ size: { width: 1600, height: 900 },
713
+ props: {
714
+ title: "string",
715
+ subtitle: "string?"
716
+ }
717
+ };
718
+
719
+ export default function Template({ title, subtitle, tw, qr, template }) {
720
+ return (
721
+ <div style={tw('w-full h-full flex flex-col justify-center p-20 bg-background')}>
722
+ <div style={tw('bg-card border rounded-xl p-16 flex flex-col')}>
723
+ <h1 style={tw('text-8xl font-bold text-foreground')}>{title}</h1>
724
+ {subtitle && (
725
+ <p style={tw('text-4xl text-muted-foreground/75')}>{subtitle}</p>
726
+ )}
727
+ </div>
728
+ </div>
729
+ );
730
+ }
731
+ ```
732
+
733
+ ### Video Template
734
+
735
+ ```tsx
736
+ export const meta = {
737
+ name: "animated-banner",
738
+ description: "Animated hero banner with shadcn design",
739
+ type: "video",
740
+ size: { width: 1600, height: 900 },
741
+ props: {
742
+ title: "string",
743
+ subtitle: "string?"
744
+ },
745
+ video: {
746
+ fps: 30,
747
+ duration: 3
748
+ }
749
+ };
750
+
751
+ export default function Template({ title, subtitle, frame, progress, tw, qr, template }) {
752
+ const opacity = Math.min(1, progress * 2);
753
+ const translateY = 20 - (progress * 20);
754
+
755
+ return (
756
+ <div style={tw('w-full h-full flex flex-col justify-center items-center bg-background p-20')}>
757
+ <div style={tw('bg-card border rounded-xl p-16 flex flex-col')}>
758
+ <h1
759
+ style={{
760
+ ...tw('text-9xl font-bold text-foreground'),
761
+ opacity,
762
+ transform: `translateY(${translateY}px)`
763
+ }}
764
+ >
765
+ {title}
766
+ </h1>
767
+ {subtitle && (
768
+ <p style={tw('text-5xl text-muted-foreground/75 mt-6')}>
769
+ {subtitle}
770
+ </p>
771
+ )}
772
+ </div>
773
+ </div>
774
+ );
775
+ }
776
+ ```
777
+
778
+ ## Commands
779
+
780
+ ### `dsgn init`
781
+
782
+ Initialize a `dsgn.json` config file with default design tokens.
783
+
784
+ ### `dsgn add <template>`
785
+
786
+ Install a template from the registry.
787
+
788
+ Options:
789
+ - `-r, --registry <url>` - Custom registry URL
790
+
791
+ ### `dsgn list`
792
+
793
+ List all installed templates with metadata.
794
+
795
+ ### `dsgn render <template>`
796
+
797
+ Render a template to an image.
798
+
799
+ Options:
800
+ - `-p, --props <file>` - Props file (JSON)
801
+ - `-o, --out <file>` - Output file path
802
+ - `--format <format>` - Output format: png, svg, webp (default: png)
803
+ - `--video` - Render as video (coming soon)
804
+
805
+ ### `dsgn validate [template]`
806
+
807
+ Validate template metadata. If no template is specified, validates all installed templates.
808
+
809
+ ## Validation & Error Messages
810
+
811
+ dsgn automatically validates templates and props before rendering, providing helpful error messages.
812
+
813
+ ### Automatic Validation
814
+
815
+ ```bash
816
+ # Missing required props
817
+ $ dsgn render banner-hero --props '{}'
818
+ ✖ Template validation failed
819
+ ✗ props: Missing required prop: "title"
820
+ → Add "title" to your props: --props '{"title":"value"}'
821
+
822
+ # Wrong prop type
823
+ $ dsgn render banner-hero --props '{"title":123}'
824
+ ✖ Template validation failed
825
+ ✗ props: Prop "title" should be a string, got number
826
+ → Change "title" to a string: "title": "value"
827
+ ```
828
+
829
+ ### Enhanced Satori Error Messages
830
+
831
+ When rendering fails, dsgn provides clear suggestions:
832
+
833
+ ```bash
834
+ $ dsgn render my-template --props props.json
835
+ ✖ Failed to render template
836
+
837
+ Error: Satori requires explicit display: flex for containers with multiple children
838
+
839
+ Suggestions:
840
+ • Add "flex" or "flex-col" to your Tailwind classes: tw("flex items-center")
841
+ • Or add display: flex directly: style={{ display: "flex", ...tw("...") }}
842
+ • Every <div> with 2+ children MUST have display: flex or display: none
843
+ ```
844
+
845
+ See `VALIDATION.md` for complete validation documentation and troubleshooting.
846
+
847
+ ## Creating Custom Templates
848
+
849
+ Templates are stored in your project's `dsgn/templates/` directory. You can create custom templates:
850
+
851
+ 1. Create a folder:
852
+
853
+ ```bash
854
+ mkdir -p dsgn/templates/my-template
855
+ ```
856
+
857
+ 2. Create `meta.json`:
858
+
859
+ ```json
860
+ {
861
+ "name": "my-template",
862
+ "description": "My custom template",
863
+ "type": "image",
864
+ "size": { "width": 1200, "height": 630 },
865
+ "props": {
866
+ "title": "string"
867
+ }
868
+ }
869
+ ```
870
+
871
+ 3. Create `my-template.tsx`:
872
+
873
+ ```tsx
874
+ export default function MyTemplate({ title }) {
875
+ return (
876
+ <div style={{
877
+ display: 'flex',
878
+ width: '100%',
879
+ height: '100%',
880
+ background: '#000',
881
+ color: '#fff',
882
+ justifyContent: 'center',
883
+ alignItems: 'center',
884
+ fontSize: '48px',
885
+ fontFamily: 'sans-serif',
886
+ }}>
887
+ {title}
888
+ </div>
889
+ );
890
+ }
891
+ ```
892
+
893
+ 4. Validate and render:
894
+
895
+ ```bash
896
+ dsgn validate my-template
897
+ dsgn render my-template --props '{"title":"Hello"}' --out test.png
898
+ ```
899
+
900
+ ## Use Cases
901
+
902
+ - 🎯 **OG Images**: Generate Open Graph images for websites
903
+ - 📱 **Social Media**: Create social media graphics
904
+ - 📧 **Email Headers**: Generate email banners
905
+ - 🎨 **Marketing**: Automate marketing asset creation
906
+ - 🤖 **AI Agents**: Let LLMs generate images programmatically
907
+
908
+ ## Video Support
909
+
910
+ Video templates use `type: "video"` and support `frame` and `progress` props for animations.
911
+
912
+ ### ⚡ No Dependencies Required
913
+
914
+ **dsgn uses pure JavaScript/WASM for video encoding** - works everywhere!
915
+
916
+ ✅ Works on **Vercel Edge Functions**
917
+ ✅ Works on **Netlify Functions**
918
+ ✅ Works on **Cloudflare Workers**
919
+ ✅ Works on **AWS Lambda**
920
+ ✅ Works **everywhere** (Docker, CI/CD, local)
921
+
922
+ **12x faster than traditional approaches** - renders a 3-second video in ~2 seconds!
923
+
924
+ ### Creating Video Templates
925
+
926
+ Video templates are similar to image templates but include `frame` and `progress` props for animations:
927
+
928
+ ```tsx
929
+ export const meta = {
930
+ name: "my-video",
931
+ type: "video",
932
+ size: { width: 1200, height: 630 },
933
+ props: { title: "string" },
934
+ video: {
935
+ fps: 30,
936
+ duration: 3
937
+ }
938
+ };
939
+
940
+ export default function Template({ title, frame, progress, tw, qr, template }) {
941
+ return (
942
+ <div
943
+ style={{
944
+ ...tw('w-full h-full flex items-center justify-center'),
945
+ opacity: progress,
946
+ transform: `translateY(${20 - progress * 20}px)`
947
+ }}
948
+ >
949
+ <h1 style={tw('text-6xl font-bold')}>{title}</h1>
950
+ </div>
951
+ );
952
+ }
953
+ ```
954
+
955
+ ### Rendering Videos
956
+
957
+ ```bash
958
+ # Render to MP4
959
+ dsgn render my-video '{"title":"Animated Title"}' -o output.mp4
960
+
961
+ # With custom quality settings
962
+ dsgn render my-video props.json --crf 18 # Higher quality (lower CRF = better quality)
963
+
964
+ # Export frames only (for manual encoding)
965
+ dsgn render my-video props.json --frames-only -o frames/
966
+ ```
967
+
968
+ **How it works:**
969
+ 1. **SVG Generation**: All frames generated in parallel (~0.8s for 90 frames)
970
+ 2. **WASM Encoding**: Frames encoded to H.264 MP4 using pure JavaScript (~1.2s for 90 frames)
971
+ 3. **Total**: 3-second video renders in ~2 seconds on M1 Mac
972
+
973
+ **Props available in video templates:**
974
+ - `frame` - Current frame number (0 to totalFrames-1)
975
+ - `progress` - Animation progress from 0 to 1
976
+ - All standard helpers: `tw`, `qr`, `template`, `config`
977
+
978
+ **Performance:**
979
+ - **12x faster** than traditional approaches
980
+ - 90 frames (3s @ 30fps) renders in ~2 seconds
981
+ - ~60 frames per second rendering speed
982
+ - Perfect for serverless environments with execution time limits
983
+
984
+ ## Documentation Website
985
+
986
+ A full documentation website is available in the `website/` folder, built with Astro.
987
+
988
+ **Local development:**
989
+
990
+ ```bash
991
+ cd website
992
+ npm install
993
+ npm run dev
994
+ ```
995
+
996
+ Open http://localhost:4321
997
+
998
+ **Deployment:**
999
+
1000
+ The site is ready to deploy on Netlify, Vercel, or Cloudflare Pages. A `netlify.toml` is included for easy Netlify deployment.
1001
+
1002
+ See `website/README.md` for more details.
1003
+
1004
+ ## Registry
1005
+
1006
+ Templates are fetched from a registry (similar to shadcn/ui). The default registry is:
1007
+
1008
+ ```
1009
+ https://dsgncli.com/r
1010
+ ```
1011
+
1012
+ You can host your own registry and use it with:
1013
+
1014
+ ```bash
1015
+ dsgn add my-template --registry https://my-registry.com
1016
+ ```
1017
+
1018
+ ## License
1019
+
1020
+ MIT