@rasamjs/core 0.1.2 → 0.3.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.
Files changed (240) hide show
  1. package/API.md +29 -9
  2. package/CHANGELOG.md +98 -1
  3. package/COMPONENTS.md +229 -0
  4. package/README.md +178 -25
  5. package/RELEASING.md +31 -21
  6. package/dist/advanced.d.ts +1 -1
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/catalog/fragment.d.ts +2 -2
  9. package/dist/catalog/fragment.d.ts.map +1 -1
  10. package/dist/catalog/fragment.js.map +1 -1
  11. package/dist/changes/derived.d.ts +10 -0
  12. package/dist/changes/derived.d.ts.map +1 -0
  13. package/dist/changes/derived.js +14 -0
  14. package/dist/changes/derived.js.map +1 -0
  15. package/dist/changes/restore.d.ts.map +1 -1
  16. package/dist/changes/restore.js +22 -4
  17. package/dist/changes/restore.js.map +1 -1
  18. package/dist/changes/service.d.ts +10 -2
  19. package/dist/changes/service.d.ts.map +1 -1
  20. package/dist/changes/service.js +45 -6
  21. package/dist/changes/service.js.map +1 -1
  22. package/dist/changes/types.d.ts +9 -0
  23. package/dist/changes/types.d.ts.map +1 -1
  24. package/dist/changes/types.js.map +1 -1
  25. package/dist/changes.d.ts +5 -3
  26. package/dist/changes.d.ts.map +1 -1
  27. package/dist/changes.js +103 -2
  28. package/dist/changes.js.map +1 -1
  29. package/dist/components/changes.d.ts +34 -0
  30. package/dist/components/changes.d.ts.map +1 -0
  31. package/dist/components/changes.js +119 -0
  32. package/dist/components/changes.js.map +1 -0
  33. package/dist/components/component.d.ts +62 -0
  34. package/dist/components/component.d.ts.map +1 -0
  35. package/dist/components/component.js +355 -0
  36. package/dist/components/component.js.map +1 -0
  37. package/dist/components/decorators.d.ts +7 -0
  38. package/dist/components/decorators.d.ts.map +1 -0
  39. package/dist/components/decorators.js +81 -0
  40. package/dist/components/decorators.js.map +1 -0
  41. package/dist/components/metadata.d.ts +10 -0
  42. package/dist/components/metadata.d.ts.map +1 -0
  43. package/dist/components/metadata.js +85 -0
  44. package/dist/components/metadata.js.map +1 -0
  45. package/dist/components/registry.d.ts +24 -0
  46. package/dist/components/registry.d.ts.map +1 -0
  47. package/dist/components/registry.js +78 -0
  48. package/dist/components/registry.js.map +1 -0
  49. package/dist/components/types.d.ts +54 -0
  50. package/dist/components/types.d.ts.map +1 -0
  51. package/dist/components/types.js +2 -0
  52. package/dist/components/types.js.map +1 -0
  53. package/dist/components/view.d.ts +9 -0
  54. package/dist/components/view.d.ts.map +1 -0
  55. package/dist/components/view.js +104 -0
  56. package/dist/components/view.js.map +1 -0
  57. package/dist/components.d.ts +6 -0
  58. package/dist/components.d.ts.map +1 -0
  59. package/dist/components.js +5 -0
  60. package/dist/components.js.map +1 -0
  61. package/dist/core/node.d.ts +7 -0
  62. package/dist/core/node.d.ts.map +1 -1
  63. package/dist/core/node.js +30 -0
  64. package/dist/core/node.js.map +1 -1
  65. package/dist/dom/images.d.ts.map +1 -1
  66. package/dist/dom/images.js +3 -1
  67. package/dist/dom/images.js.map +1 -1
  68. package/dist/elements/image.d.ts.map +1 -1
  69. package/dist/elements/image.js +4 -1
  70. package/dist/elements/image.js.map +1 -1
  71. package/dist/interchange/clone.d.ts.map +1 -1
  72. package/dist/interchange/clone.js +4 -3
  73. package/dist/interchange/clone.js.map +1 -1
  74. package/dist/interchange/dom.d.ts +3 -1
  75. package/dist/interchange/dom.d.ts.map +1 -1
  76. package/dist/interchange/dom.js +29 -5
  77. package/dist/interchange/dom.js.map +1 -1
  78. package/dist/interchange/markup.d.ts.map +1 -1
  79. package/dist/interchange/markup.js +71 -3
  80. package/dist/interchange/markup.js.map +1 -1
  81. package/dist/interchange/policy.d.ts.map +1 -1
  82. package/dist/interchange/policy.js +4 -0
  83. package/dist/interchange/policy.js.map +1 -1
  84. package/dist/interchange/reconstruct.d.ts.map +1 -1
  85. package/dist/interchange/reconstruct.js +16 -12
  86. package/dist/interchange/reconstruct.js.map +1 -1
  87. package/dist/interchange/references.d.ts.map +1 -1
  88. package/dist/interchange/references.js +10 -4
  89. package/dist/interchange/references.js.map +1 -1
  90. package/dist/interchange/types.d.ts +10 -1
  91. package/dist/interchange/types.d.ts.map +1 -1
  92. package/dist/mixins/parent-internals.d.ts +6 -0
  93. package/dist/mixins/parent-internals.d.ts.map +1 -1
  94. package/dist/mixins/parent-internals.js +34 -0
  95. package/dist/mixins/parent-internals.js.map +1 -1
  96. package/dist/mixins/parent.d.ts.map +1 -1
  97. package/dist/mixins/parent.js +6 -1
  98. package/dist/mixins/parent.js.map +1 -1
  99. package/dist/mixins/transformable.d.ts.map +1 -1
  100. package/dist/mixins/transformable.js +25 -27
  101. package/dist/mixins/transformable.js.map +1 -1
  102. package/dist/mixins/types.d.ts +2 -2
  103. package/dist/mixins/types.d.ts.map +1 -1
  104. package/dist/raster/frame.d.ts +12 -0
  105. package/dist/raster/frame.d.ts.map +1 -0
  106. package/dist/raster/frame.js +43 -0
  107. package/dist/raster/frame.js.map +1 -0
  108. package/dist/raster/images.d.ts +2 -0
  109. package/dist/raster/images.d.ts.map +1 -0
  110. package/dist/raster/images.js +60 -0
  111. package/dist/raster/images.js.map +1 -0
  112. package/dist/raster/render.d.ts +3 -0
  113. package/dist/raster/render.d.ts.map +1 -0
  114. package/dist/raster/render.js +40 -0
  115. package/dist/raster/render.js.map +1 -0
  116. package/dist/raster/types.d.ts +10 -0
  117. package/dist/raster/types.d.ts.map +1 -0
  118. package/dist/raster/types.js +2 -0
  119. package/dist/raster/types.js.map +1 -0
  120. package/dist/raster.d.ts +6 -0
  121. package/dist/raster.d.ts.map +1 -0
  122. package/dist/raster.js +10 -0
  123. package/dist/raster.js.map +1 -0
  124. package/dist/resources/references.d.ts +2 -0
  125. package/dist/resources/references.d.ts.map +1 -1
  126. package/dist/resources/references.js +3 -1
  127. package/dist/resources/references.js.map +1 -1
  128. package/dist/services/catalog.d.ts.map +1 -1
  129. package/dist/services/catalog.js +5 -3
  130. package/dist/services/catalog.js.map +1 -1
  131. package/dist/services/defs-manager.d.ts +2 -0
  132. package/dist/services/defs-manager.d.ts.map +1 -1
  133. package/dist/services/defs-manager.js +4 -0
  134. package/dist/services/defs-manager.js.map +1 -1
  135. package/dist/services/element-key.d.ts +4 -0
  136. package/dist/services/element-key.d.ts.map +1 -0
  137. package/dist/services/element-key.js +12 -0
  138. package/dist/services/element-key.js.map +1 -0
  139. package/dist/services/initialization.d.ts +7 -0
  140. package/dist/services/initialization.d.ts.map +1 -0
  141. package/dist/services/initialization.js +27 -0
  142. package/dist/services/initialization.js.map +1 -0
  143. package/dist/services/registry.d.ts +8 -0
  144. package/dist/services/registry.d.ts.map +1 -1
  145. package/dist/services/registry.js +45 -10
  146. package/dist/services/registry.js.map +1 -1
  147. package/dist/services/types.d.ts +17 -0
  148. package/dist/services/types.d.ts.map +1 -1
  149. package/dist/services/viewport.d.ts +2 -0
  150. package/dist/services/viewport.d.ts.map +1 -1
  151. package/dist/services/viewport.js +4 -3
  152. package/dist/services/viewport.js.map +1 -1
  153. package/dist/template/parts.d.ts +12 -6
  154. package/dist/template/parts.d.ts.map +1 -1
  155. package/dist/template/parts.js +36 -29
  156. package/dist/template/parts.js.map +1 -1
  157. package/dist/template/render.d.ts +6 -7
  158. package/dist/template/render.d.ts.map +1 -1
  159. package/dist/template/render.js +188 -44
  160. package/dist/template/render.js.map +1 -1
  161. package/dist/template/svg-tag.d.ts +5 -1
  162. package/dist/template/svg-tag.d.ts.map +1 -1
  163. package/dist/template/svg-tag.js.map +1 -1
  164. package/dist/text/content.d.ts +0 -2
  165. package/dist/text/content.d.ts.map +1 -1
  166. package/dist/text/content.js +4 -10
  167. package/dist/text/content.js.map +1 -1
  168. package/dist/text/internals.d.ts +1 -0
  169. package/dist/text/internals.d.ts.map +1 -1
  170. package/dist/text/policies.d.ts +7 -0
  171. package/dist/text/policies.d.ts.map +1 -0
  172. package/dist/text/policies.js +13 -0
  173. package/dist/text/policies.js.map +1 -0
  174. package/dist/types/common.d.ts +27 -0
  175. package/dist/types/common.d.ts.map +1 -1
  176. package/package.json +156 -143
  177. package/reports/adoption.json +20 -20
  178. package/reports/animation-benchmark.json +616 -616
  179. package/reports/benchmark.json +65 -65
  180. package/reports/components-benchmark-before.json +10 -0
  181. package/reports/components-benchmark.json +10 -0
  182. package/reports/creation.json +26 -26
  183. package/reports/interchange-benchmark.json +85 -85
  184. package/reports/public-api-contract.json +353 -23
  185. package/reports/public-api-review.md +23 -0
  186. package/reports/size-components.json +102 -0
  187. package/reports/size-parity.json +82 -34
  188. package/reports/size.json +6 -4
  189. package/reports/svgjs-comparison.json +3466 -3466
  190. package/reports/svgjs-comparison.md +19 -19
  191. package/reports/svgjs-parity-current.json +177 -60
  192. package/src/advanced.ts +1 -1
  193. package/src/catalog/fragment.ts +2 -2
  194. package/src/changes/derived.ts +19 -0
  195. package/src/changes/restore.ts +27 -4
  196. package/src/changes/service.ts +60 -11
  197. package/src/changes/types.ts +9 -0
  198. package/src/changes.ts +93 -4
  199. package/src/components/changes.ts +131 -0
  200. package/src/components/component.ts +401 -0
  201. package/src/components/decorators.ts +104 -0
  202. package/src/components/metadata.ts +111 -0
  203. package/src/components/registry.ts +79 -0
  204. package/src/components/types.ts +66 -0
  205. package/src/components/view.ts +123 -0
  206. package/src/components.ts +15 -0
  207. package/src/core/node.ts +32 -1
  208. package/src/dom/images.ts +2 -1
  209. package/src/elements/image.ts +4 -1
  210. package/src/interchange/clone.ts +4 -3
  211. package/src/interchange/dom.ts +34 -5
  212. package/src/interchange/markup.ts +77 -3
  213. package/src/interchange/policy.ts +6 -0
  214. package/src/interchange/reconstruct.ts +16 -12
  215. package/src/interchange/references.ts +12 -4
  216. package/src/interchange/types.ts +10 -1
  217. package/src/mixins/parent-internals.ts +37 -0
  218. package/src/mixins/parent.ts +13 -4
  219. package/src/mixins/transformable.ts +23 -25
  220. package/src/mixins/types.ts +2 -2
  221. package/src/raster/frame.ts +53 -0
  222. package/src/raster/images.ts +73 -0
  223. package/src/raster/render.ts +45 -0
  224. package/src/raster/types.ts +9 -0
  225. package/src/raster.ts +13 -0
  226. package/src/resources/references.ts +5 -1
  227. package/src/services/catalog.ts +5 -3
  228. package/src/services/defs-manager.ts +5 -0
  229. package/src/services/element-key.ts +14 -0
  230. package/src/services/initialization.ts +25 -0
  231. package/src/services/registry.ts +47 -10
  232. package/src/services/types.ts +21 -0
  233. package/src/services/viewport.ts +5 -4
  234. package/src/template/parts.ts +54 -39
  235. package/src/template/render.ts +211 -54
  236. package/src/template/svg-tag.ts +6 -1
  237. package/src/text/content.ts +4 -14
  238. package/src/text/internals.ts +1 -0
  239. package/src/text/policies.ts +18 -0
  240. package/src/types/common.ts +29 -0
