@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/README.md CHANGED
@@ -9,16 +9,19 @@ Start with the generated [public entry points](API.md) and the
9
9
  The [release procedure](RELEASING.md) describes reproducible acceptance checks;
10
10
  publishing remains separately authorized.
11
11
 
12
+ For class-defined SVG nodes with saved props, live state and undoable actions, see
13
+ the opt-in [`@rasamjs/core/components` guide](COMPONENTS.md).
14
+
12
15
  ## Start
13
16
 
14
17
  Install dependencies at the repository root, then run the remaining commands in
15
18
  this README from `packages/rasam`:
16
19
 
17
20
  ```sh
18
- npm ci
21
+ bun install --frozen-lockfile
19
22
  cd packages/rasam
20
- npm run build
21
- npm run dev
23
+ bun run build
24
+ bun run dev
22
25
  ```
23
26
 
24
27
  Open `http://127.0.0.1:5173/playground/` for the LED demonstration. It includes
@@ -26,7 +29,7 @@ template updates, nested transforms, pointer capture, gradients, a marker, and a
26
29
  After building, you can also open `playground/index.html` directly in a browser.
27
30
  That route loads the standalone script in `preview/playground.js`; rebuild after
28
31
  changing the source. The Vite route loads TypeScript directly for live development.
29
- For the standalone rectangle example, run `npm run build` and open
32
+ For the standalone rectangle example, run `bun run build` and open
30
33
  `http://127.0.0.1:5173/examples/rect.html` while the development server is running.
31
34
 
32
35
  ```ts
@@ -179,7 +182,7 @@ attributes, including pending writes, and do not resolve inherited or computed C
179
182
 
180
183
  ## Runnable examples
181
184
 
182
- Run `npm run build` and `npm run dev`, then open an example on the development
185
+ Run `bun run build` and `bun run dev`, then open an example on the development
183
186
  server. Each phase example exercises its supported editing and lifecycle behavior.
184
187
 
185
188
  | Topic | Page | Optional module |
@@ -437,20 +440,50 @@ instance.destroy();
437
440
  ```
438
441
 
439
442
  `Renderer.render(parent, template)` is also available. Each instance owns a host
440
- group and its mounted nodes. A different template literal identity destroys and
441
- replaces the prior instance. Compilation is cached by literal identity.
443
+ group and its mounted nodes. A different template literal identity replaces the prior
444
+ instance. Compilation is cached by literal identity.
445
+
446
+ A hole between elements, or at the top level, renders nested templates. It takes a
447
+ template, an array or other iterable of templates (nested arrays flatten), or `null`,
448
+ `undefined`, a boolean or `''` for nothing, so it can draw lists and conditions:
449
+
450
+ ```ts
451
+ const chip = (pins: number[], on: boolean) => svg`
452
+ <g>
453
+ <rect width="40" height="60"/>
454
+ ${pins.map((y) => svg`<line x1="0" y1="${y}" x2="-10" y2="${y}"/>`)}
455
+ ${on ? svg`<circle cx="20" cy="30" r="6" fill="lime"/>` : null}
456
+ </g>`;
457
+ ```
458
+
459
+ On update, an item whose template literal matches the one at the same index updates
460
+ in place; other items are rebuilt. An update validates every binding and builds new
461
+ items off the drawing first, so a failed update changes nothing. Nested items follow
462
+ their container's child rules, such as only `stop` inside a gradient. Removed items and
463
+ replaced templates are disposed so that undo restores them without clearing history.
442
464
 
443
465
  Static markup is trusted application code. Dynamic primitives become text or
444
466
  attribute values, never markup. Whole quoted attribute holes remove the attribute
445
- for null/undefined; mixed attributes and text use empty segments. Tag names,
446
- attribute names, and child structure are static. Text holes belong inside `text`
447
- elements. Dynamic `on*` bindings, dynamic CSS strings, and external URL references
467
+ for null/undefined; mixed attributes and text use empty segments. Tag names and
468
+ attribute names are static. Text holes belong inside `text` elements and take only
469
+ primitives. Dynamic `on*` bindings, dynamic CSS strings, and external URL references
448
470
  are rejected. Dynamic URLs are restricted to local `#id` and `url(#id)` references.
