sketchmark 2.1.0 → 2.1.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 (77) hide show
  1. package/package.json +1 -7
  2. package/ANIMATABLE_MATRIX.md +0 -177
  3. package/KERNEL_SPEC.md +0 -412
  4. package/PACKS.md +0 -81
  5. package/PRESETS.md +0 -182
  6. package/examples/1730642890464.jpg +0 -0
  7. package/examples/app-screen.svg +0 -1
  8. package/examples/app-screen.visual.json +0 -503
  9. package/examples/dashboard-table.svg +0 -1
  10. package/examples/dashboard-table.visual.json +0 -708
  11. package/examples/dev-docs.svg +0 -1
  12. package/examples/dev-docs.visual.json +0 -248
  13. package/examples/explainer.mp4 +0 -0
  14. package/examples/explainer.visual.json +0 -1713
  15. package/examples/group-origin-effects-lab-check.svg +0 -1
  16. package/examples/group-origin-effects-lab.visual.json +0 -1880
  17. package/examples/image-clip-radius.visual.json +0 -271
  18. package/examples/make-app-screen.cjs +0 -368
  19. package/examples/make-dashboard-table.cjs +0 -277
  20. package/examples/make-dev-docs.cjs +0 -233
  21. package/examples/make-explainer.cjs +0 -438
  22. package/examples/make-group-origin-effects-lab.cjs +0 -370
  23. package/examples/make-image-clip-radius.cjs +0 -169
  24. package/examples/make-modal-dialog.cjs +0 -355
  25. package/examples/make-origin-effects-lab.cjs +0 -311
  26. package/examples/make-preset-character-motion.cjs +0 -32
  27. package/examples/make-presets-demo.cjs +0 -30
  28. package/examples/make-pricing.cjs +0 -286
  29. package/examples/make-product-demo.cjs +0 -468
  30. package/examples/make-product-hero.cjs +0 -223
  31. package/examples/make-release-notes.cjs +0 -333
  32. package/examples/make-settings-panel.cjs +0 -435
  33. package/examples/make-split-preview.cjs +0 -248
  34. package/examples/make-storyboard.cjs +0 -215
  35. package/examples/make-transcript.cjs +0 -234
  36. package/examples/make-typography-test.cjs +0 -397
  37. package/examples/make-ui-demo-explainer.cjs +0 -1094
  38. package/examples/make-ui-flow.cjs +0 -762
  39. package/examples/make-walkthrough.cjs +0 -815
  40. package/examples/modal-dialog.svg +0 -1
  41. package/examples/modal-dialog.visual.json +0 -239
  42. package/examples/origin-effects-lab-check.svg +0 -1
  43. package/examples/origin-effects-lab.visual.json +0 -1412
  44. package/examples/preset-character-motion.visual.json +0 -949
  45. package/examples/presets-demo.visual.json +0 -787
  46. package/examples/pricing.svg +0 -1
  47. package/examples/pricing.visual.json +0 -652
  48. package/examples/product-demo.mp4 +0 -0
  49. package/examples/product-demo.visual.json +0 -866
  50. package/examples/product-hero.svg +0 -1
  51. package/examples/product-hero.visual.json +0 -242
  52. package/examples/release-notes.svg +0 -1
  53. package/examples/release-notes.visual.json +0 -467
  54. package/examples/settings-panel.svg +0 -1
  55. package/examples/settings-panel.visual.json +0 -501
  56. package/examples/split-preview.svg +0 -1
  57. package/examples/split-preview.visual.json +0 -124
  58. package/examples/storyboard.svg +0 -1
  59. package/examples/storyboard.visual.json +0 -312
  60. package/examples/transcript.svg +0 -1
  61. package/examples/transcript.visual.json +0 -407
  62. package/examples/typography-indent-check.svg +0 -1
  63. package/examples/typography-lineheight-0.svg +0 -1
  64. package/examples/typography-lineheight-2.svg +0 -1
  65. package/examples/typography-test-check.svg +0 -1
  66. package/examples/typography-test.svg +0 -1
  67. package/examples/typography-test.visual.json +0 -757
  68. package/examples/ui-demo-explainer-billing.svg +0 -1
  69. package/examples/ui-demo-explainer-check.svg +0 -1
  70. package/examples/ui-demo-explainer-save.svg +0 -1
  71. package/examples/ui-demo-explainer-toggle.svg +0 -1
  72. package/examples/ui-demo-explainer.mp4 +0 -0
  73. package/examples/ui-demo-explainer.visual.json +0 -2597
  74. package/examples/ui-flow.mp4 +0 -0
  75. package/examples/ui-flow.visual.json +0 -1211
  76. package/examples/walkthrough.mp4 +0 -0
  77. package/examples/walkthrough.visual.json +0 -1372