package/API.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Public entry points
2
2
 
3
3
  Generated from emitted declarations and document-free runtime imports.
4
- Run `npm run api:check` to detect changes; review differences before `api:update`.
4
+ Run `bun run api:check` to detect changes; review differences before `api:update`.
5
5
 
6
6
  Only documented operations are supported application APIs. Deep imports, mutable
7
7
  service bookkeeping, TypeScript-private methods and undocumented symbol hooks remain internal,
@@ -14,9 +14,9 @@ Runtime exports (32):
14
14
 
15
15
  `BBox`, `Circle`, `ClipPath`, `Defs`, `Ellipse`, `Group`, `Line`, `LinearGradient`, `Marker`, `Matrix`, `Path`, `PathArray`, `PathBuilder`, `PointArray`, `Polygon`, `Polyline`, `RadialGradient`, `Rect`, `Renderer`, `SVG`, `Shape`, `Stop`, `Svg`, `SvgNode`, `SvgTemplate`, `SvgText`, `Use`, `Vec2`, `ViewBox`, `ViewportError`, `attr`, `svg`.
16
16
 
17
- Type-only exports (46):
17
+ Type-only exports (48):
18
18
 
19
- `AnyNode`, `AttributeValue`, `Bounds`, `Box`, `CircleOptions`, `ClipPathOptions`, `CommonAttributes`, `Disposable`, `DrawingPointerEvent`, `EllipseOptions`, `GeometryChange`, `GradientStop`, `Length`, `LineOptions`, `LinearGradientOptions`, `MarkerOptions`, `MatrixDecomposition`, `MatrixValues`, `NestedSvgOptions`, `NodeEvents`, `PaintValue`, `PathAttributes`, `PathCommand`, `PathInput`, `PathOptions`, `PathSegment`, `Point`, `PointInput`, `PointerEventType`, `PolygonOptions`, `PolylineAttributes`, `PolylineOptions`, `RadialGradientOptions`, `RectOptions`, `ResourceReference`, `SVGOptions`, `StyleProperties`, `SvgAttributes`, `SvgOptions`, `TemplateInstance`, `TemplateValue`, `TextAttributes`, `TextMeasurementProvider`, `TextOptions`, `TransformState`, `UseOptions`.
19
+ `AnyNode`, `AttributeValue`, `Bounds`, `Box`, `CircleOptions`, `ClipPathOptions`, `CommonAttributes`, `Disposable`, `DrawingPointerEvent`, `EditRules`, `EllipseOptions`, `GeometryChange`, `GradientStop`, `Length`, `LineOptions`, `LinearGradientOptions`, `MarkerOptions`, `MatrixDecomposition`, `MatrixValues`, `NestedSvgOptions`, `NodeEvents`, `PaintValue`, `PathAttributes`, `PathCommand`, `PathInput`, `PathOptions`, `PathSegment`, `Point`, `PointInput`, `PointerEventType`, `PolygonOptions`, `PolylineAttributes`, `PolylineOptions`, `PortDefinition`, `RadialGradientOptions`, `RectOptions`, `ResourceReference`, `SVGOptions`, `StyleProperties`, `SvgAttributes`, `SvgOptions`, `TemplateInstance`, `TemplateValue`, `TextAttributes`, `TextMeasurementProvider`, `TextOptions`, `TransformState`, `UseOptions`.
20
20
 