449
471
 
450
472
  Templates initialize shapes from their bindings and keep declared resources inside
451
473
  their template-owned containers. Resource IDs may be declared in markup and remain
452
474
  immutable after initialization.
453
475
 
476
+ A custom class can share a tag with a built-in class. A `Group` subclass with
477
+ `static variant = { attribute: 'data-type', value: 'led' }` registers for
478
+ `<g data-type="led">` only. Creation writes that attribute, it can't change, and other
479
+ classes can't use its value. Once the class is registered in a drawing, adoption,
480
+ import, clone and undo/redo rebuild it instead of a plain group; `registry.resolve(element)`
481
+ returns the class an element would get. `static ownsChildren = true` reserves a
482
+ container's children for its class: outside builders, `add()`, moves, reordering,
483
+ removal and templates throw before changing anything. The class edits them inside
484
+ `this.withOwnChildren(callback)`, and adoption, clone, undo/redo and destroying the
485
+ owner keep working.
486
+
454
487
  Registration is local to each drawing. `SVG()` registers the full built-in catalog;
455
488
  `create(Constructor, options)` registers a custom constructor as it creates the node.
456
489
  With the lower-level `Svg` constructor, `Elements.register(registry)` can register
@@ -554,26 +587,26 @@ appropriate owner document/browser APIs.
554
587
 
555
588
  ## Verification
556
589
 
557
- `npm run release:check` runs the complete acceptance pipeline and records source
590
+ `bun run release:check` runs the complete acceptance pipeline and records source
558
591
  consistency, individual results and candidate readiness. `release:assess` does not
559
592
  run validation. Review public API changes with `api:check` before `api:update`.
560
593
 
561
594
  ```sh
562
- npm run typecheck
563
- npm test
564
- npm run build
595
+ bun run typecheck
596
+ bun run test
597
+ bun run build
565
598
  npx playwright install chromium firefox webkit
566
- npm run test:browser
567
- npm run check:consumer
568
- npm run check:size
569
- npm run bench
570
- npm run bench:create
571
- npm run bench:adopt
572
- npm run bench:svgjs
573
- npm run bench:svgjs -- --browser=all
574
- npm run bench:svgjs -- --browser=chromium --gc --output=reports/svgjs-comparison-gc
599
+ bun run test:browser
600
+ bun run check:consumer
601
+ bun run check:size
602
+ bun run bench
603
+ bun run bench:create
604
+ bun run bench:adopt
605
+ bun run bench:svgjs
606
+ bun run bench:svgjs -- --browser=all
607
+ bun run bench:svgjs -- --browser=chromium --gc --output=reports/svgjs-comparison-gc
575
608
  node scripts/profile-svgjs-creation.mjs
576
- npm run format:check
609
+ bun run format:check
577
610
  ```
578
611
 
579
612
  The packed consumer fixture verifies declarations, public runtime exports, typed
@@ -663,9 +696,88 @@ external subscriptions and application state are not revived. See the
663
696
  [history package](../history/README.md) for transactions, limits, conflict handling,
664
697
  and the precise supported-state contract.
665
698
 
