@svgsketch/core 0.2.0 → 0.4.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 +203 -31
- package/dist/index.d.mts +518 -22
- package/dist/index.d.ts +518 -22
- package/dist/index.js +11 -11
- package/dist/index.mjs +11 -11
- package/package.json +48 -43
package/README.md
CHANGED
|
@@ -1,31 +1,203 @@
|
|
|
1
|
-
# @svgsketch/core
|
|
2
|
-
|
|
3
|
-
Core SDK and document engine for SVGSketch.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
# @svgsketch/core
|
|
2
|
+
|
|
3
|
+
Core SDK, renderer, and document engine for SVGSketch. This package
|
|
4
|
+
defines the `.svgs` file format and provides everything needed to build,
|
|
5
|
+
serialize, load, validate, render, and migrate SVGSketch documents
|
|
6
|
+
programmatically — no browser or editor required.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @svgsketch/core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Document, Circle, Rectangle } from '@svgsketch/core';
|
|
18
|
+
|
|
19
|
+
const doc = new Document({ width: 400, height: 300 })
|
|
20
|
+
.title('Hello')
|
|
21
|
+
.add(new Circle(200, 150, 80).fill('#2563eb'))
|
|
22
|
+
.add(new Rectangle(10, 10, 100, 60).fill('#ef4444').cornerRadius(8));
|
|
23
|
+
|
|
24
|
+
const svg = doc.toSVG(); // rendered SVG string
|
|
25
|
+
const svgs = doc.toJSON(); // canonical `.svgs` document
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What it includes
|
|
29
|
+
|
|
30
|
+
| Module | Exports |
|
|
31
|
+
| ----------- | --------------------------------------------------------------- |
|
|
32
|
+
| `types` | `HistorySnapshot`, `SerializedShape`, `SerializedSymbolDef`, … |
|
|
33
|
+
| `format` | `parseDocument`, `stringifyDocument`, `migrateSnapshot`, `validateSnapshot`, `substituteVariables` |
|
|
34
|
+
| `renderer` | `renderToSvg` |
|
|
35
|
+
| `sdk` | `Document`, `Circle`, `Rectangle`, …, `Timeline`, `Track` |
|
|
36
|
+
| `codegen` | `generateCode` — SVG / React / Vue / D3 / CSS output |
|
|
37
|
+
|
|
38
|
+
## The `.svgs` format
|
|
39
|
+
|
|
40
|
+
A `.svgs` file is a **deterministic JSON serialization of a
|
|
41
|
+
`HistorySnapshot`** — the complete state of an SVGSketch document. It is
|
|
42
|
+
designed for:
|
|
43
|
+
|
|
44
|
+
- **Git-friendly diffs**: canonical key ordering, one property per line.
|
|
45
|
+
- **Round-trip fidelity**: every editor-persisted field survives save →
|
|
46
|
+
load → render without loss.
|
|
47
|
+
- **External tooling**: the schema is fully typed in `@svgsketch/core`
|
|
48
|
+
and can be produced or consumed without running the editor.
|
|
49
|
+
|
|
50
|
+
### Top-level structure
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
{
|
|
54
|
+
"schemaVersion": 1,
|
|
55
|
+
"documentMetadata": { /* title, author, license, … */ },
|
|
56
|
+
"templateVariables": [ /* { name, type, defaultValue, … } */ ],
|
|
57
|
+
"viewboxes": [ /* { id, x, y, width, height } */ ],
|
|
58
|
+
"guides": [ /* { id, orientation, position, … } */ ],
|
|
59
|
+
"measurements": [ /* dimensioning annotations */ ],
|
|
60
|
+
"groups": [ /* { id, parentId, siblingIndex, … } */ ],
|
|
61
|
+
"clipMaskGroups": [ /* <clipPath> / <mask> definitions */ ],
|
|
62
|
+
"customPatterns": [ /* user-defined fill patterns */ ],
|
|
63
|
+
"symbols": [ /* reusable component definitions */ ],
|
|
64
|
+
"animationTimeline": { /* tracks + keyframes */ },
|
|
65
|
+
"shapes": [ /* array — order IS z-order */ ]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See [`src/types/serialized.ts`](src/types/serialized.ts) for the
|
|
70
|
+
exhaustive schema.
|
|
71
|
+
|
|
72
|
+
### Ordering rules
|
|
73
|
+
|
|
74
|
+
- **`shapes` preserves array order** — the position of a shape in the
|
|
75
|
+
array *is* its painter's-model z-order. Sorting would silently reorder
|
|
76
|
+
rendering and destroy user intent.
|
|
77
|
+
- **Id-keyed collections** (`groups`, `viewboxes`, `guides`,
|
|
78
|
+
`clipMaskGroups`, `customPatterns`, `symbols`) are sorted by `id`.
|
|
79
|
+
Their order has no semantic meaning — groups use `parentId` +
|
|
80
|
+
`siblingIndex` to record position.
|
|
81
|
+
- **Object keys** within each shape are sorted alphabetically.
|
|
82
|
+
- **Indentation** is 2 spaces, one property per line, with a trailing
|
|
83
|
+
newline by default.
|
|
84
|
+
|
|
85
|
+
These rules combine to give identical documents a byte-identical
|
|
86
|
+
serialization, so diffing `.svgs` files in git produces meaningful
|
|
87
|
+
line-level changes rather than noisy reorderings.
|
|
88
|
+
|
|
89
|
+
### Schema versioning
|
|
90
|
+
|
|
91
|
+
`schemaVersion` is written on save and checked on load. The constant
|
|
92
|
+
`CURRENT_SCHEMA_VERSION` in `@svgsketch/core` is the latest version. A
|
|
93
|
+
document missing `schemaVersion` is treated as version 1.
|
|
94
|
+
|
|
95
|
+
On load, `parseDocument` calls `migrateSnapshot` which walks the
|
|
96
|
+
`MIGRATIONS` registry — each entry at key N transforms a version-N
|
|
97
|
+
snapshot into version-(N+1). Unknown older versions (no migration
|
|
98
|
+
registered) throw. Newer versions (from-the-future documents) pass
|
|
99
|
+
through unchanged; loaders should refuse them at a higher layer.
|
|
100
|
+
|
|
101
|
+
**Compatibility rules for schema changes:**
|
|
102
|
+
|
|
103
|
+
1. Never rename an existing serialized property. Add a new one alongside
|
|
104
|
+
and read both with a fallback.
|
|
105
|
+
2. Never change a property's type. Add a new property instead.
|
|
106
|
+
3. Never make a previously optional field required without a migration.
|
|
107
|
+
4. Never remove a shape type — old documents referencing it would
|
|
108
|
+
silently lose shapes.
|
|
109
|
+
5. When adding new optional properties, always handle their absence on
|
|
110
|
+
the read side with `??` or `if`-guards.
|
|
111
|
+
|
|
112
|
+
### Template variables & bindings
|
|
113
|
+
|
|
114
|
+
Two complementary mechanisms for parameterizing documents:
|
|
115
|
+
|
|
116
|
+
- **`{{variable}}` references** — string placeholders that are
|
|
117
|
+
substituted at render time. Valid in any string property, and in
|
|
118
|
+
numeric fields (e.g. `radius: "{{r}}"`) where they resolve to numbers.
|
|
119
|
+
Defaults live in `templateVariables`; overrides can be passed to
|
|
120
|
+
`substituteVariables(snapshot, { r: '50' })` or to
|
|
121
|
+
`doc.toSVG({ variables: { r: '50' } })`.
|
|
122
|
+
|
|
123
|
+
- **`state.bindings`** — a map from a geometry property name to a CSS
|
|
124
|
+
custom-property name (without the leading `--`). When a property is
|
|
125
|
+
bound, `state[property]` holds the *resolved* value (what the renderer
|
|
126
|
+
uses if no substitution runs), and `bindings` records the variable the
|
|
127
|
+
editor should re-bind to on the next edit. Example:
|
|
128
|
+
|
|
129
|
+
```jsonc
|
|
130
|
+
{
|
|
131
|
+
"id": "c1",
|
|
132
|
+
"type": "circle",
|
|
133
|
+
"state": {
|
|
134
|
+
"radius": 50,
|
|
135
|
+
"bindings": { "radius": "card-radius" }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Round-trip fidelity
|
|
141
|
+
|
|
142
|
+
The format guarantees that for any valid snapshot `s`:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
stringifyDocument(parseDocument(stringifyDocument(s))) === stringifyDocument(s);
|
|
146
|
+
renderToSvg(parseDocument(stringifyDocument(s))) === renderToSvg(s);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The `tests/format/roundtrip.test.ts` integration suite enforces this
|
|
150
|
+
across a document that touches shapes, groups, symbols, template
|
|
151
|
+
variables, bindings, and animation tracks. When the format grows a new
|
|
152
|
+
field, that test must still pass — if it regresses, a field is being
|
|
153
|
+
dropped somewhere in the pipeline.
|
|
154
|
+
|
|
155
|
+
### Validation
|
|
156
|
+
|
|
157
|
+
`validateSnapshot(snapshot)` returns `{ valid, errors, warnings }` with
|
|
158
|
+
dot-paths (e.g. `shapes[2].state.width`) for each problem. Validation
|
|
159
|
+
runs automatically on `parseDocument` unless `{ validate: false }` is
|
|
160
|
+
passed. Rules include:
|
|
161
|
+
|
|
162
|
+
- Shape IDs are unique within a document.
|
|
163
|
+
- `type` is a known shape type (`circle`, `rectangle`, `spline`, …) or
|
|
164
|
+
a plugin-registered custom type.
|
|
165
|
+
- Numeric geometry fields (`x`, `y`, `width`, `height`, `radius`, …)
|
|
166
|
+
are finite numbers — or `{{variable}}` references, which resolve to
|
|
167
|
+
numbers at render time.
|
|
168
|
+
- Viewbox dimensions are positive.
|
|
169
|
+
- Group references point to existing shapes.
|
|
170
|
+
|
|
171
|
+
Errors block loading; warnings are surfaced but do not.
|
|
172
|
+
|
|
173
|
+
## Minimal example
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import {
|
|
177
|
+
Document,
|
|
178
|
+
Circle,
|
|
179
|
+
parseDocument,
|
|
180
|
+
substituteVariables,
|
|
181
|
+
renderToSvg,
|
|
182
|
+
} from '@svgsketch/core';
|
|
183
|
+
|
|
184
|
+
// Build
|
|
185
|
+
const doc = new Document({ width: 200, height: 200 })
|
|
186
|
+
.add(new Circle(100, 100, 75).fill('{{accent}}').id('c1'))
|
|
187
|
+
.defineVariable('accent', 'color', '#3498db');
|
|
188
|
+
|
|
189
|
+
// Serialize
|
|
190
|
+
const svgs = doc.toJSON();
|
|
191
|
+
|
|
192
|
+
// Deserialize, substitute, render
|
|
193
|
+
const snapshot = parseDocument(svgs);
|
|
194
|
+
const resolved = substituteVariables(snapshot, { accent: '#e74c3c' });
|
|
195
|
+
const svg = renderToSvg(resolved);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## CLI
|
|
199
|
+
|
|
200
|
+
The companion `@svgsketch/cli` package exposes `svgsketch validate`,
|
|
201
|
+
`svgsketch render`, `svgsketch info`, `svgsketch vars`, and
|
|
202
|
+
`svgsketch codegen` commands that operate on `.svgs` files headlessly —
|
|
203
|
+
suitable for CI/CD pipelines.
|