21
21
  ## @rasamjs/core/advanced
22
22
 
@@ -24,9 +24,9 @@ Runtime exports (57):
24
24
 
25
25
  `Attributes`, `BBox`, `BoundsService`, `Circle`, `ClipPath`, `Creatable`, `Defs`, `DefsManager`, `DomFactory`, `DrawingContext`, `Elements`, `Ellipse`, `Emitter`, `Gradient`, `Group`, `Guards`, `Line`, `LinearGradient`, `Marker`, `Matrix`, `Namespace`, `Parent`, `Path`, `PathArray`, `PathBuilder`, `PathGeometry`, `PointArray`, `PointerRouter`, `Polygon`, `Polyline`, `RadialGradient`, `Rect`, `Registry`, `Renderer`, `SVG`, `Scheduler`, `Shape`, `Sizable`, `Stop`, `Styleable`, `Svg`, `SvgNode`, `SvgTag`, `SvgTemplate`, `SvgText`, `Textual`, `Transformable`, `Use`, `Vec2`, `ViewBox`, `ViewportError`, `attr`, `isParent`, `isTextual`, `isTransformable`, `mix`, `svg`.
26
26
 
27
- Type-only exports (52):
27
+ Type-only exports (55):
28
28
 
29
- `AnyNode`, `AttributeValue`, `Bounds`, `Box`, `CircleOptions`, `ClipPathOptions`, `CommonAttributes`, `CreatableFeature`, `Ctor`, `Disposable`, `DrawingPointerEvent`, `ElementClass`, `EllipseOptions`, `GeometryChange`, `GradientStop`, `Length`, `LineOptions`, `LinearGradientOptions`, `MarkerOptions`, `MatrixDecomposition`, `MatrixValues`, `NestedSvgOptions`, `NodeEvents`, `PaintValue`, `ParentFeature`, `ParentNode`, `PathAttributes`, `PathCommand`, `PathInput`, `PathOptions`, `PathSegment`, `Point`, `PointInput`, `PointerEventType`, `PolygonOptions`, `PolylineAttributes`, `PolylineOptions`, `RadialGradientOptions`, `RectOptions`, `ResourceReference`, `SVGOptions`, `SizableFeature`, `StyleProperties`, `SvgAttributes`, `SvgOptions`, `TemplateInstance`, `TemplateValue`, `TextAttributes`, `TextMeasurementProvider`, `TextOptions`, `TransformState`, `UseOptions`.
29
+ `AnyNode`, `AttributeValue`, `Bounds`, `Box`, `CircleOptions`, `ClipPathOptions`, `CommonAttributes`, `CreatableFeature`, `Ctor`, `Disposable`, `DrawingPointerEvent`, `EditRules`, `ElementClass`, `ElementVariant`, `EllipseOptions`, `GeometryChange`, `GradientStop`, `Length`, `LineOptions`, `LinearGradientOptions`, `MarkerOptions`, `MatrixDecomposition`, `MatrixValues`, `NestedSvgOptions`, `NodeEvents`, `PaintValue`, `ParentFeature`, `ParentNode`, `PathAttributes`, `PathCommand`, `PathInput`, `PathOptions`, `PathSegment`, `Point`, `PointInput`, `PointerEventType`, `PolygonOptions`, `PolylineAttributes`, `PolylineOptions`, `PortDefinition`, `RadialGradientOptions`, `RectOptions`, `ResourceReference`, `SVGOptions`, `SizableFeature`, `StyleProperties`, `SvgAttributes`, `SvgOptions`, `TemplateInstance`, `TemplateValue`, `TextAttributes`, `TextMeasurementProvider`, `TextOptions`, `TransformState`, `UseOptions`.
30
30
 