699
+ Neutral groups can carry an undo label without importing history:
700
+
701
+ ```ts
702
+ import { beginChangeGroup } from '@rasamjs/core/changes';
703
+
704
+ const edit = beginChangeGroup(drawing, { label: 'Add text' });
705
+ edit.run(() => drawing.text({ text: 'Draft' }));
706
+ edit.cancel(); // Restores only this group's edits, then ends it.
707
+ ```
708
+
709
+ `cancel()` records and restores the group's non-temporary changes even when no history
710
+ is attached. Observers receive the restoration, followed by `groupend` with `cancelled`
711
+ and `restored` set to true. Finish the synchronous `run()` callback before cancelling.
712
+ Repeated `end()` or `cancel()` calls after completion do nothing. A conflicting external
713
+ edit leaves the group open and the model unchanged by the rejected cancellation, so the
714
+ application can resolve the conflict and retry. Permanent `destroy()` invalidates active
715
+ groups; further `run()` and `cancel()` calls throw `ChangeConflictError`. Close an invalidated
716
+ group with `end()`. Use `remove()` for reversible work.
717
+ Notification failures are reported after a successful restore and completion; multiple
718
+ failures are retained in an `AggregateError`. `end(true)` alone still performs no rollback.
719
+
720
+ ## Derived model changes
721
+
722
+ `deriveChanges(drawing, callback)` from `@rasamjs/core/changes` registers a synchronous
723
+ dependent edit before an operation's final snapshot. Its event contains changed nodes
724
+ and current source/group/label/temporary metadata. Callback writes join the same
725
+ operation and group, so one undo step can contain a shape and attached connectors.
726
+ Getter-only calls, no-op writes and restoration do not invoke derivations. Callbacks
727
+ run once in registration order and must not start another attribution scope/group.
728
+ The returned unsubscribe removes the callback; context destruction clears it. Caller
729
+ transactions own failure rollback.
730
+
731
+ Core reserves `data-rasam-connector-start` and `data-rasam-connector-end` as typed local
732
+ href references so endpoints follow ID remapping through cloning and SVG import/export.
733
+
734
+ ## Document framing and import
735
+
736
+ ```ts
737
+ import { exportSvg, importSvg } from '@rasamjs/core/interchange';
738
+
739
+ const svg = exportSvg(drawing, { frame: 'content', padding: 16 });
740
+ const nodes = importSvg(drawing, svg, { document: true });
741
+ ```
742
+
743
+ Content framing sets the exported document's `viewBox`, width and height from geometric
744
+ bounds in root coordinates. Padding defaults to zero and must be finite and nonnegative.
745
+ It excludes stroke and marker extents, and throws when content bounds are unavailable.
746
+ An empty drawing uses its numeric document dimensions, with 800 by 600 as the fallback
747
+ for unresolved dimensions. Export does not change the live drawing or camera. Framing
748
+ requires document output; the default export continues to preserve existing dimensions.
749
+ Framing a nested shape carries its omitted ancestors' transforms into the export;
750
+ framing a nested SVG preserves its own viewport inside the exported document.
751
+ Nested viewport percentages retain their original bases when the outer frame changes.
752
+ Use `BBox.union(nodes.map(node => node.bounds('root')))` to union several root-space bounds.
753
+
754
+ `document: true` imports the children of a single outer SVG, preserving the destination's
755
+ camera and dimensions and discarding root presentation. Use `document: 'presentation'`
756
+ to retain root paint, text styles, opacity, transforms and references in a scoped `<g>`.
757
+ This mode omits the root viewport attributes (`x`, `y`, `width`, `height`, `viewBox`,
758
+ `preserveAspectRatio`) and does not add a group for an envelope with no remaining
759
+ attributes. Existing destination content keeps its presentation; repeated exports and
760
+ imports do not add redundant groups. Both document modes validate the complete passive
761
+ envelope before discarding attributes. They also accept fragments and a full document's prolog: an XML
762
+ declaration, comments and a DOCTYPE without an internal subset, such as the public SVG 1.1
763
+ DOCTYPE that drawing applications write. Internal subsets, entities and later processing
764
+ instructions are still rejected. Malformed XML throws `SyntaxError` containing the parser's first error line.
765
+ Existing passive-content, namespace, resource and URL validation still applies. Without
766
+ the option, importing an SVG creates a nested viewport as before.
767
+
768
+ `document: 'replace'` requires an empty owning drawing. For a full SVG document it
769
+ also clears configured root presentation (retaining root ID/class and viewport settings),
770
+ so the incoming document owns its paint, styles and compositing. This prevents configured
771
+ root opacity from applying again to restored content. Fragments retain root defaults.
772
+
773
+ Embedded PNG, JPEG, GIF, WebP, AVIF and SVG data URLs are accepted by `importSvg()`
774
+ on `<image>` elements, allowing exported images to be restored. Other data-URL
775
+ positions and MIME types remain unsupported. Absolute HTTP(S) images, links and Use
776
+ URLs still require `externalUrls: 'http'`. Register the media catalog before importing images.
777
+
666
778
  ## Release status and license
667
779
 
668
- `@rasamjs/core@0.1.1` is published,
780
+ `@rasamjs/core@0.2.0` is published,
669
781
  licensed under [MIT](./LICENSE). The name is not reserved by these checks. Recheck the intended registry at the
