sketchmark 2.0.0 → 2.1.0

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 (132) hide show
  1. package/ANIMATABLE_MATRIX.md +177 -0
  2. package/KERNEL_SPEC.md +412 -0
  3. package/PACKS.md +81 -0
  4. package/PRESETS.md +182 -0
  5. package/README.md +274 -188
  6. package/bin/editor-ui.cjs +2285 -0
  7. package/bin/preview-ui.cjs +74 -0
  8. package/bin/sketchmark.cjs +648 -2008
  9. package/dist/src/animatable.d.ts +21 -0
  10. package/dist/src/animatable.js +439 -0
  11. package/dist/src/builders/index.d.ts +1 -11
  12. package/dist/src/builders/index.js +1 -19
  13. package/dist/src/diagnostics.js +1 -64
  14. package/dist/src/edit.d.ts +27 -0
  15. package/dist/src/edit.js +162 -0
  16. package/dist/src/index.d.ts +4 -13
  17. package/dist/src/index.js +4 -13
  18. package/dist/src/keyframes.d.ts +48 -0
  19. package/dist/src/keyframes.js +182 -0
  20. package/dist/src/motion.d.ts +4 -0
  21. package/dist/src/motion.js +262 -0
  22. package/dist/src/normalize.js +120 -151
  23. package/dist/src/presets/characters.d.ts +15 -0
  24. package/dist/src/presets/characters.js +113 -0
  25. package/dist/src/presets/compose.d.ts +5 -0
  26. package/dist/src/presets/compose.js +80 -0
  27. package/dist/src/presets/effects.d.ts +40 -0
  28. package/dist/src/presets/effects.js +79 -0
  29. package/dist/src/presets/helpers.d.ts +33 -0
  30. package/dist/src/presets/helpers.js +165 -0
  31. package/dist/src/presets/index.d.ts +9 -0
  32. package/dist/src/presets/index.js +48 -0
  33. package/dist/src/presets/motions.d.ts +33 -0
  34. package/dist/src/presets/motions.js +75 -0
  35. package/dist/src/presets/scenes.d.ts +35 -0
  36. package/dist/src/presets/scenes.js +134 -0
  37. package/dist/src/presets/shapes.d.ts +71 -0
  38. package/dist/src/presets/shapes.js +96 -0
  39. package/dist/src/presets/transitions.d.ts +29 -0
  40. package/dist/src/presets/transitions.js +113 -0
  41. package/dist/src/presets/types.d.ts +34 -0
  42. package/dist/src/presets/types.js +2 -0
  43. package/dist/src/render/html.js +1 -4
  44. package/dist/src/render/svg.d.ts +2 -2
  45. package/dist/src/render/svg.js +86 -82
  46. package/dist/src/render/three-html.js +67 -113
  47. package/dist/src/scenes.js +1 -0
  48. package/dist/src/schema.js +218 -280
  49. package/dist/src/shapes/builtins.js +11 -47
  50. package/dist/src/shapes/common.js +12 -11
  51. package/dist/src/shapes/registry.d.ts +0 -1
  52. package/dist/src/shapes/registry.js +0 -4
  53. package/dist/src/shapes/types.d.ts +1 -3
  54. package/dist/src/types.d.ts +57 -288
  55. package/dist/src/utils.d.ts +2 -11
  56. package/dist/src/utils.js +13 -70
  57. package/dist/src/validate.js +321 -275
  58. package/dist/tests/run.js +576 -510
  59. package/examples/1730642890464.jpg +0 -0
  60. package/examples/app-screen.svg +1 -0
  61. package/examples/app-screen.visual.json +503 -0
  62. package/examples/dashboard-table.svg +1 -0
  63. package/examples/dashboard-table.visual.json +708 -0
  64. package/examples/dev-docs.svg +1 -0
  65. package/examples/dev-docs.visual.json +248 -0
  66. package/examples/explainer.mp4 +0 -0
  67. package/examples/explainer.visual.json +1713 -0
  68. package/examples/group-origin-effects-lab-check.svg +1 -0
  69. package/examples/group-origin-effects-lab.visual.json +1880 -0
  70. package/examples/image-clip-radius.visual.json +271 -0
  71. package/examples/make-app-screen.cjs +368 -0
  72. package/examples/make-dashboard-table.cjs +277 -0
  73. package/examples/make-dev-docs.cjs +233 -0
  74. package/examples/make-explainer.cjs +438 -0
  75. package/examples/make-group-origin-effects-lab.cjs +370 -0
  76. package/examples/make-image-clip-radius.cjs +169 -0
  77. package/examples/make-modal-dialog.cjs +355 -0
  78. package/examples/make-origin-effects-lab.cjs +311 -0
  79. package/examples/make-preset-character-motion.cjs +32 -0
  80. package/examples/make-presets-demo.cjs +30 -0
  81. package/examples/make-pricing.cjs +286 -0
  82. package/examples/make-product-demo.cjs +468 -0
  83. package/examples/make-product-hero.cjs +223 -0
  84. package/examples/make-release-notes.cjs +333 -0
  85. package/examples/make-settings-panel.cjs +435 -0
  86. package/examples/make-split-preview.cjs +248 -0
  87. package/examples/make-storyboard.cjs +215 -0
  88. package/examples/make-transcript.cjs +234 -0
  89. package/examples/make-typography-test.cjs +397 -0
  90. package/examples/make-ui-demo-explainer.cjs +1094 -0
  91. package/examples/make-ui-flow.cjs +762 -0
  92. package/examples/make-walkthrough.cjs +815 -0
  93. package/examples/modal-dialog.svg +1 -0
  94. package/examples/modal-dialog.visual.json +239 -0
  95. package/examples/origin-effects-lab-check.svg +1 -0
  96. package/examples/origin-effects-lab.visual.json +1412 -0
  97. package/examples/preset-character-motion.visual.json +949 -0
  98. package/examples/presets-demo.visual.json +787 -0
  99. package/examples/pricing.svg +1 -0
  100. package/examples/pricing.visual.json +652 -0
  101. package/examples/product-demo.mp4 +0 -0
  102. package/examples/product-demo.visual.json +866 -0
  103. package/examples/product-hero.svg +1 -0
  104. package/examples/product-hero.visual.json +242 -0
  105. package/examples/release-notes.svg +1 -0
  106. package/examples/release-notes.visual.json +467 -0
  107. package/examples/settings-panel.svg +1 -0
  108. package/examples/settings-panel.visual.json +501 -0
  109. package/examples/split-preview.svg +1 -0
  110. package/examples/split-preview.visual.json +124 -0
  111. package/examples/storyboard.svg +1 -0
  112. package/examples/storyboard.visual.json +312 -0
  113. package/examples/transcript.svg +1 -0
  114. package/examples/transcript.visual.json +407 -0
  115. package/examples/typography-indent-check.svg +1 -0
  116. package/examples/typography-lineheight-0.svg +1 -0
  117. package/examples/typography-lineheight-2.svg +1 -0
  118. package/examples/typography-test-check.svg +1 -0
  119. package/examples/typography-test.svg +1 -0
  120. package/examples/typography-test.visual.json +757 -0
  121. package/examples/ui-demo-explainer-billing.svg +1 -0
  122. package/examples/ui-demo-explainer-check.svg +1 -0
  123. package/examples/ui-demo-explainer-save.svg +1 -0
  124. package/examples/ui-demo-explainer-toggle.svg +1 -0
  125. package/examples/ui-demo-explainer.mp4 +0 -0
  126. package/examples/ui-demo-explainer.visual.json +2597 -0
  127. package/examples/ui-flow.mp4 +0 -0
  128. package/examples/ui-flow.visual.json +1211 -0
  129. package/examples/walkthrough.mp4 +0 -0
  130. package/examples/walkthrough.visual.json +1372 -0
  131. package/package.json +52 -52
  132. package/schema/visual.schema.json +1086 -930
