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,815 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- const width = 1280;
5
- const height = 720;
6
- const duration = 24;
7
- const fps = 30;
8
- const bg = "#f8fafc";
9
- const font = "Inter, system-ui, sans-serif";
10
-
11
- const colors = {
12
- hero: "#0f172a",
13
- heroSub: "#64748b",
14
- panelBg: "#ffffff",
15
- panelBorder: "#e2e8f0",
16
- panelTitle: "#1e293b",
17
- panelBody: "#475569",
18
- panelMuted: "#94a3b8",
19
- inputBg: "#f8fafc",
20
- inputBorder: "#cbd5e1",
21
- inputText: "#64748b",
22
- btnPrimaryBg: "#2563eb",
23
- btnPrimaryText: "#ffffff",
24
- btnSecondaryBg: "#ffffff",
25
- btnSecondaryText: "#475569",
26
- btnSecondaryBorder: "#e2e8f0",
27
- accent: "#2563eb",
28
- success: "#10b981",
29
- successBg: "#d1fae5",
30
- cursor: "#0f172a",
31
- cursorRing: "#3b82f6"
32
- };
33
-
34
- const curves = {
35
- ease: { type: "cubicBezier", x1: 0.4, y1: 0, x2: 0.2, y2: 1 },
36
- easeOut: { type: "cubicBezier", x1: 0, y1: 0, x2: 0.2, y2: 1 },
37
- snap: { type: "cubicBezier", x1: 0.2, y1: 1, x2: 0.2, y2: 1 }
38
- };
39
-
40
- const elements = [];
41
-
42
- // Cursor elements stored separately, added at the end so they render on top
43
- // Button positions (calculated from layout):
44
- // Create button: dashX + dashW - 140 + 56 = 996, dashY + 24 + 20 = 164
45
- // Input field: ~640, ~335 (center of input)
46
- // Save button: modalX + modalW - 150 + 61 = 791, modalY + modalH - 68 + 20 = 482
47
- const createBtnX = 996;
48
- const createBtnY = 164;
49
- const inputFieldX = 640;
50
- const inputFieldY = 335;
51
- const saveBtnX = 791;
52
- const saveBtnY = 482;
53
-
54
- const cursorElement = {
55
- id: "cursor",
56
- type: "group",
57
- x: -50,
58
- y: -50,
59
- children: [
60
- {
61
- id: "cursor-arrow",
62
- type: "path",
63
- d: "M 0 0 L 0 20 L 5 16 L 8 24 L 12 22 L 9 14 L 15 14 Z",
64
- fill: colors.cursor,
65
- stroke: "#ffffff",
66
- strokeWidth: 1.5
67
- }
68
- ],
69
- timeline: {
70
- tracks: {
71
- x: {
72
- keyframes: [
73
- { time: 0, value: -50 },
74
- // Move to Create button
75
- { time: 5.5, value: -50, out: curves.ease },
76
- { time: 6.5, value: createBtnX },
77
- { time: 8, value: createBtnX },
78
- // Move to input field
79
- { time: 10, value: createBtnX, out: curves.ease },
80
- { time: 11, value: inputFieldX },
81
- // Move to Save button
82
- { time: 16, value: inputFieldX, out: curves.ease },
83
- { time: 17, value: saveBtnX },
84
- // Exit
85
- { time: 20, value: saveBtnX, out: curves.ease },
86
- { time: 21, value: 1400 }
87
- ]
88
- },
89
- y: {
90
- keyframes: [
91
- { time: 0, value: -50 },
92
- { time: 5.5, value: -50, out: curves.ease },
93
- { time: 6.5, value: createBtnY },
94
- { time: 8, value: createBtnY },
95
- { time: 10, value: createBtnY, out: curves.ease },
96
- { time: 11, value: inputFieldY },
97
- { time: 16, value: inputFieldY, out: curves.ease },
98
- { time: 17, value: saveBtnY },
99
- { time: 20, value: saveBtnY, out: curves.ease },
100
- { time: 21, value: 800 }
101
- ]
102
- }
103
- }
104
- }
105
- };
106
-
107
- const clickRingElement = {
108
- id: "click-ring",
109
- type: "path",
110
- d: "M 0 0 m -16 0 a 16 16 0 1 1 32 0 a 16 16 0 1 1 -32 0",
111
- x: createBtnX,
112
- y: createBtnY,
113
- fill: "none",
114
- stroke: colors.cursorRing,
115
- strokeWidth: 2,
116
- opacity: 0,
117
- origin: [0, 0],
118
- timeline: {
119
- tracks: {
120
- opacity: {
121
- keyframes: [
122
- // Click 1: Create button
123
- { time: 7, value: 0 },
124
- { time: 7.1, value: 0.6 },
125
- { time: 7.5, value: 0 },
126
- // Click 2: Save button
127
- { time: 17.5, value: 0 },
128
- { time: 17.6, value: 0.6 },
129
- { time: 18, value: 0 }
130
- ]
131
- },
132
- scale: {
133
- keyframes: [
134
- { time: 7, value: 0.5, out: curves.easeOut },
135
- { time: 7.5, value: 1.5 },
136
- { time: 17.5, value: 0.5, out: curves.easeOut },
137
- { time: 18, value: 1.5 }
138
- ]
139
- },
140
- x: {
141
- keyframes: [
142
- { time: 7, value: createBtnX },
143
- { time: 17.5, value: saveBtnX }
144
- ]
145
- },
146
- y: {
147
- keyframes: [
148
- { time: 7, value: createBtnY },
149
- { time: 17.5, value: saveBtnY }
150
- ]
151
- }
152
- }
153
- }
154
- };
155
-
156
- // === SCENE 1: Hero (0-5s) ===
157
- elements.push({
158
- id: "hero-title",
159
- type: "text",
160
- x: width / 2,
161
- y: 280,
162
- text: "Build beautiful interfaces",
163
- align: "center",
164
- valign: "middle",
165
- fontSize: 56,
166
- fontFamily: font,
167
- weight: 800,
168
- fill: colors.hero,
169
- opacity: 0,
170
- timeline: {
171
- tracks: {
172
- opacity: {
173
- keyframes: [
174
- { time: 0.2, value: 0, out: curves.ease },
175
- { time: 0.8, value: 1 },
176
- { time: 4, value: 1, out: curves.ease },
177
- { time: 4.6, value: 0 }
178
- ]
179
- },
180
- y: {
181
- keyframes: [
182
- { time: 0.2, value: 300, out: curves.ease },
183
- { time: 0.8, value: 280 }
184
- ]
185
- },
186
- scale: {
187
- keyframes: [
188
- { time: 0.2, value: 0.95, out: curves.ease },
189
- { time: 0.8, value: 1 }
190
- ]
191
- }
192
- }
193
- }
194
- });
195
-
196
- elements.push({
197
- id: "hero-sub",
198
- type: "text",
199
- x: width / 2,
200
- y: 360,
201
- text: "Design, prototype, and ship — all in one place.",
202
- align: "center",
203
- valign: "middle",
204
- fontSize: 22,
205
- fontFamily: font,
206
- weight: 400,
207
- fill: colors.heroSub,
208
- opacity: 0,
209
- timeline: {
210
- tracks: {
211
- opacity: {
212
- keyframes: [
213
- { time: 0.6, value: 0, out: curves.ease },
214
- { time: 1.2, value: 1 },
215
- { time: 4, value: 1, out: curves.ease },
216
- { time: 4.6, value: 0 }
217
- ]
218
- }
219
- }
220
- }
221
- });
222
-
223
- // === SCENE 2: Dashboard panel (5-10s) ===
224
- const dashX = 200;
225
- const dashY = 120;
226
- const dashW = 880;
227
- const dashH = 480;
228
-
229
- elements.push({
230
- id: "dash-panel",
231
- type: "group",
232
- x: dashX,
233
- y: dashY,
234
- opacity: 0,
235
- children: [
236
- {
237
- id: "dash-bg",
238
- type: "path",
239
- d: roundedRect(0, 0, dashW, dashH, 12),
240
- fill: colors.panelBg,
241
- stroke: colors.panelBorder,
242
- strokeWidth: 1
243
- },
244
- // Panel header
245
- {
246
- id: "dash-title",
247
- type: "text",
248
- x: 28,
249
- y: 28,
250
- text: "Your Projects",
251
- align: "left",
252
- valign: "top",
253
- fontSize: 20,
254
- fontFamily: font,
255
- weight: 600,
256
- fill: colors.panelTitle
257
- },
258
- {
259
- id: "dash-subtitle",
260
- type: "text",
261
- x: 28,
262
- y: 56,
263
- text: "3 projects · Last updated 2 hours ago",
264
- align: "left",
265
- valign: "top",
266
- fontSize: 13,
267
- fontFamily: font,
268
- weight: 400,
269
- fill: colors.panelMuted
270
- },
271
- // Divider
272
- {
273
- id: "dash-divider",
274
- type: "path",
275
- d: `M 0 90 L ${dashW} 90`,
276
- stroke: colors.panelBorder,
277
- strokeWidth: 1,
278
- fill: "none"
279
- },
280
- // Project rows
281
- ...projectRow(0, "Marketing Website", "12 screens · Published", 110),
282
- ...projectRow(1, "Mobile App v2", "8 screens · Draft", 170),
283
- ...projectRow(2, "Dashboard Redesign", "24 screens · In review", 230)
284
- ],
285
- timeline: {
286
- tracks: {
287
- opacity: {
288
- keyframes: [
289
- { time: 5, value: 0, out: curves.ease },
290
- { time: 5.6, value: 1 },
291
- { time: 9, value: 1, out: curves.ease },
292
- { time: 9.6, value: 0 }
293
- ]
294
- },
295
- scale: {
296
- keyframes: [
297
- { time: 5, value: 0.96, out: curves.ease },
298
- { time: 5.6, value: 1 }
299
- ]
300
- }
301
- }
302
- }
303
- });
304
-
305
- // Create button (separate for zoom effect on click)
306
- elements.push({
307
- id: "create-btn",
308
- type: "group",
309
- x: dashX + dashW - 140,
310
- y: dashY + 24,
311
- opacity: 0,
312
- children: [
313
- {
314
- id: "create-btn-bg",
315
- type: "path",
316
- d: roundedRect(0, 0, 112, 40, 8),
317
- fill: colors.btnPrimaryBg,
318
- stroke: "none"
319
- },
320
- {
321
- id: "create-btn-text",
322
- type: "text",
323
- x: 56,
324
- y: 20,
325
- text: "Create New",
326
- align: "center",
327
- valign: "middle",
328
- fontSize: 14,
329
- fontFamily: font,
330
- weight: 600,
331
- fill: colors.btnPrimaryText
332
- }
333
- ],
334
- timeline: {
335
- tracks: {
336
- opacity: {
337
- keyframes: [
338
- { time: 5.3, value: 0, out: curves.ease },
339
- { time: 5.8, value: 1 },
340
- { time: 9, value: 1, out: curves.ease },
341
- { time: 9.6, value: 0 }
342
- ]
343
- },
344
- scale: {
345
- keyframes: [
346
- { time: 7, value: 1, out: curves.snap },
347
- { time: 7.15, value: 0.95 },
348
- { time: 7.3, value: 1 }
349
- ]
350
- }
351
- }
352
- },
353
- origin: [56, 20]
354
- });
355
-
356
- // === SCENE 3: Create form modal (10-17s) ===
357
- const modalW = 480;
358
- const modalH = 340;
359
- const modalX = (width - modalW) / 2;
360
- const modalY = (height - modalH) / 2;
361
-
362
- // Modal backdrop
363
- elements.push({
364
- id: "modal-backdrop",
365
- type: "path",
366
- d: `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`,
367
- fill: colors.hero,
368
- opacity: 0,
369
- timeline: {
370
- tracks: {
371
- opacity: {
372
- keyframes: [
373
- { time: 10, value: 0, out: curves.ease },
374
- { time: 10.4, value: 0.4 },
375
- { time: 19, value: 0.4, out: curves.ease },
376
- { time: 19.5, value: 0 }
377
- ]
378
- }
379
- }
380
- }
381
- });
382
-
383
- elements.push({
384
- id: "modal",
385
- type: "group",
386
- x: modalX,
387
- y: modalY,
388
- opacity: 0,
389
- children: [
390
- {
391
- id: "modal-bg",
392
- type: "path",
393
- d: roundedRect(0, 0, modalW, modalH, 12),
394
- fill: colors.panelBg,
395
- stroke: "none"
396
- },
397
- {
398
- id: "modal-title",
399
- type: "text",
400
- x: 28,
401
- y: 28,
402
- text: "Create new project",
403
- align: "left",
404
- valign: "top",
405
- fontSize: 18,
406
- fontFamily: font,
407
- weight: 600,
408
- fill: colors.panelTitle
409
- },
410
- {
411
- id: "modal-desc",
412
- type: "text",
413
- x: 28,
414
- y: 56,
415
- text: "Give your project a name and choose a template to get started.",
416
- align: "left",
417
- valign: "top",
418
- fontSize: 13,
419
- fontFamily: font,
420
- weight: 400,
421
- fill: colors.panelBody,
422
- maxWidth: modalW - 56
423
- },
424
- // Project name field
425
- {
426
- id: "field-label",
427
- type: "text",
428
- x: 28,
429
- y: 100,
430
- text: "Project name",
431
- align: "left",
432
- valign: "top",
433
- fontSize: 13,
434
- fontFamily: font,
435
- weight: 500,
436
- fill: colors.panelTitle
437
- },
438
- {
439
- id: "field-input-bg",
440
- type: "path",
441
- d: roundedRect(28, 124, modalW - 56, 42, 6),
442
- fill: colors.inputBg,
443
- stroke: colors.inputBorder,
444
- strokeWidth: 1
445
- },
446
- {
447
- id: "field-placeholder",
448
- type: "text",
449
- x: 40,
450
- y: 145,
451
- text: "Enter project name...",
452
- align: "left",
453
- valign: "middle",
454
- fontSize: 14,
455
- fontFamily: font,
456
- weight: 400,
457
- fill: colors.inputText
458
- },
459
- // Template selector
460
- {
461
- id: "template-label",
462
- type: "text",
463
- x: 28,
464
- y: 186,
465
- text: "Template",
466
- align: "left",
467
- valign: "top",
468
- fontSize: 13,
469
- fontFamily: font,
470
- weight: 500,
471
- fill: colors.panelTitle
472
- },
473
- ...templateOption(0, "Blank", 28, 210, true),
474
- ...templateOption(1, "Dashboard", 138, 210, false),
475
- ...templateOption(2, "Landing", 268, 210, false)
476
- ],
477
- timeline: {
478
- tracks: {
479
- opacity: {
480
- keyframes: [
481
- { time: 10, value: 0, out: curves.ease },
482
- { time: 10.5, value: 1 },
483
- { time: 19, value: 1, out: curves.ease },
484
- { time: 19.5, value: 0 }
485
- ]
486
- },
487
- scale: {
488
- keyframes: [
489
- { time: 10, value: 0.9, out: curves.ease },
490
- { time: 10.5, value: 1 },
491
- { time: 19, value: 1, out: curves.ease },
492
- { time: 19.5, value: 0.95 }
493
- ]
494
- },
495
- y: {
496
- keyframes: [
497
- { time: 10, value: modalY + 30, out: curves.ease },
498
- { time: 10.5, value: modalY }
499
- ]
500
- }
501
- }
502
- },
503
- origin: [modalW / 2, modalH / 2]
504
- });
505
-
506
- // Typing animation - text appears
507
- elements.push({
508
- id: "typed-text",
509
- type: "text",
510
- x: modalX + 40,
511
- y: modalY + 145,
512
- text: "Product Launch 2024",
513
- align: "left",
514
- valign: "middle",
515
- fontSize: 14,
516
- fontFamily: font,
517
- weight: 400,
518
- fill: colors.panelTitle,
519
- opacity: 0,
520
- timeline: {
521
- tracks: {
522
- opacity: {
523
- keyframes: [
524
- { time: 12, value: 0 },
525
- { time: 12.5, value: 1 },
526
- { time: 19, value: 1, out: curves.ease },
527
- { time: 19.5, value: 0 }
528
- ]
529
- }
530
- }
531
- }
532
- });
533
-
534
- // Modal buttons
535
- elements.push({
536
- id: "cancel-btn",
537
- type: "group",
538
- x: modalX + 28,
539
- y: modalY + modalH - 68,
540
- opacity: 0,
541
- children: [
542
- {
543
- id: "cancel-btn-bg",
544
- type: "path",
545
- d: roundedRect(0, 0, 90, 40, 8),
546
- fill: colors.btnSecondaryBg,
547
- stroke: colors.btnSecondaryBorder,
548
- strokeWidth: 1
549
- },
550
- {
551
- id: "cancel-btn-text",
552
- type: "text",
553
- x: 45,
554
- y: 20,
555
- text: "Cancel",
556
- align: "center",
557
- valign: "middle",
558
- fontSize: 14,
559
- fontFamily: font,
560
- weight: 500,
561
- fill: colors.btnSecondaryText
562
- }
563
- ],
564
- timeline: {
565
- tracks: {
566
- opacity: {
567
- keyframes: [
568
- { time: 10.6, value: 0, out: curves.ease },
569
- { time: 11, value: 1 },
570
- { time: 19, value: 1, out: curves.ease },
571
- { time: 19.5, value: 0 }
572
- ]
573
- }
574
- }
575
- }
576
- });
577
-
578
- elements.push({
579
- id: "save-btn",
580
- type: "group",
581
- x: modalX + modalW - 150,
582
- y: modalY + modalH - 68,
583
- opacity: 0,
584
- children: [
585
- {
586
- id: "save-btn-bg",
587
- type: "path",
588
- d: roundedRect(0, 0, 122, 40, 8),
589
- fill: colors.btnPrimaryBg,
590
- stroke: "none"
591
- },
592
- {
593
- id: "save-btn-text",
594
- type: "text",
595
- x: 61,
596
- y: 20,
597
- text: "Create Project",
598
- align: "center",
599
- valign: "middle",
600
- fontSize: 14,
601
- fontFamily: font,
602
- weight: 600,
603
- fill: colors.btnPrimaryText
604
- }
605
- ],
606
- origin: [61, 20],
607
- timeline: {
608
- tracks: {
609
- opacity: {
610
- keyframes: [
611
- { time: 10.6, value: 0, out: curves.ease },
612
- { time: 11, value: 1 },
613
- { time: 19, value: 1, out: curves.ease },
614
- { time: 19.5, value: 0 }
615
- ]
616
- },
617
- scale: {
618
- keyframes: [
619
- { time: 17.5, value: 1, out: curves.snap },
620
- { time: 17.65, value: 0.95 },
621
- { time: 17.8, value: 1 }
622
- ]
623
- }
624
- }
625
- }
626
- });
627
-
628
- // === SCENE 4: Success state (19-24s) ===
629
- elements.push({
630
- id: "success-panel",
631
- type: "group",
632
- x: (width - 400) / 2,
633
- y: 240,
634
- opacity: 0,
635
- children: [
636
- {
637
- id: "success-bg",
638
- type: "path",
639
- d: roundedRect(0, 0, 400, 200, 12),
640
- fill: colors.panelBg,
641
- stroke: colors.panelBorder,
642
- strokeWidth: 1
643
- },
644
- // Checkmark circle
645
- {
646
- id: "success-circle",
647
- type: "path",
648
- d: "M 200 50 m -30 0 a 30 30 0 1 1 60 0 a 30 30 0 1 1 -60 0",
649
- fill: colors.successBg,
650
- stroke: "none"
651
- },
652
- {
653
- id: "success-check",
654
- type: "path",
655
- d: "M 186 50 L 196 60 L 214 42",
656
- fill: "none",
657
- stroke: colors.success,
658
- strokeWidth: 3,
659
- strokeCap: "round",
660
- strokeJoin: "round"
661
- },
662
- {
663
- id: "success-title",
664
- type: "text",
665
- x: 200,
666
- y: 105,
667
- text: "Project created!",
668
- align: "center",
669
- valign: "top",
670
- fontSize: 20,
671
- fontFamily: font,
672
- weight: 600,
673
- fill: colors.panelTitle
674
- },
675
- {
676
- id: "success-desc",
677
- type: "text",
678
- x: 200,
679
- y: 135,
680
- text: "Your new project is ready. Start designing\nyour first screen now.",
681
- align: "center",
682
- valign: "top",
683
- fontSize: 14,
684
- fontFamily: font,
685
- weight: 400,
686
- lineHeight: 1.5,
687
- fill: colors.panelBody,
688
- maxWidth: 340
689
- }
690
- ],
691
- timeline: {
692
- tracks: {
693
- opacity: {
694
- keyframes: [
695
- { time: 19.5, value: 0, out: curves.ease },
696
- { time: 20.2, value: 1 },
697
- { time: 23, value: 1, out: curves.ease },
698
- { time: 23.6, value: 0 }
699
- ]
700
- },
701
- scale: {
702
- keyframes: [
703
- { time: 19.5, value: 0.9, out: curves.ease },
704
- { time: 20.2, value: 1 }
705
- ]
706
- },
707
- y: {
708
- keyframes: [
709
- { time: 19.5, value: 260, out: curves.ease },
710
- { time: 20.2, value: 240 }
711
- ]
712
- }
713
- }
714
- },
715
- origin: [200, 100]
716
- });
717
-
718
- // --- Helpers ---
719
-
720
- function projectRow(i, name, meta, yPos) {
721
- return [
722
- {
723
- id: `proj-${i}-name`,
724
- type: "text",
725
- x: 28,
726
- y: yPos,
727
- text: name,
728
- align: "left",
729
- valign: "top",
730
- fontSize: 15,
731
- fontFamily: font,
732
- weight: 500,
733
- fill: colors.panelTitle
734
- },
735
- {
736
- id: `proj-${i}-meta`,
737
- type: "text",
738
- x: 28,
739
- y: yPos + 22,
740
- text: meta,
741
- align: "left",
742
- valign: "top",
743
- fontSize: 12,
744
- fontFamily: font,
745
- weight: 400,
746
- fill: colors.panelMuted
747
- }
748
- ];
749
- }
750
-
751
- function templateOption(i, label, x, y, selected) {
752
- const w = 100;
753
- const h = 60;
754
- return [
755
- {
756
- id: `tpl-${i}-bg`,
757
- type: "path",
758
- d: roundedRect(x, y, w, h, 6),
759
- fill: selected ? colors.accent : colors.inputBg,
760
- stroke: selected ? colors.accent : colors.inputBorder,
761
- strokeWidth: selected ? 2 : 1,
762
- opacity: selected ? 0.1 : 1
763
- },
764
- {
765
- id: `tpl-${i}-border`,
766
- type: "path",
767
- d: roundedRect(x, y, w, h, 6),
768
- fill: "none",
769
- stroke: selected ? colors.accent : colors.inputBorder,
770
- strokeWidth: selected ? 2 : 1
771
- },
772
- {
773
- id: `tpl-${i}-label`,
774
- type: "text",
775
- x: x + w / 2,
776
- y: y + h / 2,
777
- text: label,
778
- align: "center",
779
- valign: "middle",
780
- fontSize: 13,
781
- fontFamily: font,
782
- weight: selected ? 600 : 400,
783
- fill: selected ? colors.accent : colors.panelBody
784
- }
785
- ];
786
- }
787
-
788
- function roundedRect(x, y, w, h, r) {
789
- return [
790
- `M ${x + r} ${y}`,
791
- `L ${x + w - r} ${y}`,
792
- `Q ${x + w} ${y} ${x + w} ${y + r}`,
793
- `L ${x + w} ${y + h - r}`,
794
- `Q ${x + w} ${y + h} ${x + w - r} ${y + h}`,
795
- `L ${x + r} ${y + h}`,
796
- `Q ${x} ${y + h} ${x} ${y + h - r}`,
797
- `L ${x} ${y + r}`,
798
- `Q ${x} ${y} ${x + r} ${y}`,
799
- "Z"
800
- ].join(" ");
801
- }
802
-
803
- // Add cursor elements last so they render on top of everything
804
- elements.push(clickRingElement);
805
- elements.push(cursorElement);
806
-
807
- const doc = {
808
- version: 1,
809
- canvas: { width, height, background: bg, duration, fps },
810
- elements
811
- };
812
-
813
- const outPath = path.join(__dirname, "walkthrough.visual.json");
814
- fs.writeFileSync(outPath, JSON.stringify(doc, null, 2));
815
- console.log("Written:", outPath);