@rasamjs/core 0.1.1 → 0.2.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 +15 -5
- package/CHANGELOG.md +51 -1
- package/README.md +140 -20
- package/RELEASING.md +29 -18
- package/dist/changes/restore.js +2 -0
- package/dist/changes/restore.js.map +1 -1
- package/dist/changes/service.d.ts +5 -2
- package/dist/changes/service.d.ts.map +1 -1
- package/dist/changes/service.js +29 -4
- 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/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/elements/text.js +4 -4
- package/dist/elements/text.js.map +1 -1
- package/dist/interchange/clone.js +1 -1
- 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/types.d.ts +10 -1
- package/dist/interchange/types.d.ts.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/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.map +1 -1
- package/dist/resources/references.js +3 -1
- package/dist/resources/references.js.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/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/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/text/rich-textual.js +3 -3
- package/dist/text/rich-textual.js.map +1 -1
- package/package.json +6 -1
- package/reports/adoption.json +20 -20
- package/reports/animation-benchmark.json +613 -613
- package/reports/benchmark.json +51 -51
- package/reports/creation.json +26 -26
- package/reports/interchange-benchmark.json +85 -85
- package/reports/public-api-contract.json +20 -7
- package/reports/public-api-review.md +23 -0
- package/reports/size-parity.json +54 -19
- package/reports/size.json +2 -2
- package/reports/svgjs-comparison.json +3542 -3542
- package/reports/svgjs-comparison.md +17 -17
- package/reports/svgjs-parity-current.json +60 -39
- package/src/changes/restore.ts +2 -0
- package/src/changes/service.ts +39 -5
- package/src/changes/types.ts +9 -0
- package/src/changes.ts +93 -4
- package/src/dom/images.ts +2 -1
- package/src/elements/image.ts +4 -1
- package/src/elements/text.ts +3 -3
- package/src/interchange/clone.ts +1 -1
- package/src/interchange/dom.ts +34 -5
- package/src/interchange/markup.ts +77 -3
- package/src/interchange/policy.ts +6 -0
- package/src/interchange/types.ts +10 -1
- package/src/mixins/transformable.ts +23 -25
- 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/viewport.ts +5 -4
- package/src/text/content.ts +4 -14
- package/src/text/policies.ts +18 -0
- package/src/text/rich-textual.ts +2 -2
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
# Rasam vs SVG.js
|
|
2
2
|
|
|
3
|
-
SVG.js 3.2.8; Rasam 0.
|
|
3
|
+
SVG.js 3.2.8; Rasam 0.2.0 workspace. CPU: Intel(R) Core(TM) i7-10610U CPU @ 1.80GHz.
|
|
4
4
|
|
|
5
5
|
All timings below are medians in milliseconds. Ratios above 1 mean Rasam took longer.
|
|
6
6
|
|
|
7
7
|
| Browser | Workload | Nodes | Samples | Rasam | SVG.js | Rasam / SVG.js |
|
|
8
8
|
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
|
9
|
-
| chromium 156.0.8078.4 | creation | 2000 | 15 |
|
|
10
|
-
| chromium 156.0.8078.4 | creation | 8000 | 15 |
|
|
11
|
-
| chromium 156.0.8078.4 | fill | 1000 | 100 | 0.
|
|
12
|
-
| chromium 156.0.8078.4 | repeated-fill | 1000 | 100 | 0.
|
|
13
|
-
| chromium 156.0.8078.4 | pointer-one | 8000 | 40 | 3.
|
|
14
|
-
| chromium 156.0.8078.4 | pointer-all | 8000 | 40 |
|
|
15
|
-
| firefox 157.0 | creation | 2000 | 15 |
|
|
16
|
-
| firefox 157.0 | creation | 8000 | 15 |
|
|
17
|
-
| firefox 157.0 | fill | 1000 | 100 | 1.
|
|
18
|
-
| firefox 157.0 | repeated-fill | 1000 | 100 | 1.
|
|
9
|
+
| chromium 156.0.8078.4 | creation | 2000 | 15 | 9.60 | 11.90 | 0.81 |
|
|
10
|
+
| chromium 156.0.8078.4 | creation | 8000 | 15 | 33.80 | 38.70 | 0.87 |
|
|
11
|
+
| chromium 156.0.8078.4 | fill | 1000 | 100 | 0.43 | 1.21 | 0.36 |
|
|
12
|
+
| chromium 156.0.8078.4 | repeated-fill | 1000 | 100 | 0.66 | 3.53 | 0.19 |
|
|
13
|
+
| chromium 156.0.8078.4 | pointer-one | 8000 | 40 | 3.73 | 3.56 | 1.05 |
|
|
14
|
+
| chromium 156.0.8078.4 | pointer-all | 8000 | 40 | 3.82 | 3.66 | 1.04 |
|
|
15
|
+
| firefox 157.0 | creation | 2000 | 15 | 20.00 | 23.00 | 0.87 |
|
|
16
|
+
| firefox 157.0 | creation | 8000 | 15 | 75.00 | 80.00 | 0.94 |
|
|
17
|
+
| firefox 157.0 | fill | 1000 | 100 | 1.20 | 1.55 | 0.77 |
|
|
18
|
+
| firefox 157.0 | repeated-fill | 1000 | 100 | 1.60 | 4.70 | 0.34 |
|
|
19
19
|
| firefox 157.0 | pointer-one | 8000 | 40 | 4.30 | 3.60 | 1.19 |
|
|
20
20
|
| firefox 157.0 | pointer-all | 8000 | 40 | 4.20 | 3.60 | 1.17 |
|
|
21
|
-
| webkit 27.2 | creation | 2000 | 15 |
|
|
22
|
-
| webkit 27.2 | creation | 8000 | 15 |
|
|
23
|
-
| webkit 27.2 | fill | 1000 | 100 | 0.
|
|
24
|
-
| webkit 27.2 | repeated-fill | 1000 | 100 |
|
|
25
|
-
| webkit 27.2 | pointer-one | 8000 | 40 | 4.
|
|
26
|
-
| webkit 27.2 | pointer-all | 8000 | 40 | 4.
|
|
21
|
+
| webkit 27.2 | creation | 2000 | 15 | 14.00 | 18.00 | 0.78 |
|
|
22
|
+
| webkit 27.2 | creation | 8000 | 15 | 55.00 | 69.00 | 0.80 |
|
|
23
|
+
| webkit 27.2 | fill | 1000 | 100 | 0.60 | 1.60 | 0.37 |
|
|
24
|
+
| webkit 27.2 | repeated-fill | 1000 | 100 | 0.90 | 4.90 | 0.18 |
|
|
25
|
+
| webkit 27.2 | pointer-one | 8000 | 40 | 4.20 | 3.40 | 1.24 |
|
|
26
|
+
| webkit 27.2 | pointer-all | 8000 | 40 | 4.20 | 3.40 | 1.24 |
|
|
27
27
|
|
|
28
28
|
## Methodology
|
|
29
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-
|
|
3
|
-
"revision": "
|
|
2
|
+
"generatedAt": "2026-11-06T20:46:53.699Z",
|
|
3
|
+
"revision": "a802f04c6f0965cedbd4c469fad996a445c6f878",
|
|
4
4
|
"dirtyTree": [
|
|
5
5
|
"M packages/rasam/reports/adoption.json",
|
|
6
6
|
" M packages/rasam/reports/creation.json",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"path": "src/changes.ts",
|
|
80
|
-
"sha256": "
|
|
80
|
+
"sha256": "3c1270b193852d88350c84c66453bba1601a4ec6da8263c20e7a4a482982738b"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": "src/changes/mutation.ts",
|
|
@@ -85,11 +85,11 @@
|
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "src/changes/restore.ts",
|
|
88
|
-
"sha256": "
|
|
88
|
+
"sha256": "a8ab84b53c4b11771af26f64e17c341862c6582756a3158796137c9c3f4ce47f"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"path": "src/changes/service.ts",
|
|
92
|
-
"sha256": "
|
|
92
|
+
"sha256": "75d5347a4bce501bd2d691a1ead98767d9a8e7d9bb116777107689ee52abdc54"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "src/changes/state.ts",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"path": "src/changes/types.ts",
|
|
100
|
-
"sha256": "
|
|
100
|
+
"sha256": "1158dc5615c62c5647cfbf3609ea7df1466819fa200b368e48a70259a30eec33"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"path": "src/core/attribute-validation.ts",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"path": "src/dom/images.ts",
|
|
160
|
-
"sha256": "
|
|
160
|
+
"sha256": "fb10a82529a22d639cc0422bc92b399c21545d8be622265515a76c89fa0f53f8"
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"path": "src/dom/namespace.ts",
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
},
|
|
218
218
|
{
|
|
219
219
|
"path": "src/elements/image.ts",
|
|
220
|
-
"sha256": "
|
|
220
|
+
"sha256": "d9c96803619761a338365ced07720f1de77d02f1b64ddd4e8823d346cc766bb4"
|
|
221
221
|
},
|
|
222
222
|
{
|
|
223
223
|
"path": "src/elements/index.ts",
|
|
@@ -289,7 +289,7 @@
|
|
|
289
289
|
},
|
|
290
290
|
{
|
|
291
291
|
"path": "src/elements/text.ts",
|
|
292
|
-
"sha256": "
|
|
292
|
+
"sha256": "0d42353408aed12961785ac6dfff511b62ec76df77d075768cfbd138abc6e820"
|
|
293
293
|
},
|
|
294
294
|
{
|
|
295
295
|
"path": "src/elements/tspan.ts",
|
|
@@ -365,19 +365,19 @@
|
|
|
365
365
|
},
|
|
366
366
|
{
|
|
367
367
|
"path": "src/interchange/clone.ts",
|
|
368
|
-
"sha256": "
|
|
368
|
+
"sha256": "ac5b0422f99912bd75117f6d25789ae7e86b858dee6662881a714be3aa1e21ad"
|
|
369
369
|
},
|
|
370
370
|
{
|
|
371
371
|
"path": "src/interchange/dom.ts",
|
|
372
|
-
"sha256": "
|
|
372
|
+
"sha256": "ca1eb93f3ed4466b941ce6f93e4ed8de30416674c5f3909c665b17c737432df4"
|
|
373
373
|
},
|
|
374
374
|
{
|
|
375
375
|
"path": "src/interchange/markup.ts",
|
|
376
|
-
"sha256": "
|
|
376
|
+
"sha256": "18671962651a97a1f97188006dcb8e1df16aa1e02e39166459a59d6a3ec5a0fd"
|
|
377
377
|
},
|
|
378
378
|
{
|
|
379
379
|
"path": "src/interchange/policy.ts",
|
|
380
|
-
"sha256": "
|
|
380
|
+
"sha256": "917bc9b3476afe360ddceebbc467046538d80be9c3ba59f5bdabff97691b9445"
|
|
381
381
|
},
|
|
382
382
|
{
|
|
383
383
|
"path": "src/interchange/reconstruct.ts",
|
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
},
|
|
394
394
|
{
|
|
395
395
|
"path": "src/interchange/types.ts",
|
|
396
|
-
"sha256": "
|
|
396
|
+
"sha256": "28745d333c1b7bb44414a305864d64fa252984f083efdaaa5f12eaa572120751"
|
|
397
397
|
},
|
|
398
398
|
{
|
|
399
399
|
"path": "src/masks.ts",
|
|
@@ -485,7 +485,7 @@
|
|
|
485
485
|
},
|
|
486
486
|
{
|
|
487
487
|
"path": "src/mixins/transformable.ts",
|
|
488
|
-
"sha256": "
|
|
488
|
+
"sha256": "35d445e5069257ecd38551c6a468806875d862e61ac55d3f560df8079e84ab6b"
|
|
489
489
|
},
|
|
490
490
|
{
|
|
491
491
|
"path": "src/mixins/types.ts",
|
|
@@ -511,6 +511,26 @@
|
|
|
511
511
|
"path": "src/query.ts",
|
|
512
512
|
"sha256": "d537a78cd1fa6668074a44eb7c41afb55b51986702a44b58c9e1ff9f1226ef57"
|
|
513
513
|
},
|
|
514
|
+
{
|
|
515
|
+
"path": "src/raster.ts",
|
|
516
|
+
"sha256": "46df66cb6c18a66c892f82a46304ef6cb8cc144100766bb4e8eb34f6aa91f204"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"path": "src/raster/frame.ts",
|
|
520
|
+
"sha256": "908222caf5c93ee704fe078d39315177c4c635dc8f2cb39c2258f28f18bf2c15"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"path": "src/raster/images.ts",
|
|
524
|
+
"sha256": "417c5e907c6e223c6ee7c517850aa77c94173608acbae9d71ed798308b1ff5ee"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
"path": "src/raster/render.ts",
|
|
528
|
+
"sha256": "ccc3cf1dbbecce83c3982b6fd59620a62ed422e6c2df6644af91959b2938b93f"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
"path": "src/raster/types.ts",
|
|
532
|
+
"sha256": "60c9cb9ac8ed335c460434581411aa513bb9c10f8005e4ab20bc1bf721766b94"
|
|
533
|
+
},
|
|
514
534
|
{
|
|
515
535
|
"path": "src/resources.ts",
|
|
516
536
|
"sha256": "9660bf7b369cb915521bd184f548ead723f9c0baa5243ea57d0d8a8993ab9f50"
|
|
@@ -521,7 +541,7 @@
|
|
|
521
541
|
},
|
|
522
542
|
{
|
|
523
543
|
"path": "src/resources/references.ts",
|
|
524
|
-
"sha256": "
|
|
544
|
+
"sha256": "49a3aaba63aee99a725b67a85849a0f071c07e7f4d3b7458aa8cfc8409298895"
|
|
525
545
|
},
|
|
526
546
|
{
|
|
527
547
|
"path": "src/resources/validation.ts",
|
|
@@ -565,7 +585,7 @@
|
|
|
565
585
|
},
|
|
566
586
|
{
|
|
567
587
|
"path": "src/services/viewport.ts",
|
|
568
|
-
"sha256": "
|
|
588
|
+
"sha256": "b33adea566823e5afb7df79bc7d2eb1c074443422a1e1d8acecda64c956a455b"
|
|
569
589
|
},
|
|
570
590
|
{
|
|
571
591
|
"path": "src/svg.ts",
|
|
@@ -589,7 +609,7 @@
|
|
|
589
609
|
},
|
|
590
610
|
{
|
|
591
611
|
"path": "src/text/content.ts",
|
|
592
|
-
"sha256": "
|
|
612
|
+
"sha256": "99e3d11eec709b19b71ba7cf5ad720b60b317bd24b1272b404442e4597f6bb88"
|
|
593
613
|
},
|
|
594
614
|
{
|
|
595
615
|
"path": "src/text/internals.ts",
|
|
@@ -599,9 +619,13 @@
|
|
|
599
619
|
"path": "src/text/measure.ts",
|
|
600
620
|
"sha256": "db023fce1361e9547adbc18040c87f5c524164fc97318322cf77d3c364ef78d2"
|
|
601
621
|
},
|
|
622
|
+
{
|
|
623
|
+
"path": "src/text/policies.ts",
|
|
624
|
+
"sha256": "488f691c4047d53b05284c5224c501296c864a741e91aeedefdd5115a0f12b8e"
|
|
625
|
+
},
|
|
602
626
|
{
|
|
603
627
|
"path": "src/text/rich-textual.ts",
|
|
604
|
-
"sha256": "
|
|
628
|
+
"sha256": "52dce3a3bb91da57261dec67b8682c020bbe1ea0f22eea7aa1c02e386eb610d2"
|
|
605
629
|
},
|
|
606
630
|
{
|
|
607
631
|
"path": "src/text/types.ts",
|
|
@@ -652,8 +676,8 @@
|
|
|
652
676
|
"sha256": "f14d89cb0695dc1c2958e831dd99b0e36cb103b0746af7bdec48c180bddf6a35"
|
|
653
677
|
},
|
|
654
678
|
{
|
|
655
|
-
"path": "../../
|
|
656
|
-
"sha256": "
|
|
679
|
+
"path": "../../bun.lock",
|
|
680
|
+
"sha256": "d38780d5a3180ba7ee8c2cc6f6ff28c0065bb650496d78f55a3db94d5ce1800f"
|
|
657
681
|
}
|
|
658
682
|
],
|
|
659
683
|
"svgjs": "3.2.8",
|
|
@@ -9609,8 +9633,8 @@
|
|
|
9609
9633
|
},
|
|
9610
9634
|
"size": {
|
|
9611
9635
|
"fixture": "fixtures/size.ts",
|
|
9612
|
-
"rawBytes":
|
|
9613
|
-
"gzipBytes":
|
|
9636
|
+
"rawBytes": 66378,
|
|
9637
|
+
"gzipBytes": 19897,
|
|
9614
9638
|
"budgetBytes": 20480,
|
|
9615
9639
|
"included": [
|
|
9616
9640
|
"src/changes/mutation.ts",
|
|
@@ -9660,7 +9684,7 @@
|
|
|
9660
9684
|
},
|
|
9661
9685
|
"performance": {
|
|
9662
9686
|
"fixture": "scripts/benchmark.mjs",
|
|
9663
|
-
"date": "2026-
|
|
9687
|
+
"date": "2026-11-06T20:46:47.900Z",
|
|
9664
9688
|
"browser": "Chromium 156.0.8078.4",
|
|
9665
9689
|
"node": "v24.21.0",
|
|
9666
9690
|
"os": "win32 10.0.26200",
|
|
@@ -9670,30 +9694,27 @@
|
|
|
9670
9694
|
"warmup": 20,
|
|
9671
9695
|
"iterations": 100,
|
|
9672
9696
|
"medianMs": 0.6000000238418579,
|
|
9673
|
-
"p95Ms": 0.
|
|
9697
|
+
"p95Ms": 0.8000000715255737,
|
|
9674
9698
|
"targetMedianMs": 16,
|
|
9675
9699
|
"includes": "logical mutations and synchronous DOM commit; excludes paint",
|
|
9676
9700
|
"timings": [
|
|
9701
|
+
0.3999999761581421, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
|
|
9677
9702
|
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
|
|
9678
|
-
0.5, 0.5, 0.
|
|
9679
|
-
0.
|
|
9680
|
-
0.
|
|
9681
|
-
0.
|
|
9682
|
-
0.5999999642372131, 0.5999999642372131, 0.5999999642372131, 0.6000000238418579,
|
|
9703
|
+
0.5, 0.5, 0.5999999046325684, 0.5999999046325684, 0.5999999046325684, 0.5999999046325684,
|
|
9704
|
+
0.5999999046325684, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9705
|
+
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9706
|
+
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9683
9707
|
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9684
9708
|
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9685
9709
|
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9686
9710
|
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9687
9711
|
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6000000238418579,
|
|
9688
|
-
0.6000000238418579, 0.
|
|
9689
|
-
0.
|
|
9690
|
-
0.
|
|
9691
|
-
0.
|
|
9692
|
-
0.7000000476837158, 0.7000000476837158, 0.
|
|
9693
|
-
0.
|
|
9694
|
-
0.800000011920929, 0.800000011920929, 0.8999999761581421, 0.8999999761581421,
|
|
9695
|
-
0.8999999761581421, 0.9000000357627869, 0.9000000357627869, 1, 1, 1.100000023841858,
|
|
9696
|
-
1.199999988079071, 1.4000000357627869
|
|
9712
|
+
0.6000000238418579, 0.6000000238418579, 0.6000000238418579, 0.6999999284744263,
|
|
9713
|
+
0.6999999284744263, 0.6999999284744263, 0.6999999284744263, 0.6999999284744263,
|
|
9714
|
+
0.6999999284744263, 0.6999999284744263, 0.6999999284744263, 0.7000000476837158,
|
|
9715
|
+
0.7000000476837158, 0.7000000476837158, 0.7000000476837158, 0.7000000476837158,
|
|
9716
|
+
0.7000000476837158, 0.7000000476837158, 0.7000000476837158, 0.7999999523162842,
|
|
9717
|
+
0.7999999523162842, 0.8000000715255737, 0.8000000715255737, 0.8999999761581421, 1, 1, 1
|
|
9697
9718
|
]
|
|
9698
9719
|
}
|
|
9699
9720
|
}
|
package/src/changes/restore.ts
CHANGED
package/src/changes/service.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ChangeEvent,
|
|
5
5
|
ChangeGroup,
|
|
6
6
|
ChangeMetadata,
|
|
7
|
+
ChangeDerivation,
|
|
7
8
|
ModelChange,
|
|
8
9
|
ModelSnapshot,
|
|
9
10
|
NodeHandle,
|
|
@@ -18,6 +19,7 @@ interface ChangeAdapter {
|
|
|
18
19
|
|
|
19
20
|
export class ChangeService {
|
|
20
21
|
private readonly listeners = new Set<(event: ChangeEvent) => void>();
|
|
22
|
+
readonly derivations = new Set<(event: ChangeDerivation) => void>();
|
|
21
23
|
private readonly identities = new WeakMap<AnyNode, NodeHandle>();
|
|
22
24
|
private readonly retained = new WeakSet<AnyNode>();
|
|
23
25
|
private readonly queue: ChangeEvent[] = [];
|
|
@@ -47,7 +49,7 @@ export class ChangeService {
|
|
|
47
49
|
constructor(private readonly context: DrawingContext) {}
|
|
48
50
|
|
|
49
51
|
get observed(): boolean {
|
|
50
|
-
return this.listeners.size > 0;
|
|
52
|
+
return this.listeners.size > 0 || this.derivations.size > 0;
|
|
51
53
|
}
|
|
52
54
|
get replaying(): boolean {
|
|
53
55
|
return this.replayDepth > 0;
|
|
@@ -194,6 +196,20 @@ export class ChangeService {
|
|
|
194
196
|
try {
|
|
195
197
|
if (node) this.touch(node);
|
|
196
198
|
result = callback();
|
|
199
|
+
if (this.before.size && this.derivations.size) {
|
|
200
|
+
const nodes = [...this.before]
|
|
201
|
+
.filter(
|
|
202
|
+
([target, previous]) =>
|
|
203
|
+
target.node.isDestroyed ||
|
|
204
|
+
this.adapter!.diff(target, previous, this.adapter!.capture(target.node)).length,
|
|
205
|
+
)
|
|
206
|
+
.map(([target]) => target.node);
|
|
207
|
+
if (nodes.length) {
|
|
208
|
+
const event = { ...this.metadata, nodes };
|
|
209
|
+
for (const derive of [...this.derivations])
|
|
210
|
+
if (this.derivations.has(derive)) derive(event);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
197
213
|
} catch (error) {
|
|
198
214
|
failure = { error };
|
|
199
215
|
}
|
|
@@ -266,13 +282,22 @@ export class ChangeService {
|
|
|
266
282
|
}
|
|
267
283
|
}
|
|
268
284
|
|
|
269
|
-
group(metadata: ChangeMetadata = {}): ChangeGroup {
|
|
285
|
+
group(metadata: ChangeMetadata = {}): Omit<ChangeGroup, 'cancel'> & {
|
|
286
|
+
end(cancelled?: boolean, restored?: boolean): void;
|
|
287
|
+
} {
|
|
270
288
|
const id = ++this.nextGroup,
|
|
271
289
|
source = metadata.source ?? 'application';
|
|
272
290
|
let active = true;
|
|
273
291
|
this.groups.add(id);
|
|
274
292
|
try {
|
|
275
|
-
this.publish(
|
|
293
|
+
this.publish(
|
|
294
|
+
Object.freeze({
|
|
295
|
+
type: 'groupstart',
|
|
296
|
+
group: id,
|
|
297
|
+
source,
|
|
298
|
+
...(metadata.label === undefined ? {} : { label: metadata.label }),
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
276
301
|
} catch (error) {
|
|
277
302
|
this.groups.delete(id);
|
|
278
303
|
this.publish(Object.freeze({ type: 'groupend', group: id, source, cancelled: true }));
|
|
@@ -284,11 +309,19 @@ export class ChangeService {
|
|
|
284
309
|
if (!active) throw new Error('Change group has ended');
|
|
285
310
|
return this.scope({ ...metadata, group: id }, callback, null);
|
|
286
311
|
},
|
|
287
|
-
end: (cancelled = false) => {
|
|
312
|
+
end: (cancelled = false, restored = false) => {
|
|
288
313
|
if (!active) return;
|
|
289
314
|
active = false;
|
|
290
315
|
this.groups.delete(id);
|
|
291
|
-
this.publish(
|
|
316
|
+
this.publish(
|
|
317
|
+
Object.freeze({
|
|
318
|
+
type: 'groupend',
|
|
319
|
+
group: id,
|
|
320
|
+
source,
|
|
321
|
+
cancelled,
|
|
322
|
+
...(restored ? { restored: true } : {}),
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
292
325
|
},
|
|
293
326
|
});
|
|
294
327
|
}
|
|
@@ -378,6 +411,7 @@ export class ChangeService {
|
|
|
378
411
|
|
|
379
412
|
destroy(): void {
|
|
380
413
|
this.listeners.clear();
|
|
414
|
+
this.derivations.clear();
|
|
381
415
|
this.queue.length = 0;
|
|
382
416
|
this.notifications.length = 0;
|
|
383
417
|
this.replacements.length = 0;
|
package/src/changes/types.ts
CHANGED
|
@@ -104,9 +104,14 @@ export type ModelChange =
|
|
|
104
104
|
|
|
105
105
|
export interface ChangeMetadata {
|
|
106
106
|
readonly source?: string;
|
|
107
|
+
readonly label?: string;
|
|
107
108
|
readonly temporary?: boolean;
|
|
108
109
|
readonly group?: number;
|
|
109
110
|
}
|
|
111
|
+
/** Runs synchronously after an operation's writes, before its final change snapshot. */
|
|
112
|
+
export interface ChangeDerivation extends ChangeMetadata {
|
|
113
|
+
readonly nodes: readonly AnyNode[];
|
|
114
|
+
}
|
|
110
115
|
export interface ChangeBatch {
|
|
111
116
|
readonly type: 'change';
|
|
112
117
|
readonly id: number;
|
|
@@ -126,7 +131,9 @@ export type ChangeEvent =
|
|
|
126
131
|
readonly type: 'groupstart' | 'groupend';
|
|
127
132
|
readonly group: number;
|
|
128
133
|
readonly source: string;
|
|
134
|
+
readonly label?: string;
|
|
129
135
|
readonly cancelled?: boolean;
|
|
136
|
+
readonly restored?: boolean;
|
|
130
137
|
}
|
|
131
138
|
| {
|
|
132
139
|
readonly type: 'dispose' | 'willdispose';
|
|
@@ -136,6 +143,8 @@ export interface ChangeGroup {
|
|
|
136
143
|
readonly id: number;
|
|
137
144
|
run<Result>(callback: () => Result): Result;
|
|
138
145
|
end(cancelled?: boolean): void;
|
|
146
|
+
/** Restore this group's own non-temporary changes, then end it as cancelled. */
|
|
147
|
+
cancel(): void;
|
|
139
148
|
}
|
|
140
149
|
|
|
141
150
|
export class ChangeConflictError extends Error {
|
package/src/changes.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { AnyNode } from './core/types.js';
|
|
2
|
-
import type { ChangeEvent, ChangeMetadata } from './changes/types.js';
|
|
3
|
-
import {
|
|
2
|
+
import type { ChangeEvent, ChangeGroup, ChangeMetadata, ModelChange } from './changes/types.js';
|
|
3
|
+
import { ChangeConflictError, ChangeObserverError } from './changes/types.js';
|
|
4
|
+
import { combineChanges, stateAdapter } from './changes/state.js';
|
|
5
|
+
import { restoreChanges } from './changes/restore.js';
|
|
4
6
|
|
|
5
7
|
export type {
|
|
6
8
|
ChangeBatch,
|
|
7
9
|
ChangeEvent,
|
|
8
10
|
ChangeGroup,
|
|
9
11
|
ChangeMetadata,
|
|
12
|
+
ChangeDerivation,
|
|
10
13
|
ModelChange,
|
|
11
14
|
ModelSnapshot,
|
|
12
15
|
ModelFeatures,
|
|
@@ -35,6 +38,20 @@ export function observeChanges(
|
|
|
35
38
|
drawing.assertLive();
|
|
36
39
|
return drawing.context.changes.observe(listener, stateAdapter);
|
|
37
40
|
}
|
|
41
|
+
/** Derived writes join the same operation and group. Callbacks never run during restoration. */
|
|
42
|
+
export function deriveChanges(
|
|
43
|
+
drawing: AnyNode,
|
|
44
|
+
derive: (event: import('./changes/types.js').ChangeDerivation) => void,
|
|
45
|
+
): () => void {
|
|
46
|
+
drawing.assertLive();
|
|
47
|
+
if (typeof derive !== 'function') throw new TypeError('Change derivation must be a function');
|
|
48
|
+
drawing.context.changes.configure(stateAdapter);
|
|
49
|
+
const callbacks = drawing.context.changes.derivations;
|
|
50
|
+
callbacks.add(derive);
|
|
51
|
+
return () => {
|
|
52
|
+
callbacks.delete(derive);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
38
55
|
export function withChanges<Result>(
|
|
39
56
|
drawing: AnyNode,
|
|
40
57
|
metadata: ChangeMetadata,
|
|
@@ -43,9 +60,81 @@ export function withChanges<Result>(
|
|
|
43
60
|
drawing.assertLive();
|
|
44
61
|
return drawing.context.changes.scope(metadata, callback, drawing);
|
|
45
62
|
}
|
|
46
|
-
export function beginChangeGroup(drawing: AnyNode, metadata: ChangeMetadata = {}) {
|
|
63
|
+
export function beginChangeGroup(drawing: AnyNode, metadata: ChangeMetadata = {}): ChangeGroup {
|
|
47
64
|
drawing.assertLive();
|
|
48
|
-
|
|
65
|
+
const owner = drawing.context.root!;
|
|
66
|
+
let id: number | undefined;
|
|
67
|
+
let changes: readonly ModelChange[] = [];
|
|
68
|
+
let invalidated = false;
|
|
69
|
+
// Subscribe before groupstart so edits made by start observers are captured too.
|
|
70
|
+
const unsubscribe = observeChanges(drawing, (event) => {
|
|
71
|
+
if (event.type === 'dispose') invalidated = true;
|
|
72
|
+
if (id === undefined && event.type === 'groupstart') id = event.group;
|
|
73
|
+
if (event.type === 'change' && event.group === id && !event.temporary)
|
|
74
|
+
changes = combineChanges([...changes, ...event.changes]);
|
|
75
|
+
});
|
|
76
|
+
let group: ReturnType<typeof drawing.context.changes.group>;
|
|
77
|
+
try {
|
|
78
|
+
group = drawing.context.changes.group(metadata);
|
|
79
|
+
id = group.id;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
unsubscribe();
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
let active = true;
|
|
85
|
+
let cancelling = false;
|
|
86
|
+
let depth = 0;
|
|
87
|
+
const assertNotDisposed = () => {
|
|
88
|
+
if (invalidated)
|
|
89
|
+
throw new ChangeConflictError('Permanent disposal invalidated the change group');
|
|
90
|
+
};
|
|
91
|
+
const finish = (cancelled: boolean, restored = false) => {
|
|
92
|
+
active = false;
|
|
93
|
+
changes = [];
|
|
94
|
+
unsubscribe();
|
|
95
|
+
group.end(cancelled, restored);
|
|
96
|
+
};
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
id: group.id,
|
|
99
|
+
run<Result>(callback: () => Result): Result {
|
|
100
|
+
if (!active || cancelling) throw new Error('Change group has ended or is cancelling');
|
|
101
|
+
assertNotDisposed();
|
|
102
|
+
depth++;
|
|
103
|
+
try {
|
|
104
|
+
return group.run(callback);
|
|
105
|
+
} finally {
|
|
106
|
+
depth--;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
end(cancelled = false) {
|
|
110
|
+
if (active && !cancelling) finish(cancelled);
|
|
111
|
+
},
|
|
112
|
+
cancel() {
|
|
113
|
+
if (!active || cancelling) return;
|
|
114
|
+
if (depth) throw new Error('Finish the change callback before cancelling its group');
|
|
115
|
+
assertNotDisposed();
|
|
116
|
+
cancelling = true;
|
|
117
|
+
const failures: unknown[] = [];
|
|
118
|
+
try {
|
|
119
|
+
try {
|
|
120
|
+
if (changes.length) restoreChanges(owner, changes, 'undo');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error instanceof ChangeObserverError) failures.push(error);
|
|
123
|
+
else throw error;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
finish(true, true);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
failures.push(error);
|
|
129
|
+
}
|
|
130
|
+
} finally {
|
|
131
|
+
cancelling = false;
|
|
132
|
+
}
|
|
133
|
+
if (failures.length === 1) throw failures[0];
|
|
134
|
+
if (failures.length)
|
|
135
|
+
throw new AggregateError(failures, 'Change group cancellation observers failed');
|
|
136
|
+
},
|
|
137
|
+
});
|
|
49
138
|
}
|
|
50
139
|
|
|
51
140
|
/** One recorder per context; ordinary change observers are unrestricted. */
|
package/src/dom/images.ts
CHANGED
|
@@ -20,7 +20,8 @@ export class ImageLoader {
|
|
|
20
20
|
return () => {
|
|
21
21
|
probe.removeEventListener('load', loaded);
|
|
22
22
|
probe.removeEventListener('error', failed);
|
|
23
|
-
|
|
23
|
+
// Resetting an already completed source starts a second native error request.
|
|
24
|
+
if (!probe.complete) probe.removeAttribute('src');
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
}
|
package/src/elements/image.ts
CHANGED
|
@@ -44,7 +44,10 @@ export class SvgImage extends Shape<SVGImageElement, ImageOptions> {
|
|
|
44
44
|
|
|
45
45
|
constructor(context: DrawingContext, options: ImageOptions = {}, existing?: SVGImageElement) {
|
|
46
46
|
super(context, options, existing);
|
|
47
|
-
this.onDispose(() =>
|
|
47
|
+
this.onDispose(() => {
|
|
48
|
+
this.cancelRequest();
|
|
49
|
+
this.el.removeAttribute('href');
|
|
50
|
+
});
|
|
48
51
|
if (this.source) this.context.scheduler.enqueue(this, '#image');
|
|
49
52
|
}
|
|
50
53
|
|
package/src/elements/text.ts
CHANGED
|
@@ -63,10 +63,10 @@ export class Text extends TextBase<SVGTextElement, TextAttributes> {
|
|
|
63
63
|
leading(): number;
|
|
64
64
|
leading(value: number): this;
|
|
65
65
|
leading(value?: number): number | this {
|
|
66
|
+
this.assertLive();
|
|
67
|
+
const content = contentFor(this);
|
|
68
|
+
if (value === undefined) return content.leading;
|
|
66
69
|
return mutation(this, () => {
|
|
67
|
-
this.assertLive();
|
|
68
|
-
const content = contentFor(this);
|
|
69
|
-
if (value === undefined) return content.leading;
|
|
70
70
|
if (!Number.isFinite(value) || value < 0)
|
|
71
71
|
throw new RangeError('Leading must be finite and nonnegative');
|
|
72
72
|
content.leading = value;
|
package/src/interchange/clone.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { dependencyRoots, remapIds } from './references.js';
|
|
|
7
7
|
import { classifyReference } from '../resources/references.js';
|
|
8
8
|
import { preflight, reconstruct, validateCloneContent } from './reconstruct.js';
|
|
9
9
|
import type { CloneOptions } from './types.js';
|
|
10
|
-
import { copyTextPolicies } from '../text/
|
|
10
|
+
import { copyTextPolicies } from '../text/policies.js';
|
|
11
11
|
|
|
12
12
|
export function cloneNode<Node extends AnyNode>(source: Node, options: CloneOptions = {}): Node {
|
|
13
13
|
return source.context.changes.run(source, () => {
|