@@ -0,0 +1,438 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const width = 1280;
5
+ const height = 720;
6
+ const duration = 20;
7
+ const fps = 30;
8
+ const bg = "#fafafa";
9
+ const font = "Inter, system-ui, sans-serif";
10
+ const monoFont = "JetBrains Mono, Fira Code, monospace";
11
+
12
+ const colors = {
13
+ headline: "#0f172a",
14
+ body: "#475569",
15
+ muted: "#94a3b8",
16
+ accent: "#2563eb",
17
+ panelBg: "#ffffff",
18
+ panelBorder: "#e2e8f0",
19
+ codeBg: "#1e293b",
20
+ codeText: "#e2e8f0",
21
+ chipBg: "#dbeafe",
22
+ chipText: "#1d4ed8",
23
+ chipGreenBg: "#dcfce7",
24
+ chipGreenText: "#166534"
25
+ };
26
+
27
+ const curves = {
28
+ ease: { type: "cubicBezier", x1: 0.25, y1: 0.1, x2: 0.25, y2: 1 },
29
+ easeOut: { type: "cubicBezier", x1: 0, y1: 0, x2: 0.2, y2: 1 }
30
+ };
31
+
32
+ const elements = [];
33
+
34
+ // Helper: create a chip group (bg + text together)
35
+ function chipGroup(id, x, y, label, bgColor, textColor) {
36
+ const padX = 14;
37
+ const h = 28;
38
+ const w = label.length * 8 + padX * 2;
39
+ const r = h / 2;
40
+ return {
41
+ id,
42
+ type: "group",
43
+ x, y,
44
+ children: [
45
+ {
46
+ id: `${id}-bg`,
47
+ type: "path",
48
+ d: roundedRect(0, 0, w, h, r),
49
+ fill: bgColor,
50
+ stroke: "none"
51
+ },
52
+ {
53
+ id: `${id}-text`,
54
+ type: "text",
55
+ x: w / 2,
56
+ y: h / 2,
57
+ text: label,
58
+ align: "center",
59
+ valign: "middle",
60
+ fontSize: 12,
61
+ fontFamily: font,
62
+ weight: 600,
63
+ fill: textColor
64
+ }
65
+ ]
66
+ };
67
+ }
68
+
69
+ // Helper: fade in/out timeline
70
+ function fadeTimeline(inStart, inEnd, outStart, outEnd, yOffset = 0, startY = 0) {
71
+ const tracks = {
72
+ opacity: {
73
+ keyframes: [
74
+ { time: inStart, value: 0, out: curves.ease },
75
+ { time: inEnd, value: 1 },
76
+ { time: outStart, value: 1, out: curves.ease },
77
+ { time: outEnd, value: 0 }
78
+ ]
79
+ }
80
+ };
81
+ if (yOffset !== 0) {
82
+ tracks.y = {
83
+ keyframes: [
84
+ { time: inStart, value: startY + yOffset, out: curves.ease },
85
+ { time: inEnd, value: startY }
86
+ ]
87
+ };
88
+ }
89
+ return { tracks };
90
+ }
91
+
92
+ // === SCENE 1: What is Sketchmark? (0-5s) ===
93
+
94
+ elements.push({
95
+ id: "s1-headline",
96
+ type: "text",
97
+ x: width / 2,
98
+ y: 200,
99
+ text: "What is Sketchmark?",
100
+ align: "center",
101
+ valign: "middle",
102
+ fontSize: 44,
103
+ fontFamily: font,
104
+ weight: 700,
105
+ fill: colors.headline,
106
+ opacity: 0,
107
+ timeline: fadeTimeline(0, 0.6, 4, 4.6, 20, 200)
108
+ });
109
+
110
+ elements.push({
111
+ id: "s1-body",
112
+ type: "text",
113
+ x: width / 2,
114
+ y: 280,
115
+ text: "A minimal render kernel that turns JSON documents\ninto SVG, HTML, or video frames.",
116
+ align: "center",
117
+ valign: "top",
118
+ fontSize: 20,
119
+ fontFamily: font,
120
+ weight: 400,
121
+ lineHeight: 1.6,
122
+ fill: colors.body,
123
+ maxWidth: 700,
124
+ opacity: 0,
125
+ timeline: fadeTimeline(0.4, 1, 4, 4.6, 15, 280)
126
+ });
127
+
128
+ // Chip row for scene 1
129
+ const s1Chips = [
130
+ { label: "JSON In", bg: colors.chipBg, text: colors.chipText },
131
+ { label: "Visuals Out", bg: colors.chipGreenBg, text: colors.chipGreenText }
132
+ ];
133
+ const s1ChipY = 380;
134
+ const s1ChipGap = 16;
135
+ const s1ChipW = s1Chips.reduce((sum, c) => sum + c.label.length * 8 + 28, 0) + s1ChipGap;
136
+ let s1ChipX = (width - s1ChipW) / 2;
137
+
138
+ s1Chips.forEach((chip, i) => {
139
+ const cw = chip.label.length * 8 + 28;
140
+ const group = chipGroup(`s1-chip-${i}`, s1ChipX, s1ChipY, chip.label, chip.bg, chip.text);
141
+ group.opacity = 0;
142
+ group.timeline = fadeTimeline(0.8 + i * 0.15, 1.3 + i * 0.15, 4, 4.6, 10, s1ChipY);
143
+ elements.push(group);
144
+ s1ChipX += cw + s1ChipGap;
145
+ });
146
+
147
+ // === SCENE 2: Define your document (5-10s) ===
148
+
149
+ elements.push({
150
+ id: "s2-headline",
151
+ type: "text",
152
+ x: 180,
153
+ y: 140,
154
+ text: "Define your document",
155
+ align: "left",
156
+ valign: "top",
157
+ fontSize: 32,
158
+ fontFamily: font,
159
+ weight: 700,
160
+ fill: colors.headline,
161
+ opacity: 0,
162
+ timeline: fadeTimeline(5, 5.5, 9, 9.5, 15, 140)
163
+ });
164
+
165
+ elements.push({
166
+ id: "s2-body",
167
+ type: "text",
168
+ x: 180,
169
+ y: 190,
170
+ text: "Declare canvas size, background, and elements.\nEach element has an ID, type, and properties.",
171
+ align: "left",
172
+ valign: "top",
173
+ fontSize: 16,
174
+ fontFamily: font,
175
+ weight: 400,
176
+ lineHeight: 1.6,
177
+ fill: colors.body,
178
+ maxWidth: 400,
179
+ opacity: 0,
180
+ timeline: fadeTimeline(5.3, 5.8, 9, 9.5, 10, 190)
181
+ });
182
+
183
+ // Code panel for scene 2
184
+ const s2CodeX = 180;
185
+ const s2CodeY = 280;
186
+ const s2CodeW = 500;
187
+ const s2CodeH = 280;
188
+
189
+ elements.push({
190
+ id: "s2-panel",
191
+ type: "group",
192
+ x: s2CodeX,
193
+ y: s2CodeY,
194
+ children: [
195
+ {
196
+ id: "s2-panel-bg",
197
+ type: "path",
198
+ d: roundedRect(0, 0, s2CodeW, s2CodeH, 10),
199
+ fill: colors.codeBg,
200
+ stroke: "none"
201
+ },
202
+ {
203
+ id: "s2-panel-label",
204
+ type: "text",
205
+ x: 16,
206
+ y: 16,
207
+ text: "canvas.visual.json",
208
+ align: "left",
209
+ valign: "top",
210
+ fontSize: 11,
211
+ fontFamily: monoFont,
212
+ weight: 500,
213
+ fill: colors.muted
214
+ },
215
+ {
216
+ id: "s2-panel-code",
217
+ type: "text",
218
+ x: 16,
219
+ y: 44,
220
+ text: `{
221
+ "version": 1,
222
+ "canvas": {
223
+ "width": 800,
224
+ "height": 600,
225
+ "background": "#ffffff"
226
+ },
227
+ "elements": [
228
+ { "id": "title", "type": "text", ... },
229
+ { "id": "shape", "type": "path", ... }
230
+ ]
231
+ }`,
232
+ align: "left",
233
+ valign: "top",
234
+ fontSize: 13,
235
+ fontFamily: monoFont,
236
+ weight: 400,
237
+ lineHeight: 1.45,
238
+ fill: colors.codeText,
239
+ maxWidth: s2CodeW - 32
240
+ }
241
+ ],
242
+ opacity: 0,
243
+ timeline: fadeTimeline(5.5, 6.2, 9, 9.5, 20, s2CodeY)
244
+ });
245
+
246
+ // Status chip for scene 2
247
+ const s2Chip = chipGroup("s2-chip", 720, 320, "Schema validated", colors.chipGreenBg, colors.chipGreenText);
248
+ s2Chip.opacity = 0;
249
+ s2Chip.timeline = fadeTimeline(6.5, 7, 9, 9.5, 10, 320);
250
+ elements.push(s2Chip);
251
+
252
+ // === SCENE 3: Add motion (10-15s) ===
253
+
254
+ elements.push({
255
+ id: "s3-headline",
256
+ type: "text",
257
+ x: width / 2,
258
+ y: 120,
259
+ text: "Add motion with timelines",
260
+ align: "center",
261
+ valign: "middle",
262
+ fontSize: 32,
263
+ fontFamily: font,
264
+ weight: 700,
265
+ fill: colors.headline,
266
+ opacity: 0,
267
+ timeline: fadeTimeline(10, 10.5, 14, 14.5, 15, 120)
268
+ });
269
+
270
+ elements.push({
271
+ id: "s3-body",
272
+ type: "text",
273
+ x: width / 2,
274
+ y: 170,
275
+ text: "Each element can have its own timeline with keyframes.\nThe kernel interpolates smoothly between values.",
276
+ align: "center",
277
+ valign: "top",
278
+ fontSize: 16,
279
+ fontFamily: font,
280
+ weight: 400,
281
+ lineHeight: 1.6,
282
+ fill: colors.body,
283
+ maxWidth: 600,
284
+ opacity: 0,
285
+ timeline: fadeTimeline(10.3, 10.8, 14, 14.5, 10, 170)
286
+ });
287
+
288
+ // Animated demo box for scene 3
289
+ const s3BoxSize = 80;
290
+ const s3BoxY = 340;
291
+
292
+ elements.push({
293
+ id: "s3-demo-box",
294
+ type: "path",
295
+ d: roundedRect(0, 0, s3BoxSize, s3BoxSize, 12),
296
+ x: 300,
297
+ y: s3BoxY,
298
+ fill: colors.accent,
299
+ stroke: "none",
300
+ opacity: 0,
301
+ timeline: {
302
+ tracks: {
303
+ opacity: {
304
+ keyframes: [
305
+ { time: 10.5, value: 0, out: curves.ease },
306
+ { time: 11, value: 1 },
307
+ { time: 14, value: 1, out: curves.ease },
308
+ { time: 14.5, value: 0 }
309
+ ]
310
+ },
311
+ x: {
312
+ keyframes: [
313
+ { time: 11, value: 300, out: curves.ease },
314
+ { time: 12.5, value: 900, out: curves.ease },
315
+ { time: 14, value: 300 }
316
+ ]
317
+ },
318
+ rotation: {
319
+ keyframes: [
320
+ { time: 11, value: 0, out: curves.ease },
321
+ { time: 12.5, value: 180, out: curves.ease },
322
+ { time: 14, value: 360 }
323
+ ]
324
+ }
325
+ }
326
+ },
327
+ origin: [s3BoxSize / 2, s3BoxSize / 2]
328
+ });
329
+
330
+ // Timeline label chips
331
+ const s3Labels = ["position", "rotation", "opacity"];
332
+ s3Labels.forEach((label, i) => {
333
+ const lx = 400 + i * 140;
334
+ const ly = 480;
335
+ const chip = chipGroup(`s3-label-${i}`, lx, ly, label, colors.chipBg, colors.chipText);
336
+ chip.opacity = 0;
337
+ chip.timeline = fadeTimeline(11.5 + i * 0.2, 12 + i * 0.2, 14, 14.5, 8, ly);
338
+ elements.push(chip);
339
+ });
340
+
341
+ // === SCENE 4: Export anywhere (15-20s) ===
342
+
343
+ elements.push({
344
+ id: "s4-headline",
345
+ type: "text",
346
+ x: width / 2,
347
+ y: 180,
348
+ text: "Export anywhere",
349
+ align: "center",
350
+ valign: "middle",
351
+ fontSize: 44,
352
+ fontFamily: font,
353
+ weight: 700,
354
+ fill: colors.headline,
355
+ opacity: 0,
356
+ timeline: fadeTimeline(15, 15.6, 19, 19.5, 20, 180)
357
+ });
358
+
359
+ elements.push({
360
+ id: "s4-body",
361
+ type: "text",
362
+ x: width / 2,
363
+ y: 260,
364
+ text: "Render to SVG for the web, PNG for assets,\nor MP4 for video content. One source, many outputs.",
365
+ align: "center",
366
+ valign: "top",
367
+ fontSize: 18,
368
+ fontFamily: font,
369
+ weight: 400,
370
+ lineHeight: 1.6,
371
+ fill: colors.body,
372
+ maxWidth: 600,
373
+ opacity: 0,
374
+ timeline: fadeTimeline(15.4, 16, 19, 19.5, 15, 260)
375
+ });
376
+
377
+ // Export format chips
378
+ const s4Formats = [
379
+ { label: "SVG", bg: colors.chipBg, text: colors.chipText },
380
+ { label: "PNG", bg: colors.chipBg, text: colors.chipText },
381
+ { label: "MP4", bg: colors.chipGreenBg, text: colors.chipGreenText },
382
+ { label: "WebM", bg: colors.chipBg, text: colors.chipText }
383
+ ];
384
+ const s4ChipY = 360;
385
+ const s4ChipGap = 14;
386
+ const s4TotalW = s4Formats.reduce((sum, c) => sum + c.label.length * 8 + 28, 0) + s4ChipGap * (s4Formats.length - 1);
387
+ let s4ChipX = (width - s4TotalW) / 2;
388
+
389
+ s4Formats.forEach((chip, i) => {
390
+ const cw = chip.label.length * 8 + 28;
391
+ const group = chipGroup(`s4-chip-${i}`, s4ChipX, s4ChipY, chip.label, chip.bg, chip.text);
392
+ group.opacity = 0;
393
+ group.timeline = fadeTimeline(16 + i * 0.15, 16.5 + i * 0.15, 19, 19.5, 10, s4ChipY);
394
+ elements.push(group);
395
+ s4ChipX += cw + s4ChipGap;
396
+ });
397
+
398
+ // Footer
399
+ elements.push({
400
+ id: "s4-footer",
401
+ type: "text",
402
+ x: width / 2,
403
+ y: 500,
404
+ text: "sketchmark.dev",
405
+ align: "center",
406
+ valign: "middle",
407
+ fontSize: 16,
408
+ fontFamily: font,
409
+ weight: 500,
410
+ fill: colors.muted,
411
+ opacity: 0,
412
+ timeline: fadeTimeline(17, 17.5, 19.5, 20, 0, 500)
413
+ });
414
+
415
+ function roundedRect(x, y, w, h, r) {
416
+ return [
417
+ `M ${x + r} ${y}`,
418
+ `L ${x + w - r} ${y}`,
419
+ `Q ${x + w} ${y} ${x + w} ${y + r}`,
420
+ `L ${x + w} ${y + h - r}`,
421
+ `Q ${x + w} ${y + h} ${x + w - r} ${y + h}`,
422
+ `L ${x + r} ${y + h}`,
423
+ `Q ${x} ${y + h} ${x} ${y + h - r}`,
424
+ `L ${x} ${y + r}`,
425
+ `Q ${x} ${y} ${x + r} ${y}`,
426
+ "Z"
427
+ ].join(" ");
428
+ }
429
+
430
+ const doc = {
431
+ version: 1,
432
+ canvas: { width, height, background: bg, duration, fps },
433
+ elements
434
+ };
435
+
436
+ const outPath = path.join(__dirname, "explainer.visual.json");
437
+ fs.writeFileSync(outPath, JSON.stringify(doc, null, 2));
438
+ console.log("Written:", outPath);