@svgsketch/core 0.2.0 → 0.5.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 CHANGED
@@ -1,31 +1,203 @@
1
- # @svgsketch/core
2
-
3
- Core SDK and document engine for SVGSketch.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install @svgsketch/core
9
- ```
10
-
11
- ## Usage
12
-
13
- ```ts
14
- import { Document, Circle } from '@svgsketch/core';
15
-
16
- const doc = new Document({ width: 400, height: 300 }).add(
17
- new Circle(200, 150, 80).fill('#2563eb')
18
- );
19
-
20
- const svg = doc.toSVG();
21
- const svgsJson = doc.toJSON();
22
- ```
23
-
24
- ## What It Includes
25
-
26
- - `types`: `.svgs` schema and shared type definitions
27
- - `format`: parse, stringify, migrate, validate, template helpers
28
- - `renderer`: headless SVG rendering
29
- - `sdk`: fluent `Document`, shape, style, and animation builders
30
- - `codegen`: SVG, React, Vue, D3, and CSS+SVG output generators
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.