31
31
  ## @rasamjs/core/animation
32
32
 
@@ -50,13 +50,23 @@ Type-only exports (7):
50
50
 
51
51
  ## @rasamjs/core/changes
52
52
 
53
- Runtime exports (14):
53
+ Runtime exports (15):
54
54
 
55
- `ChangeConflictError`, `ChangeObserverError`, `ChangeRollbackError`, `beginChangeGroup`, `captureModelState`, `changeKey`, `claimRecorder`, `combineChanges`, `observeChanges`, `restoreChanges`, `restoreModelState`, `sameState`, `validateModelState`, `withChanges`.
55
+ `ChangeConflictError`, `ChangeObserverError`, `ChangeRollbackError`, `beginChangeGroup`, `captureModelState`, `changeKey`, `claimRecorder`, `combineChanges`, `deriveChanges`, `observeChanges`, `restoreChanges`, `restoreModelState`, `sameState`, `validateModelState`, `withChanges`.
56
56
 
57
- Type-only exports (13):
57
+ Type-only exports (14):
58
58
 
59
- `ChangeBatch`, `ChangeEvent`, `ChangeGroup`, `ChangeMetadata`, `ContentState`, `ModelChange`, `ModelFeatures`, `ModelSnapshot`, `NativeNodeState`, `NodeHandle`, `Placement`, `PropertyState`, `TextPolicies`.
59
+ `ChangeBatch`, `ChangeDerivation`, `ChangeEvent`, `ChangeGroup`, `ChangeMetadata`, `ContentState`, `ModelChange`, `ModelFeatures`, `ModelSnapshot`, `NativeNodeState`, `NodeHandle`, `Placement`, `PropertyState`, `TextPolicies`.
60
+
61
+ ## @rasamjs/core/components
62
+
63
+ Runtime exports (7):
64
+
65
+ `Component`, `ComponentTypes`, `action`, `component`, `isComponentChild`, `prop`, `state`.
66
+
67
+ Type-only exports (9):
68
+
69
+ `ActionDefinition`, `ComponentClass`, `ComponentDefinition`, `ComponentFields`, `ComponentOptions`, `ComponentProps`, `PropDefinition`, `PropOptions`, `PropValue`.
60
70
 
