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
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+
3
+ export default function RotationAbsTest({ tw }) {
4
+ return (
5
+ <div style={tw('flex relative w-full h-full bg-gray-900')}>
6
+ <div style={{
7
+ ...tw('w-32 h-32 rounded-lg bg-blue-500 absolute animate-spin/90'),
8
+ top: '50%',
9
+ left: '50%',
10
+ marginTop: '-64px',
11
+ marginLeft: '-64px'
12
+ }} />
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "rotation-corner",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 3 },
6
+ "props": {}
7
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+
3
+ export default function RotationCorner({ tw }) {
4
+ return (
5
+ <div style={tw('flex w-full h-full bg-gray-900')}>
6
+ <div style={{
7
+ ...tw('w-32 h-32 rounded-lg bg-blue-500 animate-spin/90'),
8
+ transformOrigin: '64px 64px'
9
+ }} />
10
+ </div>
11
+ );
12
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "rotation-test",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 3 },
6
+ "props": {}
7
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+
3
+ export default function RotationTest({ tw }) {
4
+ return (
5
+ <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
6
+ <div style={{
7
+ ...tw('w-32 h-32 rounded-lg bg-blue-500 animate-spin/90'),
8
+ transformOrigin: '64px 64px'
9
+ }} />
10
+ </div>
11
+ );
12
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "shake-test",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 2 },
6
+ "props": {}
7
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+
3
+ export default function ShakeTest({ tw }) {
4
+ return (
5
+ <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900')}>
6
+ {/* Single element with bounce-in-up */}
7
+ <h1 style={tw('text-8xl font-bold text-white ease-out animate-bounce-in-up/0/1')}>
8
+ SHAKE TEST
9
+ </h1>
10
+ </div>
11
+ );
12
+ }
@@ -0,0 +1,7 @@
1
+ import React from "file:///Users/tommyvedvik/Dev/dsgn/node_modules/react/index.js";
2
+ function StaticImage({ tw, title, subtitle }) {
3
+ return /* @__PURE__ */ React.createElement("div", { style: tw("flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between") }, /* @__PURE__ */ React.createElement("div", { style: tw("flex items-center gap-4") }, /* @__PURE__ */ React.createElement("div", { style: tw("w-12 h-12 rounded-full bg-white/20") }), /* @__PURE__ */ React.createElement("span", { style: tw("text-white/80 text-2xl") }, "dsgn")), /* @__PURE__ */ React.createElement("div", { style: tw("flex flex-col") }, /* @__PURE__ */ React.createElement("h1", { style: tw("text-6xl font-bold text-white mb-4") }, title), /* @__PURE__ */ React.createElement("p", { style: tw("text-2xl text-white/80") }, subtitle)), /* @__PURE__ */ React.createElement("p", { style: tw("text-white/60") }, "Static Image Template Test"));
4
+ }
5
+ export {
6
+ StaticImage as default
7
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "static-image",
3
+ "type": "image",
4
+ "size": { "width": 1200, "height": 630 },
5
+ "props": {
6
+ "title": "string",
7
+ "subtitle": "string"
8
+ }
9
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+
3
+ export default function StaticImage({ tw, title, subtitle }) {
4
+ return (
5
+ <div style={tw('flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between')}>
6
+ <div style={tw('flex items-center gap-4')}>
7
+ <div style={tw('w-12 h-12 rounded-full bg-white/20')} />
8
+ <span style={tw('text-white/80 text-2xl')}>dsgn</span>
9
+ </div>
10
+
11
+ <div style={tw('flex flex-col')}>
12
+ <h1 style={tw('text-6xl font-bold text-white mb-4')}>{title}</h1>
13
+ <p style={tw('text-2xl text-white/80')}>{subtitle}</p>
14
+ </div>
15
+
16
+ <p style={tw('text-white/60')}>Static Image Template Test</p>
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "translate-test",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 2 },
6
+ "props": {}
7
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+
3
+ export default function TranslateTest({ tw }) {
4
+ return (
5
+ <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
6
+ <div style={tw('w-32 h-32 rounded-lg bg-blue-500 animate-float/60')} />
7
+ </div>
8
+ );
9
+ }
@@ -0,0 +1,15 @@
1
+ import React from "file:///Users/tommyvedvik/Dev/dsgn/node_modules/react/index.js";
2
+ function VideoLoops({ tw, title }) {
3
+ return /* @__PURE__ */ React.createElement("div", { style: tw("flex flex-col items-center justify-center w-full h-full bg-gray-900") }, /* @__PURE__ */ React.createElement("div", { style: tw("absolute top-20 left-20 w-32 h-32 rounded-full bg-blue-500/20 animate-pulse/30") }), /* @__PURE__ */ React.createElement("div", { style: tw("absolute bottom-20 right-20 w-48 h-48 rounded-full bg-purple-500/20 animate-pulse/45") }), /* @__PURE__ */ React.createElement("div", { style: tw("absolute top-40 right-40 w-8 h-8 rounded bg-blue-400 animate-float/60") }), /* @__PURE__ */ React.createElement("div", { style: tw("absolute bottom-40 left-40 w-6 h-6 rounded bg-purple-400 animate-float/50") }), /* @__PURE__ */ React.createElement("div", { style: {
4
+ ...tw("absolute w-20 h-20 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 animate-spin/90"),
5
+ top: "440px",
6
+ left: "920px"
7
+ } }), /* @__PURE__ */ React.createElement("h1", { style: tw("text-7xl font-bold text-white animate-bounce/40") }, title), /* @__PURE__ */ React.createElement("p", { style: tw("text-2xl text-white/60 mt-4 animate-wiggle/20") }, "Loop Animation Test"), /* @__PURE__ */ React.createElement("div", { style: {
8
+ ...tw("absolute w-4 h-4 rounded-full bg-green-500 animate-ping/30"),
9
+ bottom: "40px",
10
+ left: "952px"
11
+ } }));
12
+ }
13
+ export {
14
+ VideoLoops as default
15
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "video-loops",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 4 },
6
+ "props": {
7
+ "title": "string"
8
+ }
9
+ }
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+
3
+ export default function VideoLoops({ tw, title }) {
4
+ return (
5
+ <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900')}>
6
+ {/* Pulsing background circles */}
7
+ <div style={tw('absolute top-20 left-20 w-32 h-32 rounded-full bg-blue-500/20 animate-pulse/30')} />
8
+ <div style={tw('absolute bottom-20 right-20 w-48 h-48 rounded-full bg-purple-500/20 animate-pulse/45')} />
9
+
10
+ {/* Floating elements */}
11
+ <div style={tw('absolute top-40 right-40 w-8 h-8 rounded bg-blue-400 animate-float/60')} />
12
+ <div style={tw('absolute bottom-40 left-40 w-6 h-6 rounded bg-purple-400 animate-float/50')} />
13
+
14
+ {/* Spinning icon - use fixed pixel position to avoid flex jitter */}
15
+ <div style={{
16
+ ...tw('absolute w-20 h-20 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 animate-spin/90'),
17
+ top: '440px',
18
+ left: '920px'
19
+ }} />
20
+
21
+ {/* Bouncing title */}
22
+ <h1 style={tw('text-7xl font-bold text-white animate-bounce/40')}>
23
+ {title}
24
+ </h1>
25
+
26
+ {/* Wiggling subtitle */}
27
+ <p style={tw('text-2xl text-white/60 mt-4 animate-wiggle/20')}>
28
+ Loop Animation Test
29
+ </p>
30
+
31
+ {/* Ping effect - use fixed pixel position for scale animation */}
32
+ <div style={{
33
+ ...tw('absolute w-4 h-4 rounded-full bg-green-500 animate-ping/30'),
34
+ bottom: '40px',
35
+ left: '952px'
36
+ }} />
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "wrapped-spin",
3
+ "type": "video",
4
+ "size": { "width": 1920, "height": 1080 },
5
+ "video": { "fps": 30, "duration": 3 },
6
+ "props": {}
7
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ // Wrap rotating element in fixed-size container
4
+ export default function WrappedSpin({ tw }) {
5
+ return (
6
+ <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
7
+ {/* Fixed-size wrapper gets centered by flex */}
8
+ <div style={tw('flex w-32 h-32')}>
9
+ {/* Inner element rotates within stable container */}
10
+ <div style={{
11
+ ...tw('w-32 h-32 rounded-lg bg-blue-500 animate-spin/90'),
12
+ transformOrigin: '64px 64px'
13
+ }} />
14
+ </div>
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,30 @@
1
+ import { renderToSVG } from '../dist/lib/renderer.js';
2
+ import fs from 'fs/promises';
3
+
4
+ async function compare() {
5
+ console.log('=== MINIMAL-SPIN (works) ===\n');
6
+
7
+ for (let frame = 0; frame < 3; frame++) {
8
+ const progress = frame / 89;
9
+ const svg = await renderToSVG('minimal-spin', { frame, progress });
10
+ await fs.writeFile(`output/compare-minimal-${frame}.svg`, svg);
11
+
12
+ // Extract the transform from the blue square
13
+ const match = svg.match(/fill="#3b82f6"[^>]*transform="([^"]+)"/);
14
+ console.log(`Frame ${frame}: ${match ? match[1] : 'no transform'}`);
15
+ }
16
+
17
+ console.log('\n=== CENTERED-SPIN (jitters) ===\n');
18
+
19
+ for (let frame = 0; frame < 3; frame++) {
20
+ const progress = frame / 89;
21
+ const svg = await renderToSVG('centered-spin', { frame, progress });
22
+ await fs.writeFile(`output/compare-centered-${frame}.svg`, svg);
23
+
24
+ // Extract the transform from the blue square
25
+ const match = svg.match(/fill="#3b82f6"[^>]*transform="([^"]+)"/);
26
+ console.log(`Frame ${frame}: ${match ? match[1] : 'no transform'}`);
27
+ }
28
+ }
29
+
30
+ compare().catch(console.error);
@@ -0,0 +1,15 @@
1
+ import sharp from 'sharp';
2
+ import fs from 'fs/promises';
3
+
4
+ async function convert() {
5
+ // Convert centered-spin SVG frames to PNG
6
+ for (let i = 0; i < 10; i++) {
7
+ const svg = await fs.readFile(`output/centered-frame-${i}.svg`);
8
+ await sharp(svg)
9
+ .png()
10
+ .toFile(`output/centered-frame-${i}.png`);
11
+ console.log(`Converted frame ${i}`);
12
+ }
13
+ }
14
+
15
+ convert().catch(console.error);
@@ -0,0 +1,25 @@
1
+ import { renderToSVG } from '../dist/lib/renderer.js';
2
+ import fs from 'fs/promises';
3
+
4
+ async function debug() {
5
+ // Generate frames and diff
6
+ console.log('Generating centered-spin frames:\n');
7
+
8
+ for (let frame = 0; frame < 5; frame++) {
9
+ const progress = frame / 89;
10
+ const svg = await renderToSVG('centered-spin', { frame, progress });
11
+ await fs.writeFile(`output/centered-frame-${frame}.svg`, svg);
12
+ console.log(`Frame ${frame} saved`);
13
+ }
14
+
15
+ // Show the full SVG for frame 0 and 1
16
+ console.log('\n--- Frame 0 ---');
17
+ const svg0 = await fs.readFile('output/centered-frame-0.svg', 'utf-8');
18
+ console.log(svg0);
19
+
20
+ console.log('\n--- Frame 1 ---');
21
+ const svg1 = await fs.readFile('output/centered-frame-1.svg', 'utf-8');
22
+ console.log(svg1);
23
+ }
24
+
25
+ debug().catch(console.error);
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+
3
+ # Test script for loopwind templates
4
+ # Outputs to test-templates/_loopwind/outputs/
5
+
6
+ cd "$(dirname "$0")"
7
+
8
+ echo "🧪 Running loopwind template tests..."
9
+ echo ""
10
+
11
+ # Static image test
12
+ echo "📷 Testing static-image..."
13
+ loopwind render static-image '{"title":"Hello World","subtitle":"Testing static image template"}' --out _loopwind/outputs/static-image.png
14
+
15
+ # QR code test
16
+ echo "📱 Testing qr-code..."
17
+ loopwind render qr-code '{"url":"https://loopwind.dev","title":"Scan to visit"}' --out _loopwind/outputs/qr-code.png
18
+
19
+ # Composite template test
20
+ echo "🧩 Testing composite..."
21
+ loopwind render composite '{"title":"Composite Test","qrUrl":"https://github.com/tomtev/loopwind"}' --out _loopwind/outputs/composite.png
22
+
23
+ # Animated intro video test
24
+ echo "🎬 Testing animated-intro..."
25
+ loopwind render animated-intro '{"title":"Welcome","subtitle":"Animation classes test"}' --out _loopwind/outputs/animated-intro.mp4
26
+
27
+ # Video loops test
28
+ echo "🔄 Testing video-loops..."
29
+ loopwind render video-loops '{"title":"Loop Test"}' --out _loopwind/outputs/video-loops.mp4
30
+
31
+ # Easing test video
32
+ echo "📈 Testing easing-test..."
33
+ loopwind render easing-test '{}' --out _loopwind/outputs/easing-test.mp4
34
+
35
+ echo ""
36
+ echo "✅ All tests complete! Check _loopwind/outputs/ for results."
37
+ echo ""
38
+ echo "Output files:"
39
+ ls -la _loopwind/outputs/
@@ -0,0 +1,115 @@
1
+ import { defineTemplate, renderImage, renderVideo } from '../dist/sdk/index.js';
2
+ import fs from 'fs/promises';
3
+ import React from 'react';
4
+
5
+ // Test 1: Static image template
6
+ const staticTemplate = defineTemplate({
7
+ name: 'sdk-static',
8
+ type: 'image',
9
+ size: { width: 1200, height: 630 },
10
+ render: ({ tw, title, subtitle }) => (
11
+ React.createElement('div', { style: tw('flex flex-col w-full h-full bg-gradient-to-br from-green-600 to-teal-700 p-12 justify-center items-center') },
12
+ React.createElement('h1', { style: tw('text-6xl font-bold text-white mb-4') }, title),
13
+ React.createElement('p', { style: tw('text-2xl text-white/80') }, subtitle),
14
+ React.createElement('p', { style: tw('text-white/60 mt-8') }, 'Rendered via SDK')
15
+ )
16
+ )
17
+ });
18
+
19
+ // Test 2: Animated video template
20
+ const animatedTemplate = defineTemplate({
21
+ name: 'sdk-animated',
22
+ type: 'video',
23
+ size: { width: 1920, height: 1080 },
24
+ video: { fps: 30, duration: 2 },
25
+ render: ({ tw, title }) => (
26
+ React.createElement('div', { style: tw('flex flex-col items-center justify-center w-full h-full bg-gray-900') },
27
+ React.createElement('div', { style: tw('w-20 h-20 rounded-full bg-gradient-to-br from-pink-500 to-orange-500 mb-8 ease-out animate-bounce-in/0/0.3') }),
28
+ React.createElement('h1', { style: tw('text-7xl font-bold text-white ease-out animate-fade-in-up/0.2/0.6') }, title),
29
+ React.createElement('p', { style: tw('text-2xl text-white/60 mt-4 ease-out animate-fade-in/0.5/0.9') }, 'SDK Video Test')
30
+ )
31
+ )
32
+ });
33
+
34
+ // Test 3: QR code template
35
+ const qrTemplate = defineTemplate({
36
+ name: 'sdk-qr',
37
+ type: 'image',
38
+ size: { width: 800, height: 800 },
39
+ render: ({ tw, qr, url }) => (
40
+ React.createElement('div', { style: tw('flex flex-col items-center justify-center w-full h-full bg-white p-8') },
41
+ React.createElement('h2', { style: tw('text-3xl font-bold text-gray-900 mb-6') }, 'SDK QR Test'),
42
+ React.createElement('img', {
43
+ src: qr(url, { width: 400, margin: 2 }),
44
+ style: tw('w-80 h-80')
45
+ }),
46
+ React.createElement('p', { style: tw('text-gray-600 mt-6') }, url)
47
+ )
48
+ )
49
+ });
50
+
51
+ async function runTests() {
52
+ console.log('Testing dsgn SDK...\n');
53
+
54
+ // Test 1: Static image
55
+ console.log('1. Rendering static image...');
56
+ try {
57
+ const imageBuffer = await renderImage(staticTemplate, {
58
+ title: 'Hello SDK',
59
+ subtitle: 'Programmatic rendering works!'
60
+ }, { format: 'png' });
61
+ await fs.writeFile('output/sdk-static.png', imageBuffer);
62
+ console.log(` ✔ sdk-static.png (${(imageBuffer.length / 1024).toFixed(1)}KB)\n`);
63
+ } catch (err) {
64
+ console.log(` ✖ Failed: ${err.message}\n`);
65
+ }
66
+
67
+ // Test 2: Animated video
68
+ console.log('2. Rendering animated video...');
69
+ try {
70
+ const videoBuffer = await renderVideo(animatedTemplate, {
71
+ title: 'SDK Animation'
72
+ }, { format: 'mp4' });
73
+ await fs.writeFile('output/sdk-animated.mp4', videoBuffer);
74
+ console.log(` ✔ sdk-animated.mp4 (${(videoBuffer.length / 1024).toFixed(1)}KB)\n`);
75
+ } catch (err) {
76
+ console.log(` ✖ Failed: ${err.message}\n`);
77
+ }
78
+
79
+ // Test 3: QR code
80
+ console.log('3. Rendering QR code...');
81
+ try {
82
+ const qrBuffer = await renderImage(qrTemplate, {
83
+ url: 'https://dsgncli.com'
84
+ }, { format: 'png' });
85
+ await fs.writeFile('output/sdk-qr.png', qrBuffer);
86
+ console.log(` ✔ sdk-qr.png (${(qrBuffer.length / 1024).toFixed(1)}KB)\n`);
87
+ } catch (err) {
88
+ console.log(` ✖ Failed: ${err.message}\n`);
89
+ }
90
+
91
+ // Test 4: Different formats
92
+ console.log('4. Testing different formats (JPEG, WebP)...');
93
+ try {
94
+ const jpegBuffer = await renderImage(staticTemplate, {
95
+ title: 'JPEG Test',
96
+ subtitle: 'Quality 85'
97
+ }, { format: 'jpeg', quality: 85 });
98
+ await fs.writeFile('output/sdk-static.jpg', jpegBuffer);
99
+
100
+ const webpBuffer = await renderImage(staticTemplate, {
101
+ title: 'WebP Test',
102
+ subtitle: 'Modern format'
103
+ }, { format: 'webp' });
104
+ await fs.writeFile('output/sdk-static.webp', webpBuffer);
105
+
106
+ console.log(` ✔ sdk-static.jpg (${(jpegBuffer.length / 1024).toFixed(1)}KB)`);
107
+ console.log(` ✔ sdk-static.webp (${(webpBuffer.length / 1024).toFixed(1)}KB)\n`);
108
+ } catch (err) {
109
+ console.log(` ✖ Failed: ${err.message}\n`);
110
+ }
111
+
112
+ console.log('SDK tests complete!');
113
+ }
114
+
115
+ runTests().catch(console.error);
@@ -0,0 +1,5 @@
1
+ {
2
+ "_variables": {
3
+ "lastUpdateCheck": 1763410784506
4
+ }
5
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="astro/client" />
@@ -0,0 +1,112 @@
1
+ # dsgn Documentation Website
2
+
3
+ This is the documentation website for dsgn, built with [Astro](https://astro.build/) and styled with [Tailwind CSS 4](https://tailwindcss.com/) using shadcn/ui design tokens.
4
+
5
+ ## Development
6
+
7
+ Install dependencies:
8
+
9
+ ```bash
10
+ cd website
11
+ npm install
12
+ ```
13
+
14
+ Start the dev server:
15
+
16
+ ```bash
17
+ npm run dev
18
+ ```
19
+
20
+ Open http://localhost:4321
21
+
22
+ ## Build
23
+
24
+ Build for production:
25
+
26
+ ```bash
27
+ npm run build
28
+ ```
29
+
30
+ Preview the build:
31
+
32
+ ```bash
33
+ npm run preview
34
+ ```
35
+
36
+ ## Deployment
37
+
38
+ ### Netlify
39
+
40
+ This site is configured to deploy on Netlify. The `netlify.toml` file in the root directory handles the configuration.
41
+
42
+ **Netlify Settings:**
43
+ - Base directory: `website`
44
+ - Build command: `npm run build`
45
+ - Publish directory: `dist`
46
+
47
+ Just connect your GitHub repo to Netlify and it will automatically deploy!
48
+
49
+ ### Other Platforms
50
+
51
+ You can also deploy to:
52
+ - **Vercel**: Connect repo, set build settings
53
+ - **Cloudflare Pages**: Same as above
54
+ - **GitHub Pages**: May need additional configuration
55
+
56
+ ## Adding Documentation
57
+
58
+ Documentation pages are in `src/pages/` as `.mdx` files. They use the `DocsLayout` for consistent styling and navigation.
59
+
60
+ To add a new page:
61
+
62
+ 1. Create `src/pages/your-page.mdx`
63
+ 2. Add frontmatter:
64
+ ```yaml
65
+ ---
66
+ layout: ../layouts/DocsLayout.astro
67
+ title: Your Page Title
68
+ description: Page description
69
+ ---
70
+ ```
71
+ 3. Add to navigation in `src/layouts/DocsLayout.astro`
72
+
73
+ ## Styling
74
+
75
+ The site uses **Tailwind CSS 4** with shadcn/ui-inspired design tokens defined in `src/styles/global.css`:
76
+
77
+ - Dark theme optimized for readability
78
+ - Semantic color names (`background`, `foreground`, `muted`, `accent`, etc.)
79
+ - Consistent spacing and typography
80
+ - Custom brand gradient for the logo
81
+
82
+ All styles use Tailwind utility classes and the `@theme` directive for design tokens.
83
+
84
+ ## Structure
85
+
86
+ ```
87
+ website/
88
+ ├── src/
89
+ │ ├── components/
90
+ │ │ └── Hero.astro # Hero component (optional)
91
+ │ ├── layouts/
92
+ │ │ ├── BaseLayout.astro # Base HTML
93
+ │ │ └── DocsLayout.astro # Docs with sidebar (+ hero)
94
+ │ ├── pages/
95
+ │ │ ├── index.mdx # Getting Started
96
+ │ │ ├── templates.mdx # Installing Templates
97
+ │ │ ├── images.mdx # Image Rendering
98
+ │ │ ├── video.mdx # Video Rendering
99
+ │ │ ├── helpers.mdx # QR & Composition
100
+ │ │ ├── styling.mdx # Tailwind & shadcn/ui
101
+ │ │ ├── fonts.mdx # Font Handling
102
+ │ │ ├── agents.mdx # AI Agents & Cursor
103
+ │ │ └── llm.txt.ts # Combined docs endpoint
104
+ │ └── styles/
105
+ │ └── global.css # Tailwind 4 + design tokens
106
+ ├── public/
107
+ │ └── favicon.svg
108
+ ├── astro.config.mjs # Includes @tailwindcss/vite
109
+ ├── package.json
110
+ └── tsconfig.json
111
+ ```
112
+
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'astro/config';
2
+ import mdx from '@astrojs/mdx';
3
+ import tailwindcss from '@tailwindcss/vite';
4
+
5
+ // https://astro.build/config
6
+ export default defineConfig({
7
+ site: 'https://dsgn.dev',
8
+ integrations: [mdx()],
9
+ vite: {
10
+ plugins: [tailwindcss()],
11
+ },
12
+ markdown: {
13
+ shikiConfig: {
14
+ theme: 'github-dark',
15
+ },
16
+ },
17
+ });
18
+