feat: introduce local-first SVG workbench

This commit is contained in:
2026-08-02 16:31:49 +02:00
commit 39d9802daa
97 changed files with 20702 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
# Document model
## Source ranges
The lexical scan records exact half-open offsets for each opening/full element,
name and quoted attribute. CodeMirror's Lezer XML parser supplies ranged syntax
errors; a separate stack scan rejects structures browser DOM parsers might
repair. The DOM parse supplies namespace and decoded semantic values. Element
order must match the lexical tokens or parsing fails with a source-map mismatch.
DOCTYPE text remains in canonical source but is removed before DOM parsing.
Entity declarations are rejected for the semantic model. External entities are
never resolved.
## Stable keys
A unique explicit SVG `id` becomes `id:<id>`. Otherwise a key hashes structural
path, source offset, local name and optional ID; collision suffixes are
in-memory. Reparsing retains explicit-ID identity and best-effort structural
identity. Keys appear only as temporary `data-svg-tools-node` attributes in the
editing projection and are stripped from exports.
## Semantic nodes
Nodes retain namespace/name, parent and children, depth, ID/classes, decoded
attributes, exact attribute ranges, render status and leaf text. The document
also records indentation/newline/quote preference, ordered keys, diagnostics
and bounded metrics.
Unknown well-formed source remains canonical even when there is no inspector.
A targeted command patches only its attribute/tag range. Whole-source
serialization occurs only for explicit format, sanitize or optimize workflows,
each with a source diff.
## Invalid state
Every source update gets a monotonically increasing revision. If current source
is invalid, diagnostics refer to that text while the canvas/tree use the last
valid snapshot and all visual controls are disabled. Once the same current
revision parses and sanitizes successfully it atomically replaces the snapshot.
Resource limits can make otherwise well-formed source invalid for this app.
Limits cover bytes, structure, path/CSS/reference/animation/filter counts,
embedded data and total text. This prevents unbounded model construction but is
not a promise that every file below a limit is inexpensive on every device.