sketchmark 2.1.9 → 2.2.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/README.md +48 -226
- package/bin/editor-ui.cjs +211 -84
- package/bin/preview-ui.d.ts +1 -0
- package/dist/src/edit.d.ts +1 -0
- package/dist/src/edit.js +9 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +25 -1
- package/dist/src/render/embed.js +26 -16
- package/dist/src/render/index.d.ts +6 -0
- package/dist/src/render/index.js +27 -0
- package/dist/tests/run.js +3 -1
- package/package.json +12 -1
- package/skills/sketchmark/SKILL.md +16 -0
- package/skills/sketchmark/animation.md +67 -0
- package/skills/sketchmark/design.md +59 -0
- package/skills/sketchmark/kernel.md +354 -0
- package/skills/sketchmark/render.md +166 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sketchmark
|
|
3
|
+
description: Entry point for Sketchmark kernel, design, animation, and render/edit/preview guidance. Use when working with Sketchmark `.visual.json`, generators, validation, rendering, timelines, or previews.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sketchmark
|
|
7
|
+
|
|
8
|
+
Use the smallest file needed:
|
|
9
|
+
|
|
10
|
+
- `kernel.md`: valid document shape, element properties, paints, effects, timelines, curves, and animatable tracks.
|
|
11
|
+
- `design.md`: broad composition and static visual construction guidance.
|
|
12
|
+
- `animation.md`: broad timeline and motion guidance.
|
|
13
|
+
- `render.md`: validate, render, edit, preview, export, and debug workflow.
|
|
14
|
+
|
|
15
|
+
Keep `.visual.json` output pure kernel: `version`, `canvas`, and `elements`.
|
|
16
|
+
When working inside this repository, prefer example generator scripts such as `examples/make-*.cjs` over hand-authoring only generated `.visual.json` files.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Animation
|
|
2
|
+
|
|
3
|
+
Use this for timeline creation, motion timing, reveals, transitions, and animated visual states.
|
|
4
|
+
|
|
5
|
+
## Boundary
|
|
6
|
+
|
|
7
|
+
- Animation is stored in element-local `timeline.tracks`.
|
|
8
|
+
- Track names must be valid animatable properties from `kernel.md`.
|
|
9
|
+
- Curves are timeline curve objects.
|
|
10
|
+
- Helper functions may exist in generator code, but their output is only kernel timelines.
|
|
11
|
+
|
|
12
|
+
## Process
|
|
13
|
+
|
|
14
|
+
1. Create a readable static frame first.
|
|
15
|
+
2. Choose which element or group owns each motion.
|
|
16
|
+
3. Animate parent groups for broad movement.
|
|
17
|
+
4. Animate child elements for local changes.
|
|
18
|
+
5. Add keyframes at clear beats.
|
|
19
|
+
6. Use explicit curves when timing matters.
|
|
20
|
+
7. Validate.
|
|
21
|
+
8. Render checkpoint frames at key times.
|
|
22
|
+
|
|
23
|
+
## Track Selection
|
|
24
|
+
|
|
25
|
+
- Position: `position`, `x`, `y`.
|
|
26
|
+
- Visibility: `opacity`.
|
|
27
|
+
- Transform: `rotation`, `scale`, `scaleX`, `scaleY`, `origin`.
|
|
28
|
+
- Path drawing: `drawStart`, `drawEnd`.
|
|
29
|
+
- Text changes: `text`, `lines`.
|
|
30
|
+
- Paint changes: `fill`, `stroke`, paint subproperties.
|
|
31
|
+
- Reveal: `clip.d`, `mask.d`, `mask.opacity`.
|
|
32
|
+
- Effects: `effects.*`.
|
|
33
|
+
|
|
34
|
+
## Keyframes
|
|
35
|
+
|
|
36
|
+
Preferred object form:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
{ time: 0, value: [0, 0], out: { type: "cubicBezier", x1: 0.16, y1: 1, x2: 0.3, y2: 1 } }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Tuple form:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
[0, [0, 0]]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Curves
|
|
49
|
+
|
|
50
|
+
- `cubicBezier`: smooth timing.
|
|
51
|
+
- `graph`: custom normalized timing.
|
|
52
|
+
- `hold`: discrete switch.
|
|
53
|
+
|
|
54
|
+
## Timing
|
|
55
|
+
|
|
56
|
+
- Use seconds.
|
|
57
|
+
- Keep keyframes sorted by time.
|
|
58
|
+
- Use short names for curve constants in generator code.
|
|
59
|
+
- Use `hold` for instant changes.
|
|
60
|
+
- Use multiple sampled keyframes for path or radius style changes because path data is discrete.
|
|
61
|
+
|
|
62
|
+
## Final Check
|
|
63
|
+
|
|
64
|
+
- Every animated track exists in `kernel.md`.
|
|
65
|
+
- Keyframes are sorted.
|
|
66
|
+
- Curve fields contain curve objects.
|
|
67
|
+
- The animation still reads at checkpoint frames.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
Use this for composition, layout, typography, visual hierarchy, and static visual construction.
|
|
4
|
+
|
|
5
|
+
## Boundary
|
|
6
|
+
|
|
7
|
+
- Output kernel elements only: `path`, `text`, `image`, `point`, `group`.
|
|
8
|
+
- Higher-level objects compile to kernel elements.
|
|
9
|
+
- Keep authoring helper names out of `.visual.json`.
|
|
10
|
+
|
|
11
|
+
## Process
|
|
12
|
+
|
|
13
|
+
1. Set `canvas.width`, `canvas.height`, optional `background`, `duration`, and `fps`.
|
|
14
|
+
2. Define reusable constants in generator code: colors, font family, spacing, stroke widths.
|
|
15
|
+
3. Build from back to front: background, large groups, content, details.
|
|
16
|
+
4. Use `group` for local coordinate systems.
|
|
17
|
+
5. Use `path` for shape geometry.
|
|
18
|
+
6. Use `text` for all labels and copy.
|
|
19
|
+
7. Use `image` only with explicit size and fit.
|
|
20
|
+
8. Validate the document.
|
|
21
|
+
9. Render at least one preview frame.
|
|
22
|
+
|
|
23
|
+
## Layout
|
|
24
|
+
|
|
25
|
+
- Use explicit `x` and `y`.
|
|
26
|
+
- Use groups to move related elements together.
|
|
27
|
+
- Use consistent margins and spacing.
|
|
28
|
+
- Keep important content inside the canvas.
|
|
29
|
+
- Prefer stable dimensions over inferred layout.
|
|
30
|
+
|
|
31
|
+
## Text
|
|
32
|
+
|
|
33
|
+
- Set `align` and `valign`.
|
|
34
|
+
- Set `fontSize`, `lineHeight`, `weight`, and `fill`.
|
|
35
|
+
- Use `text` with newlines or `lines` for multiline text.
|
|
36
|
+
- Use `maxWidth` and `wrap` only when supported by the renderer path being used.
|
|
37
|
+
|
|
38
|
+
## Shapes
|
|
39
|
+
|
|
40
|
+
- Rectangle: `path`.
|
|
41
|
+
- Rounded rectangle: `path`.
|
|
42
|
+
- Circle/ellipse: `path`.
|
|
43
|
+
- Arrow: `path`.
|
|
44
|
+
- Divider: `path` with `stroke`.
|
|
45
|
+
- Clip/reveal shape: `clip.d` or `mask.d`.
|
|
46
|
+
|
|
47
|
+
## Images
|
|
48
|
+
|
|
49
|
+
- Set `src`, `x`, `y`, `width`, `height`.
|
|
50
|
+
- Use `fit`.
|
|
51
|
+
- Use `source.*` for cropping.
|
|
52
|
+
- Use `clip.d` for rounded or custom image boundaries.
|
|
53
|
+
|
|
54
|
+
## Final Check
|
|
55
|
+
|
|
56
|
+
- No non-kernel element types.
|
|
57
|
+
- No metadata or editor state.
|
|
58
|
+
- All visual objects have explicit position and size.
|
|
59
|
+
- Text is readable in the target canvas.
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# Kernel
|
|
2
|
+
|
|
3
|
+
## Package Entry Points
|
|
4
|
+
|
|
5
|
+
- `sketchmark`
|
|
6
|
+
- `sketchmark/presets`
|
|
7
|
+
- `sketchmark/browser-export`
|
|
8
|
+
- `sketchmark/render`
|
|
9
|
+
- `sketchmark/edit`
|
|
10
|
+
- `sketchmark/editor`
|
|
11
|
+
- `sketchmark/preview`
|
|
12
|
+
- `sketchmark/schema`
|
|
13
|
+
- CLI: `sketchmark`
|
|
14
|
+
|
|
15
|
+
## Root Kernel Exports
|
|
16
|
+
|
|
17
|
+
- types
|
|
18
|
+
- validate
|
|
19
|
+
- normalize
|
|
20
|
+
- diagnostics
|
|
21
|
+
- schema
|
|
22
|
+
- keyframes
|
|
23
|
+
- edit namespace and direct edit helpers
|
|
24
|
+
- animatable
|
|
25
|
+
- render namespace and direct render helpers
|
|
26
|
+
|
|
27
|
+
Preferred package API:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const { render, edit, validateVisualDocument } = require("sketchmark");
|
|
31
|
+
|
|
32
|
+
const svg = render.svg(doc, { time: 1 });
|
|
33
|
+
const html = render.html(doc);
|
|
34
|
+
const embed = render.embedHtml(doc, { title: "Preview" });
|
|
35
|
+
const nextDoc = edit.setProperty(doc, "element-id", "fill", "#22c55e");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
UI entry points stay separate:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const { editorHtml } = require("sketchmark/editor");
|
|
42
|
+
const { previewHtml } = require("sketchmark/preview");
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Document
|
|
46
|
+
|
|
47
|
+
- `version`: `1`
|
|
48
|
+
- `canvas`: `VisualCanvas`
|
|
49
|
+
- `elements`: `VisualElement[]`
|
|
50
|
+
|
|
51
|
+
## Canvas
|
|
52
|
+
|
|
53
|
+
- `width`: number
|
|
54
|
+
- `height`: number
|
|
55
|
+
- `background`: string
|
|
56
|
+
- `duration`: number
|
|
57
|
+
- `fps`: number
|
|
58
|
+
|
|
59
|
+
## Element Types
|
|
60
|
+
|
|
61
|
+
- `path`
|
|
62
|
+
- `text`
|
|
63
|
+
- `image`
|
|
64
|
+
- `point`
|
|
65
|
+
- `group`
|
|
66
|
+
|
|
67
|
+
## Common Element Properties
|
|
68
|
+
|
|
69
|
+
- `id`: string
|
|
70
|
+
- `type`: element type
|
|
71
|
+
- `opacity`: number
|
|
72
|
+
- `fill`: paint
|
|
73
|
+
- `stroke`: paint
|
|
74
|
+
- `strokeWidth`: number
|
|
75
|
+
- `strokeCap`: `butt | round | square`
|
|
76
|
+
- `strokeJoin`: `miter | round | bevel`
|
|
77
|
+
- `miterLimit`: number
|
|
78
|
+
- `dashArray`: number[]
|
|
79
|
+
- `dashOffset`: number
|
|
80
|
+
- `drawStart`: number
|
|
81
|
+
- `drawEnd`: number
|
|
82
|
+
- `effects`: effects
|
|
83
|
+
- `blendMode`: string
|
|
84
|
+
- `rotation`: number
|
|
85
|
+
- `scale`: number
|
|
86
|
+
- `scaleX`: number
|
|
87
|
+
- `scaleY`: number
|
|
88
|
+
- `origin`: `[number, number]`
|
|
89
|
+
- `clip`: clip shape
|
|
90
|
+
- `mask`: mask shape
|
|
91
|
+
- `timeline`: element timeline
|
|
92
|
+
|
|
93
|
+
## Path Element
|
|
94
|
+
|
|
95
|
+
- `type`: `"path"`
|
|
96
|
+
- `d`: SVG path data string
|
|
97
|
+
- `x`: number
|
|
98
|
+
- `y`: number
|
|
99
|
+
- common properties
|
|
100
|
+
|
|
101
|
+
## Text Element
|
|
102
|
+
|
|
103
|
+
- `type`: `"text"`
|
|
104
|
+
- `x`: number
|
|
105
|
+
- `y`: number
|
|
106
|
+
- `text`: string
|
|
107
|
+
- `lines`: string[]
|
|
108
|
+
- `align`: `left | center | right`
|
|
109
|
+
- `valign`: `top | middle | bottom`
|
|
110
|
+
- `fontSize`: number
|
|
111
|
+
- `fontFamily`: string
|
|
112
|
+
- `weight`: number | string
|
|
113
|
+
- `fontStyle`: string
|
|
114
|
+
- `lineHeight`: number
|
|
115
|
+
- `letterSpacing`: number
|
|
116
|
+
- `maxWidth`: number
|
|
117
|
+
- `wrap`: boolean
|
|
118
|
+
- common properties
|
|
119
|
+
|
|
120
|
+
## Image Element
|
|
121
|
+
|
|
122
|
+
- `type`: `"image"`
|
|
123
|
+
- `src`: string
|
|
124
|
+
- `x`: number
|
|
125
|
+
- `y`: number
|
|
126
|
+
- `width`: number
|
|
127
|
+
- `height`: number
|
|
128
|
+
- `fit`: `fill | contain | cover`
|
|
129
|
+
- `source.x`: number
|
|
130
|
+
- `source.y`: number
|
|
131
|
+
- `source.width`: number
|
|
132
|
+
- `source.height`: number
|
|
133
|
+
- `source.imageWidth`: number
|
|
134
|
+
- `source.imageHeight`: number
|
|
135
|
+
- common properties
|
|
136
|
+
|
|
137
|
+
## Point Element
|
|
138
|
+
|
|
139
|
+
- `type`: `"point"`
|
|
140
|
+
- `x`: number
|
|
141
|
+
- `y`: number
|
|
142
|
+
- common properties
|
|
143
|
+
|
|
144
|
+
## Group Element
|
|
145
|
+
|
|
146
|
+
- `type`: `"group"`
|
|
147
|
+
- `x`: number
|
|
148
|
+
- `y`: number
|
|
149
|
+
- `width`: number
|
|
150
|
+
- `height`: number
|
|
151
|
+
- `children`: `VisualElement[]`
|
|
152
|
+
- common properties
|
|
153
|
+
|
|
154
|
+
## Paint
|
|
155
|
+
|
|
156
|
+
- string color
|
|
157
|
+
- linear gradient
|
|
158
|
+
- radial gradient
|
|
159
|
+
- pattern
|
|
160
|
+
|
|
161
|
+
## Linear Gradient Paint
|
|
162
|
+
|
|
163
|
+
- `type`: `"linearGradient"`
|
|
164
|
+
- `from`: `[number, number]`
|
|
165
|
+
- `to`: `[number, number]`
|
|
166
|
+
- `stops`: gradient stops
|
|
167
|
+
|
|
168
|
+
## Radial Gradient Paint
|
|
169
|
+
|
|
170
|
+
- `type`: `"radialGradient"`
|
|
171
|
+
- `center`: `[number, number]`
|
|
172
|
+
- `radius`: number
|
|
173
|
+
- `focus`: `[number, number]`
|
|
174
|
+
- `stops`: gradient stops
|
|
175
|
+
|
|
176
|
+
## Pattern Paint
|
|
177
|
+
|
|
178
|
+
- `type`: `"pattern"`
|
|
179
|
+
- `src`: string
|
|
180
|
+
- `x`: number
|
|
181
|
+
- `y`: number
|
|
182
|
+
- `width`: number
|
|
183
|
+
- `height`: number
|
|
184
|
+
- `fit`: `fill | contain | cover`
|
|
185
|
+
- `opacity`: number
|
|
186
|
+
|
|
187
|
+
## Gradient Stop
|
|
188
|
+
|
|
189
|
+
- `[offset, color]`
|
|
190
|
+
- `{ offset, color }`
|
|
191
|
+
|
|
192
|
+
## Clip Shape
|
|
193
|
+
|
|
194
|
+
- `type`: `"path"`
|
|
195
|
+
- `d`: SVG path data string
|
|
196
|
+
|
|
197
|
+
## Mask Shape
|
|
198
|
+
|
|
199
|
+
- `type`: `"path"`
|
|
200
|
+
- `d`: SVG path data string
|
|
201
|
+
- `opacity`: number
|
|
202
|
+
|
|
203
|
+
## Effects
|
|
204
|
+
|
|
205
|
+
- `blur`: number
|
|
206
|
+
- `brightness`: number
|
|
207
|
+
- `contrast`: number
|
|
208
|
+
- `saturate`: number
|
|
209
|
+
- `hueRotate`: number
|
|
210
|
+
- `shadow.dx`: number
|
|
211
|
+
- `shadow.dy`: number
|
|
212
|
+
- `shadow.blur`: number
|
|
213
|
+
- `shadow.color`: string
|
|
214
|
+
- `shadow.opacity`: number
|
|
215
|
+
|
|
216
|
+
## Timeline
|
|
217
|
+
|
|
218
|
+
- `start`: number
|
|
219
|
+
- `end`: number
|
|
220
|
+
- `tracks`: record of animatable property names to timeline tracks
|
|
221
|
+
|
|
222
|
+
## Timeline Track
|
|
223
|
+
|
|
224
|
+
- `keyframes`: timeline keyframes
|
|
225
|
+
- `curve`: timeline curve
|
|
226
|
+
- `ease`: string
|
|
227
|
+
|
|
228
|
+
## Keyframe Tuple
|
|
229
|
+
|
|
230
|
+
- `[time, value]`
|
|
231
|
+
|
|
232
|
+
## Keyframe Object
|
|
233
|
+
|
|
234
|
+
- `time`: number
|
|
235
|
+
- `value`: timeline value
|
|
236
|
+
- `in`: timeline curve
|
|
237
|
+
- `out`: timeline curve
|
|
238
|
+
- `interpolation`: timeline curve
|
|
239
|
+
|
|
240
|
+
## Timeline Curve
|
|
241
|
+
|
|
242
|
+
- `{ type: "graph", points: [number, number][] }`
|
|
243
|
+
- `{ type: "cubicBezier", x1: number, y1: number, x2: number, y2: number }`
|
|
244
|
+
- `{ type: "hold" }`
|
|
245
|
+
|
|
246
|
+
## Timeline Values
|
|
247
|
+
|
|
248
|
+
- number
|
|
249
|
+
- string
|
|
250
|
+
- `[number, number]`
|
|
251
|
+
- number[]
|
|
252
|
+
- string[]
|
|
253
|
+
- JSON object
|
|
254
|
+
|
|
255
|
+
## Animatable Common Tracks
|
|
256
|
+
|
|
257
|
+
- `position`: path, point, text, image, group
|
|
258
|
+
- `x`: path, point, text, image, group
|
|
259
|
+
- `y`: path, point, text, image, group
|
|
260
|
+
- `opacity`: path, point, text, image, group
|
|
261
|
+
- `rotation`: path, text, image, group
|
|
262
|
+
- `scale`: path, text, image, group
|
|
263
|
+
- `scaleX`: path, text, image, group
|
|
264
|
+
- `scaleY`: path, text, image, group
|
|
265
|
+
- `origin`: path, text, image, group
|
|
266
|
+
- `blendMode`: path, text, image, group
|
|
267
|
+
- `clip.d`: path, text, image, group
|
|
268
|
+
- `mask.d`: path, text, image, group
|
|
269
|
+
- `mask.opacity`: path, text, image, group
|
|
270
|
+
|
|
271
|
+
## Animatable Path Tracks
|
|
272
|
+
|
|
273
|
+
- `d`
|
|
274
|
+
- `fill`
|
|
275
|
+
- `stroke`
|
|
276
|
+
- `strokeWidth`
|
|
277
|
+
- `strokeCap`
|
|
278
|
+
- `strokeJoin`
|
|
279
|
+
- `miterLimit`
|
|
280
|
+
- `dashArray`
|
|
281
|
+
- `dashOffset`
|
|
282
|
+
- `drawStart`
|
|
283
|
+
- `drawEnd`
|
|
284
|
+
|
|
285
|
+
## Animatable Text Tracks
|
|
286
|
+
|
|
287
|
+
- `text`
|
|
288
|
+
- `lines`
|
|
289
|
+
- `align`
|
|
290
|
+
- `valign`
|
|
291
|
+
- `fontFamily`
|
|
292
|
+
- `fontStyle`
|
|
293
|
+
- `fontSize`
|
|
294
|
+
- `lineHeight`
|
|
295
|
+
- `letterSpacing`
|
|
296
|
+
- `maxWidth`
|
|
297
|
+
- `weight`
|
|
298
|
+
- `fill`
|
|
299
|
+
|
|
300
|
+
## Animatable Image Tracks
|
|
301
|
+
|
|
302
|
+
- `width`
|
|
303
|
+
- `height`
|
|
304
|
+
- `src`
|
|
305
|
+
- `fit`
|
|
306
|
+
- `source.x`
|
|
307
|
+
- `source.y`
|
|
308
|
+
- `source.width`
|
|
309
|
+
- `source.height`
|
|
310
|
+
|
|
311
|
+
## Animatable Group Tracks
|
|
312
|
+
|
|
313
|
+
- `width`
|
|
314
|
+
- `height`
|
|
315
|
+
|
|
316
|
+
## Animatable Effects Tracks
|
|
317
|
+
|
|
318
|
+
- `effects.blur`
|
|
319
|
+
- `effects.brightness`
|
|
320
|
+
- `effects.contrast`
|
|
321
|
+
- `effects.saturate`
|
|
322
|
+
- `effects.hueRotate`
|
|
323
|
+
- `effects.shadow.dx`
|
|
324
|
+
- `effects.shadow.dy`
|
|
325
|
+
- `effects.shadow.blur`
|
|
326
|
+
- `effects.shadow.color`
|
|
327
|
+
- `effects.shadow.opacity`
|
|
328
|
+
|
|
329
|
+
## Animatable Paint Tracks
|
|
330
|
+
|
|
331
|
+
- `fill.x`
|
|
332
|
+
- `fill.y`
|
|
333
|
+
- `fill.width`
|
|
334
|
+
- `fill.height`
|
|
335
|
+
- `fill.opacity`
|
|
336
|
+
- `fill.from`
|
|
337
|
+
- `fill.to`
|
|
338
|
+
- `fill.center`
|
|
339
|
+
- `fill.focus`
|
|
340
|
+
- `fill.radius`
|
|
341
|
+
- `fill.stops.N.offset`
|
|
342
|
+
- `fill.stops.N.color`
|
|
343
|
+
- `stroke.x`
|
|
344
|
+
- `stroke.y`
|
|
345
|
+
- `stroke.width`
|
|
346
|
+
- `stroke.height`
|
|
347
|
+
- `stroke.opacity`
|
|
348
|
+
- `stroke.from`
|
|
349
|
+
- `stroke.to`
|
|
350
|
+
- `stroke.center`
|
|
351
|
+
- `stroke.focus`
|
|
352
|
+
- `stroke.radius`
|
|
353
|
+
- `stroke.stops.N.offset`
|
|
354
|
+
- `stroke.stops.N.color`
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Render, Edit, Preview
|
|
2
|
+
|
|
3
|
+
Use this for validating, rendering, editing, previewing, exporting, and debugging Sketchmark files.
|
|
4
|
+
If the goal is a reusable preview another UI or AI agent can mount directly, prefer a self-contained embed HTML over a one-off SVG frame.
|
|
5
|
+
Package-facing docs may use generic placeholder paths, but inside this repository the default authoring workflow is generator-first: create or update `examples/make-*.cjs` or `.js`, then regenerate the derived `.visual.json` or `.embed.html` artifacts.
|
|
6
|
+
Do not default to hand-writing only a generated `.visual.json` file for a new repo example unless the user explicitly asks for raw JSON only.
|
|
7
|
+
|
|
8
|
+
## Files
|
|
9
|
+
|
|
10
|
+
- Repo convention for examples: `examples/make-name.cjs` -> `examples/name.visual.json`
|
|
11
|
+
- Repo convention for embed demos: `examples/make-name.cjs` -> `examples/name.embed.html` and optional `examples/name.host.html`
|
|
12
|
+
- Kernel document: `path/to/document.visual.json`
|
|
13
|
+
- Optional generator script: `path/to/generator.cjs`
|
|
14
|
+
- Optional standalone embed output: `path/to/preview.embed.html`
|
|
15
|
+
- Optional host iframe page: `path/to/preview.host.html`
|
|
16
|
+
- Schema: `schema/visual.schema.json`
|
|
17
|
+
- CLI: `bin/sketchmark.cjs`
|
|
18
|
+
- Package API: `sketchmark`, `sketchmark/render`, `sketchmark/edit`
|
|
19
|
+
- UI API: `sketchmark/editor`, `sketchmark/preview`
|
|
20
|
+
- Browser export entrypoint: `sketchmark/browser-export`
|
|
21
|
+
|
|
22
|
+
## Generate
|
|
23
|
+
|
|
24
|
+
If the project uses a generator script, build first when needed and run that script:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run build
|
|
28
|
+
node path/to/generator.cjs
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Inside this repo, prefer this pattern for new examples:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run build
|
|
35
|
+
node examples/make-name.cjs
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That generator should usually be the source of truth, with `.visual.json`, `.embed.html`, or `.host.html` checked in as generated artifacts when the repo already follows that pattern.
|
|
39
|
+
|
|
40
|
+
## Validate
|
|
41
|
+
|
|
42
|
+
In code:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const { validateVisualDocument } = require("sketchmark");
|
|
46
|
+
const result = validateVisualDocument(doc);
|
|
47
|
+
if (!result.ok) throw new Error(result.issues.map((i) => `${i.path}: ${i.message}`).join("\n"));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
With CLI when available:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node bin/sketchmark.cjs validate path/to/document.visual.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Render
|
|
57
|
+
|
|
58
|
+
Render a frame:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node bin/sketchmark.cjs render path/to/document.visual.json --time 1 --out .tmp/preview.svg
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Preview/export commands may vary by package version. Check:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
node bin/sketchmark.cjs --help
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Create a reusable embed in code:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
const { render } = require("sketchmark");
|
|
74
|
+
|
|
75
|
+
const html = render.embedHtml(doc, {
|
|
76
|
+
title: "Agent Preview",
|
|
77
|
+
maxFrames: 180
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Render or edit from a standalone Node project with the public package API:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const { render, edit } = require("sketchmark");
|
|
85
|
+
|
|
86
|
+
const svg = render.svg(doc, { time: 1 });
|
|
87
|
+
const nextDoc = edit.setProperty(doc, "element-id", "x", 120);
|
|
88
|
+
const keyedDoc = edit.setKeyframe(nextDoc, "element-id", "opacity", 0.5, 1);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For explicit subpath imports:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const render = require("sketchmark/render");
|
|
95
|
+
const edit = require("sketchmark/edit");
|
|
96
|
+
const { editorHtml } = require("sketchmark/editor");
|
|
97
|
+
const { previewHtml } = require("sketchmark/preview");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Create the same output from a browser host:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import { exportVisualInBrowser } from "sketchmark/browser-export";
|
|
104
|
+
|
|
105
|
+
await exportVisualInBrowser(doc, {
|
|
106
|
+
format: "embed",
|
|
107
|
+
title: "Agent Preview",
|
|
108
|
+
sourceDocument: doc
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Edit
|
|
113
|
+
|
|
114
|
+
For small edits:
|
|
115
|
+
|
|
116
|
+
1. Find element by `id`.
|
|
117
|
+
2. Patch only the needed properties.
|
|
118
|
+
3. Preserve unrelated timelines.
|
|
119
|
+
4. Validate.
|
|
120
|
+
5. Render the changed frame.
|
|
121
|
+
|
|
122
|
+
For large edits:
|
|
123
|
+
|
|
124
|
+
1. Update the generator.
|
|
125
|
+
2. Regenerate the `.visual.json`.
|
|
126
|
+
3. Validate.
|
|
127
|
+
4. Render preview frames.
|
|
128
|
+
|
|
129
|
+
For new repo examples:
|
|
130
|
+
|
|
131
|
+
1. Add `examples/make-name.cjs` or update an existing generator.
|
|
132
|
+
2. Generate the derived `.visual.json` and any embed demo artifacts.
|
|
133
|
+
3. Validate and spot-check the rendered result.
|
|
134
|
+
|
|
135
|
+
## Preview
|
|
136
|
+
|
|
137
|
+
- SVG frame preview checks geometry, text, and paint.
|
|
138
|
+
- HTML preview checks animation playback when available.
|
|
139
|
+
- Embed preview is the preferred agent-friendly output: one HTML string, no server route, play/pause UI, scrubber, and export buttons.
|
|
140
|
+
- For host-agnostic embeds, keep the HTML self-contained: inline scripts, inline styles, and no CDN or runtime network dependency.
|
|
141
|
+
- Avoid module-script or blob/object-URL imports inside the embed. Some hosts rewrite or block blob URLs.
|
|
142
|
+
- Avoid relying on `URL.createObjectURL()` for runtime-loaded assets like SVG frame rasterization. Prefer `data:` URLs or other inline forms.
|
|
143
|
+
- Assume the embed may run inside `iframe`, `srcdoc`, webview, sandboxed previews, or artifact hosts that intercept downloads and messaging.
|
|
144
|
+
- Keep host integration generic: optional `postMessage` events are fine, but the embed should not require Claude-specific globals or a custom parent API to render.
|
|
145
|
+
- Embed chrome defaults to a transparent outer background with light/dark-aware translucent controls, so it sits more naturally inside host UIs.
|
|
146
|
+
- Current embed exports are `svg`, `png`, `jpg`, `html`, `json`, and `mp4`.
|
|
147
|
+
- Mount embed HTML in an `iframe` with `srcdoc` or the equivalent host API.
|
|
148
|
+
- Use a generated `.embed.html` file and a host page with an `iframe` as the reference pair when you need parent-child messaging.
|
|
149
|
+
- Parent pages can send `sketchmark-play`, `sketchmark-pause`, `sketchmark-show`, and `sketchmark-export` with `postMessage`.
|
|
150
|
+
- The embed posts back `sketchmark-rendered` with `title`, `duration`, and `time`.
|
|
151
|
+
- Use `maxFrames` to trade file size for smoother motion.
|
|
152
|
+
- MP4 export inside the embed requires WebCodecs. Chrome and Edge are the safest targets.
|
|
153
|
+
- Editor preview checks property editing and timeline changes.
|
|
154
|
+
- Browser export is preferred for web deployments that cannot rely on server `ffmpeg` or `sharp`.
|
|
155
|
+
|
|
156
|
+
## Debug
|
|
157
|
+
|
|
158
|
+
- Validate before rendering.
|
|
159
|
+
- Lower `maxFrames` if the embed HTML gets too large.
|
|
160
|
+
- Browser raster or MP4 export can fail on unsupported browsers or when external images block canvas export.
|
|
161
|
+
- Unknown timeline tracks are invalid.
|
|
162
|
+
- Timeline curve fields must be objects.
|
|
163
|
+
- `cornerRadius` is not kernel; use `clip.d`.
|
|
164
|
+
- Text alignment uses `align` and `valign`.
|
|
165
|
+
- Image crop uses `source.*`.
|
|
166
|
+
- Render the exact failing time.
|