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.
- package/FONTS.md +156 -0
- package/HELPERS_DEMO.md +134 -0
- package/PROJECT_STRUCTURE.md +286 -0
- package/PUBLISHING.md +171 -0
- package/README.md +1020 -0
- package/REGISTRY_SETUP.md +427 -0
- package/SHADCN_INTEGRATION.md +269 -0
- package/TAILWIND.md +228 -0
- package/TEMPLATE_SOURCES.md +363 -0
- package/_dsgn/templates/banner-hero/banner-hero.tsx +57 -0
- package/_dsgn/templates/banner-hero/meta.json +14 -0
- package/_dsgn/templates/composite-card/meta.json +16 -0
- package/_dsgn/templates/composite-card/template.tsx +44 -0
- package/_dsgn/templates/image/meta.json +13 -0
- package/_dsgn/templates/image/template.tsx +28 -0
- package/_dsgn/templates/kitchen-sink/meta.json +13 -0
- package/_dsgn/templates/kitchen-sink/template.tsx +72 -0
- package/_dsgn/templates/qr-card/meta.json +14 -0
- package/_dsgn/templates/qr-card/template.tsx +39 -0
- package/_dsgn/templates/test-parent/child/meta.json +11 -0
- package/_dsgn/templates/test-parent/child/template.tsx +27 -0
- package/_dsgn/templates/test-parent/meta.json +12 -0
- package/_dsgn/templates/test-parent/template.tsx +30 -0
- package/_dsgn/templates/test-sibling/meta.json +11 -0
- package/_dsgn/templates/test-sibling/template.tsx +20 -0
- package/_dsgn/templates/video/.tmp/template-1763421345296.mjs +43 -0
- package/_dsgn/templates/video/.tmp/template-1763421362228.mjs +43 -0
- package/_dsgn/templates/video/.tmp/template-1763421377706.mjs +43 -0
- package/_dsgn/templates/video/meta.json +17 -0
- package/_dsgn/templates/video/template.tsx +48 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +70 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +86 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/default.d.ts +2 -0
- package/dist/commands/default.d.ts.map +1 -0
- package/dist/commands/default.js +69 -0
- package/dist/commands/default.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +75 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +83 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/preview.d.ts +3 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +296 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/render.d.ts +10 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +204 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +107 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/default-templates/AGENTS.md +229 -0
- package/dist/default-templates/image/meta.json +13 -0
- package/dist/default-templates/image/template.d.ts +20 -0
- package/dist/default-templates/image/template.d.ts.map +1 -0
- package/dist/default-templates/image/template.js +18 -0
- package/dist/default-templates/image/template.js.map +1 -0
- package/dist/default-templates/image/template.tsx +20 -0
- package/dist/default-templates/image-template/meta.json +13 -0
- package/dist/default-templates/image-template/template.tsx +19 -0
- package/dist/default-templates/kitchen-sink/meta.json +13 -0
- package/dist/default-templates/kitchen-sink/template.tsx +64 -0
- package/dist/default-templates/page/meta.json +17 -0
- package/dist/default-templates/page/template.tsx +37 -0
- package/dist/default-templates/video/meta.json +17 -0
- package/dist/default-templates/video/template.d.ts +26 -0
- package/dist/default-templates/video/template.d.ts.map +1 -0
- package/dist/default-templates/video/template.js +33 -0
- package/dist/default-templates/video/template.js.map +1 -0
- package/dist/default-templates/video/template.tsx +37 -0
- package/dist/default-templates/video-template/meta.json +17 -0
- package/dist/default-templates/video-template/template.tsx +36 -0
- package/dist/default-templates/website/meta.json +16 -0
- package/dist/default-templates/website/pages/home.tsx +17 -0
- package/dist/default-templates/website/parts/footer.tsx +17 -0
- package/dist/default-templates/website/parts/header.tsx +17 -0
- package/dist/default-templates/website/template.tsx +17 -0
- package/dist/default-templates/website-template/meta.json +16 -0
- package/dist/default-templates/website-template/pages/home.tsx +16 -0
- package/dist/default-templates/website-template/parts/footer.tsx +16 -0
- package/dist/default-templates/website-template/parts/header.tsx +16 -0
- package/dist/default-templates/website-template/template.tsx +16 -0
- package/dist/lib/config.d.ts +34 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +248 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +7 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +12 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/helpers.d.ts +29 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +159 -0
- package/dist/lib/helpers.js.map +1 -0
- package/dist/lib/installer.d.ts +51 -0
- package/dist/lib/installer.d.ts.map +1 -0
- package/dist/lib/installer.js +215 -0
- package/dist/lib/installer.js.map +1 -0
- package/dist/lib/renderer.d.ts +51 -0
- package/dist/lib/renderer.d.ts.map +1 -0
- package/dist/lib/renderer.js +524 -0
- package/dist/lib/renderer.js.map +1 -0
- package/dist/lib/tailwind-config-loader.d.ts +47 -0
- package/dist/lib/tailwind-config-loader.d.ts.map +1 -0
- package/dist/lib/tailwind-config-loader.js +432 -0
- package/dist/lib/tailwind-config-loader.js.map +1 -0
- package/dist/lib/tailwind-detector.d.ts +36 -0
- package/dist/lib/tailwind-detector.d.ts.map +1 -0
- package/dist/lib/tailwind-detector.js +156 -0
- package/dist/lib/tailwind-detector.js.map +1 -0
- package/dist/lib/tailwind.d.ts +8 -0
- package/dist/lib/tailwind.d.ts.map +1 -0
- package/dist/lib/tailwind.js +994 -0
- package/dist/lib/tailwind.js.map +1 -0
- package/dist/lib/template-validator.d.ts +22 -0
- package/dist/lib/template-validator.d.ts.map +1 -0
- package/dist/lib/template-validator.js +174 -0
- package/dist/lib/template-validator.js.map +1 -0
- package/dist/lib/utils.d.ts +44 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +207 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/version-check.d.ts +16 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +88 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/video-renderer.d.ts +32 -0
- package/dist/lib/video-renderer.d.ts.map +1 -0
- package/dist/lib/video-renderer.js +226 -0
- package/dist/lib/video-renderer.js.map +1 -0
- package/dist/sdk/index.d.ts +58 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +119 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/template.d.ts +40 -0
- package/dist/sdk/template.d.ts.map +1 -0
- package/dist/sdk/template.js +60 -0
- package/dist/sdk/template.js.map +1 -0
- package/dist/types/config.d.ts +62 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +47 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/template.d.ts +79 -0
- package/dist/types/template.d.ts.map +1 -0
- package/dist/types/template.js +2 -0
- package/dist/types/template.js.map +1 -0
- package/examples/nextjs-api/README.md +180 -0
- package/examples/nextjs-api/package.json +21 -0
- package/examples/nextjs-api/pages/api/intro-video.ts +53 -0
- package/examples/nextjs-api/pages/api/og-image.ts +50 -0
- package/netlify.toml +13 -0
- package/package.json +84 -0
- package/patches/satori+0.18.3.patch +13 -0
- package/test-templates/TESTS.md +63 -0
- package/test-templates/_dsgn/templates/absolute-spin/meta.json +7 -0
- package/test-templates/_dsgn/templates/absolute-spin/template.tsx +16 -0
- package/test-templates/_dsgn/templates/animated-intro/.tmp/template-1763468771640.mjs +7 -0
- package/test-templates/_dsgn/templates/animated-intro/meta.json +10 -0
- package/test-templates/_dsgn/templates/animated-intro/template.tsx +23 -0
- package/test-templates/_dsgn/templates/centered-spin/.tmp/template-1763468525386.mjs +7 -0
- package/test-templates/_dsgn/templates/centered-spin/meta.json +7 -0
- package/test-templates/_dsgn/templates/centered-spin/template.tsx +11 -0
- package/test-templates/_dsgn/templates/composite/.tmp/template-1763468815645.mjs +7 -0
- package/test-templates/_dsgn/templates/composite/meta.json +9 -0
- package/test-templates/_dsgn/templates/composite/template.tsx +23 -0
- package/test-templates/_dsgn/templates/easing-test/.tmp/template-1763468824501.mjs +7 -0
- package/test-templates/_dsgn/templates/easing-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/easing-test/template.tsx +47 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466364336.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466584319.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466667797.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466746504.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763466930225.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467004552.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467060334.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467124493.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467174690.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467359134.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467451928.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467758275.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763467985201.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468020563.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468090428.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468211036.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/.tmp/template-1763468394057.mjs +10 -0
- package/test-templates/_dsgn/templates/minimal-spin/meta.json +7 -0
- package/test-templates/_dsgn/templates/minimal-spin/template.tsx +13 -0
- package/test-templates/_dsgn/templates/no-origin-spin/meta.json +7 -0
- package/test-templates/_dsgn/templates/no-origin-spin/template.tsx +10 -0
- package/test-templates/_dsgn/templates/opacity-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/opacity-test/template.tsx +9 -0
- package/test-templates/_dsgn/templates/qr-code/.tmp/template-1763468758954.mjs +17 -0
- package/test-templates/_dsgn/templates/qr-code/.tmp/template-1763468815672.mjs +17 -0
- package/test-templates/_dsgn/templates/qr-code/meta.json +9 -0
- package/test-templates/_dsgn/templates/qr-code/template.tsx +20 -0
- package/test-templates/_dsgn/templates/rotation-abs-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/rotation-abs-test/template.tsx +15 -0
- package/test-templates/_dsgn/templates/rotation-corner/meta.json +7 -0
- package/test-templates/_dsgn/templates/rotation-corner/template.tsx +12 -0
- package/test-templates/_dsgn/templates/rotation-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/rotation-test/template.tsx +12 -0
- package/test-templates/_dsgn/templates/shake-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/shake-test/template.tsx +12 -0
- package/test-templates/_dsgn/templates/static-image/.tmp/template-1763468746271.mjs +7 -0
- package/test-templates/_dsgn/templates/static-image/meta.json +9 -0
- package/test-templates/_dsgn/templates/static-image/template.tsx +19 -0
- package/test-templates/_dsgn/templates/translate-test/meta.json +7 -0
- package/test-templates/_dsgn/templates/translate-test/template.tsx +9 -0
- package/test-templates/_dsgn/templates/video-loops/.tmp/template-1763468793192.mjs +15 -0
- package/test-templates/_dsgn/templates/video-loops/meta.json +9 -0
- package/test-templates/_dsgn/templates/video-loops/template.tsx +39 -0
- package/test-templates/_dsgn/templates/wrapped-spin/meta.json +7 -0
- package/test-templates/_dsgn/templates/wrapped-spin/template.tsx +17 -0
- package/test-templates/compare-svgs.mjs +30 -0
- package/test-templates/convert-frames.mjs +15 -0
- package/test-templates/debug-rotation.mjs +25 -0
- package/test-templates/run-tests.sh +39 -0
- package/test-templates/test-sdk.mjs +115 -0
- package/website/.astro/settings.json +5 -0
- package/website/.astro/types.d.ts +1 -0
- package/website/README.md +112 -0
- package/website/astro.config.mjs +18 -0
- package/website/dist/_astro/fonts.DHdiHGBO.css +1 -0
- package/website/dist/fonts/index.html +193 -0
- package/website/dist/helpers/index.html +166 -0
- package/website/dist/images/index.html +314 -0
- package/website/dist/index.html +219 -0
- package/website/dist/llm.txt +2448 -0
- package/website/dist/styling/index.html +365 -0
- package/website/dist/templates/index.html +124 -0
- package/website/dist/video/index.html +636 -0
- package/website/package-lock.json +7606 -0
- package/website/package.json +23 -0
- package/website/public/robots.txt +5 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Define a template programmatically for use in serverless environments
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const ogImage = defineTemplate({
|
|
7
|
+
* name: 'og-image',
|
|
8
|
+
* type: 'image',
|
|
9
|
+
* size: { width: 1200, height: 630 },
|
|
10
|
+
* render: ({ tw, title, description }) => (
|
|
11
|
+
* <div style={tw('flex flex-col w-full h-full bg-white p-12')}>
|
|
12
|
+
* <h1 style={tw('text-6xl font-bold')}>{title}</h1>
|
|
13
|
+
* <p style={tw('text-2xl text-gray-600')}>{description}</p>
|
|
14
|
+
* </div>
|
|
15
|
+
* )
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function defineTemplate(definition) {
|
|
20
|
+
// Validate definition
|
|
21
|
+
if (!definition.name) {
|
|
22
|
+
throw new Error('Template definition must have a name');
|
|
23
|
+
}
|
|
24
|
+
if (!definition.size || !definition.size.width || !definition.size.height) {
|
|
25
|
+
throw new Error('Template definition must have a size with width and height');
|
|
26
|
+
}
|
|
27
|
+
if (!definition.render || typeof definition.render !== 'function') {
|
|
28
|
+
throw new Error('Template definition must have a render function');
|
|
29
|
+
}
|
|
30
|
+
// Set defaults
|
|
31
|
+
const type = definition.type || 'image';
|
|
32
|
+
if (type === 'video' && !definition.video) {
|
|
33
|
+
throw new Error('Video templates must have video metadata (fps, duration)');
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...definition,
|
|
37
|
+
type,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Convert a template definition to template metadata
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
export function templateToMeta(template) {
|
|
45
|
+
return {
|
|
46
|
+
name: template.name,
|
|
47
|
+
description: template.name, // Use name as description for SDK templates
|
|
48
|
+
type: template.type || 'image',
|
|
49
|
+
size: template.size,
|
|
50
|
+
video: template.video,
|
|
51
|
+
props: {}, // SDK templates don't need props metadata
|
|
52
|
+
fonts: template.fonts?.map(fontName => ({
|
|
53
|
+
name: fontName,
|
|
54
|
+
path: `fonts/${fontName}.woff`,
|
|
55
|
+
weight: 400,
|
|
56
|
+
style: 'normal',
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/sdk/template.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAC5B,UAAsC;IAEtC,sBAAsB;IACtB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,OAAO,UAAU,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,eAAe;IACf,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,OAAO,CAAC;IAExC,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,IAAI;KACL,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,4CAA4C;QACxE,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,OAAO;QAC9B,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,KAAK,EAAE,EAAE,EAAE,0CAA0C;QACrD,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,SAAS,QAAQ,OAAO;YAC9B,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,QAAiB;SACzB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface DesignConfig {
|
|
2
|
+
colors?: {
|
|
3
|
+
[key: string]: string | {
|
|
4
|
+
[shade: string]: string;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
fonts?: {
|
|
8
|
+
sans?: string[] | {
|
|
9
|
+
family: string[];
|
|
10
|
+
files?: Array<{
|
|
11
|
+
path: string;
|
|
12
|
+
weight?: number;
|
|
13
|
+
style?: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
serif?: string[] | {
|
|
17
|
+
family: string[];
|
|
18
|
+
files?: Array<{
|
|
19
|
+
path: string;
|
|
20
|
+
weight?: number;
|
|
21
|
+
style?: string;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
mono?: string[] | {
|
|
25
|
+
family: string[];
|
|
26
|
+
files?: Array<{
|
|
27
|
+
path: string;
|
|
28
|
+
weight?: number;
|
|
29
|
+
style?: string;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
[key: string]: string[] | {
|
|
33
|
+
family: string[];
|
|
34
|
+
files?: Array<{
|
|
35
|
+
path: string;
|
|
36
|
+
weight?: number;
|
|
37
|
+
style?: string;
|
|
38
|
+
}>;
|
|
39
|
+
} | undefined;
|
|
40
|
+
};
|
|
41
|
+
tokens?: {
|
|
42
|
+
spacing?: {
|
|
43
|
+
[key: string]: string;
|
|
44
|
+
};
|
|
45
|
+
borderRadius?: {
|
|
46
|
+
[key: string]: string;
|
|
47
|
+
};
|
|
48
|
+
[key: string]: any;
|
|
49
|
+
};
|
|
50
|
+
defaults?: {
|
|
51
|
+
width?: number;
|
|
52
|
+
height?: number;
|
|
53
|
+
[key: string]: any;
|
|
54
|
+
};
|
|
55
|
+
paths?: {
|
|
56
|
+
root?: string;
|
|
57
|
+
templates?: string;
|
|
58
|
+
outputs?: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export declare const DEFAULT_CONFIG: DesignConfig;
|
|
62
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAE3B,MAAM,CAAC,EAAE;QACP,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG;YAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;KACrD,CAAC;IAGF,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG;YAChB,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,KAAK,CAAC,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAClE,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG;YACjB,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,KAAK,CAAC,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAClE,CAAC;QACF,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG;YAChB,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,KAAK,CAAC,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAClE,CAAC;QACF,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG;YAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAAC,KAAK,CAAC,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GAAG,SAAS,CAAC;KAC9H,CAAC;IAGF,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACpC,YAAY,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACzC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IAGF,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IAGF,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,eAAO,MAAM,cAAc,EAAE,YAoD5B,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// shadcn/ui design system defaults (zinc theme)
|
|
2
|
+
export const DEFAULT_CONFIG = {
|
|
3
|
+
colors: {
|
|
4
|
+
// Primary colors
|
|
5
|
+
primary: '#18181b',
|
|
6
|
+
'primary-foreground': '#fafafa',
|
|
7
|
+
// Secondary colors
|
|
8
|
+
secondary: '#f4f4f5',
|
|
9
|
+
'secondary-foreground': '#18181b',
|
|
10
|
+
// Background colors
|
|
11
|
+
background: '#ffffff',
|
|
12
|
+
foreground: '#09090b',
|
|
13
|
+
// Muted colors
|
|
14
|
+
muted: '#f4f4f5',
|
|
15
|
+
'muted-foreground': '#71717a',
|
|
16
|
+
// Accent colors
|
|
17
|
+
accent: '#f4f4f5',
|
|
18
|
+
'accent-foreground': '#18181b',
|
|
19
|
+
// Destructive
|
|
20
|
+
destructive: '#ef4444',
|
|
21
|
+
'destructive-foreground': '#fafafa',
|
|
22
|
+
// Border and input
|
|
23
|
+
border: '#e4e4e7',
|
|
24
|
+
input: '#e4e4e7',
|
|
25
|
+
ring: '#18181b',
|
|
26
|
+
// Card
|
|
27
|
+
card: '#ffffff',
|
|
28
|
+
'card-foreground': '#09090b',
|
|
29
|
+
// Popover
|
|
30
|
+
popover: '#ffffff',
|
|
31
|
+
'popover-foreground': '#09090b',
|
|
32
|
+
},
|
|
33
|
+
fonts: {
|
|
34
|
+
sans: ['Noto Sans', 'system-ui', '-apple-system', 'sans-serif'],
|
|
35
|
+
serif: ['Georgia', 'serif'],
|
|
36
|
+
mono: ['Courier New', 'monospace'],
|
|
37
|
+
},
|
|
38
|
+
tokens: {
|
|
39
|
+
borderRadius: {
|
|
40
|
+
sm: '0.25rem',
|
|
41
|
+
md: '0.375rem',
|
|
42
|
+
lg: '0.5rem',
|
|
43
|
+
xl: '0.75rem',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AA6CA,gDAAgD;AAChD,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,MAAM,EAAE;QACN,iBAAiB;QACjB,OAAO,EAAE,SAAS;QAClB,oBAAoB,EAAE,SAAS;QAE/B,mBAAmB;QACnB,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,SAAS;QAEjC,oBAAoB;QACpB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,SAAS;QAErB,eAAe;QACf,KAAK,EAAE,SAAS;QAChB,kBAAkB,EAAE,SAAS;QAE7B,gBAAgB;QAChB,MAAM,EAAE,SAAS;QACjB,mBAAmB,EAAE,SAAS;QAE9B,cAAc;QACd,WAAW,EAAE,SAAS;QACtB,wBAAwB,EAAE,SAAS;QAEnC,mBAAmB;QACnB,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;QAEf,OAAO;QACP,IAAI,EAAE,SAAS;QACf,iBAAiB,EAAE,SAAS;QAE5B,UAAU;QACV,OAAO,EAAE,SAAS;QAClB,oBAAoB,EAAE,SAAS;KAChC;IACD,KAAK,EAAE;QACL,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,CAAC;QAC/D,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;QAC3B,IAAI,EAAE,CAAC,aAAa,EAAE,WAAW,CAAC;KACnC;IACD,MAAM,EAAE;QACN,YAAY,EAAE;YACZ,EAAE,EAAE,SAAS;YACb,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,QAAQ;YACZ,EAAE,EAAE,SAAS;SACd;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { DesignConfig } from './config.js';
|
|
2
|
+
export type TemplateType = "image" | "video" | "presentation" | "website";
|
|
3
|
+
export interface TemplateMeta {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
type?: TemplateType;
|
|
7
|
+
size: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
scale?: number;
|
|
11
|
+
};
|
|
12
|
+
props: Record<string, string>;
|
|
13
|
+
fonts?: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
path: string;
|
|
16
|
+
weight: number;
|
|
17
|
+
style: "normal" | "italic";
|
|
18
|
+
}>;
|
|
19
|
+
templates?: string[] | Record<string, {
|
|
20
|
+
name: string;
|
|
21
|
+
size?: {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
};
|
|
25
|
+
scale?: number;
|
|
26
|
+
}>;
|
|
27
|
+
video?: {
|
|
28
|
+
fps: number;
|
|
29
|
+
duration: number;
|
|
30
|
+
};
|
|
31
|
+
presentation?: {
|
|
32
|
+
slides: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
size?: {
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
transition?: "fade" | "slide" | "none";
|
|
40
|
+
};
|
|
41
|
+
website?: {
|
|
42
|
+
pages: Array<{
|
|
43
|
+
route: string;
|
|
44
|
+
component: string;
|
|
45
|
+
}>;
|
|
46
|
+
output?: "static" | "spa";
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface TemplateProps {
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
frame?: number;
|
|
52
|
+
progress?: number;
|
|
53
|
+
config?: DesignConfig;
|
|
54
|
+
tw?: (classes: string) => any;
|
|
55
|
+
qr?: (text: string, options?: {
|
|
56
|
+
width?: number;
|
|
57
|
+
margin?: number;
|
|
58
|
+
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
59
|
+
color?: {
|
|
60
|
+
dark?: string;
|
|
61
|
+
light?: string;
|
|
62
|
+
};
|
|
63
|
+
}) => string;
|
|
64
|
+
template?: (name: string, props?: Record<string, any>) => string;
|
|
65
|
+
}
|
|
66
|
+
export interface Template {
|
|
67
|
+
meta: TemplateMeta;
|
|
68
|
+
default: (props: TemplateProps) => JSX.Element;
|
|
69
|
+
}
|
|
70
|
+
export interface RegistryTemplate {
|
|
71
|
+
name: string;
|
|
72
|
+
version: string;
|
|
73
|
+
description: string;
|
|
74
|
+
files: Array<{
|
|
75
|
+
path: string;
|
|
76
|
+
content: string;
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/types/template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/types/template.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# dsgn Next.js API Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use the **dsgn SDK** in Next.js API routes to generate images and videos programmatically.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Serverless image generation (OG images, social cards)
|
|
8
|
+
- ✅ Serverless video generation (intro videos, animations)
|
|
9
|
+
- ✅ No file system dependencies - templates defined in code
|
|
10
|
+
- ✅ Works on Vercel, Netlify, and other serverless platforms
|
|
11
|
+
- ✅ Edge Runtime compatible
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
1. Install dependencies:
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Run the development server:
|
|
21
|
+
```bash
|
|
22
|
+
npm run dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Test the API routes:
|
|
26
|
+
- Image: `http://localhost:3000/api/og-image?title=Hello&description=World`
|
|
27
|
+
- Video: `http://localhost:3000/api/intro-video?title=Welcome`
|
|
28
|
+
|
|
29
|
+
## API Routes
|
|
30
|
+
|
|
31
|
+
### `/api/og-image` - Generate OG Images
|
|
32
|
+
|
|
33
|
+
Generate Open Graph images dynamically:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { defineTemplate, renderImage } from 'dsgn/sdk';
|
|
37
|
+
|
|
38
|
+
const ogTemplate = defineTemplate({
|
|
39
|
+
name: 'og-image',
|
|
40
|
+
size: { width: 1200, height: 630 },
|
|
41
|
+
render: ({ tw, title, description }) => (
|
|
42
|
+
<div style={tw('flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12')}>
|
|
43
|
+
<h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
|
|
44
|
+
<p style={tw('text-2xl text-white/80')}>{description}</p>
|
|
45
|
+
</div>
|
|
46
|
+
),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export default async function handler(req, res) {
|
|
50
|
+
const png = await renderImage(ogTemplate, {
|
|
51
|
+
title: req.query.title,
|
|
52
|
+
description: req.query.description,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
res.setHeader('Content-Type', 'image/png');
|
|
56
|
+
res.send(png);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Usage:**
|
|
61
|
+
```bash
|
|
62
|
+
curl http://localhost:3000/api/og-image?title=Hello&description=World > image.png
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `/api/intro-video` - Generate Videos
|
|
66
|
+
|
|
67
|
+
Generate MP4 videos dynamically:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { defineTemplate, renderVideo } from 'dsgn/sdk';
|
|
71
|
+
|
|
72
|
+
const introTemplate = defineTemplate({
|
|
73
|
+
name: 'intro-video',
|
|
74
|
+
type: 'video',
|
|
75
|
+
size: { width: 1920, height: 1080 },
|
|
76
|
+
video: { fps: 30, duration: 3 },
|
|
77
|
+
render: ({ tw, progress, title }) => (
|
|
78
|
+
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
|
|
79
|
+
<h1 style={{ ...tw('text-8xl font-bold text-white'), opacity: progress }}>
|
|
80
|
+
{title}
|
|
81
|
+
</h1>
|
|
82
|
+
</div>
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default async function handler(req, res) {
|
|
87
|
+
const mp4 = await renderVideo(introTemplate, {
|
|
88
|
+
title: req.query.title,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
res.setHeader('Content-Type', 'video/mp4');
|
|
92
|
+
res.send(mp4);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Usage:**
|
|
97
|
+
```bash
|
|
98
|
+
curl http://localhost:3000/api/intro-video?title=Welcome > video.mp4
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Deploy to Vercel
|
|
102
|
+
|
|
103
|
+
[](https://vercel.com/new/clone?repository-url=https://github.com/tomtev/dsgn/tree/main/examples/nextjs-api)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install -g vercel
|
|
107
|
+
vercel
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Key Concepts
|
|
111
|
+
|
|
112
|
+
### 1. **Define Templates Programmatically**
|
|
113
|
+
|
|
114
|
+
No file system needed - define templates directly in code:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const template = defineTemplate({
|
|
118
|
+
name: 'my-template',
|
|
119
|
+
size: { width: 1200, height: 630 },
|
|
120
|
+
render: ({ tw, title }) => (
|
|
121
|
+
<div style={tw('flex items-center justify-center w-full h-full bg-white')}>
|
|
122
|
+
<h1 style={tw('text-4xl font-bold')}>{title}</h1>
|
|
123
|
+
</div>
|
|
124
|
+
),
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2. **Render to Buffer**
|
|
129
|
+
|
|
130
|
+
Get image/video as Buffer for maximum flexibility:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const png = await renderImage(template, { title: 'Hello' });
|
|
134
|
+
const mp4 = await renderVideo(videoTemplate, { title: 'Intro' });
|
|
135
|
+
|
|
136
|
+
// Can save to file, return in API, upload to S3, etc.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. **Tailwind Styling**
|
|
140
|
+
|
|
141
|
+
Use Tailwind utility classes with the `tw` helper:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
render: ({ tw, title }) => (
|
|
145
|
+
<div style={tw('flex flex-col gap-4 p-8 bg-gradient-to-br from-blue-600 to-purple-700')}>
|
|
146
|
+
<h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 4. **Video Animations**
|
|
152
|
+
|
|
153
|
+
Use `progress` and `frame` props for animations:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
render: ({ tw, progress, title }) => {
|
|
157
|
+
const opacity = progress; // 0 to 1
|
|
158
|
+
const scale = 0.5 + progress * 0.5; // 0.5 to 1
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div style={tw('flex items-center justify-center w-full h-full')}>
|
|
162
|
+
<h1 style={{ ...tw('text-8xl font-bold'), opacity, transform: `scale(${scale})` }}>
|
|
163
|
+
{title}
|
|
164
|
+
</h1>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Performance
|
|
171
|
+
|
|
172
|
+
- **Images**: ~50-100ms per image (PNG/JPEG/WebP)
|
|
173
|
+
- **Videos**: ~7s for 3-second Full HD video (1920×1080 @ 30fps)
|
|
174
|
+
- **Edge Runtime**: Compatible with Vercel Edge Functions for global distribution
|
|
175
|
+
|
|
176
|
+
## Learn More
|
|
177
|
+
|
|
178
|
+
- [dsgn Documentation](https://dsgncli.com)
|
|
179
|
+
- [Next.js API Routes](https://nextjs.org/docs/api-routes/introduction)
|
|
180
|
+
- [Vercel Deployment](https://vercel.com/docs)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "loopwind-nextjs-example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"loopwind": "latest",
|
|
12
|
+
"next": "^14.0.0",
|
|
13
|
+
"react": "^18.2.0",
|
|
14
|
+
"react-dom": "^18.2.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^20.0.0",
|
|
18
|
+
"@types/react": "^18.2.0",
|
|
19
|
+
"typescript": "^5.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
+
import { defineTemplate, renderVideo } from 'dsgn/sdk';
|
|
3
|
+
|
|
4
|
+
// Define video template programmatically
|
|
5
|
+
const introTemplate = defineTemplate({
|
|
6
|
+
name: 'intro-video',
|
|
7
|
+
type: 'video',
|
|
8
|
+
size: { width: 1920, height: 1080 },
|
|
9
|
+
video: { fps: 30, duration: 3 },
|
|
10
|
+
render: ({ tw, progress, title }) => {
|
|
11
|
+
// Fade in animation
|
|
12
|
+
const opacity = progress < 0.5 ? progress / 0.5 : 1;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
style={tw(
|
|
17
|
+
'flex items-center justify-center w-full h-full bg-gradient-to-br from-black to-gray-900'
|
|
18
|
+
)}
|
|
19
|
+
>
|
|
20
|
+
<h1
|
|
21
|
+
style={{
|
|
22
|
+
...tw('text-8xl font-bold text-white'),
|
|
23
|
+
opacity,
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
{title}
|
|
27
|
+
</h1>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default async function handler(
|
|
34
|
+
req: NextApiRequest,
|
|
35
|
+
res: NextApiResponse
|
|
36
|
+
) {
|
|
37
|
+
const { title = 'Welcome!' } = req.query;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Render video as MP4 buffer
|
|
41
|
+
const mp4 = await renderVideo(introTemplate, {
|
|
42
|
+
title: String(title),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Return video
|
|
46
|
+
res.setHeader('Content-Type', 'video/mp4');
|
|
47
|
+
res.setHeader('Cache-Control', 's-maxage=3600');
|
|
48
|
+
res.send(mp4);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error rendering video:', error);
|
|
51
|
+
res.status(500).json({ error: 'Failed to render video' });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
+
import { defineTemplate, renderImage } from 'dsgn/sdk';
|
|
3
|
+
|
|
4
|
+
// Define template programmatically
|
|
5
|
+
const ogTemplate = defineTemplate({
|
|
6
|
+
name: 'og-image',
|
|
7
|
+
type: 'image',
|
|
8
|
+
size: { width: 1200, height: 630 },
|
|
9
|
+
render: ({ tw, title, description }) => (
|
|
10
|
+
<div
|
|
11
|
+
style={tw(
|
|
12
|
+
'flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between'
|
|
13
|
+
)}
|
|
14
|
+
>
|
|
15
|
+
<div style={tw('flex items-center gap-4')}>
|
|
16
|
+
<div style={tw('w-12 h-12 rounded-full bg-white/20')} />
|
|
17
|
+
<span style={tw('text-white/80 text-2xl')}>yoursite.com</span>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div>
|
|
21
|
+
<h1 style={tw('text-6xl font-bold text-white mb-4')}>{title}</h1>
|
|
22
|
+
<p style={tw('text-2xl text-white/80')}>{description}</p>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export default async function handler(
|
|
29
|
+
req: NextApiRequest,
|
|
30
|
+
res: NextApiResponse
|
|
31
|
+
) {
|
|
32
|
+
const { title = 'Welcome', description = 'Generated with dsgn SDK' } =
|
|
33
|
+
req.query;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Render image as PNG buffer
|
|
37
|
+
const png = await renderImage(ogTemplate, {
|
|
38
|
+
title: String(title),
|
|
39
|
+
description: String(description),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Return image
|
|
43
|
+
res.setHeader('Content-Type', 'image/png');
|
|
44
|
+
res.setHeader('Cache-Control', 's-maxage=86400, stale-while-revalidate');
|
|
45
|
+
res.send(png);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Error rendering OG image:', error);
|
|
48
|
+
res.status(500).json({ error: 'Failed to render image' });
|
|
49
|
+
}
|
|
50
|
+
}
|
package/netlify.toml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "loopwind",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "A CLI tool for AI code agents and developers for generating images and videos.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"loopwind": "./dist/cli.js",
|
|
8
|
+
"lw": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/cli.d.ts",
|
|
13
|
+
"default": "./dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"./sdk": {
|
|
16
|
+
"types": "./dist/sdk/index.d.ts",
|
|
17
|
+
"default": "./dist/sdk/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc && npm run copy-templates",
|
|
22
|
+
"copy-templates": "cp -r src/default-templates dist/",
|
|
23
|
+
"dev": "tsc --watch",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"postinstall": "patch-package"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"design",
|
|
29
|
+
"cli",
|
|
30
|
+
"templates",
|
|
31
|
+
"satori",
|
|
32
|
+
"tailwind",
|
|
33
|
+
"image-generation",
|
|
34
|
+
"og-image",
|
|
35
|
+
"social-media",
|
|
36
|
+
"marketing"
|
|
37
|
+
],
|
|
38
|
+
"author": "Tommy Vedvik",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/tomtev/loopwind.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://loopwind.dev",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/tomtev/loopwind/issues"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@resvg/resvg-js": "^2.6.0",
|
|
50
|
+
"boxen": "^7.1.1",
|
|
51
|
+
"chalk": "^5.3.0",
|
|
52
|
+
"chalk-animation": "^2.0.3",
|
|
53
|
+
"chokidar": "^4.0.3",
|
|
54
|
+
"clsx": "^2.1.1",
|
|
55
|
+
"commander": "^12.0.0",
|
|
56
|
+
"esbuild": "^0.27.0",
|
|
57
|
+
"express": "^5.1.0",
|
|
58
|
+
"gradient-string": "^3.0.0",
|
|
59
|
+
"h264-mp4-encoder": "^1.0.12",
|
|
60
|
+
"mediabunny": "^1.25.0",
|
|
61
|
+
"open": "^11.0.0",
|
|
62
|
+
"ora": "^8.0.1",
|
|
63
|
+
"pngjs": "^7.0.0",
|
|
64
|
+
"qrcode": "^1.5.4",
|
|
65
|
+
"react": "^18.2.0",
|
|
66
|
+
"satori": "^0.18.3",
|
|
67
|
+
"sharp": "^0.34.5",
|
|
68
|
+
"tailwind-merge": "^3.4.0",
|
|
69
|
+
"tailwindcss": "^3.4.1"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/chalk-animation": "^1.6.3",
|
|
73
|
+
"@types/express": "^5.0.5",
|
|
74
|
+
"@types/gradient-string": "^1.1.6",
|
|
75
|
+
"@types/node": "^20.11.0",
|
|
76
|
+
"@types/qrcode": "^1.5.6",
|
|
77
|
+
"@types/react": "^18.2.48",
|
|
78
|
+
"patch-package": "^8.0.1",
|
|
79
|
+
"typescript": "^5.3.3"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=18.0.0"
|
|
83
|
+
}
|
|
84
|
+
}
|