@@ -1,435 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- const width = 960;
5
- const height = 1200;
6
- const bg = "#f8fafc";
7
- const font = "Inter, system-ui, sans-serif";
8
-
9
- const colors = {
10
- title: "#0f172a",
11
- section: "#1e293b",
12
- label: "#334155",
13
- helper: "#64748b",
14
- placeholder: "#94a3b8",
15
- inputBg: "#ffffff",
16
- inputBorder: "#cbd5e1",
17
- toggleOn: "#2563eb",
18
- toggleOff: "#d1d5db",
19
- toggleKnob: "#ffffff",
20
- warningBg: "#fef3c7",
21
- warningBorder: "#f59e0b",
22
- warningText: "#92400e",
23
- btnPrimaryBg: "#2563eb",
24
- btnPrimaryText: "#ffffff",
25
- btnSecondaryBg: "#ffffff",
26
- btnSecondaryText: "#475569",
27
- btnSecondaryBorder: "#cbd5e1",
28
- divider: "#e2e8f0",
29
- cardBg: "#ffffff",
30
- cardBorder: "#e2e8f0"
31
- };
32
-
33
- const padX = 64;
34
- const contentW = width - padX * 2;
35
- let y = 48;
36
-
37
- const elements = [];
38
-
39
- // Panel title
40
- elements.push({
41
- id: "panel-title",
42
- type: "text",
43
- x: padX,
44
- y: y,
45
- text: "Settings",
46
- align: "left",
47
- valign: "top",
48
- fontSize: 32,
49
- fontFamily: font,
50
- weight: 700,
51
- fill: colors.title
52
- });
53
- y += 48;
54
-
55
- elements.push({
56
- id: "panel-desc",
57
- type: "text",
58
- x: padX,
59
- y: y,
60
- text: "Manage your account preferences and application configuration.",
61
- align: "left",
62
- valign: "top",
63
- fontSize: 14,
64
- fontFamily: font,
65
- weight: 400,
66
- fill: colors.helper
67
- });
68
- y += 40;
69
-
70
- // === Section 1: Profile ===
71
- y = renderSectionCard("profile", "Profile", y, function(startY) {
72
- let cy = startY;
73
-
74
- // Display Name field
75
- cy = renderTextField("display-name", "Display Name", "Your public-facing name shown across the application.", "Jane Doe", cy);
76
- cy += 24;
77
-
78
- // Email field
79
- cy = renderTextField("email", "Email Address", "Used for notifications and account recovery.", "jane@example.com", cy);
80
-
81
- return cy;
82
- });
83
- y += 32;
84
-
85
- // === Section 2: Notifications ===
86
- y = renderSectionCard("notif", "Notifications", y, function(startY) {
87
- let cy = startY;
88
-
89
- // Toggle: Email notifications
90
- cy = renderToggle("email-notif", "Email Notifications", "Receive updates about activity in your projects.", true, cy);
91
- cy += 24;
92
-
93
- // Toggle: Marketing
94
- cy = renderToggle("marketing", "Marketing Emails", "Occasional product announcements and feature previews.", false, cy);
95
- cy += 24;
96
-
97
- // Toggle: Slack integration
98
- cy = renderToggle("slack", "Slack Integration", "Push real-time alerts to your connected Slack workspace.", true, cy);
99
-
100
- return cy;
101
- });
102
- y += 32;
103
-
104
- // === Section 3: Security ===
105
- y = renderSectionCard("security", "Security", y, function(startY) {
106
- let cy = startY;
107
-
108
- // Toggle: 2FA
109
- cy = renderToggle("twofa", "Two-Factor Authentication", "Adds an extra layer of protection to your account.", true, cy);
110
- cy += 24;
111
-
112
- // Session timeout field
113
- cy = renderTextField("session", "Session Timeout", "Automatically log out after this period of inactivity.", "30 minutes", cy);
114
- cy += 24;
115
-
116
- // Warning note
117
- cy = renderWarning("security-warn", "Disabling 2FA will immediately remove the second factor from\nyour account. You will not be prompted again until re-enabled.", cy);
118
-
119
- return cy;
120
- });
121
- y += 40;
122
-
123
- // === Buttons row ===
124
- const btnH = 42;
125
- const btnR = 8;
126
- const saveBtnW = 120;
127
- const resetBtnW = 100;
128
- const btnGap = 16;
129
- const btnRowX = padX;
130
-
131
- // Reset button (secondary, left)
132
- elements.push({
133
- id: "btn-reset-bg",
134
- type: "path",
135
- d: roundedRect(btnRowX, y, resetBtnW, btnH, btnR),
136
- fill: colors.btnSecondaryBg,
137
- stroke: colors.btnSecondaryBorder,
138
- strokeWidth: 1
139
- });
140
- elements.push({
141
- id: "btn-reset-label",
142
- type: "text",
143
- x: btnRowX + resetBtnW / 2,
144
- y: y + btnH / 2,
145
- text: "Reset",
146
- align: "center",
147
- valign: "middle",
148
- fontSize: 14,
149
- fontFamily: font,
150
- weight: 500,
151
- fill: colors.btnSecondaryText
152
- });
153
-
154
- // Save button (primary, next to reset)
155
- const saveBtnX = btnRowX + resetBtnW + btnGap;
156
- elements.push({
157
- id: "btn-save-bg",
158
- type: "path",
159
- d: roundedRect(saveBtnX, y, saveBtnW, btnH, btnR),
160
- fill: colors.btnPrimaryBg,
161
- stroke: "none"
162
- });
163
- elements.push({
164
- id: "btn-save-label",
165
- type: "text",
166
- x: saveBtnX + saveBtnW / 2,
167
- y: y + btnH / 2,
168
- text: "Save Changes",
169
- align: "center",
170
- valign: "middle",
171
- fontSize: 14,
172
- fontFamily: font,
173
- weight: 600,
174
- fill: colors.btnPrimaryText
175
- });
176
-
177
- // --- Helpers ---
178
-
179
- function renderSectionCard(prefix, title, startY, contentFn) {
180
- const headerH = 44;
181
- const innerPad = 24;
182
-
183
- // We render content first to measure, then wrap in card
184
- const tempY = startY + headerH + innerPad;
185
- const endY = contentFn(tempY);
186
- const cardH = (endY - startY) + innerPad;
187
-
188
- // Card bg
189
- elements.push({
190
- id: `${prefix}-card`,
191
- type: "path",
192
- d: roundedRect(padX, startY, contentW, cardH, 10),
193
- fill: colors.cardBg,
194
- stroke: colors.cardBorder,
195
- strokeWidth: 1
196
- });
197
-
198
- // Section title
199
- elements.push({
200
- id: `${prefix}-title`,
201
- type: "text",
202
- x: padX + innerPad,
203
- y: startY + 18,
204
- text: title,
205
- align: "left",
206
- valign: "top",
207
- fontSize: 18,
208
- fontFamily: font,
209
- weight: 600,
210
- fill: colors.section
211
- });
212
-
213
- // Divider under title
214
- elements.push({
215
- id: `${prefix}-div`,
216
- type: "path",
217
- d: `M ${padX} ${startY + headerH} L ${padX + contentW} ${startY + headerH}`,
218
- stroke: colors.divider,
219
- strokeWidth: 1,
220
- fill: "none"
221
- });
222
-
223
- return startY + cardH;
224
- }
225
-
226
- function renderTextField(id, label, helper, placeholder, cy) {
227
- const innerPad = 24;
228
- const fieldX = padX + innerPad;
229
- const fieldW = contentW - innerPad * 2;
230
-
231
- // Label
232
- elements.push({
233
- id: `${id}-label`,
234
- type: "text",
235
- x: fieldX,
236
- y: cy,
237
- text: label,
238
- align: "left",
239
- valign: "top",
240
- fontSize: 14,
241
- fontFamily: font,
242
- weight: 500,
243
- fill: colors.label
244
- });
245
- cy += 22;
246
-
247
- // Helper text
248
- elements.push({
249
- id: `${id}-helper`,
250
- type: "text",
251
- x: fieldX,
252
- y: cy,
253
- text: helper,
254
- align: "left",
255
- valign: "top",
256
- fontSize: 12,
257
- fontFamily: font,
258
- weight: 400,
259
- fill: colors.helper
260
- });
261
- cy += 24;
262
-
263
- // Input field background
264
- const inputH = 38;
265
- elements.push({
266
- id: `${id}-input-bg`,
267
- type: "path",
268
- d: roundedRect(fieldX, cy, fieldW, inputH, 6),
269
- fill: colors.inputBg,
270
- stroke: colors.inputBorder,
271
- strokeWidth: 1
272
- });
273
-
274
- // Placeholder text
275
- elements.push({
276
- id: `${id}-placeholder`,
277
- type: "text",
278
- x: fieldX + 12,
279
- y: cy + inputH / 2,
280
- text: placeholder,
281
- align: "left",
282
- valign: "middle",
283
- fontSize: 13,
284
- fontFamily: font,
285
- weight: 400,
286
- fill: colors.placeholder
287
- });
288
- cy += inputH;
289
-
290
- return cy;
291
- }
292
-
293
- function renderToggle(id, label, helper, isOn, cy) {
294
- const innerPad = 24;
295
- const fieldX = padX + innerPad;
296
-
297
- // Label
298
- elements.push({
299
- id: `${id}-label`,
300
- type: "text",
301
- x: fieldX,
302
- y: cy,
303
- text: label,
304
- align: "left",
305
- valign: "top",
306
- fontSize: 14,
307
- fontFamily: font,
308
- weight: 500,
309
- fill: colors.label
310
- });
311
-
312
- // Toggle track (right-aligned)
313
- const toggleW = 40;
314
- const toggleH = 22;
315
- const toggleX = padX + contentW - innerPad - toggleW;
316
- const toggleY = cy - 2;
317
- const trackR = toggleH / 2;
318
-
319
- elements.push({
320
- id: `${id}-track`,
321
- type: "path",
322
- d: roundedRect(toggleX, toggleY, toggleW, toggleH, trackR),
323
- fill: isOn ? colors.toggleOn : colors.toggleOff,
324
- stroke: "none"
325
- });
326
-
327
- // Toggle knob
328
- const knobR = 8;
329
- const knobCx = isOn ? toggleX + toggleW - trackR : toggleX + trackR;
330
- const knobCy = toggleY + toggleH / 2;
331
- elements.push({
332
- id: `${id}-knob`,
333
- type: "path",
334
- d: `M ${knobCx} ${knobCy - knobR} A ${knobR} ${knobR} 0 1 1 ${knobCx} ${knobCy + knobR} A ${knobR} ${knobR} 0 1 1 ${knobCx} ${knobCy - knobR} Z`,
335
- fill: colors.toggleKnob,
336
- stroke: "none"
337
- });
338
-
339
- cy += 22;
340
-
341
- // Helper text
342
- elements.push({
343
- id: `${id}-helper`,
344
- type: "text",
345
- x: fieldX,
346
- y: cy,
347
- text: helper,
348
- align: "left",
349
- valign: "top",
350
- fontSize: 12,
351
- fontFamily: font,
352
- weight: 400,
353
- fill: colors.helper
354
- });
355
- cy += 20;
356
-
357
- return cy;
358
- }
359
-
360
- function renderWarning(id, text, cy) {
361
- const innerPad = 24;
362
- const fieldX = padX + innerPad;
363
- const fieldW = contentW - innerPad * 2;
364
- const warnPadX = 14;
365
- const warnPadY = 12;
366
- const lineCount = text.split("\n").length;
367
- const warnH = lineCount * 20 + warnPadY * 2;
368
-
369
- elements.push({
370
- id: `${id}-bg`,
371
- type: "path",
372
- d: roundedRect(fieldX, cy, fieldW, warnH, 6),
373
- fill: colors.warningBg,
374
- stroke: colors.warningBorder,
375
- strokeWidth: 1
376
- });
377
-
378
- elements.push({
379
- id: `${id}-icon`,
380
- type: "text",
381
- x: fieldX + warnPadX,
382
- y: cy + warnPadY,
383
- text: "⚠",
384
- align: "left",
385
- valign: "top",
386
- fontSize: 14,
387
- fontFamily: font,
388
- weight: 400,
389
- fill: colors.warningText
390
- });
391
-
392
- elements.push({
393
- id: `${id}-text`,
394
- type: "text",
395
- x: fieldX + warnPadX + 22,
396
- y: cy + warnPadY + 1,
397
- text: text,
398
- align: "left",
399
- valign: "top",
400
- fontSize: 12,
401
- fontFamily: font,
402
- weight: 400,
403
- lineHeight: 1.65,
404
- fill: colors.warningText,
405
- maxWidth: fieldW - warnPadX * 2 - 22
406
- });
407
-
408
- cy += warnH;
409
- return cy;
410
- }
411
-
412
- function roundedRect(x, y, w, h, r) {
413
- return [
414
- `M ${x + r} ${y}`,
415
- `L ${x + w - r} ${y}`,
416
- `Q ${x + w} ${y} ${x + w} ${y + r}`,
417
- `L ${x + w} ${y + h - r}`,
418
- `Q ${x + w} ${y + h} ${x + w - r} ${y + h}`,
419
- `L ${x + r} ${y + h}`,
420
- `Q ${x} ${y + h} ${x} ${y + h - r}`,
421
- `L ${x} ${y + r}`,
422
- `Q ${x} ${y} ${x + r} ${y}`,
423
- "Z"
424
- ].join(" ");
425
- }
426
-
427
- const doc = {
428
- version: 1,
429
- canvas: { width, height, background: bg },
430
- elements
431
- };
432
-
433
- const outPath = path.join(__dirname, "settings-panel.visual.json");
434
- fs.writeFileSync(outPath, JSON.stringify(doc, null, 2));
435
- console.log("Written:", outPath);
@@ -1,248 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- const width = 1200;
5
- const height = 600;
6
- const bg = "#f8fafc";
7
- const font = "Inter, system-ui, sans-serif";
8
- const monoFont = "JetBrains Mono, Fira Code, monospace";
9
-
10
- const colors = {
11
- codeBg: "#1e293b",
12
- codeText: "#e2e8f0",
13
- codeKeyword: "#7dd3fc",
14
- codeString: "#86efac",
15
- codeNumber: "#fcd34d",
16
- previewBg: "#ffffff",
17
- previewBorder: "#e2e8f0",
18
- previewTitle: "#0f172a",
19
- previewCaption: "#64748b",
20
- badgeBg: "#8b5cf6",
21
- badgeText: "#ffffff",
22
- sectionLabel: "#64748b"
23
- };
24
-
25
- const padX = 48;
26
- const padY = 48;
27
- const gapX = 40;
28
- const colW = (width - padX * 2 - gapX) / 2;
29
-
30
- const elements = [];
31
-
32
- // === LEFT SIDE: Code block ===
33
-
34
- const codeX = padX;
35
- const codeY = padY;
36
- const codeW = colW;
37
- const codeH = height - padY * 2;
38
- const codeR = 12;
39
- const codePad = 24;
40
-
41
- // Code block background
42
- elements.push({
43
- id: "code-bg",
44
- type: "path",
45
- d: roundedRect(codeX, codeY, codeW, codeH, codeR),
46
- fill: colors.codeBg,
47
- stroke: "none"
48
- });
49
-
50
- // Section label above code
51
- elements.push({
52
- id: "code-label",
53
- type: "text",
54
- x: codeX + codePad,
55
- y: codeY + 20,
56
- text: "document.visual.json",
57
- align: "left",
58
- valign: "top",
59
- fontSize: 11,
60
- fontFamily: monoFont,
61
- weight: 500,
62
- fill: colors.sectionLabel
63
- });
64
-
65
- // JSON code content
66
- const jsonCode = `{
67
- "version": 1,
68
- "canvas": {
69
- "width": 400,
70
- "height": 300,
71
- "background": "#ffffff"
72
- },
73
- "elements": [
74
- {
75
- "id": "card",
76
- "type": "path",
77
- "d": "M 20 20 L 380 20 ...",
78
- "fill": "#f1f5f9",
79
- "stroke": "#e2e8f0"
80
- },
81
- {
82
- "id": "title",
83
- "type": "text",
84
- "x": 40,
85
- "y": 60,
86
- "text": "Welcome",
87
- "fontSize": 24,
88
- "weight": 700
89
- }
90
- ]
91
- }`;
92
-
93
- elements.push({
94
- id: "code-text",
95
- type: "text",
96
- x: codeX + codePad,
97
- y: codeY + 48,
98
- text: jsonCode,
99
- align: "left",
100
- valign: "top",
101
- fontSize: 12,
102
- fontFamily: monoFont,
103
- weight: 400,
104
- lineHeight: 1.5,
105
- fill: colors.codeText,
106
- maxWidth: codeW - codePad * 2
107
- });
108
-
109
- // === RIGHT SIDE: Preview ===
110
-
111
- const previewX = padX + colW + gapX;
112
- const previewY = padY;
113
- const previewW = colW;
114
- const previewH = height - padY * 2;
115
-
116
- // Centered "Rendered output" badge above preview card
117
- const badgeW = 130;
118
- const badgeH = 26;
119
- const badgeX = previewX + (previewW - badgeW) / 2;
120
- const badgeY = previewY;
121
- const badgeR = 13;
122
-
123
- elements.push({
124
- id: "badge-bg",
125
- type: "path",
126
- d: roundedRect(badgeX, badgeY, badgeW, badgeH, badgeR),
127
- fill: colors.badgeBg,
128
- stroke: "none"
129
- });
130
-
131
- elements.push({
132
- id: "badge-text",
133
- type: "text",
134
- x: badgeX + badgeW / 2,
135
- y: badgeY + badgeH / 2,
136
- text: "Rendered output",
137
- align: "center",
138
- valign: "middle",
139
- fontSize: 11,
140
- fontFamily: font,
141
- weight: 600,
142
- fill: colors.badgeText
143
- });
144
-
145
- // Preview container
146
- const cardY = badgeY + badgeH + 20;
147
- const cardH = previewH - badgeH - 20;
148
- const cardR = 12;
149
- const cardPad = 32;
150
-
151
- elements.push({
152
- id: "preview-bg",
153
- type: "path",
154
- d: roundedRect(previewX, cardY, previewW, cardH, cardR),
155
- fill: colors.previewBg,
156
- stroke: colors.previewBorder,
157
- strokeWidth: 1
158
- });
159
-
160
- // Inner rendered card (the visual output)
161
- const innerCardX = previewX + cardPad;
162
- const innerCardY = cardY + cardPad;
163
- const innerCardW = previewW - cardPad * 2;
164
- const innerCardH = 200;
165
- const innerCardR = 8;
166
-
167
- elements.push({
168
- id: "inner-card-bg",
169
- type: "path",
170
- d: roundedRect(innerCardX, innerCardY, innerCardW, innerCardH, innerCardR),
171
- fill: "#f1f5f9",
172
- stroke: "#e2e8f0",
173
- strokeWidth: 1
174
- });
175
-
176
- // Inner card title (the "Welcome" from the JSON)
177
- elements.push({
178
- id: "inner-card-title",
179
- type: "text",
180
- x: innerCardX + 24,
181
- y: innerCardY + 32,
182
- text: "Welcome",
183
- align: "left",
184
- valign: "top",
185
- fontSize: 24,
186
- fontFamily: font,
187
- weight: 700,
188
- fill: colors.previewTitle
189
- });
190
-
191
- // Inner card body text
192
- elements.push({
193
- id: "inner-card-body",
194
- type: "text",
195
- x: innerCardX + 24,
196
- y: innerCardY + 72,
197
- text: "This card was rendered from the JSON\ndefinition on the left. The kernel resolves\nall elements into positioned primitives.",
198
- align: "left",
199
- valign: "top",
200
- fontSize: 13,
201
- fontFamily: font,
202
- weight: 400,
203
- lineHeight: 1.55,
204
- fill: colors.previewCaption,
205
- maxWidth: innerCardW - 48
206
- });
207
-
208
- // Preview caption below the inner card
209
- elements.push({
210
- id: "preview-caption",
211
- type: "text",
212
- x: previewX + cardPad,
213
- y: innerCardY + innerCardH + 28,
214
- text: "Live preview updates as the document changes.\nNo build step required — just edit and see.",
215
- align: "left",
216
- valign: "top",
217
- fontSize: 13,
218
- fontFamily: font,
219
- weight: 400,
220
- lineHeight: 1.55,
221
- fill: colors.previewCaption,
222
- maxWidth: previewW - cardPad * 2
223
- });
224
-
225
- function roundedRect(x, y, w, h, r) {
226
- return [
227
- `M ${x + r} ${y}`,
228
- `L ${x + w - r} ${y}`,
229
- `Q ${x + w} ${y} ${x + w} ${y + r}`,
230
- `L ${x + w} ${y + h - r}`,
231
- `Q ${x + w} ${y + h} ${x + w - r} ${y + h}`,
232
- `L ${x + r} ${y + h}`,
233
- `Q ${x} ${y + h} ${x} ${y + h - r}`,
234
- `L ${x} ${y + r}`,
235
- `Q ${x} ${y} ${x + r} ${y}`,
236
- "Z"
237
- ].join(" ");
238
- }
239
-
240
- const doc = {
241
- version: 1,
242
- canvas: { width, height, background: bg },
243
- elements
244
- };
245
-
246
- const outPath = path.join(__dirname, "split-preview.visual.json");
247
- fs.writeFileSync(outPath, JSON.stringify(doc, null, 2));
248
- console.log("Written:", outPath);