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.
- package/ANIMATABLE_MATRIX.md +177 -0
- package/KERNEL_SPEC.md +412 -0
- package/PACKS.md +81 -0
- package/PRESETS.md +182 -0
- package/README.md +274 -188
- package/bin/editor-ui.cjs +2285 -0
- package/bin/preview-ui.cjs +74 -0
- package/bin/sketchmark.cjs +648 -2008
- package/dist/src/animatable.d.ts +21 -0
- package/dist/src/animatable.js +439 -0
- package/dist/src/builders/index.d.ts +1 -11
- package/dist/src/builders/index.js +1 -19
- package/dist/src/diagnostics.js +1 -64
- package/dist/src/edit.d.ts +27 -0
- package/dist/src/edit.js +162 -0
- package/dist/src/index.d.ts +4 -13
- package/dist/src/index.js +4 -13
- package/dist/src/keyframes.d.ts +48 -0
- package/dist/src/keyframes.js +182 -0
- package/dist/src/motion.d.ts +4 -0
- package/dist/src/motion.js +262 -0
- package/dist/src/normalize.js +120 -151
- package/dist/src/presets/characters.d.ts +15 -0
- package/dist/src/presets/characters.js +113 -0
- package/dist/src/presets/compose.d.ts +5 -0
- package/dist/src/presets/compose.js +80 -0
- package/dist/src/presets/effects.d.ts +40 -0
- package/dist/src/presets/effects.js +79 -0
- package/dist/src/presets/helpers.d.ts +33 -0
- package/dist/src/presets/helpers.js +165 -0
- package/dist/src/presets/index.d.ts +9 -0
- package/dist/src/presets/index.js +48 -0
- package/dist/src/presets/motions.d.ts +33 -0
- package/dist/src/presets/motions.js +75 -0
- package/dist/src/presets/scenes.d.ts +35 -0
- package/dist/src/presets/scenes.js +134 -0
- package/dist/src/presets/shapes.d.ts +71 -0
- package/dist/src/presets/shapes.js +96 -0
- package/dist/src/presets/transitions.d.ts +29 -0
- package/dist/src/presets/transitions.js +113 -0
- package/dist/src/presets/types.d.ts +34 -0
- package/dist/src/presets/types.js +2 -0
- package/dist/src/render/html.js +1 -4
- package/dist/src/render/svg.d.ts +2 -2
- package/dist/src/render/svg.js +86 -82
- package/dist/src/render/three-html.js +67 -113
- package/dist/src/scenes.js +1 -0
- package/dist/src/schema.js +218 -280
- package/dist/src/shapes/builtins.js +11 -47
- package/dist/src/shapes/common.js +12 -11
- package/dist/src/shapes/registry.d.ts +0 -1
- package/dist/src/shapes/registry.js +0 -4
- package/dist/src/shapes/types.d.ts +1 -3
- package/dist/src/types.d.ts +57 -288
- package/dist/src/utils.d.ts +2 -11
- package/dist/src/utils.js +13 -70
- package/dist/src/validate.js +321 -275
- package/dist/tests/run.js +576 -510
- package/examples/1730642890464.jpg +0 -0
- package/examples/app-screen.svg +1 -0
- package/examples/app-screen.visual.json +503 -0
- package/examples/dashboard-table.svg +1 -0
- package/examples/dashboard-table.visual.json +708 -0
- package/examples/dev-docs.svg +1 -0
- package/examples/dev-docs.visual.json +248 -0
- package/examples/explainer.mp4 +0 -0
- package/examples/explainer.visual.json +1713 -0
- package/examples/group-origin-effects-lab-check.svg +1 -0
- package/examples/group-origin-effects-lab.visual.json +1880 -0
- package/examples/image-clip-radius.visual.json +271 -0
- package/examples/make-app-screen.cjs +368 -0
- package/examples/make-dashboard-table.cjs +277 -0
- package/examples/make-dev-docs.cjs +233 -0
- package/examples/make-explainer.cjs +438 -0
- package/examples/make-group-origin-effects-lab.cjs +370 -0
- package/examples/make-image-clip-radius.cjs +169 -0
- package/examples/make-modal-dialog.cjs +355 -0
- package/examples/make-origin-effects-lab.cjs +311 -0
- package/examples/make-preset-character-motion.cjs +32 -0
- package/examples/make-presets-demo.cjs +30 -0
- package/examples/make-pricing.cjs +286 -0
- package/examples/make-product-demo.cjs +468 -0
- package/examples/make-product-hero.cjs +223 -0
- package/examples/make-release-notes.cjs +333 -0
- package/examples/make-settings-panel.cjs +435 -0
- package/examples/make-split-preview.cjs +248 -0
- package/examples/make-storyboard.cjs +215 -0
- package/examples/make-transcript.cjs +234 -0
- package/examples/make-typography-test.cjs +397 -0
- package/examples/make-ui-demo-explainer.cjs +1094 -0
- package/examples/make-ui-flow.cjs +762 -0
- package/examples/make-walkthrough.cjs +815 -0
- package/examples/modal-dialog.svg +1 -0
- package/examples/modal-dialog.visual.json +239 -0
- package/examples/origin-effects-lab-check.svg +1 -0
- package/examples/origin-effects-lab.visual.json +1412 -0
- package/examples/preset-character-motion.visual.json +949 -0
- package/examples/presets-demo.visual.json +787 -0
- package/examples/pricing.svg +1 -0
- package/examples/pricing.visual.json +652 -0
- package/examples/product-demo.mp4 +0 -0
- package/examples/product-demo.visual.json +866 -0
- package/examples/product-hero.svg +1 -0
- package/examples/product-hero.visual.json +242 -0
- package/examples/release-notes.svg +1 -0
- package/examples/release-notes.visual.json +467 -0
- package/examples/settings-panel.svg +1 -0
- package/examples/settings-panel.visual.json +501 -0
- package/examples/split-preview.svg +1 -0
- package/examples/split-preview.visual.json +124 -0
- package/examples/storyboard.svg +1 -0
- package/examples/storyboard.visual.json +312 -0
- package/examples/transcript.svg +1 -0
- package/examples/transcript.visual.json +407 -0
- package/examples/typography-indent-check.svg +1 -0
- package/examples/typography-lineheight-0.svg +1 -0
- package/examples/typography-lineheight-2.svg +1 -0
- package/examples/typography-test-check.svg +1 -0
- package/examples/typography-test.svg +1 -0
- package/examples/typography-test.visual.json +757 -0
- package/examples/ui-demo-explainer-billing.svg +1 -0
- package/examples/ui-demo-explainer-check.svg +1 -0
- package/examples/ui-demo-explainer-save.svg +1 -0
- package/examples/ui-demo-explainer-toggle.svg +1 -0
- package/examples/ui-demo-explainer.mp4 +0 -0
- package/examples/ui-demo-explainer.visual.json +2597 -0
- package/examples/ui-flow.mp4 +0 -0
- package/examples/ui-flow.visual.json +1211 -0
- package/examples/walkthrough.mp4 +0 -0
- package/examples/walkthrough.visual.json +1372 -0
- package/package.json +52 -52
- 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`.
|