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,762 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- const width = 1280;
5
- const height = 720;
6
- const duration = 18;
7
- const fps = 30;
8
- const bg = "#f8fafc";
9
- const font = "Inter, system-ui, sans-serif";
10
-
11
- const colors = {
12
- text: "#0f172a",
13
- textMuted: "#64748b",
14
- panelBg: "#ffffff",
15
- panelBorder: "#e2e8f0",
16
- inputBg: "#f8fafc",
17
- inputBorder: "#cbd5e1",
18
- btnPrimary: "#2563eb",
19
- btnPrimaryText: "#ffffff",
20
- btnSecondary: "#ffffff",
21
- btnSecondaryText: "#475569",
22
- btnSecondaryBorder: "#e2e8f0",
23
- accent: "#2563eb",
24
- success: "#10b981",
25
- successBg: "#ecfdf5",
26
- cursor: "#0f172a",
27
- cursorRing: "#3b82f6",
28
- backdrop: "#0f172a"
29
- };
30
-
31
- const curves = {
32
- ease: { type: "cubicBezier", x1: 0.4, y1: 0, x2: 0.2, y2: 1 },
33
- easeOut: { type: "cubicBezier", x1: 0, y1: 0, x2: 0.2, y2: 1 },
34
- gentle: { type: "cubicBezier", x1: 0.25, y1: 0.1, x2: 0.25, y2: 1 }
35
- };
36
-
37
- const elements = [];
38
-
39
- // Layout constants
40
- const cardW = 520;
41
- const cardH = 400;
42
- const cardX = (width - cardW) / 2;
43
- const cardY = (height - cardH) / 2;
44
-
45
- // Button positions (for cursor targeting)
46
- const uploadBtnX = cardX + cardW / 2;
47
- const uploadBtnY = cardY + 260;
48
- const fileBtnX = cardX + cardW / 2;
49
- const fileBtnY = cardY + 200;
50
- const confirmBtnX = cardX + cardW - 90;
51
- const confirmBtnY = cardY + cardH - 52;
52
-
53
- // === Card background (persistent) ===
54
- elements.push({
55
- id: "main-card-bg",
56
- type: "path",
57
- d: roundedRect(cardX, cardY, cardW, cardH, 16),
58
- fill: colors.panelBg,
59
- stroke: colors.panelBorder,
60
- strokeWidth: 1,
61
- opacity: 0,
62
- timeline: {
63
- tracks: {
64
- opacity: {
65
- keyframes: [
66
- { time: 0, value: 0, out: curves.ease },
67
- { time: 0.6, value: 1 },
68
- { time: 17, value: 1, out: curves.ease },
69
- { time: 17.5, value: 0 }
70
- ]
71
- }
72
- }
73
- }
74
- });
75
-
76
- // Card title (persistent)
77
- elements.push({
78
- id: "card-title",
79
- type: "text",
80
- x: cardX + 32,
81
- y: cardY + 32,
82
- text: "Upload Document",
83
- align: "left",
84
- valign: "top",
85
- fontSize: 20,
86
- fontFamily: font,
87
- weight: 600,
88
- fill: colors.text,
89
- opacity: 0,
90
- timeline: {
91
- tracks: {
92
- opacity: {
93
- keyframes: [
94
- { time: 0, value: 0, out: curves.ease },
95
- { time: 0.6, value: 1 },
96
- { time: 17, value: 1, out: curves.ease },
97
- { time: 17.5, value: 0 }
98
- ]
99
- }
100
- }
101
- }
102
- });
103
-
104
- // Card description (persistent until success)
105
- elements.push({
106
- id: "card-desc",
107
- type: "text",
108
- x: cardX + 32,
109
- y: cardY + 62,
110
- text: "Add a file to get started with your project.",
111
- align: "left",
112
- valign: "top",
113
- fontSize: 14,
114
- fontFamily: font,
115
- weight: 400,
116
- fill: colors.textMuted,
117
- opacity: 0,
118
- timeline: {
119
- tracks: {
120
- opacity: {
121
- keyframes: [
122
- { time: 0, value: 0, out: curves.ease },
123
- { time: 0.6, value: 1 },
124
- { time: 13.5, value: 1, out: curves.ease },
125
- { time: 14, value: 0 }
126
- ]
127
- }
128
- }
129
- }
130
- });
131
-
132
- // === SCENE 1: Empty state (0-6s) - upload zone ===
133
- elements.push({
134
- id: "upload-zone",
135
- type: "group",
136
- x: cardX + 32,
137
- y: cardY + 100,
138
- children: [
139
- {
140
- id: "upload-zone-bg",
141
- type: "path",
142
- d: roundedRect(0, 0, cardW - 64, 140, 12),
143
- fill: colors.inputBg,
144
- stroke: colors.inputBorder,
145
- strokeWidth: 2,
146
- dashArray: [8, 4]
147
- },
148
- {
149
- id: "upload-icon",
150
- type: "path",
151
- d: "M 0 12 L 0 4 A 4 4 0 0 1 4 0 L 20 0 A 4 4 0 0 1 24 4 L 24 12 M 12 20 L 12 6 M 6 12 L 12 6 L 18 12",
152
- x: (cardW - 64) / 2 - 12,
153
- y: 40,
154
- fill: "none",
155
- stroke: colors.textMuted,
156
- strokeWidth: 2,
157
- strokeCap: "round",
158
- strokeJoin: "round"
159
- },
160
- {
161
- id: "upload-text",
162
- type: "text",
163
- x: (cardW - 64) / 2,
164
- y: 90,
165
- text: "Drop files here or click to browse",
166
- align: "center",
167
- valign: "top",
168
- fontSize: 13,
169
- fontFamily: font,
170
- weight: 400,
171
- fill: colors.textMuted
172
- }
173
- ],
174
- opacity: 0,
175
- timeline: {
176
- tracks: {
177
- opacity: {
178
- keyframes: [
179
- { time: 0.3, value: 0, out: curves.ease },
180
- { time: 0.8, value: 1 },
181
- // Fade out when file is selected
182
- { time: 5.8, value: 1, out: curves.ease },
183
- { time: 6.3, value: 0 }
184
- ]
185
- }
186
- }
187
- }
188
- });
189
-
190
- // Upload button (in empty state)
191
- elements.push({
192
- id: "upload-btn",
193
- type: "group",
194
- x: cardX + cardW / 2 - 70,
195
- y: cardY + 280,
196
- children: [
197
- {
198
- id: "upload-btn-bg",
199
- type: "path",
200
- d: roundedRect(0, 0, 140, 40, 8),
201
- fill: colors.btnPrimary,
202
- stroke: "none"
203
- },
204
- {
205
- id: "upload-btn-text",
206
- type: "text",
207
- x: 70,
208
- y: 20,
209
- text: "Choose File",
210
- align: "center",
211
- valign: "middle",
212
- fontSize: 14,
213
- fontFamily: font,
214
- weight: 600,
215
- fill: colors.btnPrimaryText
216
- }
217
- ],
218
- opacity: 0,
219
- origin: [70, 20],
220
- timeline: {
221
- tracks: {
222
- opacity: {
223
- keyframes: [
224
- { time: 0.5, value: 0, out: curves.ease },
225
- { time: 1, value: 1 },
226
- // Fade out with upload zone
227
- { time: 5.8, value: 1, out: curves.ease },
228
- { time: 6.3, value: 0 }
229
- ]
230
- },
231
- scale: {
232
- keyframes: [
233
- { time: 4.8, value: 1, out: curves.ease },
234
- { time: 4.95, value: 0.95 },
235
- { time: 5.1, value: 1 }
236
- ]
237
- }
238
- }
239
- }
240
- });
241
-
242
- // === SCENE 2: File selected state (6-14s) ===
243
- elements.push({
244
- id: "file-item",
245
- type: "group",
246
- x: cardX + 32,
247
- y: cardY + 100,
248
- children: [
249
- {
250
- id: "file-bg",
251
- type: "path",
252
- d: roundedRect(0, 0, cardW - 64, 64, 10),
253
- fill: colors.panelBg,
254
- stroke: colors.accent,
255
- strokeWidth: 2
256
- },
257
- // File icon
258
- {
259
- id: "file-icon-bg",
260
- type: "path",
261
- d: roundedRect(16, 14, 36, 36, 6),
262
- fill: colors.successBg,
263
- stroke: "none"
264
- },
265
- {
266
- id: "file-icon",
267
- type: "path",
268
- d: "M 28 24 L 28 40 M 28 32 L 22 32 L 28 26 L 34 32 L 28 32",
269
- fill: "none",
270
- stroke: colors.success,
271
- strokeWidth: 2,
272
- strokeCap: "round",
273
- strokeJoin: "round"
274
- },
275
- {
276
- id: "file-name",
277
- type: "text",
278
- x: 68,
279
- y: 22,
280
- text: "quarterly-report.pdf",
281
- align: "left",
282
- valign: "top",
283
- fontSize: 14,
284
- fontFamily: font,
285
- weight: 500,
286
- fill: colors.text
287
- },
288
- {
289
- id: "file-size",
290
- type: "text",
291
- x: 68,
292
- y: 42,
293
- text: "2.4 MB · PDF Document",
294
- align: "left",
295
- valign: "top",
296
- fontSize: 12,
297
- fontFamily: font,
298
- weight: 400,
299
- fill: colors.textMuted
300
- },
301
- // Checkmark
302
- {
303
- id: "file-check",
304
- type: "path",
305
- d: "M 0 0 m -10 0 a 10 10 0 1 1 20 0 a 10 10 0 1 1 -20 0",
306
- x: cardW - 64 - 32,
307
- y: 32,
308
- fill: colors.success,
309
- stroke: "none"
310
- },
311
- {
312
- id: "file-check-mark",
313
- type: "path",
314
- d: "M -4 0 L -1 3 L 4 -3",
315
- x: cardW - 64 - 32,
316
- y: 32,
317
- fill: "none",
318
- stroke: "#ffffff",
319
- strokeWidth: 2,
320
- strokeCap: "round",
321
- strokeJoin: "round"
322
- }
323
- ],
324
- opacity: 0,
325
- timeline: {
326
- tracks: {
327
- opacity: {
328
- keyframes: [
329
- // Fade in after upload zone fades out
330
- { time: 6.3, value: 0, out: curves.ease },
331
- { time: 6.8, value: 1 },
332
- // Stay visible until success state
333
- { time: 13.5, value: 1, out: curves.ease },
334
- { time: 14, value: 0 }
335
- ]
336
- },
337
- y: {
338
- keyframes: [
339
- { time: 6.3, value: cardY + 110, out: curves.ease },
340
- { time: 6.8, value: cardY + 100 }
341
- ]
342
- }
343
- }
344
- }
345
- });
346
-
347
- // Status text - ready
348
- elements.push({
349
- id: "status-text",
350
- type: "text",
351
- x: cardX + cardW / 2,
352
- y: cardY + 190,
353
- text: "File ready to upload",
354
- align: "center",
355
- valign: "top",
356
- fontSize: 13,
357
- fontFamily: font,
358
- weight: 500,
359
- fill: colors.success,
360
- opacity: 0,
361
- timeline: {
362
- tracks: {
363
- opacity: {
364
- keyframes: [
365
- { time: 7, value: 0, out: curves.ease },
366
- { time: 7.4, value: 1 },
367
- // Fade out when uploading starts
368
- { time: 10.5, value: 1, out: curves.ease },
369
- { time: 11, value: 0 }
370
- ]
371
- }
372
- }
373
- }
374
- });
375
-
376
- // Confirm button
377
- const confirmBtnW = 140;
378
- const confirmBtnH = 42;
379
- elements.push({
380
- id: "confirm-btn",
381
- type: "group",
382
- x: cardX + cardW - 32 - confirmBtnW,
383
- y: cardY + cardH - 32 - confirmBtnH,
384
- children: [
385
- {
386
- id: "confirm-btn-bg",
387
- type: "path",
388
- d: roundedRect(0, 0, confirmBtnW, confirmBtnH, 8),
389
- fill: colors.btnPrimary,
390
- stroke: "none"
391
- },
392
- {
393
- id: "confirm-btn-text",
394
- type: "text",
395
- x: confirmBtnW / 2,
396
- y: confirmBtnH / 2,
397
- text: "Upload Now",
398
- align: "center",
399
- valign: "middle",
400
- fontSize: 14,
401
- fontFamily: font,
402
- weight: 600,
403
- fill: colors.btnPrimaryText
404
- }
405
- ],
406
- opacity: 0,
407
- origin: [confirmBtnW / 2, confirmBtnH / 2],
408
- timeline: {
409
- tracks: {
410
- opacity: {
411
- keyframes: [
412
- { time: 7.2, value: 0, out: curves.ease },
413
- { time: 7.6, value: 1 },
414
- // Fade out when upload starts
415
- { time: 10.5, value: 1, out: curves.ease },
416
- { time: 11, value: 0 }
417
- ]
418
- },
419
- scale: {
420
- keyframes: [
421
- { time: 9.8, value: 1, out: curves.ease },
422
- { time: 9.95, value: 0.95 },
423
- { time: 10.1, value: 1 }
424
- ]
425
- }
426
- }
427
- }
428
- });
429
-
430
- // Cancel button
431
- elements.push({
432
- id: "cancel-btn",
433
- type: "group",
434
- x: cardX + 32,
435
- y: cardY + cardH - 32 - confirmBtnH,
436
- children: [
437
- {
438
- id: "cancel-btn-bg",
439
- type: "path",
440
- d: roundedRect(0, 0, 100, confirmBtnH, 8),
441
- fill: colors.btnSecondary,
442
- stroke: colors.btnSecondaryBorder,
443
- strokeWidth: 1
444
- },
445
- {
446
- id: "cancel-btn-text",
447
- type: "text",
448
- x: 50,
449
- y: confirmBtnH / 2,
450
- text: "Cancel",
451
- align: "center",
452
- valign: "middle",
453
- fontSize: 14,
454
- fontFamily: font,
455
- weight: 500,
456
- fill: colors.btnSecondaryText
457
- }
458
- ],
459
- opacity: 0,
460
- timeline: {
461
- tracks: {
462
- opacity: {
463
- keyframes: [
464
- { time: 7.2, value: 0, out: curves.ease },
465
- { time: 7.6, value: 1 },
466
- // Fade out when upload starts
467
- { time: 10.5, value: 1, out: curves.ease },
468
- { time: 11, value: 0 }
469
- ]
470
- }
471
- }
472
- }
473
- });
474
-
475
- // === SCENE 3: Uploading state (11-14s) ===
476
- elements.push({
477
- id: "uploading-text",
478
- type: "text",
479
- x: cardX + cardW / 2,
480
- y: cardY + 190,
481
- text: "Uploading...",
482
- align: "center",
483
- valign: "top",
484
- fontSize: 13,
485
- fontFamily: font,
486
- weight: 500,
487
- fill: colors.accent,
488
- opacity: 0,
489
- timeline: {
490
- tracks: {
491
- opacity: {
492
- keyframes: [
493
- { time: 11, value: 0, out: curves.ease },
494
- { time: 11.4, value: 1 },
495
- { time: 13.5, value: 1, out: curves.ease },
496
- { time: 14, value: 0 }
497
- ]
498
- }
499
- }
500
- }
501
- });
502
-
503
- // Progress bar track
504
- elements.push({
505
- id: "progress-track",
506
- type: "path",
507
- d: roundedRect(cardX + 80, cardY + 220, cardW - 160, 8, 4),
508
- fill: colors.inputBg,
509
- stroke: colors.inputBorder,
510
- strokeWidth: 1,
511
- opacity: 0,
512
- timeline: {
513
- tracks: {
514
- opacity: {
515
- keyframes: [
516
- { time: 11, value: 0, out: curves.ease },
517
- { time: 11.4, value: 1 },
518
- { time: 13.5, value: 1, out: curves.ease },
519
- { time: 14, value: 0 }
520
- ]
521
- }
522
- }
523
- }
524
- });
525
-
526
- // Progress bar fill (using drawEnd for animation)
527
- elements.push({
528
- id: "progress-fill",
529
- type: "path",
530
- d: `M ${cardX + 82} ${cardY + 222} L ${cardX + cardW - 82} ${cardY + 222} L ${cardX + cardW - 82} ${cardY + 226} L ${cardX + 82} ${cardY + 226} Z`,
531
- fill: colors.accent,
532
- stroke: "none",
533
- opacity: 0,
534
- drawEnd: 0,
535
- timeline: {
536
- tracks: {
537
- opacity: {
538
- keyframes: [
539
- { time: 11.2, value: 0, out: curves.ease },
540
- { time: 11.5, value: 1 },
541
- { time: 13.5, value: 1, out: curves.ease },
542
- { time: 14, value: 0 }
543
- ]
544
- },
545
- drawEnd: {
546
- keyframes: [
547
- { time: 11.5, value: 0, out: curves.gentle },
548
- { time: 13.5, value: 1 }
549
- ]
550
- }
551
- }
552
- }
553
- });
554
-
555
- // === SCENE 4: Success state (14-18s) ===
556
- elements.push({
557
- id: "success-overlay",
558
- type: "group",
559
- x: cardX + cardW / 2,
560
- y: cardY + cardH / 2 - 20,
561
- children: [
562
- // Large checkmark circle
563
- {
564
- id: "success-circle",
565
- type: "path",
566
- d: "M 0 0 m -40 0 a 40 40 0 1 1 80 0 a 40 40 0 1 1 -80 0",
567
- fill: colors.successBg,
568
- stroke: colors.success,
569
- strokeWidth: 2
570
- },
571
- {
572
- id: "success-check",
573
- type: "path",
574
- d: "M -14 0 L -4 10 L 14 -10",
575
- fill: "none",
576
- stroke: colors.success,
577
- strokeWidth: 3,
578
- strokeCap: "round",
579
- strokeJoin: "round"
580
- },
581
- {
582
- id: "success-title",
583
- type: "text",
584
- x: 0,
585
- y: 70,
586
- text: "Upload Complete",
587
- align: "center",
588
- valign: "top",
589
- fontSize: 18,
590
- fontFamily: font,
591
- weight: 600,
592
- fill: colors.text
593
- },
594
- {
595
- id: "success-desc",
596
- type: "text",
597
- x: 0,
598
- y: 98,
599
- text: "Your document has been uploaded successfully.",
600
- align: "center",
601
- valign: "top",
602
- fontSize: 13,
603
- fontFamily: font,
604
- weight: 400,
605
- fill: colors.textMuted
606
- }
607
- ],
608
- opacity: 0,
609
- origin: [0, 0],
610
- timeline: {
611
- tracks: {
612
- opacity: {
613
- keyframes: [
614
- { time: 14, value: 0, out: curves.ease },
615
- { time: 14.6, value: 1 },
616
- { time: 17, value: 1, out: curves.ease },
617
- { time: 17.5, value: 0 }
618
- ]
619
- },
620
- scale: {
621
- keyframes: [
622
- { time: 14, value: 0.8, out: curves.ease },
623
- { time: 14.6, value: 1 }
624
- ]
625
- }
626
- }
627
- }
628
- });
629
-
630
- // === CURSOR (added last to render on top) ===
631
-
632
- // Calculated positions:
633
- // Upload button: x = cardX + cardW/2 = 640, y = cardY + 280 + 20 = 460
634
- // Confirm button: x = cardX + cardW - 32 - confirmBtnW/2 = 798, y = cardY + cardH - 32 - confirmBtnH/2 = 507
635
- const uploadBtnCenter = { x: cardX + cardW / 2, y: cardY + 280 + 20 };
636
- const confirmBtnCenter = { x: cardX + cardW - 32 - confirmBtnW / 2, y: cardY + cardH - 32 - confirmBtnH / 2 };
637
-
638
- // Click ring
639
- elements.push({
640
- id: "click-ring",
641
- type: "path",
642
- d: "M 0 0 m -18 0 a 18 18 0 1 1 36 0 a 18 18 0 1 1 -36 0",
643
- x: uploadBtnCenter.x,
644
- y: uploadBtnCenter.y,
645
- fill: "none",
646
- stroke: colors.cursorRing,
647
- strokeWidth: 2,
648
- opacity: 0,
649
- origin: [0, 0],
650
- timeline: {
651
- tracks: {
652
- opacity: {
653
- keyframes: [
654
- // Click 1: Upload button
655
- { time: 4.8, value: 0 },
656
- { time: 4.9, value: 0.5 },
657
- { time: 5.3, value: 0 },
658
- // Click 2: Confirm button
659
- { time: 9.8, value: 0 },
660
- { time: 9.9, value: 0.5 },
661
- { time: 10.3, value: 0 }
662
- ]
663
- },
664
- scale: {
665
- keyframes: [
666
- { time: 4.8, value: 0.5, out: curves.easeOut },
667
- { time: 5.3, value: 1.8 },
668
- { time: 9.8, value: 0.5, out: curves.easeOut },
669
- { time: 10.3, value: 1.8 }
670
- ]
671
- },
672
- x: {
673
- keyframes: [
674
- { time: 4.8, value: uploadBtnCenter.x },
675
- { time: 9.8, value: confirmBtnCenter.x }
676
- ]
677
- },
678
- y: {
679
- keyframes: [
680
- { time: 4.8, value: uploadBtnCenter.y },
681
- { time: 9.8, value: confirmBtnCenter.y }
682
- ]
683
- }
684
- }
685
- }
686
- });
687
-
688
- // Cursor
689
- elements.push({
690
- id: "cursor",
691
- type: "group",
692
- x: -50,
693
- y: height / 2,
694
- children: [
695
- {
696
- id: "cursor-arrow",
697
- type: "path",
698
- d: "M 0 0 L 0 18 L 4.5 14.5 L 7.5 22 L 11 20.5 L 8 13 L 13.5 13 Z",
699
- fill: colors.cursor,
700
- stroke: "#ffffff",
701
- strokeWidth: 1.5
702
- }
703
- ],
704
- timeline: {
705
- tracks: {
706
- x: {
707
- keyframes: [
708
- // Enter from left
709
- { time: 2, value: -50, out: curves.ease },
710
- { time: 3, value: uploadBtnCenter.x },
711
- // Stay on upload button, click at 4.8
712
- { time: 5.5, value: uploadBtnCenter.x },
713
- // Move to confirm button
714
- { time: 7.5, value: uploadBtnCenter.x, out: curves.ease },
715
- { time: 8.5, value: confirmBtnCenter.x },
716
- // Stay on confirm button, click at 9.8
717
- { time: 10.5, value: confirmBtnCenter.x },
718
- // Exit
719
- { time: 13, value: confirmBtnCenter.x, out: curves.ease },
720
- { time: 14, value: width + 50 }
721
- ]
722
- },
723
- y: {
724
- keyframes: [
725
- { time: 2, value: height / 2, out: curves.ease },
726
- { time: 3, value: uploadBtnCenter.y },
727
- { time: 5.5, value: uploadBtnCenter.y },
728
- { time: 7.5, value: uploadBtnCenter.y, out: curves.ease },
729
- { time: 8.5, value: confirmBtnCenter.y },
730
- { time: 10.5, value: confirmBtnCenter.y },
731
- { time: 13, value: confirmBtnCenter.y, out: curves.ease },
732
- { time: 14, value: height + 50 }
733
- ]
734
- }
735
- }
736
- }
737
- });
738
-
739
- function roundedRect(x, y, w, h, r) {
740
- return [
741
- `M ${x + r} ${y}`,
742
- `L ${x + w - r} ${y}`,
743
- `Q ${x + w} ${y} ${x + w} ${y + r}`,
744
- `L ${x + w} ${y + h - r}`,
745
- `Q ${x + w} ${y + h} ${x + w - r} ${y + h}`,
746
- `L ${x + r} ${y + h}`,
747
- `Q ${x} ${y + h} ${x} ${y + h - r}`,
748
- `L ${x} ${y + r}`,
749
- `Q ${x} ${y} ${x + r} ${y}`,
750
- "Z"
751
- ].join(" ");
752
- }
753
-
754
- const doc = {
755
- version: 1,
756
- canvas: { width, height, background: bg, duration, fps },
757
- elements
758
- };
759
-
760
- const outPath = path.join(__dirname, "ui-flow.visual.json");
761
- fs.writeFileSync(outPath, JSON.stringify(doc, null, 2));
762
- console.log("Written:", outPath);