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,177 @@
1
+ # Animatable Property Matrix
2
+
3
+ This matrix defines the kernel animation surface. Unknown timeline tracks are invalid.
4
+
5
+ Interpolation kinds:
6
+
7
+ - `number`: numeric interpolation
8
+ - `point2`: `[x,y]` interpolation
9
+ - `numberArray`: same-length numeric array interpolation
10
+ - `color`: hex color interpolation for color strings, otherwise discrete
11
+ - `paint`: whole paint value; color strings can interpolate, structured paint switches discretely unless animating supported internals
12
+ - `discrete`: previous value until the target keyframe
13
+
14
+ ## Shared Position Tracks
15
+
16
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
17
+ |---|---:|---:|---:|---:|---:|---|---|---|
18
+ | `position` | yes | yes | yes | yes | yes | `[x,y]` | `point2` | current `x/y` |
19
+ | `x` | yes | yes | yes | yes | yes | number | `number` | `0` |
20
+ | `y` | yes | yes | yes | yes | yes | number | `number` | `0` |
21
+
22
+ ## Transform And Visibility
23
+
24
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
25
+ |---|---:|---:|---:|---:|---:|---|---|---|
26
+ | `opacity` | yes | yes | yes | yes | yes | number | `number` | `1` |
27
+ | `rotation` | yes | yes | yes | no | yes | number | `number` | `0` |
28
+ | `scale` | yes | yes | yes | no | yes | number | `number` | `1` |
29
+ | `scaleX` | yes | yes | yes | no | yes | number | `number` | `1` |
30
+ | `scaleY` | yes | yes | yes | no | yes | number | `number` | `1` |
31
+ | `origin` | yes | yes | yes | no | yes | `[x,y]` | `point2` | renderer-derived |
32
+ | `blendMode` | yes | yes | yes | no | yes | string | `discrete` | none |
33
+
34
+ ## Path Drawing And Stroke
35
+
36
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
37
+ |---|---:|---:|---:|---:|---:|---|---|---|
38
+ | `d` | yes | no | no | no | no | string | `discrete` | required path `d` |
39
+ | `stroke` | yes | no | no | no | no | paint | `paint` | none |
40
+ | `strokeWidth` | yes | no | no | no | no | number | `number` | `1` |
41
+ | `strokeCap` | yes | no | no | no | no | string | `discrete` | renderer default |
42
+ | `strokeJoin` | yes | no | no | no | no | string | `discrete` | renderer default |
43
+ | `miterLimit` | yes | no | no | no | no | number | `number` | renderer default |
44
+ | `dashArray` | yes | no | no | no | no | number array | `numberArray` | none |
45
+ | `dashOffset` | yes | no | no | no | no | number | `number` | `0` |
46
+ | `drawStart` | yes | no | no | no | no | number | `number` | `0` |
47
+ | `drawEnd` | yes | no | no | no | no | number | `number` | `1` |
48
+
49
+ ## Fill Paint
50
+
51
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
52
+ |---|---:|---:|---:|---:|---:|---|---|---|
53
+ | `fill` | yes | yes | no | no | no | paint | `paint` | renderer default |
54
+
55
+ Whole `fill` and `stroke` tracks can switch structured paint values. For smooth gradient or pattern changes, animate the supported nested paint properties below.
56
+
57
+ ## Text
58
+
59
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
60
+ |---|---:|---:|---:|---:|---:|---|---|---|
61
+ | `text` | no | yes | no | no | no | string | `discrete` | none |
62
+ | `lines` | no | yes | no | no | no | string array | `discrete` | none |
63
+ | `align` | no | yes | no | no | no | string | `discrete` | renderer default |
64
+ | `valign` | no | yes | no | no | no | string | `discrete` | renderer default |
65
+ | `fontFamily` | no | yes | no | no | no | string | `discrete` | renderer default |
66
+ | `fontStyle` | no | yes | no | no | no | string | `discrete` | none |
67
+ | `fontSize` | no | yes | no | no | no | number | `number` | `16` |
68
+ | `lineHeight` | no | yes | no | no | no | number | `number` | `1.2` |
69
+ | `letterSpacing` | no | yes | no | no | no | number | `number` | `0` |
70
+ | `maxWidth` | no | yes | no | no | no | number | `number` | none |
71
+ | `weight` | no | yes | no | no | no | number or string | `discrete` | `400` |
72
+
73
+ `wrap` is a kernel field but is not currently an animatable property.
74
+
75
+ ## Image
76
+
77
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
78
+ |---|---:|---:|---:|---:|---:|---|---|---|
79
+ | `width` | no | no | yes | no | yes | number | `number` | `0` |
80
+ | `height` | no | no | yes | no | yes | number | `number` | `0` |
81
+ | `src` | no | no | yes | no | no | string | `discrete` | required image `src` |
82
+ | `fit` | no | no | yes | no | no | string | `discrete` | renderer default |
83
+ | `source.x` | no | no | yes | no | no | number | `number` | `0` |
84
+ | `source.y` | no | no | yes | no | no | number | `number` | `0` |
85
+ | `source.width` | no | no | yes | no | no | number | `number` | image width |
86
+ | `source.height` | no | no | yes | no | no | number | `number` | image height |
87
+
88
+ Image radius is not a kernel field. Use `clip.d`.
89
+
90
+ ## Clip And Mask
91
+
92
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
93
+ |---|---:|---:|---:|---:|---:|---|---|---|
94
+ | `clip.d` | yes | yes | yes | no | yes | string | `discrete` | full-plane clip if authored through edit helper |
95
+ | `mask.d` | yes | yes | yes | no | yes | string | `discrete` | full-plane mask if authored through edit helper |
96
+ | `mask.opacity` | yes | yes | yes | no | yes | number | `number` | `1` |
97
+
98
+ `clip.d` and `mask.d` are discrete. Smooth-looking path/radius changes must be sampled above the kernel.
99
+
100
+ ## Effects
101
+
102
+ | Property | path | text | image | point | group | Value | Interpolation | Default |
103
+ |---|---:|---:|---:|---:|---:|---|---|---|
104
+ | `effects.blur` | yes | yes | yes | no | yes | number | `number` | `0` |
105
+ | `effects.brightness` | yes | yes | yes | no | yes | number | `number` | `1` |
106
+ | `effects.contrast` | yes | yes | yes | no | yes | number | `number` | `1` |
107
+ | `effects.saturate` | yes | yes | yes | no | yes | number | `number` | `1` |
108
+ | `effects.hueRotate` | yes | yes | yes | no | yes | number | `number` | `0` |
109
+ | `effects.shadow.dx` | yes | yes | yes | no | yes | number | `number` | `0` |
110
+ | `effects.shadow.dy` | yes | yes | yes | no | yes | number | `number` | `0` |
111
+ | `effects.shadow.blur` | yes | yes | yes | no | yes | number | `number` | `0` |
112
+ | `effects.shadow.color` | yes | yes | yes | no | yes | string | `color` | `#000000` |
113
+ | `effects.shadow.opacity` | yes | yes | yes | no | yes | number | `number` | `1` |
114
+
115
+ ## Structured Paint Internals
116
+
117
+ These tracks are valid only when the element currently has the matching structured paint type on the matching root (`fill` or `stroke`).
118
+
119
+ ### Linear Gradient
120
+
121
+ | Property | path fill | text fill | path stroke | Value | Interpolation |
122
+ |---|---:|---:|---:|---|---|
123
+ | `fill.from` | yes | yes | no | `[x,y]` | `point2` |
124
+ | `fill.to` | yes | yes | no | `[x,y]` | `point2` |
125
+ | `stroke.from` | no | no | yes | `[x,y]` | `point2` |
126
+ | `stroke.to` | no | no | yes | `[x,y]` | `point2` |
127
+
128
+ ### Radial Gradient
129
+
130
+ | Property | path fill | text fill | path stroke | Value | Interpolation |
131
+ |---|---:|---:|---:|---|---|
132
+ | `fill.center` | yes | yes | no | `[x,y]` | `point2` |
133
+ | `fill.focus` | yes | yes | no | `[x,y]` | `point2` |
134
+ | `fill.radius` | yes | yes | no | number | `number` |
135
+ | `stroke.center` | no | no | yes | `[x,y]` | `point2` |
136
+ | `stroke.focus` | no | no | yes | `[x,y]` | `point2` |
137
+ | `stroke.radius` | no | no | yes | number | `number` |
138
+
139
+ ### Gradient Stops
140
+
141
+ | Property pattern | path fill | text fill | path stroke | Value | Interpolation |
142
+ |---|---:|---:|---:|---|---|
143
+ | `fill.stops.N.offset` | yes | yes | no | number | `number` |
144
+ | `fill.stops.N.color` | yes | yes | no | string | `color` |
145
+ | `stroke.stops.N.offset` | no | no | yes | number | `number` |
146
+ | `stroke.stops.N.color` | no | no | yes | string | `color` |
147
+
148
+ `N` must point to an existing stop. The kernel does not create missing paint stops.
149
+
150
+ ### Pattern
151
+
152
+ | Property | path fill | text fill | path stroke | Value | Interpolation |
153
+ |---|---:|---:|---:|---|---|
154
+ | `fill.x` | yes | yes | no | number | `number` |
155
+ | `fill.y` | yes | yes | no | number | `number` |
156
+ | `fill.width` | yes | yes | no | number | `number` |
157
+ | `fill.height` | yes | yes | no | number | `number` |
158
+ | `fill.opacity` | yes | yes | no | number | `number` |
159
+ | `stroke.x` | no | no | yes | number | `number` |
160
+ | `stroke.y` | no | no | yes | number | `number` |
161
+ | `stroke.width` | no | no | yes | number | `number` |
162
+ | `stroke.height` | no | no | yes | number | `number` |
163
+ | `stroke.opacity` | no | no | yes | number | `number` |
164
+
165
+ ## Conflict Warnings
166
+
167
+ The validator allows but warns for overlapping timeline representations:
168
+
169
+ - `position` with `x` or `y`
170
+ - `scale` with `scaleX` or `scaleY`
171
+ - whole paint tracks such as `fill` with nested paint tracks such as `fill.to`
172
+
173
+ Prefer one representation per property family.
174
+
175
+ ## Authoring Rule
176
+
177
+ If a desired animation is not in this matrix, do not add a new kernel field casually. Prefer a preset/editor helper that compiles into existing tracks.
package/KERNEL_SPEC.md ADDED
@@ -0,0 +1,412 @@
1
+ # Sketchmark Kernel Spec
2
+
3
+ Version: `1`
4
+
5
+ Sketchmark kernel documents are a strict 2D render and animation interchange format. The kernel is not an authoring language, editor project file, AI prompt format, or preset scene graph.
6
+
7
+ The kernel's job is to be:
8
+
9
+ - renderable
10
+ - deterministic
11
+ - validatable
12
+ - easy for editors and compilers to target
13
+ - small enough to keep stable
14
+
15
+ ## Document Shape
16
+
17
+ Allowed top-level fields:
18
+
19
+ - `version`: must be `1`
20
+ - `canvas`: required
21
+ - `elements`: optional array
22
+
23
+ No other top-level fields are allowed.
24
+
25
+ ```json
26
+ {
27
+ "version": 1,
28
+ "canvas": {
29
+ "width": 960,
30
+ "height": 540,
31
+ "background": "#ffffff",
32
+ "duration": 2,
33
+ "fps": 30
34
+ },
35
+ "elements": []
36
+ }
37
+ ```
38
+
39
+ ## Canvas
40
+
41
+ Required fields:
42
+
43
+ - `width`: number
44
+ - `height`: number
45
+
46
+ Optional fields:
47
+
48
+ - `background`: string
49
+ - `duration`: non-negative number
50
+ - `fps`: positive number
51
+
52
+ Canvas has no camera, 3D space, scene list, assets map, imports, exports, or global timeline.
53
+
54
+ ## Elements
55
+
56
+ Allowed element types:
57
+
58
+ - `path`
59
+ - `text`
60
+ - `image`
61
+ - `point`
62
+ - `group`
63
+
64
+ Common optional fields:
65
+
66
+ - `id`
67
+ - `opacity`
68
+ - `fill`
69
+ - `stroke`
70
+ - `strokeWidth`
71
+ - `strokeCap`
72
+ - `strokeJoin`
73
+ - `miterLimit`
74
+ - `dashArray`
75
+ - `dashOffset`
76
+ - `drawStart`
77
+ - `drawEnd`
78
+ - `effects`
79
+ - `blendMode`
80
+ - `rotation`
81
+ - `scale`
82
+ - `scaleX`
83
+ - `scaleY`
84
+ - `origin`
85
+ - `clip`
86
+ - `mask`
87
+ - `timeline`
88
+
89
+ Element IDs must start with a letter or `_` and may contain letters, numbers, `_`, `-`, or `.`. Dot-separated IDs are allowed so authoring layers can generate readable names such as `hero.head`.
90
+
91
+ Explicitly not allowed:
92
+
93
+ - editor metadata
94
+ - selections
95
+ - locks
96
+ - guides
97
+ - snapping
98
+ - scene IDs
99
+ - semantic objects
100
+ - rectangles/circles/arrows as separate types
101
+ - `cornerRadius`
102
+
103
+ Those belong in editor/project/preset layers and must compile down to kernel fields.
104
+
105
+ ## Path
106
+
107
+ Required:
108
+
109
+ - `type: "path"`
110
+ - `d`: SVG path data string
111
+
112
+ Optional:
113
+
114
+ - `x`
115
+ - `y`
116
+ - common fields
117
+
118
+ Path `x/y` are rendered as a translation.
119
+
120
+ ## Text
121
+
122
+ Required:
123
+
124
+ - `type: "text"`
125
+ - `x`
126
+ - `y`
127
+
128
+ Optional:
129
+
130
+ - `text`
131
+ - `lines`
132
+ - `align`: `left`, `center`, `right`
133
+ - `valign`: `top`, `middle`, `bottom`
134
+ - `fontSize`
135
+ - `fontFamily`
136
+ - `weight`
137
+ - `fontStyle`
138
+ - `lineHeight`
139
+ - `letterSpacing`
140
+ - `maxWidth`
141
+ - `wrap`
142
+ - common fields
143
+
144
+ If both `text` and `lines` exist, renderers may prefer explicit `lines`.
145
+
146
+ ## Image
147
+
148
+ Required:
149
+
150
+ - `type: "image"`
151
+ - `src`
152
+ - `x`
153
+ - `y`
154
+ - `width`
155
+ - `height`
156
+
157
+ Optional:
158
+
159
+ - `fit`: `fill`, `contain`, `cover`
160
+ - `source`: crop rectangle
161
+ - common fields
162
+
163
+ Rounded images are represented with `clip.d`, not `cornerRadius`.
164
+
165
+ ## Point
166
+
167
+ Required:
168
+
169
+ - `type: "point"`
170
+ - `x`
171
+ - `y`
172
+
173
+ Points are render-neutral anchors. They can be referenced by authoring tools and timelines, but renderers do not draw visible geometry for points.
174
+
175
+ ## Group
176
+
177
+ Required:
178
+
179
+ - `type: "group"`
180
+ - `x`
181
+ - `y`
182
+ - `children`
183
+
184
+ Optional:
185
+
186
+ - `width`
187
+ - `height`
188
+ - common fields
189
+
190
+ Group `x/y` are rendered as group translation. Child coordinates are local to the group.
191
+
192
+ ## Paint
193
+
194
+ Paint can be:
195
+
196
+ - color string
197
+ - linear gradient
198
+ - radial gradient
199
+ - pattern
200
+
201
+ Linear gradient:
202
+
203
+ ```json
204
+ {
205
+ "type": "linearGradient",
206
+ "from": [0, 0],
207
+ "to": [100, 0],
208
+ "stops": [[0, "#000000"], [1, "#ffffff"]]
209
+ }
210
+ ```
211
+
212
+ Radial gradient:
213
+
214
+ ```json
215
+ {
216
+ "type": "radialGradient",
217
+ "center": [50, 50],
218
+ "radius": 40,
219
+ "focus": [50, 50],
220
+ "stops": [[0, "#ffffff"], [1, "#000000"]]
221
+ }
222
+ ```
223
+
224
+ Pattern:
225
+
226
+ ```json
227
+ {
228
+ "type": "pattern",
229
+ "src": "image.png",
230
+ "x": 0,
231
+ "y": 0,
232
+ "width": 32,
233
+ "height": 32,
234
+ "fit": "cover",
235
+ "opacity": 1
236
+ }
237
+ ```
238
+
239
+ ## Clip And Mask
240
+
241
+ Clip:
242
+
243
+ ```json
244
+ {
245
+ "type": "path",
246
+ "d": "M 0 0 H 100 V 100 H 0 Z"
247
+ }
248
+ ```
249
+
250
+ Mask:
251
+
252
+ ```json
253
+ {
254
+ "type": "path",
255
+ "d": "M 0 0 H 100 V 100 H 0 Z",
256
+ "opacity": 0.8
257
+ }
258
+ ```
259
+
260
+ Clip and mask paths use user-space coordinates for the rendered element context. If an editor wants rounded rectangles, arbitrary reveals, or soft masks, it should compile them into these fields.
261
+
262
+ ## Timeline
263
+
264
+ Animation is element-local.
265
+
266
+ ```json
267
+ {
268
+ "timeline": {
269
+ "start": 0,
270
+ "end": 2,
271
+ "tracks": {
272
+ "position": {
273
+ "keyframes": [
274
+ { "time": 0, "value": [10, 20] },
275
+ { "time": 1, "value": [100, 20] }
276
+ ]
277
+ }
278
+ }
279
+ }
280
+ }
281
+ ```
282
+
283
+ Timeline fields:
284
+
285
+ - `start`: optional non-negative number
286
+ - `end`: optional non-negative number
287
+ - `tracks`: map of known animatable property names to track objects
288
+
289
+ Unknown tracks are invalid.
290
+
291
+ ## Keyframes
292
+
293
+ Legacy tuple form remains valid:
294
+
295
+ ```json
296
+ [0, 100]
297
+ ```
298
+
299
+ Preferred object form:
300
+
301
+ ```json
302
+ {
303
+ "time": 0,
304
+ "value": 100,
305
+ "out": {
306
+ "type": "cubicBezier",
307
+ "x1": 0,
308
+ "y1": 0,
309
+ "x2": 0.58,
310
+ "y2": 1
311
+ }
312
+ }
313
+ ```
314
+
315
+ Object keyframe fields:
316
+
317
+ - `time`: finite seconds
318
+ - `value`: JSON-safe timeline value
319
+ - `in`: optional curve
320
+ - `out`: optional curve
321
+ - `interpolation`: optional curve
322
+
323
+ Keyframes must be sorted by time.
324
+
325
+ ## Curves
326
+
327
+ Supported curves:
328
+
329
+ - `graph`
330
+ - `cubicBezier`
331
+ - `hold`
332
+
333
+ Graph curve:
334
+
335
+ ```json
336
+ {
337
+ "type": "graph",
338
+ "points": [[0, 0], [0.5, 0.2], [1, 1]]
339
+ }
340
+ ```
341
+
342
+ Graph `x` is normalized time progress. Graph `y` is normalized value progress.
343
+
344
+ Cubic Bezier:
345
+
346
+ ```json
347
+ {
348
+ "type": "cubicBezier",
349
+ "x1": 0.42,
350
+ "y1": 0,
351
+ "x2": 0.58,
352
+ "y2": 1
353
+ }
354
+ ```
355
+
356
+ Hold:
357
+
358
+ ```json
359
+ {
360
+ "type": "hold"
361
+ }
362
+ ```
363
+
364
+ Segment curve resolution order:
365
+
366
+ 1. previous keyframe `out`
367
+ 2. previous keyframe `interpolation`
368
+ 3. next keyframe `in`
369
+ 4. track `curve`
370
+ 5. legacy `ease`
371
+ 6. linear
372
+
373
+ Named `ease` is legacy compatibility. New authoring tools should emit explicit curves.
374
+
375
+ ## Interpolation Rules
376
+
377
+ - number to number: numeric interpolation
378
+ - `[x,y]` to `[x,y]`: point interpolation
379
+ - same-length number arrays: numeric array interpolation
380
+ - hex color string to hex color string: color interpolation
381
+ - all other string/object values: discrete switch at the target keyframe
382
+
383
+ Path data such as `d` and `clip.d` is discrete in the kernel. Smooth-looking path/radius changes should be sampled by an authoring compiler into multiple keyframes.
384
+
385
+ ## Determinism Rules
386
+
387
+ For the same document and time:
388
+
389
+ - validation result must be stable
390
+ - resolved frame must be stable
391
+ - SVG output should be stable
392
+ - generated schema must match `schema/visual.schema.json`
393
+
394
+ Renderers should not read editor/project state, ambient AI context, hidden assets maps, or non-kernel fields.
395
+
396
+ ## Versioning
397
+
398
+ `version: 1` is the frozen kernel line.
399
+
400
+ Breaking changes require a new document version. Additive features must still preserve deterministic validation and rendering.
401
+
402
+ ## Kernel Boundary
403
+
404
+ Do not add these to the kernel:
405
+
406
+ - motion presets such as `rise`, `fadeIn`, `stagger`, `dogWalk`
407
+ - project source objects such as rectangles, circles, charts, diagrams
408
+ - editor state such as selected, locked, hidden, guides, snapping
409
+ - AI prompts or semantic descriptions
410
+ - benchmark task metadata
411
+
412
+ Those belong above the kernel and compile down to this spec.
package/PACKS.md ADDED
@@ -0,0 +1,81 @@
1
+ # Sketchmark Packs
2
+
3
+ Packs are shareable collections of presets.
4
+
5
+ This milestone documents the pack contract only. Sketchmark does not load third-party packs yet.
6
+
7
+ ## Folder Shape
8
+
9
+ ```txt
10
+ my-pack/
11
+ sketchmark-pack.json
12
+ index.cjs
13
+ presets/
14
+ ```
15
+
16
+ ## Manifest
17
+
18
+ ```json
19
+ {
20
+ "name": "my-pack",
21
+ "version": "1.0.0",
22
+ "sketchmark": ">=2.0.0",
23
+ "presets": [
24
+ {
25
+ "id": "motions.float",
26
+ "kind": "motion",
27
+ "title": "Float"
28
+ }
29
+ ]
30
+ }
31
+ ```
32
+
33
+ Fields:
34
+
35
+ - `name`: package name
36
+ - `version`: pack version
37
+ - `sketchmark`: compatible Sketchmark range
38
+ - `presets`: list of exported preset IDs
39
+
40
+ Preset `kind` should be one of:
41
+
42
+ - `shape`
43
+ - `character`
44
+ - `motion`
45
+ - `effect`
46
+ - `transition`
47
+ - `scene`
48
+
49
+ ## Runtime Contract
50
+
51
+ `index.cjs` should export preset functions:
52
+
53
+ ```js
54
+ module.exports = {
55
+ presets: {
56
+ "motions.float": function float({ id, start = 0, duration = 1 }) {
57
+ return {
58
+ timelines: {
59
+ [id]: {
60
+ position: {
61
+ keyframes: [
62
+ { time: start, value: [0, 0], out: { type: "cubicBezier", x1: 0.42, y1: 0, x2: 0.58, y2: 1 } },
63
+ { time: start + duration / 2, value: [0, -16], out: { type: "cubicBezier", x1: 0.42, y1: 0, x2: 0.58, y2: 1 } },
64
+ { time: start + duration, value: [0, 0] }
65
+ ]
66
+ }
67
+ }
68
+ }
69
+ };
70
+ }
71
+ }
72
+ };
73
+ ```
74
+
75
+ ## Rules
76
+
77
+ - Packs are authoring code, not kernel schema.
78
+ - Pack presets must compile to pure `PresetFragment` objects.
79
+ - Pack output must validate as a normal Sketchmark kernel document after application.
80
+ - Trusted `.cjs` packs may execute local code. A safer declarative pack format can come later.
81
+ - Pack manifests should describe what exists; they should not be copied into `.visual.json`.