sketchmark 2.1.0 → 2.1.2

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 (117) 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/dist/src/builders/index.d.ts +0 -64
  7. package/dist/src/builders/index.js +0 -212
  8. package/dist/src/compounds.d.ts +0 -13
  9. package/dist/src/compounds.js +0 -118
  10. package/dist/src/deck.d.ts +0 -4
  11. package/dist/src/deck.js +0 -91
  12. package/dist/src/export/index.d.ts +0 -8
  13. package/dist/src/export/index.js +0 -15
  14. package/dist/src/kernel.d.ts +0 -8
  15. package/dist/src/kernel.js +0 -68
  16. package/dist/src/motion.d.ts +0 -4
  17. package/dist/src/motion.js +0 -262
  18. package/dist/src/patch.d.ts +0 -5
  19. package/dist/src/patch.js +0 -72
  20. package/dist/src/player/index.d.ts +0 -68
  21. package/dist/src/player/index.js +0 -600
  22. package/dist/src/project.d.ts +0 -11
  23. package/dist/src/project.js +0 -107
  24. package/dist/src/render/raw-three.d.ts +0 -7
  25. package/dist/src/render/raw-three.js +0 -17
  26. package/dist/src/render/three-html.d.ts +0 -2
  27. package/dist/src/render/three-html.js +0 -257
  28. package/dist/src/render/three-preview-svg.d.ts +0 -3
  29. package/dist/src/render/three-preview-svg.js +0 -102
  30. package/dist/src/scenes.d.ts +0 -4
  31. package/dist/src/scenes.js +0 -26
  32. package/dist/src/sequences.d.ts +0 -43
  33. package/dist/src/sequences.js +0 -109
  34. package/dist/src/shapes/builtins.d.ts +0 -2
  35. package/dist/src/shapes/builtins.js +0 -393
  36. package/dist/src/shapes/common.d.ts +0 -9
  37. package/dist/src/shapes/common.js +0 -76
  38. package/dist/src/shapes/geometry.d.ts +0 -22
  39. package/dist/src/shapes/geometry.js +0 -166
  40. package/dist/src/shapes/index.d.ts +0 -2
  41. package/dist/src/shapes/index.js +0 -18
  42. package/dist/src/shapes/registry.d.ts +0 -8
  43. package/dist/src/shapes/registry.js +0 -31
  44. package/dist/src/shapes/types.d.ts +0 -32
  45. package/dist/src/shapes/types.js +0 -2
  46. package/examples/1730642890464.jpg +0 -0
  47. package/examples/app-screen.svg +0 -1
  48. package/examples/app-screen.visual.json +0 -503
  49. package/examples/dashboard-table.svg +0 -1
  50. package/examples/dashboard-table.visual.json +0 -708
  51. package/examples/dev-docs.svg +0 -1
  52. package/examples/dev-docs.visual.json +0 -248
  53. package/examples/explainer.mp4 +0 -0
  54. package/examples/explainer.visual.json +0 -1713
  55. package/examples/group-origin-effects-lab-check.svg +0 -1
  56. package/examples/group-origin-effects-lab.visual.json +0 -1880
  57. package/examples/image-clip-radius.visual.json +0 -271
  58. package/examples/make-app-screen.cjs +0 -368
  59. package/examples/make-dashboard-table.cjs +0 -277
  60. package/examples/make-dev-docs.cjs +0 -233
  61. package/examples/make-explainer.cjs +0 -438
  62. package/examples/make-group-origin-effects-lab.cjs +0 -370
  63. package/examples/make-image-clip-radius.cjs +0 -169
  64. package/examples/make-modal-dialog.cjs +0 -355
  65. package/examples/make-origin-effects-lab.cjs +0 -311
  66. package/examples/make-preset-character-motion.cjs +0 -32
  67. package/examples/make-presets-demo.cjs +0 -30
  68. package/examples/make-pricing.cjs +0 -286
  69. package/examples/make-product-demo.cjs +0 -468
  70. package/examples/make-product-hero.cjs +0 -223
  71. package/examples/make-release-notes.cjs +0 -333
  72. package/examples/make-settings-panel.cjs +0 -435
  73. package/examples/make-split-preview.cjs +0 -248
  74. package/examples/make-storyboard.cjs +0 -215
  75. package/examples/make-transcript.cjs +0 -234
  76. package/examples/make-typography-test.cjs +0 -397
  77. package/examples/make-ui-demo-explainer.cjs +0 -1094
  78. package/examples/make-ui-flow.cjs +0 -762
  79. package/examples/make-walkthrough.cjs +0 -815
  80. package/examples/modal-dialog.svg +0 -1
  81. package/examples/modal-dialog.visual.json +0 -239
  82. package/examples/origin-effects-lab-check.svg +0 -1
  83. package/examples/origin-effects-lab.visual.json +0 -1412
  84. package/examples/preset-character-motion.visual.json +0 -949
  85. package/examples/presets-demo.visual.json +0 -787
  86. package/examples/pricing.svg +0 -1
  87. package/examples/pricing.visual.json +0 -652
  88. package/examples/product-demo.mp4 +0 -0
  89. package/examples/product-demo.visual.json +0 -866
  90. package/examples/product-hero.svg +0 -1
  91. package/examples/product-hero.visual.json +0 -242
  92. package/examples/release-notes.svg +0 -1
  93. package/examples/release-notes.visual.json +0 -467
  94. package/examples/settings-panel.svg +0 -1
  95. package/examples/settings-panel.visual.json +0 -501
  96. package/examples/split-preview.svg +0 -1
  97. package/examples/split-preview.visual.json +0 -124
  98. package/examples/storyboard.svg +0 -1
  99. package/examples/storyboard.visual.json +0 -312
  100. package/examples/transcript.svg +0 -1
  101. package/examples/transcript.visual.json +0 -407
  102. package/examples/typography-indent-check.svg +0 -1
  103. package/examples/typography-lineheight-0.svg +0 -1
  104. package/examples/typography-lineheight-2.svg +0 -1
  105. package/examples/typography-test-check.svg +0 -1
  106. package/examples/typography-test.svg +0 -1
  107. package/examples/typography-test.visual.json +0 -757
  108. package/examples/ui-demo-explainer-billing.svg +0 -1
  109. package/examples/ui-demo-explainer-check.svg +0 -1
  110. package/examples/ui-demo-explainer-save.svg +0 -1
  111. package/examples/ui-demo-explainer-toggle.svg +0 -1
  112. package/examples/ui-demo-explainer.mp4 +0 -0
  113. package/examples/ui-demo-explainer.visual.json +0 -2597
  114. package/examples/ui-flow.mp4 +0 -0
  115. package/examples/ui-flow.visual.json +0 -1211
  116. package/examples/walkthrough.mp4 +0 -0
  117. 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);