loopwind 0.18.1 → 0.20.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/README.md +83 -0
- package/dist/commands/preview.d.ts.map +1 -1
- package/dist/commands/preview.js +2 -1
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/render.d.ts.map +1 -1
- package/dist/commands/render.js +2 -0
- package/dist/commands/render.js.map +1 -1
- package/dist/default-templates/AGENTS.md +54 -0
- package/dist/lib/renderer.d.ts.map +1 -1
- package/dist/lib/renderer.js +3 -0
- package/dist/lib/renderer.js.map +1 -1
- package/dist/lib/tailwind-browser.d.ts.map +1 -1
- package/dist/lib/tailwind-browser.js +169 -6
- package/dist/lib/tailwind-browser.js.map +1 -1
- package/dist/lib/tailwind.d.ts.map +1 -1
- package/dist/lib/tailwind.js +178 -7
- package/dist/lib/tailwind.js.map +1 -1
- package/dist/lib/video-preview.d.ts +1 -1
- package/dist/lib/video-preview.d.ts.map +1 -1
- package/dist/lib/video-preview.js +266 -249
- package/dist/lib/video-preview.js.map +1 -1
- package/dist/lib/video-renderer.d.ts +2 -0
- package/dist/lib/video-renderer.d.ts.map +1 -1
- package/dist/lib/video-renderer.js +4 -4
- package/dist/lib/video-renderer.js.map +1 -1
- package/dist/sdk/compiler.d.ts +94 -0
- package/dist/sdk/compiler.d.ts.map +1 -0
- package/dist/sdk/compiler.js +122 -0
- package/dist/sdk/compiler.js.map +1 -0
- package/dist/sdk/index.d.ts +3 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +2 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/preview.d.ts +65 -0
- package/dist/sdk/preview.d.ts.map +1 -0
- package/dist/sdk/preview.js +262 -0
- package/dist/sdk/preview.js.map +1 -0
- package/dist/sdk/template.d.ts +47 -24
- package/dist/sdk/template.d.ts.map +1 -1
- package/dist/sdk/template.js +53 -93
- package/dist/sdk/template.js.map +1 -1
- package/examples/nextjs-template-import.ts +2 -2
- package/examples/sdk-video-preview.tsx +120 -0
- package/examples/template-compiler-workflow.ts +251 -0
- package/package.json +6 -2
- package/render-examples-600x400.mjs +161 -0
- package/render-spring-variants-fixed.mjs +60 -0
- package/render-staggered-text.mjs +56 -0
- package/test-jsx-support.mjs +32 -6
- package/test-sdk-config.mjs +138 -81
- package/test-sdk-source-config.mjs +427 -0
- package/test-static-debug.tsx +19 -0
- package/test-templates/config-test.mjs +17 -0
- package/test-templates/test-sdk.mjs +46 -22
- package/test-video-props.json +3 -0
- package/website/DEPLOYMENT.md +1 -0
- package/website/OG_IMAGES.md +1 -0
- package/website/astro.config.mjs +18 -2
- package/website/dist/.gitkeep +1 -0
- package/website/dist/_worker.js/index.js +1 -1
- package/website/dist/_worker.js/{manifest_BAAoOzaU.mjs → manifest_CT_D-YDe.mjs} +1 -1
- package/website/dist/llm.txt +1 -1
- package/website/dist/sdk/index.html +405 -102
- package/website/dist/sitemap.xml +12 -12
- package/website/package-lock.json +2866 -7080
- package/website/package.json +1 -2
- package/website/public/.gitkeep +1 -0
- package/website/templates/og-image.tsx +20 -21
- package/website/test-playground.mjs +45 -0
package/test-jsx-support.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test JSX support
|
|
2
|
+
* Test JSX support with compileTemplate + defineTemplateFromSource
|
|
3
|
+
*
|
|
4
|
+
* This test demonstrates the recommended workflow:
|
|
5
|
+
* 1. compileTemplate() - transforms JSX to JavaScript (admin/build time)
|
|
6
|
+
* 2. defineTemplateFromSource() - loads pre-compiled JavaScript (production)
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
9
|
import { defineTemplateFromSource, renderImage } from './dist/sdk/index.js';
|
|
10
|
+
import { compileTemplate } from './dist/sdk/compiler.js';
|
|
6
11
|
|
|
7
|
-
console.log('Testing JSX Support
|
|
12
|
+
console.log('Testing JSX Support with Pre-compilation\n');
|
|
8
13
|
|
|
9
14
|
// Test 1: Simple JSX template
|
|
10
15
|
const simpleJSX = `
|
|
@@ -22,7 +27,11 @@ export default ({ tw, title }) => (
|
|
|
22
27
|
`;
|
|
23
28
|
|
|
24
29
|
try {
|
|
25
|
-
|
|
30
|
+
// Step 1: Compile JSX to JavaScript (admin/build time)
|
|
31
|
+
const compiledJS = compileTemplate(simpleJSX);
|
|
32
|
+
|
|
33
|
+
// Step 2: Load compiled template (production runtime)
|
|
34
|
+
const template1 = defineTemplateFromSource(compiledJS);
|
|
26
35
|
const png1 = await renderImage(template1, { title: 'JSX Works!' });
|
|
27
36
|
console.log('✓ Simple JSX template works');
|
|
28
37
|
console.log(' PNG size:', png1.length, 'bytes\n');
|
|
@@ -53,7 +62,11 @@ export default ({ tw, title, subtitle, items }) => (
|
|
|
53
62
|
`;
|
|
54
63
|
|
|
55
64
|
try {
|
|
56
|
-
|
|
65
|
+
// Step 1: Compile JSX to JavaScript
|
|
66
|
+
const compiledJS2 = compileTemplate(nestedJSX);
|
|
67
|
+
|
|
68
|
+
// Step 2: Load compiled template
|
|
69
|
+
const template2 = defineTemplateFromSource(compiledJS2);
|
|
57
70
|
const png2 = await renderImage(template2, {
|
|
58
71
|
title: 'Nested JSX',
|
|
59
72
|
subtitle: 'With multiple levels',
|
|
@@ -82,7 +95,11 @@ export default ({ tw }) => (
|
|
|
82
95
|
`;
|
|
83
96
|
|
|
84
97
|
try {
|
|
85
|
-
|
|
98
|
+
// Step 1: Compile JSX to JavaScript
|
|
99
|
+
const compiledJS3 = compileTemplate(selfClosing);
|
|
100
|
+
|
|
101
|
+
// Step 2: Load compiled template
|
|
102
|
+
const template3 = defineTemplateFromSource(compiledJS3);
|
|
86
103
|
const png3 = await renderImage(template3, {});
|
|
87
104
|
console.log('✓ Self-closing tags work');
|
|
88
105
|
console.log(' PNG size:', png3.length, 'bytes\n');
|
|
@@ -109,7 +126,11 @@ export default ({ tw, title, count }) => (
|
|
|
109
126
|
`;
|
|
110
127
|
|
|
111
128
|
try {
|
|
112
|
-
|
|
129
|
+
// Step 1: Compile JSX to JavaScript
|
|
130
|
+
const compiledJS4 = compileTemplate(complexAttrs);
|
|
131
|
+
|
|
132
|
+
// Step 2: Load compiled template
|
|
133
|
+
const template4 = defineTemplateFromSource(compiledJS4);
|
|
113
134
|
const png4 = await renderImage(template4, { title: 'Complex JSX', count: 42 });
|
|
114
135
|
console.log('✓ Complex attributes work');
|
|
115
136
|
console.log(' PNG size:', png4.length, 'bytes\n');
|
|
@@ -118,3 +139,8 @@ try {
|
|
|
118
139
|
}
|
|
119
140
|
|
|
120
141
|
console.log('✅ All JSX tests completed!');
|
|
142
|
+
console.log('\nWorkflow Summary:');
|
|
143
|
+
console.log('1. compileTemplate() - Transform JSX → JS (admin panel)');
|
|
144
|
+
console.log('2. defineTemplateFromSource() - Load compiled JS (production)');
|
|
145
|
+
console.log('3. renderImage() - Render with props');
|
|
146
|
+
console.log('\nBenefit: No @babel/standalone in production! 🎉');
|
package/test-sdk-config.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { defineTemplate, renderImage, renderVideo } from './dist/sdk/index.js';
|
|
7
|
+
import * as configTestTemplate from './test-templates/config-test.mjs';
|
|
7
8
|
import React from 'react';
|
|
8
9
|
import fs from 'fs/promises';
|
|
9
10
|
import path from 'path';
|
|
@@ -12,6 +13,11 @@ import { fileURLToPath } from 'url';
|
|
|
12
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const { createElement: h } = React;
|
|
14
15
|
|
|
16
|
+
// Helper to create inline template modules for testing
|
|
17
|
+
function createTemplateModule(meta, render) {
|
|
18
|
+
return { meta, default: render };
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
// Test utilities
|
|
16
22
|
let testsPassed = 0;
|
|
17
23
|
let testsFailed = 0;
|
|
@@ -52,15 +58,7 @@ await test('Config in defineTemplate - Image', async () => {
|
|
|
52
58
|
},
|
|
53
59
|
};
|
|
54
60
|
|
|
55
|
-
const template = defineTemplate({
|
|
56
|
-
name: 'test-config-template',
|
|
57
|
-
size: { width: 400, height: 400 },
|
|
58
|
-
config,
|
|
59
|
-
render: ({ tw, title }) =>
|
|
60
|
-
h('div', { style: tw('flex items-center justify-center w-full h-full bg-background') },
|
|
61
|
-
h('h1', { style: tw('text-4xl font-bold text-primary') }, title)
|
|
62
|
-
),
|
|
63
|
-
});
|
|
61
|
+
const template = defineTemplate(configTestTemplate, { config });
|
|
64
62
|
|
|
65
63
|
assert(template.config, 'Template should have config');
|
|
66
64
|
assert(template.config.colors.primary === '#ff0000', 'Config colors should match');
|
|
@@ -74,14 +72,20 @@ await test('Config in defineTemplate - Image', async () => {
|
|
|
74
72
|
|
|
75
73
|
// Test 2: Config in renderImage options
|
|
76
74
|
await test('Config in renderImage options', async () => {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
const templateModule = createTemplateModule(
|
|
76
|
+
{
|
|
77
|
+
name: 'test-render-config',
|
|
78
|
+
type: 'image',
|
|
79
|
+
size: { width: 400, height: 400 },
|
|
80
|
+
props: { title: 'string' },
|
|
81
|
+
},
|
|
82
|
+
({ tw, title }) =>
|
|
81
83
|
h('div', { style: tw('flex items-center justify-center w-full h-full bg-secondary') },
|
|
82
84
|
h('h1', { style: tw('text-4xl font-bold text-accent') }, title)
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const template = defineTemplate(templateModule);
|
|
85
89
|
|
|
86
90
|
const svg = await renderImage(
|
|
87
91
|
template,
|
|
@@ -104,18 +108,25 @@ await test('Config in renderImage options', async () => {
|
|
|
104
108
|
|
|
105
109
|
// Test 3: Config override (options override template)
|
|
106
110
|
await test('Config override - Options override template', async () => {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
const templateModule = createTemplateModule(
|
|
112
|
+
{
|
|
113
|
+
name: 'test-override',
|
|
114
|
+
type: 'image',
|
|
115
|
+
size: { width: 400, height: 400 },
|
|
116
|
+
props: {},
|
|
117
|
+
},
|
|
118
|
+
({ tw }) =>
|
|
119
|
+
h('div', { style: tw('flex items-center justify-center w-full h-full bg-brand') },
|
|
120
|
+
h('h1', { style: tw('text-4xl font-bold') }, 'Override Test')
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const template = defineTemplate(templateModule, {
|
|
110
125
|
config: {
|
|
111
126
|
colors: {
|
|
112
127
|
brand: '#111111',
|
|
113
128
|
},
|
|
114
129
|
},
|
|
115
|
-
render: ({ tw }) =>
|
|
116
|
-
h('div', { style: tw('flex items-center justify-center w-full h-full bg-brand') },
|
|
117
|
-
h('h1', { style: tw('text-4xl font-bold') }, 'Override Test')
|
|
118
|
-
),
|
|
119
130
|
});
|
|
120
131
|
|
|
121
132
|
const svg = await renderImage(
|
|
@@ -138,14 +149,20 @@ await test('Config override - Options override template', async () => {
|
|
|
138
149
|
|
|
139
150
|
// Test 4: PNG format with config
|
|
140
151
|
await test('PNG format with config', async () => {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
const templateModule = createTemplateModule(
|
|
153
|
+
{
|
|
154
|
+
name: 'test-png',
|
|
155
|
+
type: 'image',
|
|
156
|
+
size: { width: 200, height: 200 },
|
|
157
|
+
props: {},
|
|
158
|
+
},
|
|
159
|
+
({ tw }) =>
|
|
145
160
|
h('div', { style: tw('flex items-center justify-center w-full h-full bg-primary') },
|
|
146
161
|
h('h1', { style: tw('text-2xl font-bold text-white') }, 'PNG')
|
|
147
|
-
)
|
|
148
|
-
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const template = defineTemplate(templateModule);
|
|
149
166
|
|
|
150
167
|
const png = await renderImage(
|
|
151
168
|
template,
|
|
@@ -168,21 +185,27 @@ await test('PNG format with config', async () => {
|
|
|
168
185
|
|
|
169
186
|
// Test 5: Video with config
|
|
170
187
|
await test('Video with config', async () => {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
primary: '#ff6600',
|
|
179
|
-
},
|
|
188
|
+
const templateModule = createTemplateModule(
|
|
189
|
+
{
|
|
190
|
+
name: 'test-video',
|
|
191
|
+
type: 'video',
|
|
192
|
+
size: { width: 400, height: 400 },
|
|
193
|
+
video: { fps: 30, duration: 0.5 },
|
|
194
|
+
props: {},
|
|
180
195
|
},
|
|
181
|
-
|
|
196
|
+
({ tw, progress }) => {
|
|
182
197
|
const opacity = progress;
|
|
183
198
|
return h('div', { style: tw('flex items-center justify-center w-full h-full bg-white') },
|
|
184
199
|
h('h1', { style: { ...tw('text-4xl font-bold text-primary'), opacity } }, 'Video')
|
|
185
200
|
);
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const template = defineTemplate(templateModule, {
|
|
205
|
+
config: {
|
|
206
|
+
colors: {
|
|
207
|
+
primary: '#ff6600',
|
|
208
|
+
},
|
|
186
209
|
},
|
|
187
210
|
});
|
|
188
211
|
|
|
@@ -197,20 +220,26 @@ await test('Video with config', async () => {
|
|
|
197
220
|
|
|
198
221
|
// Test 6: Video with config override
|
|
199
222
|
await test('Video with config override', async () => {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
const templateModule = createTemplateModule(
|
|
224
|
+
{
|
|
225
|
+
name: 'test-video-override',
|
|
226
|
+
type: 'video',
|
|
227
|
+
size: { width: 400, height: 400 },
|
|
228
|
+
video: { fps: 30, duration: 0.5 },
|
|
229
|
+
props: {},
|
|
230
|
+
},
|
|
231
|
+
({ tw, progress }) =>
|
|
232
|
+
h('div', { style: tw('flex items-center justify-center w-full h-full bg-brand') },
|
|
233
|
+
h('h1', { style: { ...tw('text-4xl font-bold'), opacity: progress } }, 'Video')
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const template = defineTemplate(templateModule, {
|
|
205
238
|
config: {
|
|
206
239
|
colors: {
|
|
207
240
|
brand: '#333333',
|
|
208
241
|
},
|
|
209
242
|
},
|
|
210
|
-
render: ({ tw, progress }) =>
|
|
211
|
-
h('div', { style: tw('flex items-center justify-center w-full h-full bg-brand') },
|
|
212
|
-
h('h1', { style: { ...tw('text-4xl font-bold'), opacity: progress } }, 'Video')
|
|
213
|
-
),
|
|
214
243
|
});
|
|
215
244
|
|
|
216
245
|
const mp4 = await renderVideo(template, {}, {
|
|
@@ -237,18 +266,23 @@ await test('Multiple custom colors', async () => {
|
|
|
237
266
|
},
|
|
238
267
|
};
|
|
239
268
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
const templateModule = createTemplateModule(
|
|
270
|
+
{
|
|
271
|
+
name: 'test-multi-colors',
|
|
272
|
+
type: 'image',
|
|
273
|
+
size: { width: 600, height: 400 },
|
|
274
|
+
props: {},
|
|
275
|
+
},
|
|
276
|
+
({ tw }) =>
|
|
245
277
|
h('div', { style: tw('flex flex-col items-center justify-center w-full h-full bg-brand-light p-8') },
|
|
246
278
|
h('h1', { style: tw('text-4xl font-bold text-brand') }, 'Brand'),
|
|
247
279
|
h('p', { style: tw('text-2xl text-brand-dark') }, 'Brand Dark'),
|
|
248
280
|
h('p', { style: tw('text-xl text-accent') }, 'Accent'),
|
|
249
281
|
h('p', { style: tw('text-lg text-success') }, 'Success')
|
|
250
|
-
)
|
|
251
|
-
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const template = defineTemplate(templateModule, { config });
|
|
252
286
|
|
|
253
287
|
const svg = await renderImage(template, {}, { format: 'svg' });
|
|
254
288
|
const svgString = svg.toString();
|
|
@@ -272,16 +306,21 @@ await test('Fonts in config', async () => {
|
|
|
272
306
|
},
|
|
273
307
|
};
|
|
274
308
|
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
309
|
+
const templateModule = createTemplateModule(
|
|
310
|
+
{
|
|
311
|
+
name: 'test-fonts',
|
|
312
|
+
type: 'image',
|
|
313
|
+
size: { width: 400, height: 400 },
|
|
314
|
+
props: {},
|
|
315
|
+
},
|
|
316
|
+
({ tw }) =>
|
|
280
317
|
h('div', { style: tw('flex flex-col items-center justify-center w-full h-full bg-white') },
|
|
281
318
|
h('h1', { style: tw('font-sans text-4xl text-text') }, 'Sans Font'),
|
|
282
319
|
h('p', { style: tw('font-mono text-xl text-text') }, 'Mono Font')
|
|
283
|
-
)
|
|
284
|
-
|
|
320
|
+
)
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const template = defineTemplate(templateModule, { config });
|
|
285
324
|
|
|
286
325
|
const svg = await renderImage(template, {}, { format: 'svg' });
|
|
287
326
|
const svgString = svg.toString();
|
|
@@ -292,14 +331,20 @@ await test('Fonts in config', async () => {
|
|
|
292
331
|
|
|
293
332
|
// Test 9: Empty config (should work with defaults)
|
|
294
333
|
await test('Empty config - should work with defaults', async () => {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
334
|
+
const templateModule = createTemplateModule(
|
|
335
|
+
{
|
|
336
|
+
name: 'test-no-config',
|
|
337
|
+
type: 'image',
|
|
338
|
+
size: { width: 400, height: 400 },
|
|
339
|
+
props: {},
|
|
340
|
+
},
|
|
341
|
+
({ tw }) =>
|
|
299
342
|
h('div', { style: tw('flex items-center justify-center w-full h-full bg-white') },
|
|
300
343
|
h('h1', { style: tw('text-4xl font-bold text-black') }, 'No Config')
|
|
301
|
-
)
|
|
302
|
-
|
|
344
|
+
)
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const template = defineTemplate(templateModule);
|
|
303
348
|
|
|
304
349
|
const svg = await renderImage(template, {}, { format: 'svg' });
|
|
305
350
|
assert(svg.length > 0, 'SVG should be generated without config');
|
|
@@ -307,19 +352,26 @@ await test('Empty config - should work with defaults', async () => {
|
|
|
307
352
|
|
|
308
353
|
// Test 10: Partial config merge
|
|
309
354
|
await test('Partial config merge', async () => {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
355
|
+
const templateModule = createTemplateModule(
|
|
356
|
+
{
|
|
357
|
+
name: 'test-partial-merge',
|
|
358
|
+
type: 'image',
|
|
359
|
+
size: { width: 400, height: 400 },
|
|
360
|
+
props: {},
|
|
361
|
+
},
|
|
362
|
+
({ tw }) =>
|
|
363
|
+
h('div', { style: tw('flex items-center justify-center w-full h-full bg-secondary') },
|
|
364
|
+
h('h1', { style: tw('text-4xl font-bold text-primary') }, 'Merge')
|
|
365
|
+
)
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const template = defineTemplate(templateModule, {
|
|
313
369
|
config: {
|
|
314
370
|
colors: {
|
|
315
371
|
primary: '#111111',
|
|
316
372
|
secondary: '#222222',
|
|
317
373
|
},
|
|
318
374
|
},
|
|
319
|
-
render: ({ tw }) =>
|
|
320
|
-
h('div', { style: tw('flex items-center justify-center w-full h-full bg-secondary') },
|
|
321
|
-
h('h1', { style: tw('text-4xl font-bold text-primary') }, 'Merge')
|
|
322
|
-
),
|
|
323
375
|
});
|
|
324
376
|
|
|
325
377
|
const svg = await renderImage(
|
|
@@ -363,16 +415,21 @@ await test('Font files from URLs', async () => {
|
|
|
363
415
|
},
|
|
364
416
|
};
|
|
365
417
|
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
418
|
+
const templateModule = createTemplateModule(
|
|
419
|
+
{
|
|
420
|
+
name: 'test-font-files',
|
|
421
|
+
type: 'image',
|
|
422
|
+
size: { width: 600, height: 400 },
|
|
423
|
+
props: {},
|
|
424
|
+
},
|
|
425
|
+
({ tw }) =>
|
|
371
426
|
h('div', { style: tw('flex flex-col items-center justify-center w-full h-full bg-white p-8') },
|
|
372
427
|
h('h1', { style: tw('font-sans text-6xl font-bold text-text') }, 'Bold Text'),
|
|
373
428
|
h('p', { style: tw('font-sans text-3xl font-normal text-text') }, 'Normal Text')
|
|
374
|
-
)
|
|
375
|
-
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
const template = defineTemplate(templateModule, { config });
|
|
376
433
|
|
|
377
434
|
const svg = await renderImage(template, {}, { format: 'svg' });
|
|
378
435
|
assert(svg.length > 0, 'SVG should be generated with font files from URLs');
|