@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.
- package/API.md +29 -9
- package/CHANGELOG.md +98 -1
- package/COMPONENTS.md +229 -0
- package/README.md +178 -25
- package/RELEASING.md +31 -21
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/catalog/fragment.d.ts +2 -2
- package/dist/catalog/fragment.d.ts.map +1 -1
- package/dist/catalog/fragment.js.map +1 -1
- package/dist/changes/derived.d.ts +10 -0
- package/dist/changes/derived.d.ts.map +1 -0
- package/dist/changes/derived.js +14 -0
- package/dist/changes/derived.js.map +1 -0
- package/dist/changes/restore.d.ts.map +1 -1
- package/dist/changes/restore.js +22 -4
- package/dist/changes/restore.js.map +1 -1
- package/dist/changes/service.d.ts +10 -2
- package/dist/changes/service.d.ts.map +1 -1
- package/dist/changes/service.js +45 -6
- package/dist/changes/service.js.map +1 -1
- package/dist/changes/types.d.ts +9 -0
- package/dist/changes/types.d.ts.map +1 -1
- package/dist/changes/types.js.map +1 -1
- package/dist/changes.d.ts +5 -3
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +103 -2
- package/dist/changes.js.map +1 -1
- package/dist/components/changes.d.ts +34 -0
- package/dist/components/changes.d.ts.map +1 -0
- package/dist/components/changes.js +119 -0
- package/dist/components/changes.js.map +1 -0
- package/dist/components/component.d.ts +62 -0
- package/dist/components/component.d.ts.map +1 -0
- package/dist/components/component.js +355 -0
- package/dist/components/component.js.map +1 -0
- package/dist/components/decorators.d.ts +7 -0
- package/dist/components/decorators.d.ts.map +1 -0
- package/dist/components/decorators.js +81 -0
- package/dist/components/decorators.js.map +1 -0
- package/dist/components/metadata.d.ts +10 -0
- package/dist/components/metadata.d.ts.map +1 -0
- package/dist/components/metadata.js +85 -0
- package/dist/components/metadata.js.map +1 -0
- package/dist/components/registry.d.ts +24 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/registry.js +78 -0
- package/dist/components/registry.js.map +1 -0
- package/dist/components/types.d.ts +54 -0
- package/dist/components/types.d.ts.map +1 -0
- package/dist/components/types.js +2 -0
- package/dist/components/types.js.map +1 -0
- package/dist/components/view.d.ts +9 -0
- package/dist/components/view.d.ts.map +1 -0
- package/dist/components/view.js +104 -0
- package/dist/components/view.js.map +1 -0
- package/dist/components.d.ts +6 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +5 -0
- package/dist/components.js.map +1 -0
- package/dist/core/node.d.ts +7 -0
- package/dist/core/node.d.ts.map +1 -1
- package/dist/core/node.js +30 -0
- package/dist/core/node.js.map +1 -1
- package/dist/dom/images.d.ts.map +1 -1
- package/dist/dom/images.js +3 -1
- package/dist/dom/images.js.map +1 -1
- package/dist/elements/image.d.ts.map +1 -1
- package/dist/elements/image.js +4 -1
- package/dist/elements/image.js.map +1 -1
- package/dist/interchange/clone.d.ts.map +1 -1
- package/dist/interchange/clone.js +4 -3
- package/dist/interchange/clone.js.map +1 -1
- package/dist/interchange/dom.d.ts +3 -1
- package/dist/interchange/dom.d.ts.map +1 -1
- package/dist/interchange/dom.js +29 -5
- package/dist/interchange/dom.js.map +1 -1
- package/dist/interchange/markup.d.ts.map +1 -1
- package/dist/interchange/markup.js +71 -3
- package/dist/interchange/markup.js.map +1 -1
- package/dist/interchange/policy.d.ts.map +1 -1
- package/dist/interchange/policy.js +4 -0
- package/dist/interchange/policy.js.map +1 -1
- package/dist/interchange/reconstruct.d.ts.map +1 -1
- package/dist/interchange/reconstruct.js +16 -12
- package/dist/interchange/reconstruct.js.map +1 -1
- package/dist/interchange/references.d.ts.map +1 -1
- package/dist/interchange/references.js +10 -4
- package/dist/interchange/references.js.map +1 -1
- package/dist/interchange/types.d.ts +10 -1
- package/dist/interchange/types.d.ts.map +1 -1
- package/dist/mixins/parent-internals.d.ts +6 -0
- package/dist/mixins/parent-internals.d.ts.map +1 -1
- package/dist/mixins/parent-internals.js +34 -0
- package/dist/mixins/parent-internals.js.map +1 -1
- package/dist/mixins/parent.d.ts.map +1 -1
- package/dist/mixins/parent.js +6 -1
- package/dist/mixins/parent.js.map +1 -1
- package/dist/mixins/transformable.d.ts.map +1 -1
- package/dist/mixins/transformable.js +25 -27
- package/dist/mixins/transformable.js.map +1 -1
- package/dist/mixins/types.d.ts +2 -2
- package/dist/mixins/types.d.ts.map +1 -1
- package/dist/raster/frame.d.ts +12 -0
- package/dist/raster/frame.d.ts.map +1 -0
- package/dist/raster/frame.js +43 -0
- package/dist/raster/frame.js.map +1 -0
- package/dist/raster/images.d.ts +2 -0
- package/dist/raster/images.d.ts.map +1 -0
- package/dist/raster/images.js +60 -0
- package/dist/raster/images.js.map +1 -0
- package/dist/raster/render.d.ts +3 -0
- package/dist/raster/render.d.ts.map +1 -0
- package/dist/raster/render.js +40 -0
- package/dist/raster/render.js.map +1 -0
- package/dist/raster/types.d.ts +10 -0
- package/dist/raster/types.d.ts.map +1 -0
- package/dist/raster/types.js +2 -0
- package/dist/raster/types.js.map +1 -0
- package/dist/raster.d.ts +6 -0
- package/dist/raster.d.ts.map +1 -0
- package/dist/raster.js +10 -0
- package/dist/raster.js.map +1 -0
- package/dist/resources/references.d.ts +2 -0
- package/dist/resources/references.d.ts.map +1 -1
- package/dist/resources/references.js +3 -1
- package/dist/resources/references.js.map +1 -1
- package/dist/services/catalog.d.ts.map +1 -1
- package/dist/services/catalog.js +5 -3
- package/dist/services/catalog.js.map +1 -1
- package/dist/services/defs-manager.d.ts +2 -0
- package/dist/services/defs-manager.d.ts.map +1 -1
- package/dist/services/defs-manager.js +4 -0
- package/dist/services/defs-manager.js.map +1 -1
- package/dist/services/element-key.d.ts +4 -0
- package/dist/services/element-key.d.ts.map +1 -0
- package/dist/services/element-key.js +12 -0
- package/dist/services/element-key.js.map +1 -0
- package/dist/services/initialization.d.ts +7 -0
- package/dist/services/initialization.d.ts.map +1 -0
- package/dist/services/initialization.js +27 -0
- package/dist/services/initialization.js.map +1 -0
- package/dist/services/registry.d.ts +8 -0
- package/dist/services/registry.d.ts.map +1 -1
- package/dist/services/registry.js +45 -10
- package/dist/services/registry.js.map +1 -1
- package/dist/services/types.d.ts +17 -0
- package/dist/services/types.d.ts.map +1 -1
- package/dist/services/viewport.d.ts +2 -0
- package/dist/services/viewport.d.ts.map +1 -1
- package/dist/services/viewport.js +4 -3
- package/dist/services/viewport.js.map +1 -1
- package/dist/template/parts.d.ts +12 -6
- package/dist/template/parts.d.ts.map +1 -1
- package/dist/template/parts.js +36 -29
- package/dist/template/parts.js.map +1 -1
- package/dist/template/render.d.ts +6 -7
- package/dist/template/render.d.ts.map +1 -1
- package/dist/template/render.js +188 -44
- package/dist/template/render.js.map +1 -1
- package/dist/template/svg-tag.d.ts +5 -1
- package/dist/template/svg-tag.d.ts.map +1 -1
- package/dist/template/svg-tag.js.map +1 -1
- package/dist/text/content.d.ts +0 -2
- package/dist/text/content.d.ts.map +1 -1
- package/dist/text/content.js +4 -10
- package/dist/text/content.js.map +1 -1
- package/dist/text/internals.d.ts +1 -0
- package/dist/text/internals.d.ts.map +1 -1
- package/dist/text/policies.d.ts +7 -0
- package/dist/text/policies.d.ts.map +1 -0
- package/dist/text/policies.js +13 -0
- package/dist/text/policies.js.map +1 -0
- package/dist/types/common.d.ts +27 -0
- package/dist/types/common.d.ts.map +1 -1
- package/package.json +156 -143
- package/reports/adoption.json +20 -20
- package/reports/animation-benchmark.json +616 -616
- package/reports/benchmark.json +65 -65
- package/reports/components-benchmark-before.json +10 -0
- package/reports/components-benchmark.json +10 -0
- package/reports/creation.json +26 -26
- package/reports/interchange-benchmark.json +85 -85
- package/reports/public-api-contract.json +353 -23
- package/reports/public-api-review.md +23 -0
- package/reports/size-components.json +102 -0
- package/reports/size-parity.json +82 -34
- package/reports/size.json +6 -4
- package/reports/svgjs-comparison.json +3466 -3466
- package/reports/svgjs-comparison.md +19 -19
- package/reports/svgjs-parity-current.json +177 -60
- package/src/advanced.ts +1 -1
- package/src/catalog/fragment.ts +2 -2
- package/src/changes/derived.ts +19 -0
- package/src/changes/restore.ts +27 -4
- package/src/changes/service.ts +60 -11
- package/src/changes/types.ts +9 -0
- package/src/changes.ts +93 -4
- package/src/components/changes.ts +131 -0
- package/src/components/component.ts +401 -0
- package/src/components/decorators.ts +104 -0
- package/src/components/metadata.ts +111 -0
- package/src/components/registry.ts +79 -0
- package/src/components/types.ts +66 -0
- package/src/components/view.ts +123 -0
- package/src/components.ts +15 -0
- package/src/core/node.ts +32 -1
- package/src/dom/images.ts +2 -1
- package/src/elements/image.ts +4 -1
- package/src/interchange/clone.ts +4 -3
- package/src/interchange/dom.ts +34 -5
- package/src/interchange/markup.ts +77 -3
- package/src/interchange/policy.ts +6 -0
- package/src/interchange/reconstruct.ts +16 -12
- package/src/interchange/references.ts +12 -4
- package/src/interchange/types.ts +10 -1
- package/src/mixins/parent-internals.ts +37 -0
- package/src/mixins/parent.ts +13 -4
- package/src/mixins/transformable.ts +23 -25
- package/src/mixins/types.ts +2 -2
- package/src/raster/frame.ts +53 -0
- package/src/raster/images.ts +73 -0
- package/src/raster/render.ts +45 -0
- package/src/raster/types.ts +9 -0
- package/src/raster.ts +13 -0
- package/src/resources/references.ts +5 -1
- package/src/services/catalog.ts +5 -3
- package/src/services/defs-manager.ts +5 -0
- package/src/services/element-key.ts +14 -0
- package/src/services/initialization.ts +25 -0
- package/src/services/registry.ts +47 -10
- package/src/services/types.ts +21 -0
- package/src/services/viewport.ts +5 -4
- package/src/template/parts.ts +54 -39
- package/src/template/render.ts +211 -54
- package/src/template/svg-tag.ts +6 -1
- package/src/text/content.ts +4 -14
- package/src/text/internals.ts +1 -0
- package/src/text/policies.ts +18 -0
- 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
|
-
|
|
21
|
+
bun install --frozen-lockfile
|
|
19
22
|
cd packages/rasam
|
|
20
|
-
|
|
21
|
-
|
|
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 `
|
|
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 `
|
|
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
|
|
441
|
-
|
|
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
|
|
447
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
595
|
+
bun run typecheck
|
|
596
|
+
bun run test
|
|
597
|
+
bun run build
|
|
565
598
|
npx playwright install chromium firefox webkit
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
34
|
+
bun install --frozen-lockfile
|
|
25
35
|
npx playwright install chromium firefox webkit
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
74
|
-
|
|
83
|
+
bun run build
|
|
84
|
+
bun run --filter @rasamjs/core api:check
|
|
75
85
|
# After reviewing the actual change:
|
|
76
|
-
|
|
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 `
|
|
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/
|
|
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.
|
|
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.
|
package/dist/advanced.d.ts
CHANGED
|
@@ -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';
|
package/dist/advanced.d.ts.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAI7C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,iGAAiG;AACjG,MAAM,OAAO,WAAW;IACb,KAAK,CAAQ;IACtB,YAAY,KAAc,EAAE,KAAK,GAAuB,EAAE;QACxD,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;IAClE,CAAC;IACD,IAAI,KAAK;QACP,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,GAAG,CAAuB,IAAU,EAAE,KAAc;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CACJ,WAAyC,EACzC,
|
|
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;
|
|
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"}
|
package/dist/changes/restore.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|