61
71
  ## @rasamjs/core/data
62
72
 
@@ -128,6 +138,16 @@ Type-only exports (1):
128
138
 
129
139
  `NodePredicate`.
130
140
 
141
+ ## @rasamjs/core/raster
142
+
143
+ Runtime exports (1):
144
+
145
+ `exportImage`.
146
+
147
+ Type-only exports (1):
148
+
149
+ `RasterOptions`.
150
+
131
151
  ## @rasamjs/core/resources
132
152
 
133
153
  Runtime exports (4):
package/CHANGELOG.md CHANGED
@@ -1,6 +1,103 @@
1
1
  # Changelog
2
2
 
3
- ## [0.1.2] (unreleased)
3
+ ## Unreleased
4
+
5
+ ## [0.3.0] - 2026-11-07
6
+
7
+ - Add `DefsManager.consumersOf(target)`, the nodes holding a typed reference to a target,
8
+ read from the reference index instead of scanning the drawing.
9
+ - Treat connector ends (`data-rasam-connector-start` and `-end`) as links, not
10
+ resources: exporting or copying a connector no longer copies its targets into
11
+ `<defs>`, and importing one whose target was left out drops that link instead of
12
+ throwing.
13
+ - Add component ports: override `ports()` to declare named connection points from
14
+ props and the saved size, in local coordinates with an optional side. The frozen,
15
+ validated `portDefinitions` getter is computed again only when saved props change,
16
+ and core exports the structural `PortDefinition` type that connectors read.
17
+ - Add `actionDefinitions` to components: their `@action` methods with undo labels.
18
+ - Add `resize: 'props'` components and edit rules: the size is saved as built-in
19
+ `width` and `height` props that `render()` reads through `width()` and `height()`,
20
+ `size()` changes them as one undo step and redraws at once, and the transformer
21
+ resizes such a component through `size()` instead of scaling it.
22
+ - Add the `notation: 'si'` prop option, so editors accept and show engineering
23
+ notation such as `4.7k`, `4k7` or `4.7 kΩ`.
24
+ - Render `@state` changes for every component in one shared frame per drawing, as one
25
+ temporary change. History now restores a component's props, position and existence
26
+ but not its rendered children; after undo, redo or a cancelled group the component
27
+ redraws them from restored props and current live state. Live renders that add or
28
+ remove nodes no longer make undo fail, recreated instances start from state
29
+ defaults, and the conflict exception for live-painted holes is removed.
30
+ - Preserve restored props and redo when `created()` writes defaults; the hook still
31
+ runs on every copy. Isolate internal edits from application `edit()` methods,
32
+ validate raw props JSON before mutation, and rebuild saved views with extra attrs.
33
+ - Share component change routing per drawing, discover only added subtrees, and cache
34
+ sibling order between structural edits so moves do not scale with drawing size.
35
+ - Rename the type registry to `ComponentTypes` and infer `create()` option names and
36
+ value types from public primitive fields. Record delegated 34 KiB catalog, 41 KiB
37
+ components and 20.5 KiB root gzip caps, with a reproducible component benchmark.
38
+ - Add the opt-in `@rasamjs/core/components` entry: class components, `@component`,
39
+ `@prop`, `@state`, `@action`, per-drawing type registries, metadata and icons.
40
+ Preserve classes and props through migration, native adoption, import, clone,
41
+ clipboard and replay; bind saved children before subsequent updates. Batch live
42
+ state per frame as temporary changes and reject inherited-member field collisions.
43
+
44
+ - Compare template bindings with current model values so rendering after undo or
45
+ cancellation restores attributes and text without replacing element nodes.
46
+ - Add element variants: a class with `static variant` registers for one `data-*`
47
+ attribute value of its tag, next to the tag's base class. Creation, adoption, import,
48
+ clone and change replay resolve it, and the identifying value is fixed and reserved.
49
+ The registry adds `resolve(element)`, `registered(Constructor)` and `variant()`.
50
+ - Templates render nested templates in holes between elements or at the top level:
51
+ a template, an iterable of templates, or `null`/`undefined`/booleans/`''` for
52
+ nothing. Items whose literal matches by index update in place; updates validate and
53
+ build new items off the drawing before changing it. Replacing a template, or removing
54
+ nested items, now disposes them semantically, so undo restores them and no longer
55
+ clears history.
56
+ - Add the structural `EditRules` type that editing tools read from `node.editRules`
57
+ to limit rotation, resizing and flips per node.
58
+ - Add `static ownsChildren` for containers whose class owns its children. Outside
59
+ structural edits throw before allocating or moving anything; the class edits them
60
+ through `withOwnChildren()`, and core's adoption, replay and owner destruction pass.
61
+
62
+ ## [0.2.0] - 2026-11-06
63
+
64
+ - Add `document: 'presentation'` imports to retain scoped root styles and compositing
65
+ while omitting the viewport envelope. Validate document envelopes before unwrapping.
66
+ Add `document: 'replace'` for empty drawings so full-document restoration does not
67
+ apply configured root presentation twice; fragments retain their defaults.
68
+ - Isolate the text-policy cloning bridge so interchange bundles do not retain the
69
+ rich-text implementation; keep the existing interchange size budget.
70
+
71
+ - Preserve omitted ancestor transforms and nested SVG viewports in content-framed
72
+ subtree exports, including raster exports of nested shapes.
73
+
74
+ - Keep matrix and transform-snapshot reads outside model edit transactions.
75
+ Release destroyed native image sources and avoid restarting a missing-source
76
+ request when disposing a completed image metadata probe.
77
+
78
+ - Add opt-in `deriveChanges()` to include synchronous dependent writes in the same
79
+ operation/group before recording, skipping getters and restoration. Recognize reserved
80
+ connector endpoint references during SVG export/import and clone ID remapping.
81
+
82
+ - Restore exported embedded image data URLs on `<image>` elements during SVG import.
83
+ Data URLs on links and Use instances, unsupported MIME types and active URL schemes
84
+ remain rejected.
85
+
86
+ - Add optional `./raster` PNG/JPEG/WebP export with scale, background, content/viewBox
87
+ framing, same-origin/data image embedding, snapshot isolation and clear decode or
88
+ tainted-canvas failures. Document nonembedded web fonts/styles and pixel limits.
89
+ - Add an isolated 12 KiB raster budget; existing entry budgets are unchanged.
90
+
91
+ - Add labelled change groups and `ChangeGroup.cancel()`, including rollback without
92
+ history, restored completion signals, conflict detection and observer-error reporting.
93
+ - Add content-framed SVG export with padding and whole-document import with `SyntaxError`
94
+ parse diagnostics. Document import accepts an XML declaration and a DOCTYPE without an
95
+ internal subset. Default export and nested SVG import behavior remain compatible.
96
+
97
+ - Add editor Phase 0 test-only group rollback spikes for text creation, attribute edits,
98
+ observer notifications and preservation of unrelated edits.
99
+
100
+ ## [0.1.2] - 2026-11-02
4
101
 
