aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-04-11 20:45:18 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-04-11 20:45:18 +0200
commit12e7bf692084e14563ea53b34d3135d2fd1cc11b (patch)
tree6a3d3d672163637ceb173c994e8d266385adc50e
parentchore(deps): bump frontend dependencies (diff)
downloadrefinery-12e7bf692084e14563ea53b34d3135d2fd1cc11b.tar.gz
refinery-12e7bf692084e14563ea53b34d3135d2fd1cc11b.tar.zst
refinery-12e7bf692084e14563ea53b34d3135d2fd1cc11b.zip
feat(web): embed SVG into HTML directly
* Makes sure element IDs and CSS do not interfere with other diagrams in the same HTML document. * Disables SVGO to allow embedding in Docusaurus with CSS intact. * Replaces PNG figures with SVG in documentation.
-rw-r--r--subprojects/docs/docusaurus.config.ts1
-rw-r--r--subprojects/docs/src/css/custom.css4
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig1.pngbin20110 -> 0 bytes
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig1.svg72
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license (renamed from subprojects/docs/src/learn/tutorials/file-system/fig1.png.license)0
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig2.pngbin44020 -> 0 bytes
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig2.svg145
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license (renamed from subprojects/docs/src/learn/tutorials/file-system/fig2.png.license)0
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig3.pngbin26950 -> 0 bytes
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig3.svg124
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license (renamed from subprojects/docs/src/learn/tutorials/file-system/fig3.png.license)0
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig4.pngbin38143 -> 0 bytes
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig4.svg131
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license (renamed from subprojects/docs/src/learn/tutorials/file-system/fig4.png.license)0
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/index.md16
-rw-r--r--subprojects/docs/src/plugins/loadersPlugin.ts72
-rw-r--r--subprojects/frontend/src/graph/export/ExportPanel.tsx52
-rw-r--r--subprojects/frontend/src/graph/export/ExportSettingsStore.ts28
-rw-r--r--subprojects/frontend/src/graph/export/exportDiagram.tsx122
-rw-r--r--yarn.lock132
20 files changed, 801 insertions, 98 deletions
diff --git a/subprojects/docs/docusaurus.config.ts b/subprojects/docs/docusaurus.config.ts
index ed5e450e..62d963a1 100644
--- a/subprojects/docs/docusaurus.config.ts
+++ b/subprojects/docs/docusaurus.config.ts
@@ -52,6 +52,7 @@ export default {
52 markdownOptions satisfies PagesOptions, 52 markdownOptions satisfies PagesOptions,
53 ], 53 ],
54 '@docusaurus/plugin-sitemap', 54 '@docusaurus/plugin-sitemap',
55 './src/plugins/loadersPlugin.ts',
55 './src/plugins/swcMinifyPlugin.ts', 56 './src/plugins/swcMinifyPlugin.ts',
56 ], 57 ],
57 themes: [ 58 themes: [
diff --git a/subprojects/docs/src/css/custom.css b/subprojects/docs/src/css/custom.css
index f9c14bc4..51d9e698 100644
--- a/subprojects/docs/src/css/custom.css
+++ b/subprojects/docs/src/css/custom.css
@@ -125,3 +125,7 @@ code {
125.hero__title { 125.hero__title {
126 font-weight: 800; 126 font-weight: 800;
127} 127}
128
129.markdown svg {
130 max-width: 100%;
131}
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.png b/subprojects/docs/src/learn/tutorials/file-system/fig1.png
deleted file mode 100644
index da30af3c..00000000
--- a/subprojects/docs/src/learn/tutorials/file-system/fig1.png
+++ /dev/null
Binary files differ
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg
new file mode 100644
index 00000000..f39a43fe
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg
@@ -0,0 +1,72 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="157pt" height="242pt" viewBox="-6 -6 169.47000122070312 254" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-_27O8Mh6OPBYQczvSCWrX"><style>.refinery-_27O8Mh6OPBYQczvSCWrX{}.refinery-_27O8Mh6OPBYQczvSCWrX .node{}.refinery-_27O8Mh6OPBYQczvSCWrX .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .node .node-outline{stroke:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .node .node-header{fill:rgb(53, 161, 173);}.refinery-_27O8Mh6OPBYQczvSCWrX .node .node-bg{fill:#fff;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-h .node-header{fill:#e06c75;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-i .node-header{fill:#98c379;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-j .node-header{fill:#c678dd;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-n .node-header{fill:#abcc94;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge{}.refinery-_27O8Mh6OPBYQczvSCWrX .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge .edge-line{stroke:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge .edge-arrow{fill:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN{}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN text{fill:#696c77;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR{}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR text{fill:#ca1243;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-_27O8Mh6OPBYQczvSCWrX .icon-TRUE{fill:#19202b;}.refinery-_27O8Mh6OPBYQczvSCWrX .icon-UNKNOWN{fill:#696c77;}.refinery-_27O8Mh6OPBYQczvSCWrX .icon-ERROR{fill:#ca1243;}.refinery-_27O8Mh6OPBYQczvSCWrX text.label-UNKNOWN{fill:#696c77;}.refinery-_27O8Mh6OPBYQczvSCWrX text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX{}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node{}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge{}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN{}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR{}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-_27O8Mh6OPBYQczvSCWrX text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-_27O8Mh6OPBYQczvSCWrX-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-_27O8Mh6OPBYQczvSCWrX-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs>
3<g id="" class="graph" transform="translate(4, 238)">
4<!-- n0 -->
5<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="5.5" y="-228.5" width="100" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
6
7<rect stroke="none" x="0" y="-234" width="99.3" height="48.80000000000001" rx="12" ry="12" id="" class="node-bg"/>
8<rect stroke="none" x="-4" y="-238" width="107" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-0)" id="" class="node-header"/>
9<text text-anchor="start" x="5" y="-218.2" font-size="12.00">FileSystem::new</text>
10<use x="6" y="-204.4" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
11<g id=""><text text-anchor="start" x="22" y="-194.8" font-size="12.00" class="label label-TRUE">FileSystem</text>
12</g>
13<polyline points="0,-210.6 99.3,-210.6" class="node-outline"/>
14<rect fill="none" x="0" y="-234" width="99.3" height="48.80000000000001" rx="12" ry="12" id="" class="node-outline"/>
15<clipPath id="refinery-_27O8Mh6OPBYQczvSCWrX-clip-0"><rect stroke="none" x="0" y="-234" width="99.3" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n1 -->
17<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="25.5" y="-42.5" width="59" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
18
19<rect stroke="none" x="20.24" y="-48.8" width="58.82000000000001" height="48.8" rx="12" ry="12" id="" class="node-bg"/>
20<rect stroke="none" x="16" y="-52" width="66" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-1)" id="" class="node-header"/>
21<text text-anchor="start" x="25.24" y="-33" font-size="12.00">File::new</text>
22<use x="26.2441" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
23<g id=""><text text-anchor="start" x="42.24" y="-9.6" font-size="12.00" class="label label-TRUE">File</text>
24</g>
25<polyline points="20.24,-25.4 79.06,-25.4" class="node-outline"/>
26<rect fill="none" x="20.24" y="-48.8" width="58.82000000000001" height="48.8" rx="12" ry="12" id="" class="node-outline"/>
27<clipPath id="refinery-_27O8Mh6OPBYQczvSCWrX-clip-1"><rect stroke="none" x="20.24" y="-48.8" width="58.82000000000001" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n0&#45;&gt;n1 -->
29<g id="" class="edge edge-UNKNOWN">
30
31<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M45.29,-185.28C43.46,-174.36 41.55,-161.16 40.65,-149.2 38.5,-120.66 38.5,-113.34 40.65,-84.8 41.26,-76.76 42.32,-68.17 43.5,-60.08" class="edge-line"/>
32<polygon stroke-width="2" points="46.48,-60.83 44.82,-51.71 40.43,-59.88 46.48,-60.83" class="edge-line edge-arrow"/>
33<text text-anchor="start" x="16.71" y="-121.15" font-weight="bold" font-size="10.50">root</text>
34</g>
35<!-- n2 -->
36<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="54.5" y="-143.5" width="57" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
37
38<rect stroke="none" x="49.4" y="-149.2" width="56.50000000000001" height="64.39999999999999" rx="12" ry="12" id="" class="node-bg"/>
39<rect stroke="none" x="45" y="-153" width="64" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-2)" id="" class="node-header"/>
40<text text-anchor="start" x="54.4" y="-133.4" font-size="12.00">Dir::new</text>
41<use x="55.4014" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
42<g id=""><text text-anchor="start" x="70.9" y="-110" font-size="12.00" class="label label-TRUE">File</text>
43</g>
44<use x="55.4014" y="-103.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
45<g id=""><text text-anchor="start" x="71.4" y="-94" font-size="12.00" class="label label-TRUE">Dir</text>
46</g>
47<polyline points="49.4,-125.8 105.9,-125.8" class="node-outline"/>
48<rect fill="none" x="49.4" y="-149.2" width="56.50000000000001" height="64.39999999999999" rx="12" ry="12" id="" class="node-outline"/>
49<clipPath id="refinery-_27O8Mh6OPBYQczvSCWrX-clip-2"><rect stroke="none" x="49.4" y="-149.2" width="56.50000000000001" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g>
50<!-- n0&#45;&gt;n2 -->
51<g id="" class="edge edge-UNKNOWN">
52
53<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M56.86,-185.27C59.23,-177.61 61.94,-168.85 64.6,-160.24" class="edge-line"/>
54<polygon stroke-width="2" points="67.49,-161.27 67.14,-152 61.63,-159.46 67.49,-161.27" class="edge-line edge-arrow"/>
55<text text-anchor="start" x="40.15" y="-171.26" font-weight="bold" font-size="10.50">root</text>
56</g>
57<!-- n2&#45;&gt;n1 -->
58<g id="" class="edge edge-UNKNOWN">
59
60<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M68.02,-84.82C65.52,-76.75 62.82,-68.01 60.28,-59.79" class="edge-line"/>
61<polygon stroke-width="2" points="63.23,-58.97 57.72,-51.51 57.38,-60.78 63.23,-58.97" class="edge-line edge-arrow"/>
62<text text-anchor="start" x="18.87" y="-70.97" font-weight="bold" font-size="10.50">element</text>
63</g>
64<!-- n2&#45;&gt;n2 -->
65<g id="" class="edge edge-UNKNOWN">
66
67<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M105.65,-134.35C115.7,-134.72 123.9,-128.94 123.9,-117 123.9,-110.1 121.16,-105.26 116.91,-102.47" class="edge-line"/>
68<polygon stroke-width="2" points="117.82,-99.54 108.59,-100.39 116.33,-105.48 117.82,-99.54" class="edge-line edge-arrow"/>
69<text text-anchor="start" x="105.88" y="-138.5" font-weight="bold" font-size="10.50">element</text>
70</g>
71</g>
72</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.png.license b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license
index ff75bc7c..ff75bc7c 100644
--- a/subprojects/docs/src/learn/tutorials/file-system/fig1.png.license
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.png b/subprojects/docs/src/learn/tutorials/file-system/fig2.png
deleted file mode 100644
index f42e3d3e..00000000
--- a/subprojects/docs/src/learn/tutorials/file-system/fig2.png
+++ /dev/null
Binary files differ
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg
new file mode 100644
index 00000000..f7e85fd5
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg
@@ -0,0 +1,145 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="178pt" height="443pt" viewBox="-6 -6 190.0399932861328 454.79998779296875" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-qAo8dBdD08mqlzpuHY9q_"><style>.refinery-qAo8dBdD08mqlzpuHY9q_{}.refinery-qAo8dBdD08mqlzpuHY9q_ .node{}.refinery-qAo8dBdD08mqlzpuHY9q_ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-outline{stroke:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-header{fill:rgb(53, 161, 173);}.refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-bg{fill:#fff;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-h .node-header{fill:#e06c75;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-i .node-header{fill:#98c379;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-j .node-header{fill:#c678dd;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-n .node-header{fill:#abcc94;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge{}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge .edge-line{stroke:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge .edge-arrow{fill:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN{}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN text{fill:#696c77;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR{}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR text{fill:#ca1243;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-qAo8dBdD08mqlzpuHY9q_ .icon-TRUE{fill:#19202b;}.refinery-qAo8dBdD08mqlzpuHY9q_ .icon-UNKNOWN{fill:#696c77;}.refinery-qAo8dBdD08mqlzpuHY9q_ .icon-ERROR{fill:#ca1243;}.refinery-qAo8dBdD08mqlzpuHY9q_ text.label-UNKNOWN{fill:#696c77;}.refinery-qAo8dBdD08mqlzpuHY9q_ text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_{}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node{}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge{}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN{}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR{}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-qAo8dBdD08mqlzpuHY9q_ text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-qAo8dBdD08mqlzpuHY9q_-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-qAo8dBdD08mqlzpuHY9q_-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs>
3<g id="" class="graph" transform="translate(4, 438.79998779296875)">
4<!-- n0 -->
5<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="26.5" y="-428.5" width="100" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
6
7<rect stroke="none" x="21.89" y="-434.8" width="99.3" height="48.80000000000001" rx="12" ry="12" id="" class="node-bg"/>
8<rect stroke="none" x="17" y="-438" width="107" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-0)" id="" class="node-header"/>
9<text text-anchor="start" x="26.89" y="-419" font-size="12.00">FileSystem::new</text>
10<use x="27.891" y="-405.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
11<g id=""><text text-anchor="start" x="43.89" y="-395.6" font-size="12.00" class="label label-TRUE">FileSystem</text>
12</g>
13<polyline points="21.89,-411.4 121.19,-411.4" class="node-outline"/>
14<rect fill="none" x="21.89" y="-434.8" width="99.3" height="48.80000000000001" rx="12" ry="12" id="" class="node-outline"/>
15<clipPath id="refinery-qAo8dBdD08mqlzpuHY9q_-clip-0"><rect stroke="none" x="21.89" y="-434.8" width="99.3" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n1 -->
17<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="34.5" y="-42.5" width="59" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
18
19<rect stroke="none" x="29.14" y="-48.8" width="58.81" height="48.8" rx="12" ry="12" id="" class="node-bg"/>
20<rect stroke="none" x="25" y="-52" width="66" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-1)" id="" class="node-header"/>
21<text text-anchor="start" x="34.14" y="-33" font-size="12.00">File::new</text>
22<use x="35.1351" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
23<g id=""><text text-anchor="start" x="51.14" y="-9.6" font-size="12.00" class="label label-TRUE">File</text>
24</g>
25<polyline points="29.14,-25.4 87.95,-25.4" class="node-outline"/>
26<rect fill="none" x="29.14" y="-48.8" width="58.81" height="48.8" rx="12" ry="12" id="" class="node-outline"/>
27<clipPath id="refinery-qAo8dBdD08mqlzpuHY9q_-clip-1"><rect stroke="none" x="29.14" y="-48.8" width="58.81" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n0&#45;&gt;n1 -->
29<g id="" class="edge edge-UNKNOWN">
30
31<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M52.75,-386.01C45.57,-375.62 38.22,-362.85 34.54,-350 5.26,-247.62 32.09,-120.09 48.29,-59.9" class="edge-line"/>
32<polygon stroke-width="2" points="51.18,-60.96 50.56,-51.71 45.27,-59.32 51.18,-60.96" class="edge-line edge-arrow"/>
33<text text-anchor="start" x="0" y="-219.98" font-weight="bold" font-size="10.50">root</text>
34</g>
35<!-- n2 -->
36<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="48.5" y="-344.5" width="57" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
37
38<rect stroke="none" x="43.29" y="-350" width="56.50000000000001" height="64.39999999999998" rx="12" ry="12" id="" class="node-bg"/>
39<rect stroke="none" x="39" y="-354" width="64" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-2)" id="" class="node-header"/>
40<text text-anchor="start" x="48.29" y="-334.2" font-size="12.00">Dir::new</text>
41<use x="49.2923" y="-320.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
42<g id=""><text text-anchor="start" x="64.79" y="-310.8" font-size="12.00" class="label label-TRUE">File</text>
43</g>
44<use x="49.2923" y="-304.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
45<g id=""><text text-anchor="start" x="65.29" y="-294.8" font-size="12.00" class="label label-TRUE">Dir</text>
46</g>
47<polyline points="43.29,-326.6 99.79,-326.6" class="node-outline"/>
48<rect fill="none" x="43.29" y="-350" width="56.50000000000001" height="64.39999999999998" rx="12" ry="12" id="" class="node-outline"/>
49<clipPath id="refinery-qAo8dBdD08mqlzpuHY9q_-clip-2"><rect stroke="none" x="43.29" y="-350" width="56.50000000000001" height="64.39999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
50<!-- n0&#45;&gt;n2 -->
51<g id="" class="edge edge-UNKNOWN">
52
53<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M71.54,-386.07C71.54,-378.58 71.54,-370.05 71.54,-361.62" class="edge-line"/>
54<polygon stroke-width="2" points="74.6,-361.69 71.54,-352.94 68.48,-361.69 74.6,-361.69" class="edge-line edge-arrow"/>
55<text text-anchor="start" x="49.21" y="-372.49" font-weight="bold" font-size="10.50">root</text>
56</g>
57<!-- n2&#45;&gt;n1 -->
58<!-- n2&#45;&gt;n1 -->
59<g id="" class="edge edge-UNKNOWN">
60
61<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M66.95,-285.69C62.24,-252.11 55.24,-197 52.54,-149.2 50.93,-120.62 51.1,-113.39 52.54,-84.8 52.94,-76.89 53.64,-68.42 54.41,-60.42" class="edge-line"/>
62<polygon stroke-width="2" points="57.45,-60.81 55.32,-51.79 51.36,-60.17 57.45,-60.81" class="edge-line edge-arrow"/>
63<text text-anchor="start" x="8.96" y="-153.34" font-weight="bold" font-size="10.50">element</text>
64</g><g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
65
66<rect stroke="none" x="100.02" y="-249.6" width="65.04" height="64.4" rx="12" ry="12" id="" class="node-bg"/>
67<rect stroke="none" x="96" y="-253" width="73" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-3)" id="" class="node-header"/>
68<text text-anchor="start" x="105.02" y="-233.8" font-size="12.00">resources</text>
69<use x="106.024" y="-220.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
70<g id=""><text text-anchor="start" x="121.53" y="-210.4" font-size="12.00" class="label label-TRUE">File</text>
71</g>
72<use x="106.024" y="-204.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
73<g id=""><text text-anchor="start" x="122.02" y="-194.4" font-size="12.00" class="label label-TRUE">Dir</text>
74</g>
75<polyline points="100.02,-226.2 165.06,-226.2" class="node-outline"/>
76<rect fill="none" x="100.02" y="-249.6" width="65.04" height="64.4" rx="12" ry="12" id="" class="node-outline"/>
77<clipPath id="refinery-qAo8dBdD08mqlzpuHY9q_-clip-3"><rect stroke="none" x="100.02" y="-249.6" width="65.04" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
78<!-- n2&#45;&gt;n2 -->
79
80<!-- n2&#45;&gt;n2 -->
81<g id="" class="edge edge-UNKNOWN">
82
83<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M99.54,-335.15C109.59,-335.52 117.79,-329.74 117.79,-317.8 117.79,-310.9 115.05,-306.06 110.8,-303.27" class="edge-line"/>
84<polygon stroke-width="2" points="111.71,-300.34 102.48,-301.19 110.22,-306.28 111.71,-300.34" class="edge-line edge-arrow"/>
85<text text-anchor="start" x="99.78" y="-339.3" font-weight="bold" font-size="10.50">element</text>
86</g><g id="" class="edge edge-UNKNOWN">
87
88<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M100.14,-386.27C110.37,-376.29 120.79,-363.74 126.54,-350 138.25,-322.01 139.44,-287.68 137.86,-261.1" class="edge-line"/>
89<polygon stroke-width="2" points="140.92,-261 137.2,-252.51 134.82,-261.47 140.92,-261" class="edge-line edge-arrow"/>
90<text text-anchor="start" x="112.62" y="-325.05" font-weight="bold" font-size="10.50">root</text>
91</g>
92
93<!-- n2&#45;&gt;n3 -->
94<g id="" class="edge edge-UNKNOWN">
95
96<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M96.92,-275.87C100.36,-270.32 103.89,-264.63 107.32,-259.08" class="edge-line"/>
97<polygon stroke-width="2" points="94.45,-274.04 92.44,-283.09 99.65,-277.27 94.45,-274.04" class="edge-line edge-arrow"/>
98<polygon stroke-width="2" points="109.76,-260.96 111.77,-251.9 104.56,-257.73 109.76,-260.96" class="edge-line edge-arrow"/>
99<text text-anchor="start" x="58.37" y="-271.89" font-weight="bold" font-size="10.50">element</text>
100</g>
101<!-- n3&#45;&gt;n1 -->
102<g id="" class="edge edge-UNKNOWN">
103
104<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M134.25,-185.32C134.53,-157.62 131.82,-116.65 116.54,-84.8 111.59,-74.48 104.13,-64.85 96.2,-56.47" class="edge-line"/>
105<polygon stroke-width="2" points="98.37,-54.31 89.99,-50.34 94.06,-58.67 98.37,-54.31" class="edge-line edge-arrow"/>
106<text text-anchor="start" x="126.45" y="-116.67" font-weight="bold" font-size="10.50">element</text>
107</g>
108<!-- n4 -->
109<g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
110
111<rect stroke="none" x="61.54" y="-149.2" width="46.00000000000001" height="64.39999999999999" rx="12" ry="12" id="" class="node-bg"/>
112<rect stroke="none" x="57" y="-153" width="54" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-4)" id="" class="node-header"/>
113<text text-anchor="start" x="74.16" y="-133.4" font-size="12.00">img</text>
114<use x="67.5423" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
115<g id=""><text text-anchor="start" x="83.04" y="-110" font-size="12.00" class="label label-TRUE">File</text>
116</g>
117<use x="67.5423" y="-103.8" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-UNKNOWN"/>
118<g id=""><text text-anchor="start" x="83.54" y="-94" font-size="12.00" class="label label-UNKNOWN">Dir</text>
119</g>
120<polyline points="61.54,-125.8 107.54,-125.8" class="node-outline"/>
121<rect fill="none" x="61.54" y="-149.2" width="46.00000000000001" height="64.39999999999999" rx="12" ry="12" id="" class="node-outline"/>
122<clipPath id="refinery-qAo8dBdD08mqlzpuHY9q_-clip-4"><rect stroke="none" x="61.54" y="-149.2" width="46.00000000000001" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g>
123<!-- n3&#45;&gt;n4 -->
124<g id="" class="edge edge-TRUE">
125
126<path fill="none" stroke-width="2" d="M117.35,-185.27C113.33,-177.02 108.93,-167.99 104.69,-159.29" class="edge-line"/>
127<polygon stroke-width="2" points="107.55,-158.17 100.96,-151.65 102.04,-160.86 107.55,-158.17" class="edge-line edge-arrow"/>
128<text text-anchor="start" x="64.91" y="-171.25" font-weight="bold" font-size="10.50">element</text>
129</g>
130<!-- n4&#45;&gt;n1 -->
131<g id="" class="edge edge-UNKNOWN">
132
133<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M75.59,-84.82C73.28,-76.75 70.77,-68.01 68.41,-59.79" class="edge-line"/>
134<polygon stroke-width="2" points="71.4,-59.1 66.04,-51.53 65.51,-60.79 71.4,-59.1" class="edge-line edge-arrow"/>
135<text text-anchor="start" x="26.85" y="-70.97" font-weight="bold" font-size="10.50">element</text>
136</g>
137<!-- n4&#45;&gt;n2 -->
138<g id="" class="edge edge-UNKNOWN">
139
140<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M82.5,-149.17C80.3,-182.82 76.79,-236.62 74.33,-274.16" class="edge-line"/>
141<polygon stroke-width="2" points="71.29,-273.79 73.77,-282.72 77.4,-274.19 71.29,-273.79" class="edge-line edge-arrow"/>
142<text text-anchor="start" x="34.45" y="-221.7" font-weight="bold" font-size="10.50">element</text>
143</g>
144</g>
145</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.png.license b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license
index ff75bc7c..ff75bc7c 100644
--- a/subprojects/docs/src/learn/tutorials/file-system/fig2.png.license
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.png b/subprojects/docs/src/learn/tutorials/file-system/fig3.png
deleted file mode 100644
index 9506417a..00000000
--- a/subprojects/docs/src/learn/tutorials/file-system/fig3.png
+++ /dev/null
Binary files differ
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg
new file mode 100644
index 00000000..4137e035
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg
@@ -0,0 +1,124 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="526pt" height="88pt" viewBox="-6 -6 537.6199951171875 100.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-KGyg4OhNwvGOkw-tAzU-g"><style>.refinery-KGyg4OhNwvGOkw-tAzU-g{}.refinery-KGyg4OhNwvGOkw-tAzU-g .node{}.refinery-KGyg4OhNwvGOkw-tAzU-g .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-outline{stroke:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-header{fill:rgb(53, 161, 173);}.refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-bg{fill:#fff;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-h .node-header{fill:#e06c75;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-i .node-header{fill:#98c379;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-j .node-header{fill:#c678dd;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-n .node-header{fill:#abcc94;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge{}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge .edge-line{stroke:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge .edge-arrow{fill:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN{}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN text{fill:#696c77;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR{}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR text{fill:#ca1243;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-KGyg4OhNwvGOkw-tAzU-g .icon-TRUE{fill:#19202b;}.refinery-KGyg4OhNwvGOkw-tAzU-g .icon-UNKNOWN{fill:#696c77;}.refinery-KGyg4OhNwvGOkw-tAzU-g .icon-ERROR{fill:#ca1243;}.refinery-KGyg4OhNwvGOkw-tAzU-g text.label-UNKNOWN{fill:#696c77;}.refinery-KGyg4OhNwvGOkw-tAzU-g text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g{}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node{}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge{}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN{}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR{}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KGyg4OhNwvGOkw-tAzU-g text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KGyg4OhNwvGOkw-tAzU-g-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs>
3<g id="" class="graph" transform="translate(4, 84.4000015258789)">
4<!-- n0 -->
5<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="5.5" y="-66.5" width="100" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
6
7<rect stroke="none" x="0" y="-72.4" width="99.3" height="64.4" rx="12" ry="12" id="" class="node-bg"/>
8<rect stroke="none" x="-4" y="-76" width="107" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-0)" id="" class="node-header"/>
9<text text-anchor="start" x="5" y="-56.6" font-size="12.00">FileSystem::new</text>
10<use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="6" y="-43" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
11<g id=""><text text-anchor="start" x="21.76" y="-17.2" font-size="12.00" class="label label-TRUE">FileSystem</text>
12</g><g id=""><text text-anchor="start" x="22" y="-33.2" font-size="12.00" class="label label-UNKNOWN">exists</text>
13</g>
14<polyline points="0,-49 99.3,-49" class="node-outline"/>
15
16<rect fill="none" x="0" y="-72.4" width="99.3" height="64.4" rx="12" ry="12" id="" class="node-outline"/>
17
18<clipPath id="refinery-KGyg4OhNwvGOkw-tAzU-g-clip-0"><rect stroke="none" x="0" y="-72.4" width="99.3" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
19<!-- n1 -->
20<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="140.5" y="-66.5" width="60" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
21
22<rect stroke="none" x="135.15" y="-72.4" width="59" height="64.4" rx="12" ry="12" id="" class="node-bg"/>
23<rect stroke="none" x="131" y="-76" width="67" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-1)" id="" class="node-header"/>
24<text text-anchor="start" x="140.24" y="-56.6" font-size="12.00">File::new</text>
25<use x="141.151" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="141.151" y="-43" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
26<g id=""><text text-anchor="start" x="157.15" y="-17.2" font-size="12.00" class="label label-TRUE">File</text>
27</g><g id=""><text text-anchor="start" x="156.78" y="-33.2" font-size="12.00" class="label label-UNKNOWN">exists</text>
28</g>
29<polyline points="135.15,-49 194.15,-49" class="node-outline"/>
30
31<rect fill="none" x="135.15" y="-72.4" width="59" height="64.4" rx="12" ry="12" id="" class="node-outline"/>
32
33<clipPath id="refinery-KGyg4OhNwvGOkw-tAzU-g-clip-1"><rect stroke="none" x="135.15" y="-72.4" width="59" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g id="" class="edge edge-UNKNOWN">
34
35<path fill="none" stroke-dasharray="5,2" d="M99.21,-51.89C109.72,-50.7 117.3,-46.8 117.3,-40.2 117.3,-36.28 114.63,-33.31 110.26,-31.3" class="edge-line"/>
36<polygon points="111.23,-27.94 100.68,-28.89 109.52,-34.73 111.23,-27.94" class="edge-line edge-arrow"/>
37<text text-anchor="middle" x="116.8" y="-54.85" font-size="10.50">equals</text>
38</g>
39<!-- n2 -->
40<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="235.5" y="-74.5" width="59" height="81" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
41
42<rect stroke="none" x="230.15" y="-80.4" width="58.99999999999997" height="80.4" rx="12" ry="12" id="" class="node-bg"/>
43<rect stroke="none" x="226" y="-84" width="66" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-2)" id="" class="node-header"/>
44<text text-anchor="start" x="236.4" y="-64.6" font-size="12.00">Dir::new</text>
45<use x="236.151" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="236.151" y="-51" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
46<g id=""><text text-anchor="start" x="252.15" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
47</g><g id=""><text text-anchor="start" x="251.78" y="-41.2" font-size="12.00" class="label label-UNKNOWN">exists</text>
48</g>
49<polyline points="230.15,-57 289.15,-57" class="node-outline"/><use x="236.151" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
50
51
52<rect fill="none" x="230.15" y="-80.4" width="58.99999999999997" height="80.4" rx="12" ry="12" id="" class="node-outline"/><g id=""><text text-anchor="start" x="252.15" y="-9.2" font-size="12.00" class="label label-TRUE">Dir</text>
53</g>
54
55
56<clipPath id="refinery-KGyg4OhNwvGOkw-tAzU-g-clip-2"><rect stroke="none" x="230.15" y="-80.4" width="58.99999999999997" height="80.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
57<!-- n3 -->
58<!-- n2&#45;&gt;n2 -->
59<g id="" class="edge edge-UNKNOWN">
60
61<path fill="none" stroke-dasharray="5,2" d="M288.87,-55.27C298.99,-55.43 307.15,-50.41 307.15,-40.2 307.15,-34.14 304.27,-29.91 299.82,-27.5" class="edge-line"/>
62<polygon points="300.87,-24.15 290.35,-25.45 299.38,-30.99 300.87,-24.15" class="edge-line edge-arrow"/>
63<text text-anchor="middle" x="305.48" y="-58.42" font-size="10.50">equals</text>
64</g>
65<!-- n3 -->
66<g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
67
68<rect stroke="none" x="325.13" y="-80.4" width="65.04000000000002" height="80.4" rx="12" ry="12" id="" class="node-bg"/>
69<rect stroke="none" x="321" y="-84" width="73" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-3)" id="" class="node-header"/>
70<text text-anchor="start" x="330.13" y="-64.6" font-size="12.00">resources</text>
71<use x="331.133" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="331.133" y="-51" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
72<g id=""><text text-anchor="start" x="347.13" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
73</g><g id=""><text text-anchor="start" x="346.76" y="-41.2" font-size="12.00" class="label label-TRUE">exists</text>
74</g>
75<polyline points="325.13,-57 390.17,-57" class="node-outline"/><use x="331.133" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
76
77
78<rect fill="none" x="325.13" y="-80.4" width="65.04000000000002" height="80.4" rx="12" ry="12" id="" class="node-outline"/><g id=""><text text-anchor="start" x="347.13" y="-9.2" font-size="12.00" class="label label-TRUE">Dir</text>
79</g>
80
81
82<clipPath id="refinery-KGyg4OhNwvGOkw-tAzU-g-clip-3"><rect stroke="none" x="325.13" y="-80.4" width="65.04000000000002" height="80.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g id="" class="edge edge-UNKNOWN">
83
84<path fill="none" stroke-dasharray="5,2" d="M193.87,-52.27C203.99,-52.4 212.15,-48.38 212.15,-40.2 212.15,-35.47 209.42,-32.13 205.17,-30.18" class="edge-line"/>
85<polygon points="205.83,-26.75 195.36,-28.4 204.57,-33.63 205.83,-26.75" class="edge-line edge-arrow"/>
86<text text-anchor="middle" x="210.48" y="-55.42" font-size="10.50">equals</text>
87</g>
88<!-- n4 -->
89
90<!-- n3&#45;&gt;n3 -->
91<g id="" class="edge edge-TRUE">
92
93<path fill="none" d="M389.72,-55.27C400.01,-55.12 408.17,-50.09 408.17,-40.2 408.17,-34.33 405.29,-30.17 400.8,-27.73" class="edge-line"/>
94<polygon points="401.73,-24.35 391.19,-25.47 400.13,-31.16 401.73,-24.35" class="edge-line edge-arrow"/>
95<text text-anchor="middle" x="406.34" y="-58.41" font-size="10.50">equals</text>
96</g>
97<!-- n4 -->
98<g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
99
100<rect stroke="none" x="426.15" y="-80.4" width="59" height="80.4" rx="12" ry="12" id="" class="node-bg"/>
101<rect stroke="none" x="422" y="-84" width="67" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-4)" id="" class="node-header"/>
102<text text-anchor="start" x="445.27" y="-64.6" font-size="12.00">img</text>
103<use x="432.151" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="432.151" y="-51" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
104<g id=""><text text-anchor="start" x="448.15" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
105</g><g id=""><text text-anchor="start" x="447.78" y="-41.2" font-size="12.00" class="label label-TRUE">exists</text>
106</g>
107<polyline points="426.15,-57 485.15,-57" class="node-outline"/><use x="432.151" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
108
109
110<rect fill="none" x="426.15" y="-80.4" width="59" height="80.4" rx="12" ry="12" id="" class="node-outline"/><g id=""><text text-anchor="start" x="448.15" y="-9.2" font-size="12.00" class="label label-UNKNOWN">Dir</text>
111</g>
112
113
114<clipPath id="refinery-KGyg4OhNwvGOkw-tAzU-g-clip-4"><rect stroke="none" x="426.15" y="-80.4" width="59" height="80.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
115
116<!-- n4&#45;&gt;n4 -->
117<g id="" class="edge edge-TRUE">
118
119<path fill="none" d="M484.87,-55.27C494.99,-55.43 503.15,-50.41 503.15,-40.2 503.15,-34.14 500.27,-29.91 495.82,-27.5" class="edge-line"/>
120<polygon points="496.87,-24.15 486.35,-25.45 495.38,-30.99 496.87,-24.15" class="edge-line edge-arrow"/>
121<text text-anchor="middle" x="501.48" y="-58.42" font-size="10.50">equals</text>
122</g>
123</g>
124</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.png.license b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license
index ff75bc7c..ff75bc7c 100644
--- a/subprojects/docs/src/learn/tutorials/file-system/fig3.png.license
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.png b/subprojects/docs/src/learn/tutorials/file-system/fig4.png
deleted file mode 100644
index cc012f24..00000000
--- a/subprojects/docs/src/learn/tutorials/file-system/fig4.png
+++ /dev/null
Binary files differ
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg
new file mode 100644
index 00000000..704f9404
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg
@@ -0,0 +1,131 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="204pt" height="358pt" viewBox="-6 -6 215.91000366210938 370" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-D5mYxKifz-hFmWmmvYTA9"><style>.refinery-D5mYxKifz-hFmWmmvYTA9{}.refinery-D5mYxKifz-hFmWmmvYTA9 .node{}.refinery-D5mYxKifz-hFmWmmvYTA9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-outline{stroke:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-header{fill:rgb(53, 161, 173);}.refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-bg{fill:#fff;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-i .node-header{fill:#98c379;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge{}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge .edge-line{stroke:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge .edge-arrow{fill:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN{}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN text{fill:#696c77;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR{}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR text{fill:#ca1243;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-D5mYxKifz-hFmWmmvYTA9 .icon-TRUE{fill:#19202b;}.refinery-D5mYxKifz-hFmWmmvYTA9 .icon-UNKNOWN{fill:#696c77;}.refinery-D5mYxKifz-hFmWmmvYTA9 .icon-ERROR{fill:#ca1243;}.refinery-D5mYxKifz-hFmWmmvYTA9 text.label-UNKNOWN{fill:#696c77;}.refinery-D5mYxKifz-hFmWmmvYTA9 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9{}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node{}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge{}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN{}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR{}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-D5mYxKifz-hFmWmmvYTA9 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-D5mYxKifz-hFmWmmvYTA9-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-D5mYxKifz-hFmWmmvYTA9-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs>
3<g id="" class="graph" transform="translate(4, 354)">
4<!-- n0 -->
5<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="40.5" y="-344.5" width="100" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
6
7<rect stroke="none" x="35.76" y="-350" width="99.30000000000001" height="48.80000000000001" rx="12" ry="12" id="" class="node-bg"/>
8<rect stroke="none" x="31" y="-354" width="107" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-0)" id="" class="node-header"/>
9<text text-anchor="start" x="40.76" y="-334.2" font-size="12.00">FileSystem::new</text>
10<use x="41.7559" y="-320.40000000000003" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
11<g id=""><text text-anchor="start" x="57.76" y="-310.8" font-size="12.00" class="label label-TRUE">FileSystem</text>
12</g>
13<polyline points="35.76,-326.6 135.06,-326.6" class="node-outline"/>
14<rect fill="none" x="35.76" y="-350" width="99.30000000000001" height="48.80000000000001" rx="12" ry="12" id="" class="node-outline"/>
15<clipPath id="refinery-D5mYxKifz-hFmWmmvYTA9-clip-0"><rect stroke="none" x="35.76" y="-350" width="99.30000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n1 -->
17<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="5.5" y="-50.5" width="59" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
18
19<rect stroke="none" x="0" y="-56.6" width="58.81" height="48.800000000000004" rx="12" ry="12" id="" class="node-bg"/>
20<rect stroke="none" x="-4" y="-60" width="66" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-1)" id="" class="node-header"/>
21<text text-anchor="start" x="5" y="-40.8" font-size="12.00">File::new</text>
22<use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
23<g id=""><text text-anchor="start" x="22" y="-17.4" font-size="12.00" class="label label-TRUE">File</text>
24</g>
25<polyline points="0,-33.2 58.81,-33.2" class="node-outline"/>
26<rect fill="none" x="0" y="-56.6" width="58.81" height="48.800000000000004" rx="12" ry="12" id="" class="node-outline"/>
27<clipPath id="refinery-D5mYxKifz-hFmWmmvYTA9-clip-1"><rect stroke="none" x="0" y="-56.6" width="58.81" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n2 -->
29<g id="" class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="62.5" y="-259.5" width="57" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg" id=""/>
30
31<rect stroke="none" x="57.16" y="-265.2" width="56.5" height="64.39999999999998" rx="12" ry="12" id="" class="node-bg"/>
32<rect stroke="none" x="53" y="-269" width="64" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-2)" id="" class="node-header"/>
33<text text-anchor="start" x="62.16" y="-249.4" font-size="12.00">Dir::new</text>
34<use x="63.1572" y="-235.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
35<g id=""><text text-anchor="start" x="78.66" y="-226" font-size="12.00" class="label label-TRUE">File</text>
36</g>
37<polyline points="57.16,-241.8 113.66,-241.8" class="node-outline"/><use x="63.1572" y="-219.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
38
39<rect fill="none" x="57.16" y="-265.2" width="56.5" height="64.39999999999998" rx="12" ry="12" id="" class="node-outline"/><g id=""><text text-anchor="start" x="79.16" y="-210" font-size="12.00" class="label label-TRUE">Dir</text>
40</g>
41
42<clipPath id="refinery-D5mYxKifz-hFmWmmvYTA9-clip-2"><rect stroke="none" x="57.16" y="-265.2" width="56.5" height="64.39999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g><g id="" class="edge edge-UNKNOWN">
43
44<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M67.32,-301.34C60.16,-290.87 52.64,-278 48.41,-265.2 26.21,-198.07 25.48,-114.34 27.24,-68.05" class="edge-line"/>
45<polygon stroke-width="2" points="30.3,-68.21 27.64,-59.33 24.18,-67.93 30.3,-68.21" class="edge-line edge-arrow"/>
46<text text-anchor="start" x="8.62" y="-185.04" font-weight="bold" font-size="10.50">root</text>
47</g>
48<!-- n3 -->
49<!-- n2&#45;&gt;n2 -->
50
51<!-- n3 -->
52<g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
53
54<rect stroke="none" x="101.89" y="-164.8" width="65.04" height="64.4" rx="12" ry="12" id="" class="node-bg"/>
55<rect stroke="none" x="97" y="-168" width="73" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-3)" id="" class="node-header"/>
56<text text-anchor="start" x="106.89" y="-149" font-size="12.00">resources</text>
57<use x="107.889" y="-135.4" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
58<g id=""><text text-anchor="start" x="123.39" y="-125.6" font-size="12.00" class="label label-TRUE">File</text>
59</g>
60<polyline points="101.89,-141.4 166.93,-141.4" class="node-outline"/><use x="107.889" y="-119.4" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
61
62<rect fill="none" x="101.89" y="-164.8" width="65.04" height="64.4" rx="12" ry="12" id="" class="node-outline"/><g id=""><text text-anchor="start" x="123.89" y="-109.6" font-size="12.00" class="label label-TRUE">Dir</text>
63</g>
64
65<clipPath id="refinery-D5mYxKifz-hFmWmmvYTA9-clip-3"><rect stroke="none" x="101.89" y="-164.8" width="65.04" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
66<!-- n4 -->
67<g id="" class="edge edge-UNKNOWN">
68
69<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M114.62,-301.32C124.8,-291.4 135.02,-278.94 140.41,-265.2 151.53,-236.82 149.44,-202.32 144.89,-175.77" class="edge-line"/>
70<polygon stroke-width="2" points="147.96,-175.54 143.31,-167.52 141.95,-176.69 147.96,-175.54" class="edge-line edge-arrow"/>
71<text text-anchor="start" x="125.22" y="-239.21" font-weight="bold" font-size="10.50">root</text>
72</g>
73<!-- n3&#45;&gt;n3 -->
74<g id="" class="edge edge-UNKNOWN">
75
76<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M76.66,-200.94C66.45,-164.72 49.64,-105.03 39.06,-67.48" class="edge-line"/>
77<polygon stroke-width="2" points="42.14,-67.1 36.82,-59.5 36.24,-68.76 42.14,-67.1" class="edge-line edge-arrow"/>
78<text text-anchor="start" x="12.66" y="-132.63" font-weight="bold" font-size="10.50">element</text>
79</g>
80<!-- n4 -->
81<!-- n3&#45;&gt;n1 -->
82<g id="" class="edge edge-UNKNOWN">
83
84<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M102.05,-101.27C89.51,-89.53 75.18,-76.09 62.57,-64.28" class="edge-line"/>
85<polygon stroke-width="2" points="64.85,-62.21 56.37,-58.46 60.66,-66.68 64.85,-62.21" class="edge-line edge-arrow"/>
86<text text-anchor="start" x="34.34" y="-82.82" font-weight="bold" font-size="10.50">element</text>
87</g>
88<!-- n4 -->
89<g id="" class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
90
91<rect stroke="none" x="76.91" y="-64.4" width="119" height="64.4" rx="12" ry="12" id="" class="node-bg"/>
92<rect stroke="none" x="72" y="-68" width="127" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-4)" id="" class="node-header"/>
93<text text-anchor="start" x="126.02" y="-48.6" font-size="12.00">img</text>
94<use x="82.9072" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/><use x="82.9072" y="-35" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-ERROR"/>
95<g id=""><text text-anchor="start" x="98.91" y="-9.2" font-size="12.00" class="label label-TRUE">File</text>
96</g><g id=""><text text-anchor="start" x="98.59" y="-25.2" font-size="12.00" class="label label-ERROR">invalidContainer</text>
97</g>
98<polyline points="76.91,-41 195.91,-41" class="node-outline"/>
99
100<rect fill="none" x="76.91" y="-64.4" width="119" height="64.4" rx="12" ry="12" id="" class="node-outline"/>
101
102<clipPath id="refinery-D5mYxKifz-hFmWmmvYTA9-clip-4"><rect stroke="none" x="76.91" y="-64.4" width="119" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g id="" class="edge edge-UNKNOWN">
103
104<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M85.41,-301.27C85.41,-293.78 85.41,-285.25 85.41,-276.82" class="edge-line"/>
105<polygon stroke-width="2" points="88.47,-276.89 85.41,-268.14 82.34,-276.89 88.47,-276.89" class="edge-line edge-arrow"/>
106<text text-anchor="start" x="63.08" y="-287.69" font-weight="bold" font-size="10.50">root</text>
107</g>
108<g id="" class="edge edge-UNKNOWN">
109
110<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M113.4,-250.35C123.46,-250.72 131.66,-244.94 131.66,-233 131.66,-226.1 128.92,-221.26 124.66,-218.47" class="edge-line"/>
111<polygon stroke-width="2" points="125.57,-215.54 116.34,-216.39 124.09,-221.48 125.57,-215.54" class="edge-line edge-arrow"/>
112<text text-anchor="start" x="113.64" y="-254.5" font-weight="bold" font-size="10.50">element</text>
113</g>
114<!-- n4&#45;&gt;n4 -->
115<g id="" class="edge edge-UNKNOWN">
116
117<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M106.08,-190.49C108.65,-185.33 111.28,-180.05 113.85,-174.88" class="edge-line"/>
118<polygon stroke-width="2" points="103.42,-188.96 102.26,-198.16 108.9,-191.69 103.42,-188.96" class="edge-line edge-arrow"/>
119<polygon stroke-width="2" points="116.5,-176.44 117.65,-167.24 111.01,-173.71 116.5,-176.44" class="edge-line edge-arrow"/>
120<text text-anchor="start" x="66.38" y="-186.84" font-weight="bold" font-size="10.50">element</text>
121</g>
122
123<!-- n3&#45;&gt;n4 -->
124<g id="" class="edge edge-ERROR">
125
126<path fill="none" stroke-width="2" d="M135.04,-100.47C135.2,-92.58 135.38,-83.98 135.54,-75.64" class="edge-line"/>
127<polygon stroke-width="2" points="138.6,-75.97 135.72,-67.16 132.48,-75.84 138.6,-75.97" class="edge-line edge-arrow"/>
128<text text-anchor="start" x="91.82" y="-86.47" font-weight="bold" font-size="10.50">element</text>
129</g>
130</g>
131</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.png.license b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license
index ff75bc7c..ff75bc7c 100644
--- a/subprojects/docs/src/learn/tutorials/file-system/fig4.png.license
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license
diff --git a/subprojects/docs/src/learn/tutorials/file-system/index.md b/subprojects/docs/src/learn/tutorials/file-system/index.md
index 1d15512f..365d0fba 100644
--- a/subprojects/docs/src/learn/tutorials/file-system/index.md
+++ b/subprojects/docs/src/learn/tutorials/file-system/index.md
@@ -42,7 +42,9 @@ import Link from '@docusaurus/Link';
42- Notice that the syntax is essentially identical to [Xcore](https://wiki.eclipse.org/Xcore). 42- Notice that the syntax is essentially identical to [Xcore](https://wiki.eclipse.org/Xcore).
43- Review the partial model visualization. You should get something like this: 43- Review the partial model visualization. You should get something like this:
44 44
45![Initial model](fig1.png) 45import Fig1 from './fig1.svg';
46
47<Fig1 title="Initial model" />
46 48
47- Add some statements about a partial model: 49- Add some statements about a partial model:
48 50
@@ -64,7 +66,9 @@ File(img).
64scope node = 10. 66scope node = 10.
65``` 67```
66 68
67![Partial model extended with new facts](fig2.png) 69import Fig2 from './fig2.svg';
70
71<Fig2 title="Partial model extended with new facts" />
68 72
69### Partial models 73### Partial models
70 74
@@ -72,7 +76,9 @@ scope node = 10.
72 76
73- Check the disabled `equals` and `exist` predicates. check the visual annotation of those predicates in the visualization (dashed line, shadow). 77- Check the disabled `equals` and `exist` predicates. check the visual annotation of those predicates in the visualization (dashed line, shadow).
74 78
75![Object existence and equality](fig3.png) 79import Fig3 from './fig3.svg';
80
81<Fig3 title="Object existence and equality" />
76 82
77### Information merging 83### Information merging
78 84
@@ -95,7 +101,9 @@ element(resources, img).
95 101
96- Inconsistent models parts in a partial model typically make the problem trivially unsatisfiable. 102- Inconsistent models parts in a partial model typically make the problem trivially unsatisfiable.
97 103
98![Inconsistent partial model](fig4.png) 104import Fig4 from './fig4.svg';
105
106<Fig4 title="Inconsistent partial model" />
99 107
100- However, the model can be saved if the inconsistent part may not exist... 108- However, the model can be saved if the inconsistent part may not exist...
101 109
diff --git a/subprojects/docs/src/plugins/loadersPlugin.ts b/subprojects/docs/src/plugins/loadersPlugin.ts
new file mode 100644
index 00000000..28474511
--- /dev/null
+++ b/subprojects/docs/src/plugins/loadersPlugin.ts
@@ -0,0 +1,72 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import type { Plugin } from '@docusaurus/types';
8
9export default function loadersPlugin(): Plugin {
10 return {
11 name: 'refinery-loaders-plugin',
12 configureWebpack(config) {
13 let svgoDisabled = false;
14 const rules = [...(config.module?.rules ?? [])];
15 rules.forEach((rule) => {
16 // Compare with
17 // https://github.com/facebook/docusaurus/blob/73016d4936164ba38d4b86ec2aa8c168b5904a21/packages/docusaurus-utils/src/webpackUtils.ts#L128-L166
18 if (
19 typeof rule !== 'object' ||
20 rule === null ||
21 !('test' in rule) ||
22 !(rule.test instanceof RegExp) ||
23 !rule.test.test('.svg') ||
24 !('oneOf' in rule)
25 ) {
26 return;
27 }
28 const {
29 oneOf: [svgLoader],
30 } = rule;
31 if (
32 typeof svgLoader !== 'object' ||
33 svgLoader === null ||
34 !('use' in svgLoader) ||
35 typeof svgLoader.use !== 'object' ||
36 svgLoader.use === null ||
37 !(0 in svgLoader.use)
38 ) {
39 return;
40 }
41 const {
42 use: [loader],
43 } = svgLoader;
44 if (
45 typeof loader !== 'object' ||
46 loader === null ||
47 !('options' in loader)
48 ) {
49 return;
50 }
51 loader.options = {
52 ...(typeof loader.options === 'object' ? loader.options : {}),
53 // Disable SVGO, because it interferes styling figures exported from Refinery with CSS.
54 svgo: false,
55 svgoConfig: undefined,
56 };
57 svgoDisabled = true;
58 });
59 if (!svgoDisabled) {
60 throw new Error('Failed to disable SVGO.');
61 }
62 return {
63 mergeStrategy: {
64 'module.rules': 'replace',
65 },
66 module: {
67 rules,
68 },
69 };
70 },
71 };
72}
diff --git a/subprojects/frontend/src/graph/export/ExportPanel.tsx b/subprojects/frontend/src/graph/export/ExportPanel.tsx
index c93fa837..8d82b95c 100644
--- a/subprojects/frontend/src/graph/export/ExportPanel.tsx
+++ b/subprojects/frontend/src/graph/export/ExportPanel.tsx
@@ -6,6 +6,7 @@
6 6
7import ChevronRightIcon from '@mui/icons-material/ChevronRight'; 7import ChevronRightIcon from '@mui/icons-material/ChevronRight';
8import ContentCopyIcon from '@mui/icons-material/ContentCopy'; 8import ContentCopyIcon from '@mui/icons-material/ContentCopy';
9import ContrastIcon from '@mui/icons-material/Contrast';
9import DarkModeIcon from '@mui/icons-material/DarkMode'; 10import DarkModeIcon from '@mui/icons-material/DarkMode';
10import ImageIcon from '@mui/icons-material/Image'; 11import ImageIcon from '@mui/icons-material/Image';
11import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; 12import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined';
@@ -50,6 +51,13 @@ const SwitchButtonGroup = styled(ToggleButtonGroup, {
50 }, 51 },
51})); 52}));
52 53
54const AutoThemeMessage = styled(Typography, {
55 name: 'ExportPanel-AutoThemeMessage',
56})(({ theme }) => ({
57 width: '260px',
58 marginInline: theme.spacing(2),
59}));
60
53function getLabel(value: number): string { 61function getLabel(value: number): string {
54 return `${value}%`; 62 return `${value}%`;
55} 63}
@@ -155,29 +163,40 @@ function ExportPanel({
155 </SwitchButtonGroup> 163 </SwitchButtonGroup>
156 <SwitchButtonGroup size="small" className="rounded"> 164 <SwitchButtonGroup size="small" className="rounded">
157 <ToggleButton 165 <ToggleButton
158 value="svg" 166 value="light"
159 selected={exportSettingsStore.theme === 'light'} 167 selected={exportSettingsStore.theme === 'light'}
160 onClick={() => exportSettingsStore.setTheme('light')} 168 onClick={() => exportSettingsStore.setTheme('light')}
161 > 169 >
162 <LightModeIcon fontSize="small" /> Light 170 <LightModeIcon fontSize="small" /> Light
163 </ToggleButton> 171 </ToggleButton>
164 <ToggleButton 172 <ToggleButton
165 value="png" 173 value="dark"
166 selected={exportSettingsStore.theme === 'dark'} 174 selected={exportSettingsStore.theme === 'dark'}
167 onClick={() => exportSettingsStore.setTheme('dark')} 175 onClick={() => exportSettingsStore.setTheme('dark')}
168 > 176 >
169 <DarkModeIcon fontSize="small" /> Dark 177 <DarkModeIcon fontSize="small" /> Dark
170 </ToggleButton> 178 </ToggleButton>
179 {exportSettingsStore.canSetDynamicTheme && (
180 <ToggleButton
181 value="dynamic"
182 selected={exportSettingsStore.theme === 'dynamic'}
183 onClick={() => exportSettingsStore.setTheme('dynamic')}
184 >
185 <ContrastIcon fontSize="small" /> Auto
186 </ToggleButton>
187 )}
171 </SwitchButtonGroup> 188 </SwitchButtonGroup>
172 <FormControlLabel 189 {exportSettingsStore.canChangeTransparency && (
173 control={ 190 <FormControlLabel
174 <Switch 191 control={
175 checked={exportSettingsStore.transparent} 192 <Switch
176 onClick={() => exportSettingsStore.toggleTransparent()} 193 checked={exportSettingsStore.transparent}
177 /> 194 onClick={() => exportSettingsStore.toggleTransparent()}
178 } 195 />
179 label="Transparent background" 196 }
180 /> 197 label="Transparent background"
198 />
199 )}
181 {exportSettingsStore.canEmbedFonts && ( 200 {exportSettingsStore.canEmbedFonts && (
182 <FormControlLabel 201 <FormControlLabel
183 control={ 202 control={
@@ -200,6 +219,17 @@ function ExportPanel({
200 } 219 }
201 /> 220 />
202 )} 221 )}
222 {exportSettingsStore.theme === 'dynamic' && (
223 <>
224 <AutoThemeMessage mt={2}>
225 For embedding into HTML directly
226 </AutoThemeMessage>
227 <AutoThemeMessage variant="caption" mt={1}>
228 Set <code>data-theme=&quot;dark&quot;</code> on a containing element
229 to use a dark theme
230 </AutoThemeMessage>
231 </>
232 )}
203 {exportSettingsStore.canScale && ( 233 {exportSettingsStore.canScale && (
204 <Box mx={4} mt={1} mb={2}> 234 <Box mx={4} mt={1} mb={2}>
205 <Slider 235 <Slider
diff --git a/subprojects/frontend/src/graph/export/ExportSettingsStore.ts b/subprojects/frontend/src/graph/export/ExportSettingsStore.ts
index 53a161ab..478227af 100644
--- a/subprojects/frontend/src/graph/export/ExportSettingsStore.ts
+++ b/subprojects/frontend/src/graph/export/ExportSettingsStore.ts
@@ -7,7 +7,7 @@
7import { makeAutoObservable } from 'mobx'; 7import { makeAutoObservable } from 'mobx';
8 8
9export type ExportFormat = 'svg' | 'pdf' | 'png'; 9export type ExportFormat = 'svg' | 'pdf' | 'png';
10export type ExportTheme = 'light' | 'dark'; 10export type ExportTheme = 'light' | 'dark' | 'dynamic';
11 11
12export default class ExportSettingsStore { 12export default class ExportSettingsStore {
13 format: ExportFormat = 'svg'; 13 format: ExportFormat = 'svg';
@@ -28,14 +28,24 @@ export default class ExportSettingsStore {
28 28
29 setFormat(format: ExportFormat): void { 29 setFormat(format: ExportFormat): void {
30 this.format = format; 30 this.format = format;
31 if (this.theme === 'dynamic' && this.format !== 'svg') {
32 this.theme = 'light';
33 }
31 } 34 }
32 35
33 setTheme(theme: ExportTheme): void { 36 setTheme(theme: ExportTheme): void {
34 this.theme = theme; 37 this.theme = theme;
38 if (this.theme === 'dynamic') {
39 this.format = 'svg';
40 this.transparent = true;
41 }
35 } 42 }
36 43
37 toggleTransparent(): void { 44 toggleTransparent(): void {
38 this.transparent = !this.transparent; 45 this.transparent = !this.transparent;
46 if (!this.transparent && this.theme === 'dynamic') {
47 this.theme = 'light';
48 }
39 } 49 }
40 50
41 toggleEmbedFonts(): void { 51 toggleEmbedFonts(): void {
@@ -55,10 +65,24 @@ export default class ExportSettingsStore {
55 this.embedPDFFonts = embedFonts; 65 this.embedPDFFonts = embedFonts;
56 } 66 }
57 this.embedSVGFonts = embedFonts; 67 this.embedSVGFonts = embedFonts;
68 if (this.embedSVGFonts && this.theme === 'dynamic') {
69 this.theme = 'light';
70 }
71 }
72
73 get canSetDynamicTheme(): boolean {
74 return this.format === 'svg';
75 }
76
77 get canChangeTransparency(): boolean {
78 return this.theme !== 'dynamic';
58 } 79 }
59 80
60 get canEmbedFonts(): boolean { 81 get canEmbedFonts(): boolean {
61 return this.format === 'svg' || this.format === 'pdf'; 82 return (
83 (this.format === 'svg' || this.format === 'pdf') &&
84 this.theme !== 'dynamic'
85 );
62 } 86 }
63 87
64 get canScale(): boolean { 88 get canScale(): boolean {
diff --git a/subprojects/frontend/src/graph/export/exportDiagram.tsx b/subprojects/frontend/src/graph/export/exportDiagram.tsx
index 6abbcfdf..a0c3460c 100644
--- a/subprojects/frontend/src/graph/export/exportDiagram.tsx
+++ b/subprojects/frontend/src/graph/export/exportDiagram.tsx
@@ -16,6 +16,7 @@ import cancelSVG from '@material-icons/svg/svg/cancel/baseline.svg?raw';
16import labelSVG from '@material-icons/svg/svg/label/baseline.svg?raw'; 16import labelSVG from '@material-icons/svg/svg/label/baseline.svg?raw';
17import labelOutlinedSVG from '@material-icons/svg/svg/label/outline.svg?raw'; 17import labelOutlinedSVG from '@material-icons/svg/svg/label/outline.svg?raw';
18import type { Theme } from '@mui/material/styles'; 18import type { Theme } from '@mui/material/styles';
19import { nanoid } from 'nanoid';
19 20
20import { darkTheme, lightTheme } from '../../theme/ThemeProvider'; 21import { darkTheme, lightTheme } from '../../theme/ThemeProvider';
21import { copyBlob, saveBlob } from '../../utils/fileIO'; 22import { copyBlob, saveBlob } from '../../utils/fileIO';
@@ -48,6 +49,36 @@ importSVG(labelSVG, 'icon-TRUE');
48importSVG(labelOutlinedSVG, 'icon-UNKNOWN'); 49importSVG(labelOutlinedSVG, 'icon-UNKNOWN');
49importSVG(cancelSVG, 'icon-ERROR'); 50importSVG(cancelSVG, 'icon-ERROR');
50 51
52function fixIDs(id: string, svgDocument: XMLDocument) {
53 const idMap = new Map<string, string>();
54 let i = 0;
55 svgDocument.querySelectorAll('[id]').forEach((node) => {
56 const oldId = node.getAttribute('id');
57 if (oldId === null) {
58 return;
59 }
60 if (oldId.endsWith(',clip')) {
61 const newId = `refinery-${id}-clip-${i}`;
62 i += 1;
63 idMap.set(`url(#${oldId})`, `url(#${newId})`);
64 node.setAttribute('id', newId);
65 } else {
66 node.setAttribute('id', '');
67 }
68 });
69 svgDocument.querySelectorAll('[clip-path]').forEach((node) => {
70 const oldPath = node.getAttribute('clip-path');
71 if (oldPath === null) {
72 return;
73 }
74 const newPath = idMap.get(oldPath);
75 if (newPath === undefined) {
76 return;
77 }
78 node.setAttribute('clip-path', newPath);
79 });
80}
81
51function addBackground( 82function addBackground(
52 svgDocument: XMLDocument, 83 svgDocument: XMLDocument,
53 svg: SVGSVGElement, 84 svg: SVGSVGElement,
@@ -142,40 +173,54 @@ async function fetchVariableFontCSS(): Promise<string> {
142 return variableFontCSS; 173 return variableFontCSS;
143} 174}
144 175
176interface ThemeVariant {
177 selector: string;
178 theme: Theme;
179}
180
145function appendStyles( 181function appendStyles(
182 id: string,
146 svgDocument: XMLDocument, 183 svgDocument: XMLDocument,
147 svg: SVGSVGElement, 184 svg: SVGSVGElement,
148 theme: Theme, 185 themes: ThemeVariant[],
149 colorNodes: boolean, 186 colorNodes: boolean,
150 hexTypeHashes: string[], 187 hexTypeHashes: string[],
151 fontsCSS: string, 188 fontsCSS: string,
152): void { 189): void {
153 const cache = createCache({ 190 const className = `refinery-${id}`;
154 key: 'refinery', 191 svg.classList.add(className);
155 container: svg,
156 prepend: true,
157 });
158 // @ts-expect-error `CSSObject` types don't match up between `@mui/material` and
159 // `@emotion/serialize`, but they are compatible in practice.
160 const styles = serializeStyles([createGraphTheme], cache.registered, {
161 theme,
162 colorNodes,
163 hexTypeHashes,
164 noEmbedIcons: true,
165 });
166 const rules: string[] = [fontsCSS]; 192 const rules: string[] = [fontsCSS];
167 const sheet = { 193 themes.forEach(({ selector, theme }) => {
168 insert(rule) { 194 const cache = createCache({
169 rules.push(rule); 195 key: 'refinery',
170 }, 196 container: svg,
171 } as StyleSheet; 197 prepend: true,
172 cache.insert('', styles, sheet, false); 198 });
199 // @ts-expect-error `CSSObject` types don't match up between `@mui/material` and
200 // `@emotion/serialize`, but they are compatible in practice.
201 const styles = serializeStyles([createGraphTheme], cache.registered, {
202 theme,
203 colorNodes,
204 hexTypeHashes,
205 noEmbedIcons: true,
206 });
207 const sheet = {
208 insert(rule) {
209 rules.push(rule);
210 },
211 } as StyleSheet;
212 cache.insert(`${selector} .${className}`, styles, sheet, false);
213 });
173 const styleElement = svgDocument.createElementNS(SVG_NS, 'style'); 214 const styleElement = svgDocument.createElementNS(SVG_NS, 'style');
174 svg.prepend(styleElement); 215 svg.prepend(styleElement);
175 styleElement.innerHTML = rules.join(''); 216 styleElement.innerHTML = rules.join('');
176} 217}
177 218
178function fixForeignObjects(svgDocument: XMLDocument, svg: SVGSVGElement): void { 219function fixForeignObjects(
220 id: string,
221 svgDocument: XMLDocument,
222 svg: SVGSVGElement,
223): void {
179 const foreignObjects: SVGForeignObjectElement[] = []; 224 const foreignObjects: SVGForeignObjectElement[] = [];
180 svg 225 svg
181 .querySelectorAll('foreignObject') 226 .querySelectorAll('foreignObject')
@@ -197,7 +242,7 @@ function fixForeignObjects(svgDocument: XMLDocument, svg: SVGSVGElement): void {
197 object.children[0]?.classList?.forEach((className) => { 242 object.children[0]?.classList?.forEach((className) => {
198 useElement.classList.add(className); 243 useElement.classList.add(className);
199 if (ICONS.has(className)) { 244 if (ICONS.has(className)) {
200 useElement.setAttribute('href', `#${className}`); 245 useElement.setAttribute('href', `#refinery-${id}-${className}`);
201 } 246 }
202 }); 247 });
203 object.replaceWith(useElement); 248 object.replaceWith(useElement);
@@ -206,6 +251,7 @@ function fixForeignObjects(svgDocument: XMLDocument, svg: SVGSVGElement): void {
206 svg.prepend(defs); 251 svg.prepend(defs);
207 ICONS.forEach((value) => { 252 ICONS.forEach((value) => {
208 const importedValue = svgDocument.importNode(value, true); 253 const importedValue = svgDocument.importNode(value, true);
254 importedValue.id = `refinery-${id}-${importedValue.id}`;
209 defs.appendChild(importedValue); 255 defs.appendChild(importedValue);
210 }); 256 });
211} 257}
@@ -322,12 +368,37 @@ export default async function exportDiagram(
322 svgDocument.replaceChild(copyOfSVG, originalRoot); 368 svgDocument.replaceChild(copyOfSVG, originalRoot);
323 } 369 }
324 370
325 const theme = settings.theme === 'light' ? lightTheme : darkTheme; 371 const id = nanoid();
372 fixIDs(id, svgDocument);
373
374 let theme: Theme;
375 let themes: ThemeVariant[];
376 if (settings.theme === 'dynamic') {
377 theme = lightTheme;
378 themes = [
379 {
380 selector: '',
381 theme: lightTheme,
382 },
383 {
384 selector: '[data-theme="dark"]',
385 theme: darkTheme,
386 },
387 ];
388 } else {
389 theme = settings.theme === 'light' ? lightTheme : darkTheme;
390 themes = [
391 {
392 selector: '',
393 theme,
394 },
395 ];
396 }
326 if (!settings.transparent) { 397 if (!settings.transparent) {
327 addBackground(svgDocument, copyOfSVG, theme); 398 addBackground(svgDocument, copyOfSVG, theme);
328 } 399 }
329 400
330 fixForeignObjects(svgDocument, copyOfSVG); 401 fixForeignObjects(id, svgDocument, copyOfSVG);
331 402
332 const { colorNodes } = graph; 403 const { colorNodes } = graph;
333 let fontsCSS = ''; 404 let fontsCSS = '';
@@ -339,9 +410,10 @@ export default async function exportDiagram(
339 fontsCSS = await fetchFontCSS(); 410 fontsCSS = await fetchFontCSS();
340 } 411 }
341 appendStyles( 412 appendStyles(
413 id,
342 svgDocument, 414 svgDocument,
343 copyOfSVG, 415 copyOfSVG,
344 theme, 416 themes,
345 colorNodes, 417 colorNodes,
346 graph.hexTypeHashes, 418 graph.hexTypeHashes,
347 fontsCSS, 419 fontsCSS,
diff --git a/yarn.lock b/yarn.lock
index 25d0919d..0dd63986 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3141,93 +3141,107 @@ __metadata:
3141 languageName: node 3141 languageName: node
3142 linkType: hard 3142 linkType: hard
3143 3143
3144"@rollup/rollup-android-arm-eabi@npm:4.13.0": 3144"@rollup/rollup-android-arm-eabi@npm:4.14.1":
3145 version: 4.13.0 3145 version: 4.14.1
3146 resolution: "@rollup/rollup-android-arm-eabi@npm:4.13.0" 3146 resolution: "@rollup/rollup-android-arm-eabi@npm:4.14.1"
3147 conditions: os=android & cpu=arm 3147 conditions: os=android & cpu=arm
3148 languageName: node 3148 languageName: node
3149 linkType: hard 3149 linkType: hard
3150 3150
3151"@rollup/rollup-android-arm64@npm:4.13.0": 3151"@rollup/rollup-android-arm64@npm:4.14.1":
3152 version: 4.13.0 3152 version: 4.14.1
3153 resolution: "@rollup/rollup-android-arm64@npm:4.13.0" 3153 resolution: "@rollup/rollup-android-arm64@npm:4.14.1"
3154 conditions: os=android & cpu=arm64 3154 conditions: os=android & cpu=arm64
3155 languageName: node 3155 languageName: node
3156 linkType: hard 3156 linkType: hard
3157 3157
3158"@rollup/rollup-darwin-arm64@npm:4.13.0": 3158"@rollup/rollup-darwin-arm64@npm:4.14.1":
3159 version: 4.13.0 3159 version: 4.14.1
3160 resolution: "@rollup/rollup-darwin-arm64@npm:4.13.0" 3160 resolution: "@rollup/rollup-darwin-arm64@npm:4.14.1"
3161 conditions: os=darwin & cpu=arm64 3161 conditions: os=darwin & cpu=arm64
3162 languageName: node 3162 languageName: node
3163 linkType: hard 3163 linkType: hard
3164 3164
3165"@rollup/rollup-darwin-x64@npm:4.13.0": 3165"@rollup/rollup-darwin-x64@npm:4.14.1":
3166 version: 4.13.0 3166 version: 4.14.1
3167 resolution: "@rollup/rollup-darwin-x64@npm:4.13.0" 3167 resolution: "@rollup/rollup-darwin-x64@npm:4.14.1"
3168 conditions: os=darwin & cpu=x64 3168 conditions: os=darwin & cpu=x64
3169 languageName: node 3169 languageName: node
3170 linkType: hard 3170 linkType: hard
3171 3171
3172"@rollup/rollup-linux-arm-gnueabihf@npm:4.13.0": 3172"@rollup/rollup-linux-arm-gnueabihf@npm:4.14.1":
3173 version: 4.13.0 3173 version: 4.14.1
3174 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.13.0" 3174 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.14.1"
3175 conditions: os=linux & cpu=arm 3175 conditions: os=linux & cpu=arm
3176 languageName: node 3176 languageName: node
3177 linkType: hard 3177 linkType: hard
3178 3178
3179"@rollup/rollup-linux-arm64-gnu@npm:4.13.0": 3179"@rollup/rollup-linux-arm64-gnu@npm:4.14.1":
3180 version: 4.13.0 3180 version: 4.14.1
3181 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.13.0" 3181 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.14.1"
3182 conditions: os=linux & cpu=arm64 & libc=glibc 3182 conditions: os=linux & cpu=arm64 & libc=glibc
3183 languageName: node 3183 languageName: node
3184 linkType: hard 3184 linkType: hard
3185 3185
3186"@rollup/rollup-linux-arm64-musl@npm:4.13.0": 3186"@rollup/rollup-linux-arm64-musl@npm:4.14.1":
3187 version: 4.13.0 3187 version: 4.14.1
3188 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.13.0" 3188 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.14.1"
3189 conditions: os=linux & cpu=arm64 & libc=musl 3189 conditions: os=linux & cpu=arm64 & libc=musl
3190 languageName: node 3190 languageName: node
3191 linkType: hard 3191 linkType: hard
3192 3192
3193"@rollup/rollup-linux-riscv64-gnu@npm:4.13.0": 3193"@rollup/rollup-linux-powerpc64le-gnu@npm:4.14.1":
3194 version: 4.13.0 3194 version: 4.14.1
3195 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.13.0" 3195 resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.14.1"
3196 conditions: os=linux & cpu=ppc64le & libc=glibc
3197 languageName: node
3198 linkType: hard
3199
3200"@rollup/rollup-linux-riscv64-gnu@npm:4.14.1":
3201 version: 4.14.1
3202 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.14.1"
3196 conditions: os=linux & cpu=riscv64 & libc=glibc 3203 conditions: os=linux & cpu=riscv64 & libc=glibc
3197 languageName: node 3204 languageName: node
3198 linkType: hard 3205 linkType: hard
3199 3206
3200"@rollup/rollup-linux-x64-gnu@npm:4.13.0": 3207"@rollup/rollup-linux-s390x-gnu@npm:4.14.1":
3201 version: 4.13.0 3208 version: 4.14.1
3202 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.13.0" 3209 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.14.1"
3210 conditions: os=linux & cpu=s390x & libc=glibc
3211 languageName: node
3212 linkType: hard
3213
3214"@rollup/rollup-linux-x64-gnu@npm:4.14.1":
3215 version: 4.14.1
3216 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.14.1"
3203 conditions: os=linux & cpu=x64 & libc=glibc 3217 conditions: os=linux & cpu=x64 & libc=glibc
3204 languageName: node 3218 languageName: node
3205 linkType: hard 3219 linkType: hard
3206 3220
3207"@rollup/rollup-linux-x64-musl@npm:4.13.0": 3221"@rollup/rollup-linux-x64-musl@npm:4.14.1":
3208 version: 4.13.0 3222 version: 4.14.1
3209 resolution: "@rollup/rollup-linux-x64-musl@npm:4.13.0" 3223 resolution: "@rollup/rollup-linux-x64-musl@npm:4.14.1"
3210 conditions: os=linux & cpu=x64 & libc=musl 3224 conditions: os=linux & cpu=x64 & libc=musl
3211 languageName: node 3225 languageName: node
3212 linkType: hard 3226 linkType: hard
3213 3227
3214"@rollup/rollup-win32-arm64-msvc@npm:4.13.0": 3228"@rollup/rollup-win32-arm64-msvc@npm:4.14.1":
3215 version: 4.13.0 3229 version: 4.14.1
3216 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.13.0" 3230 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.14.1"
3217 conditions: os=win32 & cpu=arm64 3231 conditions: os=win32 & cpu=arm64
3218 languageName: node 3232 languageName: node
3219 linkType: hard 3233 linkType: hard
3220 3234
3221"@rollup/rollup-win32-ia32-msvc@npm:4.13.0": 3235"@rollup/rollup-win32-ia32-msvc@npm:4.14.1":
3222 version: 4.13.0 3236 version: 4.14.1
3223 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.13.0" 3237 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.14.1"
3224 conditions: os=win32 & cpu=ia32 3238 conditions: os=win32 & cpu=ia32
3225 languageName: node 3239 languageName: node
3226 linkType: hard 3240 linkType: hard
3227 3241
3228"@rollup/rollup-win32-x64-msvc@npm:4.13.0": 3242"@rollup/rollup-win32-x64-msvc@npm:4.14.1":
3229 version: 4.13.0 3243 version: 4.14.1
3230 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.13.0" 3244 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.14.1"
3231 conditions: os=win32 & cpu=x64 3245 conditions: os=win32 & cpu=x64
3232 languageName: node 3246 languageName: node
3233 linkType: hard 3247 linkType: hard
@@ -14138,22 +14152,24 @@ __metadata:
14138 linkType: hard 14152 linkType: hard
14139 14153
14140"rollup@npm:^4.13.0": 14154"rollup@npm:^4.13.0":
14141 version: 4.13.0 14155 version: 4.14.1
14142 resolution: "rollup@npm:4.13.0" 14156 resolution: "rollup@npm:4.14.1"
14143 dependencies: 14157 dependencies:
14144 "@rollup/rollup-android-arm-eabi": "npm:4.13.0" 14158 "@rollup/rollup-android-arm-eabi": "npm:4.14.1"
14145 "@rollup/rollup-android-arm64": "npm:4.13.0" 14159 "@rollup/rollup-android-arm64": "npm:4.14.1"
14146 "@rollup/rollup-darwin-arm64": "npm:4.13.0" 14160 "@rollup/rollup-darwin-arm64": "npm:4.14.1"
14147 "@rollup/rollup-darwin-x64": "npm:4.13.0" 14161 "@rollup/rollup-darwin-x64": "npm:4.14.1"
14148 "@rollup/rollup-linux-arm-gnueabihf": "npm:4.13.0" 14162 "@rollup/rollup-linux-arm-gnueabihf": "npm:4.14.1"
14149 "@rollup/rollup-linux-arm64-gnu": "npm:4.13.0" 14163 "@rollup/rollup-linux-arm64-gnu": "npm:4.14.1"
14150 "@rollup/rollup-linux-arm64-musl": "npm:4.13.0" 14164 "@rollup/rollup-linux-arm64-musl": "npm:4.14.1"
14151 "@rollup/rollup-linux-riscv64-gnu": "npm:4.13.0" 14165 "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.14.1"
14152 "@rollup/rollup-linux-x64-gnu": "npm:4.13.0" 14166 "@rollup/rollup-linux-riscv64-gnu": "npm:4.14.1"
14153 "@rollup/rollup-linux-x64-musl": "npm:4.13.0" 14167 "@rollup/rollup-linux-s390x-gnu": "npm:4.14.1"
14154 "@rollup/rollup-win32-arm64-msvc": "npm:4.13.0" 14168 "@rollup/rollup-linux-x64-gnu": "npm:4.14.1"
14155 "@rollup/rollup-win32-ia32-msvc": "npm:4.13.0" 14169 "@rollup/rollup-linux-x64-musl": "npm:4.14.1"
14156 "@rollup/rollup-win32-x64-msvc": "npm:4.13.0" 14170 "@rollup/rollup-win32-arm64-msvc": "npm:4.14.1"
14171 "@rollup/rollup-win32-ia32-msvc": "npm:4.14.1"
14172 "@rollup/rollup-win32-x64-msvc": "npm:4.14.1"
14157 "@types/estree": "npm:1.0.5" 14173 "@types/estree": "npm:1.0.5"
14158 fsevents: "npm:~2.3.2" 14174 fsevents: "npm:~2.3.2"
14159 dependenciesMeta: 14175 dependenciesMeta:
@@ -14171,8 +14187,12 @@ __metadata:
14171 optional: true 14187 optional: true
14172 "@rollup/rollup-linux-arm64-musl": 14188 "@rollup/rollup-linux-arm64-musl":
14173 optional: true 14189 optional: true
14190 "@rollup/rollup-linux-powerpc64le-gnu":
14191 optional: true
14174 "@rollup/rollup-linux-riscv64-gnu": 14192 "@rollup/rollup-linux-riscv64-gnu":
14175 optional: true 14193 optional: true
14194 "@rollup/rollup-linux-s390x-gnu":
14195 optional: true
14176 "@rollup/rollup-linux-x64-gnu": 14196 "@rollup/rollup-linux-x64-gnu":
14177 optional: true 14197 optional: true
14178 "@rollup/rollup-linux-x64-musl": 14198 "@rollup/rollup-linux-x64-musl":
@@ -14187,7 +14207,7 @@ __metadata:
14187 optional: true 14207 optional: true
14188 bin: 14208 bin:
14189 rollup: dist/bin/rollup 14209 rollup: dist/bin/rollup
14190 checksum: 10c0/90f8cdf9c2115223cbcfe91d932170a85c0928ae1943f45af6877907ea150585b80f656cf2bc471c6f809cb7e158dd85dbea9f91ab4fd5bce0eaf6c3f5f4fd92 14210 checksum: 10c0/c9028c04537f7f16f9b5e4d75c84d2f0dc960d280fc4eca5960f0d67e786d993b8b707a63fc8b2e054b018fdb3a5a98d5eb7ed5674635c7612dd0b66696805fa
14191 languageName: node 14211 languageName: node
14192 linkType: hard 14212 linkType: hard
14193 14213