670
782
  time of a separately authorized publication.
671
783
  The package remains `private: true` until publication is separately authorized.
@@ -674,3 +786,44 @@ See [CHANGELOG.md](./CHANGELOG.md) and the [public API review](./reports/public-
674
786
  `release:check` verifies CI wiring for the configured GitHub remote, `hdevlop/rasam`.
675
787
  The [CI workflow](../../.github/workflows/ci.yml) runs the full gate on pushes to `main`
676
788
  and pull requests once committed and pushed. See [RELEASING.md](RELEASING.md).
789
+
790
+ ## Raster image export
791
+
792
+ The optional `@rasamjs/core/raster` entry exports
793
+ `exportImage(root, options): Promise<Blob>` and `RasterOptions`. Importing needs no
794
+ DOM; rendering needs a browser.
795
+
796
+ ```ts
797
+ import { exportImage } from '@rasamjs/core/raster';
798
+ const png = await exportImage(drawing, {
799
+ type: 'image/png',
800
+ scale: 2,
801
+ frame: 'content',
802
+ padding: 16,
803
+ });
804
+ ```
805
+
806
+ PNG is the default; `image/jpeg` and `image/webp` request the browser's native
807
+ encoder. Unsupported encoders reject clearly instead of returning a different
808
+ format. `frame: 'content'` (default) uses geometric content bounds in drawing units,
809
+ independently of the camera, with zero padding by default. `frame: 'viewBox'` uses
810
+ the current document view and ignores padding. Positive finite scale rounds output
811
+ dimensions up to pixels. No export changes the source model or camera; snapshots
812
+ are taken before asynchronous resource loading. Add padding for strokes, markers
813
+ and filters, which can extend beyond geometric bounds.
814
+
815
+ `background` accepts a CSS color. PNG/WebP default to transparent; JPEG defaults to
816
+ white. Zero-size content needs positive padding. Output is limited to 32,767 pixels
817
+ per axis and 16,777,216 total pixels; reduce scale for larger documents.
818
+
819
+ Images must be data URLs or direct same-origin HTTP(S) resources. Resources are
820
+ fetched, validated and embedded, including images inside nested SVG image files.
821
+ Redirects, other protocols, cross-origin resources, corrupt images and cyclic or
822
+ overly nested resources reject. Tainted canvases produce a clear error; temporary
823
+ object URLs are released on success and failure.
824
+
825
+ **Web fonts are not embedded.** External stylesheets, computed page styles and
826
+ external SVG/CSS references are not embedded either. Rendering uses authored SVG
827
+ attributes and browser SVG-as-image rules; fallback fonts can differ from the live
828
+ editor. See [SVG processing modes](https://www.w3.org/TR/SVG2/conform.html) and
829
+ [canvas encoding rules](https://html.spec.whatwg.org/multipage/canvas.html).
package/RELEASING.md CHANGED
@@ -1,19 +1,29 @@
1
1
  # Release preparation
2
2
 
3
- The source manifest is the `@rasamjs/core@0.1.2` candidate, ESM-only, MIT-licensed and
4
- kept `private: true` between releases. It has no runtime dependencies. Core 0.1.1 is
5
- published; 0.1.2 is a patch release with no public API change. It gives replay observers
6
- the restored sibling order and keeps text reads out of change tracking, so observers that
7
- measure or edit during structural undo no longer record stray history entries. It pairs
8
- with the editor candidates `@rasamjs/transformer@0.4.0`, `@rasamjs/selection@0.3.0`,
9
- `@rasamjs/tools@0.2.0` and the first `@rasamjs/viewport@0.1.0`, whose core peer range
10
- stays `^0.1.0`. History 0.1.0 is unchanged and needs no republishing. Clipboard stays
11
- private until its real-Safari check. A published version
3
+ The source manifest is `@rasamjs/core@0.3.0`, ESM-only, MIT-licensed and kept
4
+ `private: true` between releases. It has no runtime dependencies. The 0.3.0 release
5
+ adds class components, named ports, nested templates and the reference-index APIs
6
+ used by connectors 0.2.0. The owner authorized publication on 2026-11-07. Core 0.1.2 was
7
+ published on 2026-11-02 with transformer 0.4.0, selection 0.3.0, tools 0.2.0 and the first
8
+ viewport 0.1.0 in the [editor release](https://github.com/hdevlop/rasam/releases/tag/viewport-v0.1.0);
9
+ it gives replay observers the restored sibling order and keeps text reads out of change
10
+ tracking, with no public API change. History 0.1.0 is unchanged. The first
11
+ `@rasamjs/clipboard@0.1.0` followed on 2026-11-02 in the
12
+ [clipboard release](https://github.com/hdevlop/rasam/releases/tag/clipboard-v0.1.0),
13
+ after its real-Safari check, with core peer range `^0.1.0`. A published version
12
14
  must never be overwritten. Select subsequent versions from the actual public API
13
15
  changes and record migration notes before a later release.
14
16
 
15
17
  ## Local acceptance gate
16
18
 
19
+ Editor Phase 16 performance work keeps transform snapshot/matrix reads outside
20
+ change transactions and releases image resources during disposal. Completed
21
+ metadata probes retain their source until collection, avoiding a second native
22
+ missing-source request. Scoped regression and benchmark evidence is recorded in
23
+ the [integrated performance report](../../apps/playground/reports/editor-performance.md).
24
+ Existing entry budgets, peers and the public API remain unchanged. The editor
25
+ plan's scoped-check policy applies to this work; no publication is authorized.
26
+
17
27
  Install dependencies at the repository root, which holds the only lockfile. The
18
28
  root builds rasam, then `@rasamjs/history` and `@rasamjs/transformer`, runs all packages
19
29
  for `test`, `test:browser`, `typecheck`, `format` and `format:check`, and runs all release gates
@@ -21,11 +31,11 @@ serially for `release:check`. Run other package scripts from the root with
21
31
  `-w @rasamjs/core`, or run them directly inside `packages/rasam`.
22
32
 
23
33
  ```sh
24
- npm ci
34
+ bun install --frozen-lockfile
25
35
  npx playwright install chromium firefox webkit
26
- npm run build
27
- npm run api:check -w @rasamjs/core
28
- npm run release:check
36
+ bun run build
37
+ bun run --filter @rasamjs/core api:check
38
+ bun run release:check
29
39
  ```
30
40
 
31
41
  The rasam release gate runs checks serially: formatting, coverage freshness, typecheck,
@@ -70,10 +80,10 @@ methods and symbol hooks. Review changed exports, return types,
70
80
  declarations and prototype reachability before updating that inventory:
71
81
 
72
82
  ```sh
73
- npm run build
74
- npm run api:check -w @rasamjs/core
83
+ bun run build
84
+ bun run --filter @rasamjs/core api:check
75
85
  # After reviewing the actual change:
76
- npm run api:update -w @rasamjs/core
86
+ bun run --filter @rasamjs/core api:update
77
87
  ```
78
88
 
79
89
  The packed-consumer fixture checks positive and negative type examples, every
@@ -91,7 +101,7 @@ stable-v1 application contract.
91
101
  The configured GitHub remote is `hdevlop/rasam`. The
92
102
  [CI workflow](../../.github/workflows/ci.yml) runs on pushes to `main` and pull requests.
93
103
  It uses Node.js 24, installs locked dependencies and Chromium, Firefox and WebKit,
94
- runs `npm run release:check` from the root, and uploads
104
+ runs `bun run release:check` from the root, and uploads
95
105
  `packages/rasam/reports/release-readiness.json`,
96
106
  `packages/history/reports/release-readiness.json`, and
97
107
  `packages/transformer/reports/release-readiness.json` even when validation fails.
@@ -115,14 +125,14 @@ validation script removes `private: true`, sends messages, creates a tag, upload
115
125
  artifacts or invokes `npm publish`.
116
126
 
117
127
  The public names are `@rasamjs/core`, `@rasamjs/history`, `@rasamjs/transformer`,
118
- `@rasamjs/selection`, `@rasamjs/tools` and `@rasamjs/viewport`.
128
+ `@rasamjs/selection`, `@rasamjs/tools`, `@rasamjs/viewport` and `@rasamjs/clipboard`.
119
129
  Keep the source manifests guarded for candidate validation. After authorization,
120
130
  extract each validated tarball into an isolated publication directory, remove only
121
131
  the manifest's `private` field, and repack it. Verify that every other packed file
122
132
  and manifest value matches the validated candidate, then test the exact public
123
133
  tarballs together as installed consumers. Record their new integrity values and
124
134
  publish new versions in dependency order: rasam, history, transformer, selection,
125
- tools, viewport. For the current editor candidate, publish core 0.1.2, then transformer
126
- 0.4.0, selection 0.3.0, tools 0.2.0 and viewport 0.1.0. History 0.1.0 stays as published.
127
- Verify the new registry
135
+ tools, viewport, clipboard. Verify the new registry
128
136
  versions and integrity values before publishing the GitHub release.
137
+
138
+ The optional raster entry has a 12 KiB gzip budget and must stay outside existing bundles. Raster acceptance covers decoded pixels, formats, same-origin/data resources, corrupt images, camera/model preservation and tainted-canvas errors. Browser font/style embedding is not provided.
@@ -7,7 +7,7 @@ export { Namespace } from './dom/namespace.js';
7
7
  export { DrawingContext } from './services/context.js';
8
8
  export { Scheduler } from './services/scheduler.js';
9
9
  export { Registry } from './services/registry.js';
10
- export type { ElementClass } from './services/types.js';
10
+ export type { ElementClass, ElementVariant } from './services/types.js';
11
11
  export { BoundsService } from './services/bounds.js';
12
12
  export { DefsManager } from './services/defs-manager.js';
13
13
  export { Transformable } from './mixins/transformable.js';
@@ -1 +1 @@
1
- {"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { AnyNode } from '../core/types.js';
2
- import type { ElementClass } from '../services/types.js';
2
+ import type { ElementClass, CreationOptions } from '../services/types.js';
3
3
  import type { ParentNode } from '../mixins/types.js';
4
4
  /** A detached managed forest. Insertion moves its nodes and leaves this reusable value empty. */
5
5
  export declare class SvgFragment {
@@ -8,7 +8,7 @@ export declare class SvgFragment {
8
8
  get isDestroyed(): boolean;
9
9
  get nodes(): readonly AnyNode[];
10
10
  add<Node extends AnyNode>(node: Node, index?: number): Node;
11
- create<Options, Child extends AnyNode>(Constructor: ElementClass<Options, Child>, options: Options): Child;
11
+ create<Options, Child extends AnyNode>(Constructor: ElementClass<Options, Child>, options: NoInfer<CreationOptions<Options, Child>>): Child;
12
12
  insertInto(parent: ParentNode, index?: number): readonly AnyNode[];
13
13
  detachChildren(): readonly AnyNode[];
14
14
  clear(): this;
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../src/catalog/fragment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIrD,iGAAiG;AACjG,qBAAa,WAAW;;IAEtB,YAAY,KAAK,EAAE,OAAO,EAAE,KAAK,GAAE,SAAS,OAAO,EAAO,EAUzD;IACD,IAAI,WAAW,IAAI,OAAO,CAEzB;IACD,IAAI,KAAK,IAAI,SAAS,OAAO,EAAE,CAG9B;IAED,GAAG,CAAC,IAAI,SAAS,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAE1D;IACD,MAAM,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO,EACnC,WAAW,EAAE,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,EACzC,OAAO,EAAE,OAAO,GACf,KAAK,CAEP;IACD,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,SAAyB,GAAG,SAAS,OAAO,EAAE,CAsBjF;IACD,cAAc,IAAI,SAAS,OAAO,EAAE,CAMnC;IACD,KAAK,IAAI,IAAI,CAKZ;IACD,OAAO,IAAI,IAAI,CAEd;IAED,OAAO,CAAC,SAAS;CA2BlB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,GAAE,SAAS,OAAO,EAAO,GAAG,WAAW,CAEpF"}
1
+ {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../src/catalog/fragment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIrD,iGAAiG;AACjG,qBAAa,WAAW;;IAEtB,YAAY,KAAK,EAAE,OAAO,EAAE,KAAK,GAAE,SAAS,OAAO,EAAO,EAUzD;IACD,IAAI,WAAW,IAAI,OAAO,CAEzB;IACD,IAAI,KAAK,IAAI,SAAS,OAAO,EAAE,CAG9B;IAED,GAAG,CAAC,IAAI,SAAS,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAE1D;IACD,MAAM,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO,EACnC,WAAW,EAAE,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,EACzC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,GAChD,KAAK,CAEP;IACD,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,SAAyB,GAAG,SAAS,OAAO,EAAE,CAsBjF;IACD,cAAc,IAAI,SAAS,OAAO,EAAE,CAMnC;IACD,KAAK,IAAI,IAAI,CAKZ;IACD,OAAO,IAAI,IAAI,CAEd;IAED,OAAO,CAAC,SAAS;CA2BlB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,GAAE,SAAS,OAAO,EAAO,GAAG,WAAW,CAEpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/catalog/fragment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/catalog/fragment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AnyNode } from '../core/types.js';
2
+ import type { ModelChange } from './types.js';
3
+ /** Marks a class whose children are drawn from its own state, such as a component. */
4
+ export declare const derivesChildren: unique symbol;
5
+ export declare const ownsDerived: (Constructor: unknown) => boolean;
6
+ /** Records the owner's current descendants as derived. The owner redraws them after replay. */
7
+ export declare function markDerived(owner: AnyNode): void;
8
+ /** Replay skips derived nodes and their owners' child lists. */
9
+ export declare const isDerivedChange: (change: ModelChange) => boolean;
10
+ //# sourceMappingURL=derived.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derived.d.ts","sourceRoot":"","sources":["../../src/changes/derived.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,sFAAsF;AACtF,eAAO,MAAM,eAAe,eAA4B,CAAC;AAGzD,eAAO,MAAM,WAAW,gBAAiB,OAAO,KAAG,OACgC,CAAC;AAEpF,+FAA+F;AAC/F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEhD;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,WAAY,WAAW,KAAG,OAEyB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** Marks a class whose children are drawn from its own state, such as a component. */
2
+ export const derivesChildren = Symbol('derivesChildren');
3
+ const derived = new WeakSet();
4
+ export const ownsDerived = (Constructor) => !!Constructor?.[derivesChildren];
5
+ /** Records the owner's current descendants as derived. The owner redraws them after replay. */
6
+ export function markDerived(owner) {
7
+ for (const node of owner.descendants())
8
+ if (node !== owner)
9
+ derived.add(node);
10
+ }
11
+ /** Replay skips derived nodes and their owners' child lists. */
12
+ export const isDerivedChange = (change) => derived.has(change.target.node) ||
13
+ (change.domain === 'children' && ownsDerived(change.target.node.constructor));
14
+ //# sourceMappingURL=derived.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derived.js","sourceRoot":"","sources":["../../src/changes/derived.ts"],"names":[],"mappings":"AAGA,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;AACzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAW,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAoB,EAAW,EAAE,CAC3D,CAAC,CAAE,WAA2D,EAAE,CAAC,eAAe,CAAC,CAAC;AAEpF,+FAA+F;AAC/F,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,WAAW,EAAE;QAAE,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChF,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAmB,EAAW,EAAE,CAC9D,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAC/B,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/changes/restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAahD,OAAO,KAAK,EAEV,WAAW,EAIZ,MAAM,YAAY,CAAC;AA6CpB,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,GACzB,IAAI,CAKN"}
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/changes/restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAehD,OAAO,KAAK,EAEV,WAAW,EAIZ,MAAM,YAAY,CAAC;AAqDpB,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,GACzB,IAAI,CAKN"}
@@ -1,7 +1,9 @@
1
1
  import { destroySubtrees } from '../core/destruction.js';
2
- import { validateChild } from '../mixins/parent-internals.js';
2
+ import { validateChild, withInternalStructure } from '../mixins/parent-internals.js';
3
3
  import { readTransformState } from '../mixins/transform-state.js';
4
4
  import { referenceValue } from '../services/reference-value.js';
5
+ import { initializeNode } from '../services/initialization.js';
6
+ import { isDerivedChange, ownsDerived } from './derived.js';
5
7
  import { captureState, sameState, stateAdapter, combineChanges } from './state.js';
6
8
  import { ChangeConflictError, ChangeRollbackError, restoreModelState, validateModelState, } from './types.js';
7
9
  function currentValue(snapshot, change) {
@@ -15,6 +17,13 @@ function currentValue(snapshot, change) {
15
17
  return snapshot.contributesToBounds;
16
18
  return snapshot[change.domain];
17
19
  }
20
+ /** An owner's derived child list is redrawn, not restored, so conflicts ignore it. */
21
+ function comparable(value, change) {
22
+ const snapshot = value;
23
+ return change.domain === 'existence' && snapshot && ownsDerived(snapshot.constructor)
24
+ ? { ...snapshot, children: undefined }
25
+ : value;
26
+ }
18
27
  function contentFeatures(content) {
19
28
  return {
20
29
  parts: content.parts.map((part) => (typeof part === 'string' ? part : part.node)),
@@ -56,7 +65,8 @@ export function restoreChanges(owner, changes, direction) {
56
65
  }
57
66
  function applyChanges(owner, changes, direction) {
58
67
  const service = owner.context.changes;
59
- changes = combineChanges(changes);
68
+ // An owner redraws its derived children after replay, from the state replay restored.
69
+ changes = combineChanges(changes).filter((change) => !isDerivedChange(change));
60
70
  service.resetOrders();
61
71
  service.configure(stateAdapter);
62
72
  try {
@@ -73,7 +83,8 @@ function applyChanges(owner, changes, direction) {
73
83
  domains.set(change.target, new Set());
74
84
  }
75
85
  const expected = direction === 'undo' ? change.after : change.before;
76
- if (!sameState(currentValue(before.get(change.target), change), expected))
86
+ const current = currentValue(before.get(change.target), change);
87
+ if (!sameState(comparable(current, change), comparable(expected, change)))
77
88
  throw new ChangeConflictError(`Conflicting ${change.domain} state on node ${change.target.id}`);
78
89
  desired.set(change.target, applyStateChange(desired.get(change.target), change, direction === 'undo' ? change.before : change.after));
79
90
  domains
@@ -136,7 +147,7 @@ function validateGraph(owner, states, counts) {
136
147
  for (const [target, state] of states) {
137
148
  if (!state)
138
149
  continue;
139
- if (context.registry.get(state.constructor.tag) !== state.constructor)
150
+ if (context.registry.registered(state.constructor) !== state.constructor)
140
151
  throw new ChangeConflictError(`Constructor is not registered: ${state.constructor.tag}`);
141
152
  const id = state.properties.id?.value;
142
153
  if (id !== undefined &&
@@ -203,7 +214,11 @@ function validateGraph(owner, states, counts) {
203
214
  if (counts.get(node) > 0)
204
215
  throw new ChangeConflictError('A removed node would retain live typed references');
205
216
  }
217
+ /** Replay restores exact prior trees, including children that their class owns. */
206
218
  function applyPrepared(owner, states, domains) {
219
+ withInternalStructure(() => applyStates(owner, states, domains));
220
+ }
221
+ function applyStates(owner, states, domains) {
207
222
  const context = owner.context;
208
223
  const recreated = new Set();
209
224
  // Allocate all nodes before restoring any reference or content edge.
@@ -285,6 +300,7 @@ function applyPrepared(owner, states, domains) {
285
300
  // Parent order is authoritative across compound edits and separate edit callbacks.
286
301
  for (const [target, state] of states)
287
302
  if (state?.children &&
303
+ !ownsDerived(state.constructor) &&
288
304
  (recreated.has(target) ||
289
305
  domains.get(target).has('children') ||
290
306
  domains.get(target).has('existence'))) {
@@ -312,6 +328,8 @@ function applyPrepared(owner, states, domains) {
312
328
  const roots = [...retired].filter((node) => !node.parent || !retired.has(node.parent));
313
329
  if (roots.length)
314
330
  owner[destroySubtrees](roots);
331
+ for (const target of recreated)
332
+ initializeNode(target.node);
315
333
  context.bounds.invalidate();
316
334
  }
317
335
  //# sourceMappingURL=restore.js.map