5
102
  ### Fixed
6
103
 
package/COMPONENTS.md ADDED
@@ -0,0 +1,229 @@
1
+ # Class components
2
+
3
+ Import the opt-in `@rasamjs/core/components` entry. Components are trusted application
4
+ classes extending `Component`, which is a closed `Group`. Position, rotation and scale
5
+ belong to that group; transforming it never calls `render()`.
6
+
7
+ ```ts
8
+ import { SVG, svg } from '@rasamjs/core';
9
+ import {
10
+ Component,
11
+ ComponentTypes,
12
+ component,
13
+ prop,
14
+ state,
15
+ action,
16
+ } from '@rasamjs/core/components';
17
+
18
+ @component({ type: 'status', name: 'Status', category: 'Indicators', version: 1 })
19
+ class Status extends Component {
20
+ @prop({ label: 'Caption' }) caption = 'Ready';
21
+ @prop() active = false;
22
+ @state() brightness = 1;
23
+
24
+ @action('Toggle status')
25
+ toggle() {
26
+ this.active = !this.active;
27
+ }
28
+
29
+ override render() {
30
+ return svg`<circle r="15" fill="${this.active ? 'lime' : 'gray'}" opacity="${this.brightness}"/><text y="35">${this.caption}</text>`;
31
+ }
32
+ }
33
+
34
+ const drawing = SVG(document.querySelector<HTMLElement>('#canvas')!, { elements: [Status] });
35
+ const types = new ComponentTypes(drawing, [Status]);
36
+ const status = drawing.create(Status, { caption: 'Connected' });
37
+ status.translate(100, 80);
38
+ status.toggle();
39
+ ```
40
+
41
+ Use standard TypeScript decorators with `useDefineForClassFields` (the default for
42
+ ES2022). Do not enable legacy `experimentalDecorators`. The playground's Vite transform
43
+ handles these decorators. Each concrete type needs its own `@component` decorator.
44
+ Application classes declare no static fields: the decorator supplies core's variant
45
+ registration, and the base class supplies owned-child behavior.
46
+
47
+ `@prop()` supports strings, finite numbers, booleans and null. Defaults come from field
48
+ initializers. Its options are `label`, `unit`, `min`, `max`, `step`, `options`,
49
+ `type: 'color'` and `notation: 'si'` for property-panel metadata. With `notation: 'si'`,
50
+ editors accept `4.7k`, `4k7` or `100n` and show values as `4.7 kΩ`. Numeric limits and choice lists are checked
51
+ on construction, loading and assignment. Unknown props and fields colliding with
52
+ rasam's inherited members are rejected. Use names such as `active` and `renderedFill`:
53
+ `on` and `fill` already name rasam methods. `propDefinitions` exposes frozen metadata.
54
+ The same validation applies to direct `data-rasam-props` writes, before any model
55
+ or rendered value changes.
56
+
57
+ `create(Type, options)` infers field names and value types from the concrete class,
58
+ so misspelled keys and wrong value types are TypeScript errors. Standard decorators
59
+ cannot distinguish `@prop` from state, helpers or getters in TypeScript's type system:
60
+ the inferred `ComponentFields<Type>` includes public primitive values. Runtime
61
+ validation still rejects every creation option that is not a decorated prop.
62
+
63
+ Every changed prop creates one labelled edit. Rendering runs synchronously as a
64
+ derived change in that edit, updating existing template holes. An enclosing core
65
+ change group or `@action('Label')` groups several writes into one undo step. Actions
66
+ must be synchronous. Failed actions roll back saved props and rendered children;
67
+ live state remains outside history. `actionDefinitions` lists an instance's `@action`
68
+ methods as frozen `{ name, label }` entries, inherited ones first; an overriding
69
+ `@action` keeps its place and takes the new label. Editors use it for action buttons. Plain helper fields do not trigger rendering.
70
+
71
+ `@state()` keeps values in memory. Assignments coalesce into one shared animation
72
+ frame per drawing: every changed instance renders once, together, as one temporary
73
+ `components` change, so a thousand animated instances cost one change event per frame.
74
+ They never create undo steps or dirty the editor's document autosave. Live state may
75
+ change structure too, for example a glow drawn only while `on` is true, or a row of
76
+ pins whose length is a state value.
77
+
78
+ If one instance's render throws, the others still render and the error surfaces from
79
+ that frame. `resetState()` restores the instance's
80
+ initial state; `types.resetState()` resets its drawing. Object state defaults must be
81
+ structured-cloneable; nested mutations do not trigger a render, so assign a new value.
82
+
83
+ Subclasses inherit decorated fields, ordinary methods and edit rules, and can add
84
+ fields or override `render()`. Redeclaring an inherited decorated field is rejected.
85
+ `created()` runs synchronously after initialization for every instance, including
86
+ load, clone, paste and history reconstruction. It may initialize props for a brand-new
87
+ instance. On restored instances (`this.adopting`), prop assignments in this hook are
88
+ ignored, including raw props-attribute writes, so saved values and redo remain intact.
89
+ Use the hook for per-instance state and subscriptions; use `migrate` to update saved
90
+ props. `destroyed()` runs on disposal and `changed(name)` for decorated-field changes,
91
+ including replayed prop changes.
92
+
93
+ Rendered children are derived from props and live state. History restores a
94
+ component's props, position and existence, never its children: right after undo,
95
+ redo or a cancelled group, each restored or recreated component redraws, as one
96
+ temporary change, from its restored props and its current live state. A recreated
97
+ instance starts from its state defaults. So live renders that add or remove nodes
98
+ never break undo, and an outside write to a rendered child lasts only until the next
99
+ render.
100
+
101
+ `@component` takes a stable lowercase hyphenated `type`, a display `name`, optional
102
+ `category` (default `General`), `version` (default 1), and editing rules. `rotate` is
103
+ `'free'`, `false`, or a positive degree step dividing 360; `flip` defaults to true;
104
+ `resize` defaults to `'scale'` and can be false or `'props'`. The read-only `editRules`
105
+ getter lets the existing transformer and flip commands enforce them without importing
106
+ this entry.
107
+
108
+ With `resize: 'props'`, a resize changes the component's size instead of scaling it,
109
+ so strokes and text keep their size. The size is saved as built-in `width` and `height`
110
+ props, with defaults from `@component({ resize: 'props', width, height })` (100 when
111
+ left out). Read them in `render()` with `this.width()` and `this.height()`; set them
112
+ with `size(width, height)`, `width(value)`, `height(value)` or `create(Type, { width })`.
113
+ Each change is one undo step and redraws at once. Sizes are at least 1 user unit. The
114
+ transformer resizes such a component through `size()` when it is selected alone; a
115
+ multi-selection that contains one cannot resize. Draw the component inside the box
116
+ from (0, 0) to its width and height, so resize handles follow the pointer exactly.
117
+
118
+ ```ts
119
+ @component({ type: 'panel', name: 'Panel', resize: 'props', width: 150, height: 90 })
120
+ class Panel extends Component {
121
+ @prop() title = 'Notes';
122
+ override render() {
123
+ return svg`<g><rect width="${this.width()}" height="${this.height()}" fill="white" stroke="black"/><text x="10" y="17">${this.title}</text></g>`;
124
+ }
125
+ }
126
+ ```
127
+
128
+ Multi-selections use the strictest permissions and the smallest rotation step shared
129
+ by all members. The editor's geometry properties follow the same rules. Application
130
+ commands can use `combineEditRules` from `@rasamjs/transformer` before making edits;
131
+ core setters remain available for programmatic transforms.
132
+
133
+ Outside structural child edits throw before allocating or moving anything. Components
134
+ can themselves be moved, removed, cloned and edited as normal nodes. Compose
135
+ `!isComponentChild(node)` with a selection filter to prevent drilling into their
136
+ rendered children. The editor playground demonstrates that composition.
137
+ `@rasamjs/editor/components` adds editor registration, palette placement tools and a
138
+ properties service built from `@prop` metadata.
139
+
140
+ ## Ports
141
+
142
+ Ports are named points where links attach, such as the two ends of a resistor or the
143
+ pins of a chip. Override `ports()` to declare them. Like `render()`, it reads `this`:
144
+ use props and, with `resize: 'props'`, the saved size. Points are in the component's
145
+ local coordinates at 0°, so moves, rotations and flips carry them along. The optional
146
+ `side` (`'left'`, `'right'`, `'top'` or `'bottom'`) is the direction a link leaves in.
147
+
148
+ ```ts
149
+ @component({ type: 'chip', name: 'Chip' })
150
+ class Chip extends Component {
151
+ @prop({ min: 2, max: 16, step: 2 }) pins = 4;
152
+
153
+ override ports(): PortDefinition[] {
154
+ const half = this.pins / 2;
155
+ return Array.from({ length: this.pins }, (_, index) => ({
156
+ name: `p${index + 1}`,
157
+ x: index < half ? 0 : 60,
158
+ y: 10 + (index % half) * 20,
159
+ side: index < half ? 'left' : 'right',
160
+ }));
161
+ }
162
+
163
+ override render() {
164
+ return svg`<rect width="60" height="${(this.pins / 2) * 20}"/>`;
165
+ }
166
+ }
167
+ ```
168
+
169
+ `portDefinitions` returns them validated and frozen. It calls `ports()` again only when
170
+ the saved props change, including after undo, redo or a raw props write. Names must be
171
+ unique, non-empty strings, `x` and `y` finite numbers, and `side` one of the four
172
+ names; anything else throws a clear error. Live state never moves a port: reading a
173
+ `@state` field inside `ports()` throws. Subclasses inherit ports and can extend them
174
+ with `super.ports()`. Components have no ports by default. `PortDefinition` comes from
175
+ `@rasamjs/core`, so other packages can read `node.portDefinitions` without importing
176
+ this entry, the way tools read `editRules`. `@rasamjs/connectors` attaches link ends
177
+ to them with `{ target, port }`.
178
+
179
+ ## Save, load and migrate
180
+
181
+ Saved SVG contains the rendered children and three attributes on the component group:
182
+
183
+ ```xml
184
+ <g data-rasam-component="status" data-rasam-version="1"
185
+ data-rasam-props='{"caption":"Connected","active":false}'>…</g>
186
+ ```
187
+
188
+ Register constructors through `SVG(..., { elements: [Status] })` before loading, or
189
+ `new ComponentTypes(drawing, [Status])` before import. Core's `parent.create()` also
190
+ registers the constructor for that drawing. Loads, clones, clipboard operations and
191
+ history retain the class. Reconstruction finishes after references and model features
192
+ are copied; the engine then binds matching saved wrappers or replaces the saved tree
193
+ when the template structure changed. Later rendering never appends a second copy.
194
+ Saved hosts or descendants with attributes absent from the template are rebuilt,
195
+ including unmanaged native attributes, so loading cannot retain stale styling.
196
+
197
+ For a new saved version, provide `migrate(props, oldVersion)` returning props for the
198
+ current version. It is called once on older saved input. Missing props use current
199
+ field defaults. Future versions, missing migrations for old versions, malformed JSON
200
+ and invalid props throw. Migration code should be synchronous and free of external
201
+ side effects. Unknown types remain plain groups with their saved appearance; a
202
+ `ComponentTypes` service reports each unknown type once through `console.warn`.
203
+
204
+ State fields are absent from saved props. Explicit SVG export includes their current
205
+ appearance, so passive viewers see the current drawing. Loading recomputes the look
206
+ from saved props and fresh state defaults.
207
+
208
+ ## Type registry and icons
209
+
210
+ `ComponentTypes.register(Type)` registers another type locally. `list(category?)` returns
211
+ sorted frozen definitions; `get(type)` returns the constructor. `icon(type, parent)`
212
+ renders a default instance through the same class into a supplied drawing or group.
213
+ Use a separate small drawing for palette icons. `destroy()` stops the service's
214
+ observer; drawing disposal also cleans it up.
215
+
216
+ The engine shares one change observer and one derivation callback per drawing.
217
+ Dispatch targets changed components, and type discovery visits only newly added
218
+ subtrees or a node whose type attribute changed. Movement does not scan the drawing.
219
+
220
+ The Phase 1 demos use primitive template bindings. Core's separately developed
221
+ template-power work controls nested templates and arrays; component definitions
222
+ do not require those features. Filters, embedded HTML and component nesting are not
223
+ part of this phase.
224
+
225
+ Validate the entry with `bun run check:size:components` (42 KiB gzip cap).
226
+ `bun run api:check`, `bun run check:consumer`
227
+ and `bun run check:docs` also cover the new public entry.
228
+ Use `bun run bench:components` for the 2,000-object/200-move benchmark, or
229
+ `bun run bench:components --revision=07a1160` to reproduce the reviewed version.