aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2024-04-26 20:27:32 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-26 20:27:32 +0200
commit2ef2233a7c6f062deaf9b7e76beb51e7f9945c71 (patch)
treea7554de9d813334170eeaaba46793490568b9413 /subprojects
parentfix(language): Sonar lint issue (diff)
parentchore(deps): bump dependencies (diff)
downloadrefinery-2ef2233a7c6f062deaf9b7e76beb51e7f9945c71.tar.gz
refinery-2ef2233a7c6f062deaf9b7e76beb51e7f9945c71.tar.zst
refinery-2ef2233a7c6f062deaf9b7e76beb51e7f9945c71.zip
Merge pull request #58 from graphs4value/docs
Add documentation site with Docusaurus
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/docs/.gitignore6
-rw-r--r--subprojects/docs/babel.config.cts12
-rw-r--r--subprojects/docs/build.gradle.kts142
-rw-r--r--subprojects/docs/docusaurus.config.ts297
-rw-r--r--subprojects/docs/package.json71
-rw-r--r--subprojects/docs/src/components/Features/fi1.svg1
-rw-r--r--subprojects/docs/src/components/Features/fi1.svg.license4
-rw-r--r--subprojects/docs/src/components/Features/fi2.svg1
-rw-r--r--subprojects/docs/src/components/Features/fi2.svg.license4
-rw-r--r--subprojects/docs/src/components/Features/fi3.svg1
-rw-r--r--subprojects/docs/src/components/Features/fi3.svg.license4
-rw-r--r--subprojects/docs/src/components/Features/fi4.svg1
-rw-r--r--subprojects/docs/src/components/Features/fi4.svg.license4
-rw-r--r--subprojects/docs/src/components/Features/fi5.svg1
-rw-r--r--subprojects/docs/src/components/Features/fi5.svg.license4
-rw-r--r--subprojects/docs/src/components/Features/index.module.css130
-rw-r--r--subprojects/docs/src/components/Features/index.tsx114
-rw-r--r--subprojects/docs/src/components/UseCases/index.module.css104
-rw-r--r--subprojects/docs/src/components/UseCases/index.tsx106
-rw-r--r--subprojects/docs/src/components/UseCases/uc1.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc1.svg.license4
-rw-r--r--subprojects/docs/src/components/UseCases/uc2.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc2.svg.license4
-rw-r--r--subprojects/docs/src/components/UseCases/uc3.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc3.svg.license4
-rw-r--r--subprojects/docs/src/components/UseCases/uc4.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc4.svg.license4
-rw-r--r--subprojects/docs/src/components/UseCases/uc5.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc5.svg.license4
-rw-r--r--subprojects/docs/src/components/UseCases/uc6.svg1
-rw-r--r--subprojects/docs/src/components/UseCases/uc6.svg.license4
-rw-r--r--subprojects/docs/src/components/Video/cover-background.pngbin0 -> 1979050 bytes
-rw-r--r--subprojects/docs/src/components/Video/cover-background.png.license9
-rw-r--r--subprojects/docs/src/components/Video/cover.svg1
-rw-r--r--subprojects/docs/src/components/Video/cover.svg.license3
-rw-r--r--subprojects/docs/src/components/Video/index.module.css79
-rw-r--r--subprojects/docs/src/components/Video/index.tsx62
-rw-r--r--subprojects/docs/src/css/custom.css142
-rw-r--r--subprojects/docs/src/css/sr-only.css22
-rw-r--r--subprojects/docs/src/develop/contributing/commands.md172
-rw-r--r--subprojects/docs/src/develop/contributing/ide-setup.md94
-rw-r--r--subprojects/docs/src/develop/contributing/index.md59
-rw-r--r--subprojects/docs/src/develop/index.md13
-rw-r--r--subprojects/docs/src/develop/javadoc.md42
-rw-r--r--subprojects/docs/src/learn/docker.md175
-rw-r--r--subprojects/docs/src/learn/index.md11
-rw-r--r--subprojects/docs/src/learn/language/_category_.yml10
-rw-r--r--subprojects/docs/src/learn/language/classes/ContainmentInstance.svg227
-rw-r--r--subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/InvalidInstance.svg20
-rw-r--r--subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg229
-rw-r--r--subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg29
-rw-r--r--subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg38
-rw-r--r--subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg69
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg24
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesSimple.svg43
-rw-r--r--subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/classes/index.md212
-rw-r--r--subprojects/docs/src/learn/language/logic/AssertionsError.svg20
-rw-r--r--subprojects/docs/src/learn/language/logic/AssertionsError.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/AssertionsExample.svg99
-rw-r--r--subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/DefaultAssertions.svg129
-rw-r--r--subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/MultiObjects.svg81
-rw-r--r--subprojects/docs/src/learn/language/logic/MultiObjects.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/ObjectScopes.svg58
-rw-r--r--subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg58
-rw-r--r--subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/logic/index.md256
-rw-r--r--subprojects/docs/src/learn/language/predicates/DerivedFeature.svg76
-rw-r--r--subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license3
-rw-r--r--subprojects/docs/src/learn/language/predicates/index.md284
-rw-r--r--subprojects/docs/src/learn/tutorials/_category_.yml11
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig1.svg72
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license3
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig2.svg145
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license3
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig3.svg124
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license3
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig4.svg131
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license3
-rw-r--r--subprojects/docs/src/learn/tutorials/file-system/index.md209
-rw-r--r--subprojects/docs/src/pages/index.module.css41
-rw-r--r--subprojects/docs/src/pages/index.tsx243
-rw-r--r--subprojects/docs/src/pages/license.md100
-rw-r--r--subprojects/docs/src/plugins/loadersPlugin.ts117
-rw-r--r--subprojects/docs/src/plugins/remarkPosix2Windows.ts169
-rw-r--r--subprojects/docs/src/plugins/swcMinifyPlugin.ts41
-rw-r--r--subprojects/docs/src/types.d.ts39
-rw-r--r--subprojects/docs/static/.nojekyll3
-rw-r--r--subprojects/docs/static/CNAME1
-rw-r--r--subprojects/docs/static/CNAME.license4
-rw-r--r--subprojects/docs/static/apple-touch-icon.pngbin0 -> 3711 bytes
-rw-r--r--subprojects/docs/static/apple-touch-icon.png.license3
-rw-r--r--subprojects/docs/static/favicon-96x96.pngbin0 -> 2044 bytes
-rw-r--r--subprojects/docs/static/favicon-96x96.png.license3
-rw-r--r--subprojects/docs/static/favicon.pngbin0 -> 822 bytes
-rw-r--r--subprojects/docs/static/favicon.png.license3
-rw-r--r--subprojects/docs/static/favicon.svg1
-rw-r--r--subprojects/docs/static/favicon.svg.license3
-rw-r--r--subprojects/docs/static/icon-192x192.pngbin0 -> 4094 bytes
-rw-r--r--subprojects/docs/static/icon-192x192.png.license3
-rw-r--r--subprojects/docs/static/icon-512x512.pngbin0 -> 11257 bytes
-rw-r--r--subprojects/docs/static/icon-512x512.png.license3
-rw-r--r--subprojects/docs/static/icon-any.svg1
-rw-r--r--subprojects/docs/static/icon-any.svg.license3
-rw-r--r--subprojects/docs/static/logo-dark.svg1
-rw-r--r--subprojects/docs/static/logo-dark.svg.license3
-rw-r--r--subprojects/docs/static/logo.svg1
-rw-r--r--subprojects/docs/static/logo.svg.license3
-rw-r--r--subprojects/docs/static/manifest.webmanifest35
-rw-r--r--subprojects/docs/static/manifest.webmanifest.license3
-rw-r--r--subprojects/docs/static/mask-icon.svg1
-rw-r--r--subprojects/docs/static/mask-icon.svg.license3
-rw-r--r--subprojects/docs/static/papers/icse24-demo.pdfbin0 -> 953075 bytes
-rw-r--r--subprojects/docs/static/papers/icse24-demo.pdf.license3
-rw-r--r--subprojects/docs/static/robots.txt8
-rw-r--r--subprojects/docs/static/screenshot.pngbin0 -> 167306 bytes
-rw-r--r--subprojects/docs/static/screenshot.png.license3
-rw-r--r--subprojects/docs/tsconfig.json34
-rw-r--r--subprojects/frontend/.gitignore6
-rw-r--r--subprojects/frontend/build.gradle.kts54
-rw-r--r--subprojects/frontend/index.html1
-rw-r--r--subprojects/frontend/package.json38
-rw-r--r--subprojects/frontend/src/ToggleDarkModeButton.tsx13
-rw-r--r--subprojects/frontend/src/TopBar.tsx54
-rw-r--r--subprojects/frontend/src/editor/ConnectButton.tsx51
-rw-r--r--subprojects/frontend/src/editor/EditorButtons.tsx187
-rw-r--r--subprojects/frontend/src/editor/SearchToolbar.tsx94
-rw-r--r--subprojects/frontend/src/graph/SlideInPanel.tsx21
-rw-r--r--subprojects/frontend/src/graph/VisibilityPanel.tsx2
-rw-r--r--subprojects/frontend/src/graph/ZoomButtons.tsx38
-rw-r--r--subprojects/frontend/src/graph/export/ExportPanel.tsx54
-rw-r--r--subprojects/frontend/src/graph/export/ExportSettingsStore.ts44
-rw-r--r--subprojects/frontend/src/graph/export/exportDiagram.tsx122
-rw-r--r--subprojects/frontend/src/table/SymbolSelector.tsx2
-rw-r--r--subprojects/frontend/src/table/TableArea.tsx62
-rw-r--r--subprojects/frontend/src/theme/ThemeProvider.tsx3
-rw-r--r--subprojects/language-web/build.gradle.kts6
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java4
148 files changed, 6059 insertions, 308 deletions
diff --git a/subprojects/docs/.gitignore b/subprojects/docs/.gitignore
new file mode 100644
index 00000000..2d94aa21
--- /dev/null
+++ b/subprojects/docs/.gitignore
@@ -0,0 +1,6 @@
1# SPDX-FileCopyrightText: 2024 The Refinery Authors
2#
3# SPDX-License-Identifier: CC0-1.0
4
5.docusaurus
6.yarn
diff --git a/subprojects/docs/babel.config.cts b/subprojects/docs/babel.config.cts
new file mode 100644
index 00000000..b1bc1281
--- /dev/null
+++ b/subprojects/docs/babel.config.cts
@@ -0,0 +1,12 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/>
4 *
5 * SPDX-License-Identifier: MIT AND EPL-2.0
6 */
7
8import type { TransformOptions } from '@babel/core';
9
10module.exports = {
11 presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
12} satisfies TransformOptions;
diff --git a/subprojects/docs/build.gradle.kts b/subprojects/docs/build.gradle.kts
new file mode 100644
index 00000000..e559ed3e
--- /dev/null
+++ b/subprojects/docs/build.gradle.kts
@@ -0,0 +1,142 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import org.siouan.frontendgradleplugin.infrastructure.gradle.RunYarn
8import tools.refinery.gradle.utils.SonarPropertiesUtils
9
10plugins {
11 id("tools.refinery.gradle.frontend-workspace")
12 id("tools.refinery.gradle.sonarqube")
13}
14
15frontend {
16 assembleScript.set("run build")
17}
18
19val javadocs: Configuration by configurations.creating {
20 isCanBeConsumed = false
21 isCanBeResolved = true
22}
23
24dependencies {
25 javadocs(project(":refinery-generator", "javadocElements"))
26 javadocs(project(":refinery-generator-cli", "javadocElements"))
27 javadocs(project(":refinery-interpreter", "javadocElements"))
28 javadocs(project(":refinery-interpreter-localsearch", "javadocElements"))
29 javadocs(project(":refinery-interpreter-rete", "javadocElements"))
30 javadocs(project(":refinery-interpreter-rete-recipes", "javadocElements"))
31 javadocs(project(":refinery-language", "javadocElements"))
32 javadocs(project(":refinery-language-ide", "javadocElements"))
33 javadocs(project(":refinery-language-model", "javadocElements"))
34 javadocs(project(":refinery-language-semantics", "javadocElements"))
35 javadocs(project(":refinery-language-web", "javadocElements"))
36 javadocs(project(":refinery-logic", "javadocElements"))
37 javadocs(project(":refinery-store", "javadocElements"))
38 javadocs(project(":refinery-store-dse", "javadocElements"))
39 javadocs(project(":refinery-store-dse-visualization", "javadocElements"))
40 javadocs(project(":refinery-store-query", "javadocElements"))
41 javadocs(project(":refinery-store-query-interpreter", "javadocElements"))
42 javadocs(project(":refinery-store-reasoning", "javadocElements"))
43 javadocs(project(":refinery-store-reasoning-scope", "javadocElements"))
44 javadocs(project(":refinery-store-reasoning-smt", "javadocElements"))
45}
46
47val srcDir = "src"
48
49val docusaurusOutputDir = layout.buildDirectory.dir("docusaurus")
50
51val javadocsDir = layout.buildDirectory.dir("javadocs")
52
53val javadocsDocsDir = javadocsDir.map { root -> root.dir("develop/javadoc") }
54
55val configFiles: FileCollection = files(
56 rootProject.file("yarn.lock"),
57 rootProject.file("package.json"),
58 "package.json",
59 rootProject.file("tsconfig.base.json"),
60 "tsconfig.json",
61 "babel.config.config.ts",
62 "docusaurus.config.ts",
63)
64
65val lintConfigFiles: FileCollection = configFiles + files(
66 rootProject.file(".eslintrc.cjs"),
67 rootProject.file("prettier.config.cjs")
68)
69
70tasks {
71 val extractJavadocs by registering {
72 dependsOn(javadocs)
73 outputs.dir(javadocsDir)
74 doFirst {
75 delete(javadocsDir)
76 }
77 doLast {
78 javadocs.resolvedConfiguration.resolvedArtifacts.forEach { artifact ->
79 copy {
80 from(zipTree(artifact.file))
81 into(javadocsDocsDir.map { root -> root.dir(artifact.moduleVersion.id.name) })
82 }
83 }
84 }
85 }
86
87 assembleFrontend {
88 dependsOn(extractJavadocs)
89 inputs.dir(srcDir)
90 inputs.dir("static")
91 inputs.dir(javadocsDir)
92 inputs.files(configFiles)
93 outputs.dir(docusaurusOutputDir)
94 }
95
96 val typeCheckFrontend by registering(RunYarn::class) {
97 dependsOn(installFrontend)
98 inputs.dir(srcDir)
99 inputs.files(configFiles)
100 outputs.dir(layout.buildDirectory.dir("typescript"))
101 script.set("run typecheck")
102 group = "verification"
103 description = "Check for TypeScript type errors."
104 }
105
106 val lintFrontend by registering(RunYarn::class) {
107 dependsOn(installFrontend)
108 dependsOn(typeCheckFrontend)
109 inputs.dir(srcDir)
110 inputs.files(lintConfigFiles)
111 outputs.file(layout.buildDirectory.file("eslint.json"))
112 script.set("run lint")
113 group = "verification"
114 description = "Check for TypeScript lint errors and warnings."
115 }
116
117 register<RunYarn>("fixFrontend") {
118 dependsOn(installFrontend)
119 dependsOn(typeCheckFrontend)
120 inputs.dir(srcDir)
121 inputs.files(lintConfigFiles)
122 script.set("run lint:fix")
123 group = "verification"
124 description = "Check for TypeScript lint errors and warnings."
125 }
126
127 check {
128 dependsOn(typeCheckFrontend)
129 dependsOn(lintFrontend)
130 }
131
132 clean {
133 delete(".docusaurus")
134 delete(".yarn")
135 }
136}
137
138sonarqube.properties {
139 SonarPropertiesUtils.addToList(properties, "sonar.sources", srcDir)
140 property("sonar.nodejs.executable", "${frontend.nodeInstallDirectory.get()}/bin/node")
141 property("sonar.eslint.reportPaths", "${layout.buildDirectory.get()}/eslint.json")
142}
diff --git a/subprojects/docs/docusaurus.config.ts b/subprojects/docs/docusaurus.config.ts
new file mode 100644
index 00000000..5245e415
--- /dev/null
+++ b/subprojects/docs/docusaurus.config.ts
@@ -0,0 +1,297 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/>
4 *
5 * SPDX-License-Identifier: MIT AND EPL-2.0
6 */
7
8import type { MDXOptions } from '@docusaurus/mdx-loader';
9import type { Options as DocsOptions } from '@docusaurus/plugin-content-docs';
10import type { Options as PagesOptions } from '@docusaurus/plugin-content-pages';
11import type { Options as ClassicThemeOptions } from '@docusaurus/theme-classic';
12import type { UserThemeConfig } from '@docusaurus/theme-common';
13import type { UserThemeConfig as AlgoliaConfig } from '@docusaurus/theme-search-algolia';
14import type { Config } from '@docusaurus/types';
15import { Config as SwcConfig } from '@swc/core';
16import { themes } from 'prism-react-renderer';
17import smartypants from 'remark-smartypants';
18
19import remarkPosix2Windows from './src/plugins/remarkPosix2Windows';
20
21const markdownOptions: Partial<MDXOptions> = {
22 remarkPlugins: [[smartypants, { dashes: 'oldschool' }], remarkPosix2Windows],
23};
24
25const docsOptions = {
26 ...markdownOptions,
27 sidebarPath: undefined,
28 editUrl:
29 'https://github.com/graphs4value/refinery/edit/main/subprojects/docs',
30} satisfies DocsOptions;
31
32export default {
33 title: 'Refinery',
34 tagline: 'An efficient graph solver for generating well-formed models',
35 url: 'https://refinery.tools',
36 baseUrl: '/',
37 baseUrlIssueBanner: false,
38 trailingSlash: true,
39 staticDirectories: ['static', 'build/javadocs'],
40 plugins: [
41 [
42 '@docusaurus/plugin-content-docs',
43 {
44 id: 'learn',
45 path: 'src/learn',
46 routeBasePath: '/learn',
47 ...docsOptions,
48 } satisfies DocsOptions,
49 ],
50 [
51 '@docusaurus/plugin-content-docs',
52 {
53 id: 'develop',
54 path: 'src/develop',
55 routeBasePath: '/develop',
56 ...docsOptions,
57 } satisfies DocsOptions,
58 ],
59 [
60 '@docusaurus/plugin-content-pages',
61 markdownOptions satisfies PagesOptions,
62 ],
63 '@docusaurus/plugin-sitemap',
64 './src/plugins/loadersPlugin.ts',
65 './src/plugins/swcMinifyPlugin.ts',
66 ],
67 themes: [
68 [
69 '@docusaurus/theme-classic',
70 {
71 customCss: [require.resolve('./src/css/custom.css')],
72 } satisfies ClassicThemeOptions,
73 ],
74 '@docusaurus/theme-search-algolia',
75 ],
76 themeConfig: {
77 colorMode: {
78 respectPrefersColorScheme: true,
79 },
80 prism: {
81 additionalLanguages: ['bash', 'java'],
82 theme: themes.oneLight,
83 darkTheme: themes.oneDark,
84 },
85 navbar: {
86 title: 'Refinery',
87 logo: {
88 src: '/logo.svg',
89 srcDark: '/logo-dark.svg',
90 },
91 hideOnScroll: true,
92 items: [
93 {
94 label: 'Learn',
95 to: '/learn',
96 },
97 {
98 label: 'Develop',
99 to: '/develop',
100 },
101 {
102 label: 'GitHub',
103 position: 'right',
104 href: 'https://github.com/graphs4value/refinery',
105 },
106 {
107 label: 'Try now',
108 position: 'right',
109 href: 'https://refinery.services/',
110 className: 'navbar__link--try-now',
111 },
112 ],
113 },
114 footer: {
115 links: [
116 {
117 title: 'Learn',
118 items: [
119 {
120 label: 'Introduction',
121 to: '/learn',
122 },
123 {
124 label: 'Tutorials',
125 to: '/learn/tutorials',
126 },
127 {
128 label: 'Langauge reference',
129 to: '/learn/language',
130 },
131 {
132 label: 'Run in Docker',
133 to: '/learn/docker',
134 },
135 ],
136 },
137 {
138 title: 'Develop',
139 items: [
140 {
141 label: 'Programming guide',
142 to: '/develop',
143 },
144 {
145 label: 'Contributing',
146 to: '/develop/contributing',
147 },
148 {
149 label: 'Javadoc',
150 to: '/develop/javadoc',
151 },
152 ],
153 },
154 {
155 title: 'More',
156 items: [
157 {
158 label: 'Try now',
159 href: 'https://refinery.services/',
160 },
161 {
162 label: 'GitHub',
163 href: 'https://github.com/graphs4value/refinery',
164 },
165 {
166 label: 'License',
167 to: '/license',
168 },
169 ],
170 },
171 {
172 title: 'Supporters',
173 items: [
174 {
175 label: 'BME MIT FTSRG',
176 href: 'https://ftsrg.mit.bme.hu/en/',
177 },
178 {
179 label: 'McGill ECE',
180 href: 'https://www.mcgill.ca/',
181 },
182 {
183 label: '2022 Amazon Research Awards',
184 href: 'https://www.amazon.science/research-awards/recipients/daniel-varro-fall-2021',
185 },
186 {
187 label: 'LiU Software and Systems',
188 href: 'https://liu.se/en/organisation/liu/ida/sas',
189 },
190 {
191 label: 'WASP',
192 href: 'https://wasp-sweden.org/',
193 },
194 ],
195 },
196 ],
197 copyright: `
198 Copyright &copy; 2021-2024
199 <a href="https://github.com/graphs4value/refinery/blob/main/CONTRIBUTORS.md#the-refinery-authors" target="_blank">The Refinery Authors</a>.
200 Available under the
201 <a href="/license/">Eclipse Public License - v 2.0</a>.
202 `,
203 },
204 algolia: {
205 appId: 'KYHOYEO80F',
206 apiKey: '152acfb8d1ad9e10f29f083a6b017a69',
207 indexName: 'refinery',
208 },
209 } satisfies UserThemeConfig & AlgoliaConfig,
210 webpack: {
211 // Speed up builds by using a native Javascript loader.
212 // See: https://github.com/facebook/docusaurus/issues/4765#issuecomment-841135926
213 // But we follow the Docusaurus upstream from
214 // https://github.com/facebook/docusaurus/blob/791da2e4a1a53aa6309887059e3f112fcb35bec4/website/docusaurus.config.ts#L152-L171
215 // and use swc instead of esbuild.
216 jsLoader: (isServer) => ({
217 loader: require.resolve('swc-loader'),
218 options: {
219 jsc: {
220 parser: {
221 syntax: 'typescript',
222 tsx: true,
223 },
224 transform: {
225 react: {
226 runtime: 'automatic',
227 },
228 },
229 target: 'es2022',
230 },
231 module: {
232 type: isServer ? 'commonjs' : 'es6',
233 },
234 } satisfies SwcConfig,
235 }),
236 },
237 headTags: [
238 {
239 tagName: 'link',
240 attributes: {
241 rel: 'icon',
242 href: '/favicon.svg',
243 type: 'image/svg+xml',
244 },
245 },
246 {
247 tagName: 'link',
248 attributes: {
249 rel: 'icon',
250 href: '/favicon.png',
251 type: 'image/png',
252 sizes: '32x32',
253 },
254 },
255 {
256 tagName: 'link',
257 attributes: {
258 rel: 'icon',
259 href: '/favicon-96x96.png',
260 type: 'image/png',
261 sizes: '96x96',
262 },
263 },
264 {
265 tagName: 'link',
266 attributes: {
267 rel: 'apple-touch-icon',
268 href: '/apple-touch-icon.png',
269 type: 'image/png',
270 sizes: '180x180',
271 },
272 },
273 {
274 tagName: 'link',
275 attributes: {
276 rel: 'manifest',
277 href: '/manifest.webmanifest',
278 },
279 },
280 {
281 tagName: 'meta',
282 attributes: {
283 name: 'theme-color',
284 media: '(prefers-color-scheme:light)',
285 content: '#f5f5f5',
286 },
287 },
288 {
289 tagName: 'meta',
290 attributes: {
291 name: 'theme-color',
292 media: '(prefers-color-scheme:dark)',
293 content: '#282c34',
294 },
295 },
296 ],
297} satisfies Config;
diff --git a/subprojects/docs/package.json b/subprojects/docs/package.json
new file mode 100644
index 00000000..15c129c2
--- /dev/null
+++ b/subprojects/docs/package.json
@@ -0,0 +1,71 @@
1{
2 "//": [
3 "SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>",
4 "",
5 "SPDX-License-Identifier: EPL-2.0"
6 ],
7 "name": "@refinery/docs",
8 "version": "0.0.0",
9 "description": "Documentation for Refinery",
10 "private": true,
11 "scripts": {
12 "build": "WEBPACK_URL_LOADER_LIMIT=0 docusaurus build --out-dir build/docusaurus",
13 "serve": "docusaurus serve --dir build/docusaurus --no-open",
14 "dev": "WEBPACK_URL_LOADER_LIMIT=0 docusaurus start --no-open",
15 "docusaurus": "docusaurus",
16 "write-translations": "docusaurus write-translations",
17 "write-heading-ids": "docusaurus write-heading-ids",
18 "typecheck": "yarn run g:tsc -p subprojects/docs/tsconfig.json",
19 "lint": "yarn run g:lint subprojects/docs",
20 "lint:fix": "yarn run lint --fix"
21 },
22 "repository": {
23 "type": "git",
24 "url": "git+https://github.com/graphs4value/refinery.git"
25 },
26 "author": "The Refinery Authors <https://refinery.tools/>",
27 "license": "EPL-2.0",
28 "bugs": {
29 "url": "https://github.com/graphs4value/refinery/issues"
30 },
31 "homepage": "https://refinery.tools",
32 "dependencies": {
33 "@docusaurus/core": "^3.2.1",
34 "@docusaurus/plugin-content-docs": "^3.2.1",
35 "@docusaurus/plugin-content-pages": "^3.2.1",
36 "@docusaurus/plugin-sitemap": "^3.2.1",
37 "@docusaurus/theme-classic": "^3.2.1",
38 "@docusaurus/theme-common": "^3.2.1",
39 "@docusaurus/theme-search-algolia": "^3.2.1",
40 "@fontsource-variable/jetbrains-mono": "^5.0.21",
41 "@fontsource-variable/open-sans": "^5.0.29",
42 "@fontsource/open-sans": "^5.0.28",
43 "@material-icons/svg": "^1.0.33",
44 "@mdx-js/react": "^3.0.1",
45 "@swc/core": "^1.5.0",
46 "clsx": "^2.1.1",
47 "mdast-util-mdx": "^3.0.0",
48 "prism-react-renderer": "^2.3.1",
49 "react": "^18.3.1",
50 "react-dom": "^18.3.1",
51 "remark-smartypants": "^3.0.1",
52 "responsive-loader": "^3.1.2",
53 "sharp": "^0.33.3",
54 "swc-loader": "^0.2.6",
55 "terser-webpack-plugin": "^5.3.10",
56 "unified": "^11.0.4",
57 "unist-util-visit": "^5.0.0",
58 "webpack": "^5.91.0"
59 },
60 "devDependencies": {
61 "@docusaurus/mdx-loader": "^3.2.1",
62 "@docusaurus/module-type-aliases": "^3.2.1",
63 "@docusaurus/types": "^3.2.1",
64 "@types/babel__core": "^7.20.5",
65 "@types/mdast": "^4.0.3",
66 "@types/node": "^20.12.7",
67 "@types/react": "^18.3.0",
68 "@types/react-dom": "^18.3.0",
69 "@types/unist": "^3.0.2"
70 }
71}
diff --git a/subprojects/docs/src/components/Features/fi1.svg b/subprojects/docs/src/components/Features/fi1.svg
new file mode 100644
index 00000000..0acaf3f0
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi1.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="356" height="356" viewBox="0 0 356 356"><path d="M293.85 121c22.865 39.603 22.865 88.397 0 128S228.729 313 183 313v-79.879l6.248-.63c25.813-5.282 45.23-28.12 45.23-55.495 0-3.91-.397-7.728-1.151-11.416l-2.537-8.17zM178 48c45.791 0 88.104 24.369 111 63.928l-62.474 35.981-1.387-2.55c-10.194-15.051-27.456-24.947-47.035-24.947s-36.841 9.896-47.035 24.947L129.632 148 67 111.928C89.896 72.37 132.209 48 178 48zM61.149 121l63.238 36.51-2.505 8.07a57 57 0 0 0-1.15 11.416c0 27.375 19.416 50.213 45.23 55.495l6.04.609V313c-45.73 0-87.987-24.397-110.85-64-22.866-39.603-22.866-88.397 0-128z" class="fibackground"/><path d="M184.177 125.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M171.824 127.99c-9.227 0-16.824-7.261-16.824-16.262v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262" class="fiforeground"/><path d="M184.177 125.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M171.824 127.99c-9.227 0-16.824-7.261-16.824-16.262v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262M120.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M107.824 56.99C98.597 56.99 91 49.73 91 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354C100.677 27.99 95 33.498 95 40.252v.476c0 6.754 5.678 12.262 12.824 12.262" class="fiforeground"/><path d="M120.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M107.824 56.99C98.597 56.99 91 49.73 91 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354C100.677 27.99 95 33.498 95 40.252v.476c0 6.754 5.678 12.262 12.824 12.262M166 97.99c-8.712-9.286-20.675-22.007-31-33" class="fiforeground"/><path d="M164.541 99.36c-8.711-9.286-20.672-22.008-30.998-33l2.916-2.739c10.324 10.992 22.286 23.714 31 33.002zM138 62.23l-10-5.24 4.353 10z" class="fiforeground"/><path d="m131.59 70.25-7.688-17.664 17.664 9.254zm1.525-6.518 1.32-1.113-2.335-1.223zM184.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M171.824 56.99C162.597 56.99 155 49.73 155 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262" class="fiforeground"/><path d="M184.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M171.824 56.99C162.597 56.99 155 49.73 155 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262M176 97.99v-30h4v30z" class="fiforeground"/><path d="m182 67.991-4-10-4 10z" class="fiforeground"/><path d="M171.047 69.99 178 52.605l6.955 17.385zm5.908-4h2.092L178 63.377zM248.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M235.824 56.99C226.597 56.99 219 49.73 219 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262" class="fiforeground"/><path d="M248.177 54.99c8.187 0 14.824-6.385 14.824-14.261v-.476c0-7.877-6.637-14.262-14.824-14.262h-12.353c-8.187 0-14.823 6.386-14.823 14.262v.476c0 7.876 6.637 14.262 14.823 14.262z" class="fiforeground"/><path d="M235.824 56.99C226.597 56.99 219 49.73 219 40.728v-.476c0-9 7.598-16.262 16.824-16.262h12.354c9.227 0 16.822 7.261 16.822 16.262v.476c0 9-7.595 16.262-16.822 16.262zm0-4h12.354c7.147 0 12.822-5.508 12.822-12.262v-.476c0-6.754-5.675-12.262-12.822-12.262h-12.354c-7.147 0-12.824 5.508-12.824 12.262v.476c0 6.754 5.678 12.262 12.824 12.262M190 97.99c8.713-9.286 20.675-22.007 31-33" class="fiforeground"/><path d="m191.459 99.36-2.918-2.737c8.714-9.288 20.678-22.01 31.002-33.002l2.916 2.738c-10.325 10.993-22.289 23.715-31 33M223.648 66.99l4.352-10-10 5.24z" class="fiforeground"/><path d="m224.41 70.25-9.974-8.41 17.664-9.254zm-1.525-6.518 1.017-2.338-2.335 1.225zM46.117 137.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 135.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754" class="fiforeground"/><path d="M46.117 137.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 135.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754M108.117 286.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M119.883 284.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754" class="fiforeground"/><path d="M108.117 286.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M119.883 284.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754M46.117 212.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 210.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754" class="fiforeground"/><path d="M46.117 212.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 210.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754M54 167.016v31h-4v-31z" class="fiforeground"/><path d="m49 197.016 3.5 11 3.5-11z" class="fiforeground"/><path d="M58.734 195.016 52.5 214.612l-6.236-19.596zm-5.468 4h-1.532l.766 2.404zM64 241.016c8.15 9.85 19.341 23.342 29 35" class="fiforeground"/><path d="M65.541 239.74c8.15 9.848 19.339 23.341 28.998 35l-3.08 2.551c-9.659-11.658-20.848-25.149-29-35z" class="fiforeground"/><path d="m90 278.253 10 5.763-4.353-11z" class="fiforeground"/><path d="m96.432 269.563 7.537 19.05-17.318-9.982zm-1.569 6.908-1.513 1.404 2.681 1.545zM46.117 286.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 284.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754" class="fiforeground"/><path d="M46.117 286.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z" class="fiforeground"/><path d="M57.883 284.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754H46.117c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4H46.117c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754M59 241.016c.894 8.736 1.192 20.372.88 31" class="fiforeground"/><path d="M60.99 240.813c.908 8.87 1.204 20.554.889 31.262l-3.998-.118c.31-10.547.01-22.136-.871-30.738zM57 272.015l2.47 11L64 272.772z" class="fiforeground"/><path d="m54.436 269.727 12.5 1.351-8.088 18.291zm5.128 4.578.53 2.357.968-2.195zM46 287.016c-1.81-8.994-2.388-21.037-1.75-32" class="fiforeground"/><path d="m42.254 254.9 3.992.233c-.628 10.808-.042 22.752 1.715 31.488l-3.922.789c-1.86-9.252-2.432-21.392-1.785-32.51z" class="fiforeground"/><path d="m49 257.016-3.177-13L40 256.108z" class="fiforeground"/><path d="m46.441 238.121 5.172 21.168-14.654-1.476zm-1.236 11.787-2.166 4.496 3.346.338zM314.88 146.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M303.12 144.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H303.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754" class="fiforeground"/><path d="M314.88 146.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M303.12 144.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H303.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754M252.88 295.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M241.12 293.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H241.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754" class="fiforeground"/><path d="M252.88 295.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M241.12 293.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H241.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754M252.88 221.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M241.12 219.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H241.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754" class="fiforeground"/><path d="M252.88 221.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M241.12 219.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H241.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754M297 176.02c-8.15 9.568-19.341 22.675-29 34.001" class="fiforeground"/><path d="M295.48 174.72c-8.149 9.566-19.338 22.673-28.998 34l3.043 2.596c9.658-11.325 20.851-24.432 29.002-34.002z" class="fiforeground"/><path d="m271 212.25-10 5.763 4.353-11z" class="fiforeground"/><path d="m264.57 203.56-7.537 19.051 17.318-9.98zm1.568 6.91 1.514 1.405-2.681 1.544zM314.88 221.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M303.12 219.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H303.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754" class="fiforeground"/><path d="M314.88 221.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z" class="fiforeground"/><path d="M303.12 219.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H303.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754M302 176.02c-.894 8.736-1.192 20.372-.879 31" class="fiforeground"/><path d="M300.01 175.81c-.908 8.87-1.204 20.553-.889 31.262l4-.117c-.31-10.548-.01-22.136.87-30.738z" class="fiforeground"/><path d="m304 206.02-2.472 11.001-4.529-10.244z" class="fiforeground"/><path d="m306.56 203.73-12.5 1.352 8.086 18.29zm-5.129 4.578-.53 2.358-.97-2.196zM245 250.02v31h4v-31zM251 281.02l-3.501 11-3.5-11z" class="fiforeground"/><path d="m241.27 279.02 6.234 19.596 6.237-19.596zm5.469 4h1.531l-.766 2.404zM315 221.02c1.808-8.713 2.387-20.38 1.749-31" class="fiforeground"/><path d="m318.75 189.9-3.992.24c.628 10.46.042 22.024-1.711 30.473l3.916.813c1.863-8.978 2.435-20.746 1.787-31.525z" class="fiforeground"/><path d="m312 191.02 2.824-12L320 190.182z" class="fiforeground"/><path d="m314.2 172.92-4.795 20.375 13.584-1.422zm1.242 12.186 1.563 3.371-2.416.254z" class="fiforeground"/></svg>
diff --git a/subprojects/docs/src/components/Features/fi1.svg.license b/subprojects/docs/src/components/Features/fi1.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi1.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Features/fi2.svg b/subprojects/docs/src/components/Features/fi2.svg
new file mode 100644
index 00000000..c1cba58d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi2.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="356" height="356" viewBox="0 0 356 356"><path d="M639 181.35c19.279 0 37.645-3.906 54.351-10.97l8.286-3.991L639 49.999l-62.637 116.39 8.287 3.991c16.706 7.064 35.072 10.97 54.35 10.97zM646.318 312h133.68l-72.851-135.37-9.271 4.465a150.3 150.3 0 0 1-43.412 11.103l-8.148.412zm-148.32 0h137.6V192.81l-12.061-.609a150.3 150.3 0 0 1-43.412-11.103l-9.27-4.465z" class="fibackground" transform="translate(-461 -10)"/><g class="fiforeground"><path d="M61.88 224.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755H50.115c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M50.12 226.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H50.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754"/><path d="M61.88 224.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755H50.115c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M50.12 226.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H50.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754M61.88 287.984c7.797 0 14.118-6.826 14.118-15.246v-.508c0-8.42-6.321-15.246-14.118-15.246H50.115c-7.797 0-14.118 6.826-14.118 15.246v.508c0 8.42 6.321 15.246 14.118 15.246z"/><path d="M50.12 289.984c-8.964 0-16.117-7.825-16.117-17.246v-.508c0-9.42 7.153-17.246 16.117-17.246h11.766c8.964 0 16.119 7.825 16.119 17.246v.508c0 9.421-7.155 17.246-16.119 17.246zm0-4h11.766c6.63 0 12.119-5.827 12.119-13.246v-.508c0-7.419-5.49-13.246-12.119-13.246H50.12c-6.63 0-12.117 5.827-12.117 13.246v.508c0 7.42 5.488 13.246 12.117 13.246"/><path d="M61.88 287.984c7.797 0 14.118-6.826 14.118-15.246v-.508c0-8.42-6.321-15.246-14.118-15.246H50.115c-7.797 0-14.118 6.826-14.118 15.246v.508c0 8.42 6.321 15.246 14.118 15.246z"/><path d="M50.12 289.984c-8.964 0-16.117-7.825-16.117-17.246v-.508c0-9.42 7.153-17.246 16.117-17.246h11.766c8.964 0 16.119 7.825 16.119 17.246v.508c0 9.421-7.155 17.246-16.119 17.246zm0-4h11.766c6.63 0 12.119-5.827 12.119-13.246v-.508c0-7.419-5.49-13.246-12.119-13.246H50.12c-6.63 0-12.117 5.827-12.117 13.246v.508c0 7.42 5.488 13.246 12.117 13.246"/></g><path d="M54 269.98v-31h4v31z" class="fiforeground"/><g class="fiforeground"><path d="m59 239.98-3.5-11-3.5 11z"/><path d="m49.27 241.98 6.234-19.596 6.235 19.596zm5.469-4h1.531l-.766-2.404zM147.88 287.984c7.797 0 14.118-6.826 14.118-15.246v-.508c0-8.42-6.321-15.246-14.118-15.246h-11.765c-7.797 0-14.118 6.826-14.118 15.246v.508c0 8.42 6.321 15.246 14.118 15.246z"/><path d="M136.12 289.984c-8.964 0-16.117-7.825-16.117-17.246v-.508c0-9.42 7.153-17.246 16.117-17.246h11.766c8.964 0 16.119 7.825 16.119 17.246v.508c0 9.421-7.155 17.246-16.119 17.246zm0-4h11.766c6.63 0 12.119-5.827 12.119-13.246v-.508c0-7.419-5.49-13.246-12.119-13.246H136.12c-6.63 0-12.117 5.827-12.117 13.246v.508c0 7.42 5.488 13.246 12.117 13.246"/><path d="M147.88 287.984c7.797 0 14.118-6.826 14.118-15.246v-.508c0-8.42-6.321-15.246-14.118-15.246h-11.765c-7.797 0-14.118 6.826-14.118 15.246v.508c0 8.42 6.321 15.246 14.118 15.246z"/><path d="M136.12 289.984c-8.964 0-16.117-7.825-16.117-17.246v-.508c0-9.42 7.153-17.246 16.117-17.246h11.766c8.964 0 16.119 7.825 16.119 17.246v.508c0 9.421-7.155 17.246-16.119 17.246zm0-4h11.766c6.63 0 12.119-5.827 12.119-13.246v-.508c0-7.419-5.49-13.246-12.119-13.246H136.12c-6.63 0-12.117 5.827-12.117 13.246v.508c0 7.42 5.488 13.246 12.117 13.246"/></g><path d="M76.02 274v-4h31v4z" class="fiforeground"/><g class="fiforeground"><path d="m106.02 276 11-3.5-11-3.5z"/><path d="M104.02 278.736v-12.471l19.596 6.234zm4-5.47 2.404-.766-2.404-.766zM222.88 229.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755h-11.765c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M211.12 231.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H211.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754"/><path d="M222.88 229.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755h-11.765c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M211.12 231.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H211.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754M222.88 291.984c7.797 0 14.118-6.606 14.118-14.754v-.492c0-8.148-6.321-14.754-14.118-14.754h-11.765c-7.797 0-14.118 6.606-14.118 14.754v.492c0 8.148 6.321 14.754 14.118 14.754z"/><path d="M211.12 293.984c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.667 0 12.119-5.64 12.119-12.754v-.492c0-7.114-5.452-12.754-12.119-12.754H211.12c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754"/><path d="M222.88 291.984c7.797 0 14.118-6.606 14.118-14.754v-.492c0-8.148-6.321-14.754-14.118-14.754h-11.765c-7.797 0-14.118 6.606-14.118 14.754v.492c0 8.148 6.321 14.754 14.118 14.754z"/><path d="M211.12 293.984c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.667 0 12.119-5.64 12.119-12.754v-.492c0-7.114-5.452-12.754-12.119-12.754H211.12c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754"/></g><path d="M215 274.98v-31h4v31z" class="fiforeground"/><g class="fiforeground"><path d="m220 243.98-3.5-11-3.5 11z"/><path d="m210.27 245.98 6.234-19.596 6.235 19.596zm5.469-4h1.531l-.766-2.404zM308.88 229.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755h-11.765c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M297.12 231.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H297.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754"/><path d="M308.88 229.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755h-11.765c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M297.12 231.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H297.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754"/></g><path d="M237.02 217v-4h31v4z" class="fiforeground"/><g class="fiforeground"><path d="m267.02 218 11-3.501-11-3.5z"/><path d="M265.02 220.74v-12.471l19.596 6.234zm4-5.47 2.404-.766-2.404-.766zM140.53 72.98c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.755-14.471-14.755h-12.059c-7.992 0-14.471 6.606-14.471 14.755v.491c0 8.149 6.479 14.754 14.471 14.754z"/><path d="M128.47 74.98c-9.094 0-16.471-7.545-16.471-16.754v-.492c0-9.21 7.377-16.754 16.471-16.754h12.059c9.094 0 16.473 7.545 16.473 16.754v.492c0 9.21-7.379 16.754-16.473 16.754zm0-4h12.059c6.89 0 12.473-5.665 12.473-12.754v-.492c0-7.09-5.583-12.754-12.473-12.754H128.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.089 5.58 12.754 12.471 12.754"/><path d="M140.53 72.98c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.755-14.471-14.755h-12.059c-7.992 0-14.471 6.606-14.471 14.755v.491c0 8.149 6.479 14.754 14.471 14.754z"/><path d="M128.47 74.98c-9.094 0-16.471-7.545-16.471-16.754v-.492c0-9.21 7.377-16.754 16.471-16.754h12.059c9.094 0 16.473 7.545 16.473 16.754v.492c0 9.21-7.379 16.754-16.473 16.754zm0-4h12.059c6.89 0 12.473-5.665 12.473-12.754v-.492c0-7.09-5.583-12.754-12.473-12.754H128.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.089 5.58 12.754 12.471 12.754M140.53 134.98c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.755-14.471-14.755h-12.059c-7.992 0-14.471 6.606-14.471 14.755v.491c0 8.149 6.479 14.754 14.471 14.754z"/><path d="M128.47 136.98c-9.094 0-16.471-7.545-16.471-16.754v-.492c0-9.21 7.377-16.754 16.471-16.754h12.059c9.094 0 16.473 7.545 16.473 16.754v.492c0 9.21-7.379 16.754-16.473 16.754zm0-4h12.059c6.89 0 12.473-5.665 12.473-12.754v-.492c0-7.09-5.583-12.754-12.473-12.754H128.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.089 5.58 12.754 12.471 12.754"/><path d="M140.53 134.98c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.755-14.471-14.755h-12.059c-7.992 0-14.471 6.606-14.471 14.755v.491c0 8.149 6.479 14.754 14.471 14.754z"/><path d="M128.47 136.98c-9.094 0-16.471-7.545-16.471-16.754v-.492c0-9.21 7.377-16.754 16.471-16.754h12.059c9.094 0 16.473 7.545 16.473 16.754v.492c0 9.21-7.379 16.754-16.473 16.754zm0-4h12.059c6.89 0 12.473-5.665 12.473-12.754v-.492c0-7.09-5.583-12.754-12.473-12.754H128.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.089 5.58 12.754 12.471 12.754"/></g><path d="M133 117.98v-31h4v31z" class="fiforeground"/><g class="fiforeground"><path d="m138 86.99-3.5-10-3.5 10z"/><path d="m128.18 88.99 6.318-18.055 6.319 18.055zm5.639-4h1.36l-.68-1.943z"/></g><path d="M215.12 74.984c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H215.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754" class="fiforeground"/><path class="fiempty" d="M226.88 134.98c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.321-14.755-14.118-14.755h-11.765c-7.797 0-14.118 6.606-14.118 14.755v.491c0 8.149 6.321 14.754 14.118 14.754z"/><path d="M215.12 136.98c-8.926 0-16.117-7.57-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754h11.766c8.927 0 16.119 7.57 16.119 16.754v.492c0 9.183-7.193 16.754-16.119 16.754zm0-4h11.766c6.668 0 12.119-5.64 12.119-12.754v-.492c0-7.115-5.452-12.754-12.119-12.754H215.12c-6.667 0-12.117 5.639-12.117 12.754v.492c0 7.115 5.45 12.754 12.117 12.754" class="fiforeground"/><path stroke-dasharray="4, 4" d="M155.02 122v-4h4v4zm8 0v-4h4v4zm8 0v-4h4v4zm8 0v-4h4v4z" class="fiforeground"/><g class="fiforeground"><path d="m185.02 124 11-3.501-11-3.5z"/><path d="M183.02 126.74v-12.471l19.596 6.234zm4-5.47 2.404-.766-2.404-.766z"/></g><path stroke-dasharray="4, 4" d="M155.02 60v-4h4v4zm8 0v-4h4v4zm8 0v-4h4v4zm8 0v-4h4v4z" class="fiforeground"/><g class="fiforeground"><path d="m185.02 62 11-4.001-11-4z"/><path d="M183.02 64.86V51.147l18.852 6.856zm4-5.713 3.148-1.144-3.148-1.145z"/></g></svg>
diff --git a/subprojects/docs/src/components/Features/fi2.svg.license b/subprojects/docs/src/components/Features/fi2.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi2.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Features/fi3.svg b/subprojects/docs/src/components/Features/fi3.svg
new file mode 100644
index 00000000..a901aa56
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi3.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="356" height="356" viewBox="0 0 356 356"><g class="fiforeground"><path d="m219.4 147.87-9.322 21.336h-2.746l-7.841-21.336h3.107l6.27 17.958 7.605-17.958zM137.87 167.38l9.322-21.336h2.746l7.841 21.336h-3.107l-6.27-17.958-7.605 17.958zM185.6 147.14v-10.37h-19.692v-2.385h22.258v12.755zM216 181.66v23.035h-15.23v-2.511h12.412V194h-12.213v-2.53h12.213v-7.298H200.77v-2.512zM140.95 181.02l2.276 6.558h10.262l2.765-6.558h2.98L149.1 204.163h-2.765L137.7 181.02zm11.49 9.033h-8.365l3.794 10.731zM190.5 210.64h-.217q-5.33.325-11.11 2.33-5.782 1.988-8.311 6.306l-2.096-1.391q2.349-3.74 6.287-5.727 3.939-2.006 10.406-2.638v-.199q-6.486-.777-10.424-2.8-3.92-2.023-6.269-5.655l2.096-1.39q2.565 4.209 8.328 6.268 5.764 2.042 11.093 2.367h.217zm0 5.872q-6.739 0-10.822 2.348-4.083 2.349-6.757 6.902l-2.041-1.518q3.378-5.366 7.967-7.732 4.589-2.385 11.653-2.349z"/></g><path d="M1015.4 261.94h14.295c9.467 0 17.149 8.053 17.149 17.986v.599c0 9.933-7.682 17.985-17.149 17.985h-14.285c-9.477 0-17.154-8.052-17.154-17.985v-.599c0-9.933 7.677-17.986 17.144-17.986m42.964-29.783-3.954 3.264c-9.502 6.422-20.957 10.172-33.293 10.172-12.326 0-23.78-3.75-33.288-10.172l-3.138-2.595-38.937 38.951 75.962 75.996 76.111-76.137zm-129.62-65.353c9.477 0 17.154 8.053 17.154 17.986v.599c0 9.933-7.677 17.985-17.154 17.985h-14.285c-9.477 0-17.154-8.052-17.154-17.985v-.599c0-9.933 7.677-17.986 17.154-17.986zm178.94-57.016-39.782 39.796 2.593 3.145c6.42 9.506 10.17 20.966 10.168 33.302.001 12.335-3.749 23.795-10.168 33.302l-2.498 3.028 39.546 39.56 76.109-76.137zm-171.78-.141-76.112 76.137 76.252 76.278 38.876-38.89-3.169-3.839c-6.418-9.507-10.166-20.967-10.166-33.302s3.749-23.796 10.166-33.302l3.269-3.956zm93.866-36.288c9.467 0 17.149 8.052 17.149 17.985v.6c0 9.932-7.682 17.985-17.149 17.985h-14.285c-9.477 0-17.154-8.053-17.154-17.986v-.599c0-9.933 7.677-17.985 17.154-17.985zm-8.047-49.564L945.62 99.932l39.186 39.206 3.029-2.499c9.506-6.422 20.962-10.172 33.288-10.172 12.336 0 23.79 3.75 33.293 10.172l3.838 3.168 39.721-39.735z" class="fibackground" transform="translate(-844 -10)"/><g class="fiforeground"><path d="M271.4 158.02c-9.354 0-16.936 7.926-16.936 17.705v.59c0 9.778 7.582 17.705 16.936 17.705h14.112c9.354 0 16.936-7.927 16.936-17.705v-.59c0-9.779-7.582-17.705-16.936-17.705z"/><path d="M271.4 156.02c-10.483 0-18.936 8.893-18.936 19.705v.59c0 10.812 8.452 19.705 18.936 19.705h14.113c10.483 0 18.936-8.894 18.936-19.705v-.59c0-10.813-8.452-19.705-18.936-19.705zm0 4h14.113c8.224 0 14.936 6.96 14.936 15.705v.59c0 8.744-6.712 15.705-14.936 15.705H271.4c-8.224 0-14.936-6.961-14.936-15.705v-.59c0-8.745 6.712-15.705 14.936-15.705"/></g><g class="fiforeground"><path d="m170.8 260.1-3.43 3.352 6.69 6.527-6.69 6.527 3.43 3.353 6.69-6.528 6.68 6.528 3.44-3.353-6.69-6.527 6.69-6.527-3.44-3.352-6.68 6.527zm-.52-8.102h14.42c9.55 0 17.29 7.927 17.29 17.705v.59c0 9.778-7.74 17.705-17.29 17.705h-14.42c-9.55 0-17.29-7.927-17.29-17.705v-.59c0-9.778 7.74-17.705 17.29-17.705"/><path d="M170.3 250.02c-10.657 0-19.289 8.87-19.289 19.705v.59c0 10.833 8.632 19.705 19.289 19.705h14.422c10.657 0 19.289-8.872 19.289-19.705v-.59c0-10.834-8.633-19.705-19.289-19.705zm0 4h14.422c8.443 0 15.289 6.981 15.289 15.705v.59c0 8.722-6.846 15.705-15.289 15.705H170.3c-8.443 0-15.289-6.983-15.289-15.705v-.59c0-8.724 6.846-15.705 15.289-15.705M170.3 61.02c-10.688 0-19.289 9.12-19.289 20.197v.605c0 11.076 8.602 20.197 19.289 20.197h14.422c10.688 0 19.289-9.12 19.289-20.197v-.605c0-11.077-8.602-20.197-19.289-20.197zm0 4h14.422c8.413 0 15.289 7.174 15.289 16.197v.605c0 9.023-6.877 16.197-15.289 16.197H170.3c-8.413 0-15.289-7.174-15.289-16.197v-.605c0-9.023 6.876-16.197 15.289-16.197"/></g></svg>
diff --git a/subprojects/docs/src/components/Features/fi3.svg.license b/subprojects/docs/src/components/Features/fi3.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi3.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Features/fi4.svg b/subprojects/docs/src/components/Features/fi4.svg
new file mode 100644
index 00000000..b7ad5a1d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi4.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="356" height="356" viewBox="0 0 356 356"><path d="M319.81 29.75c-4.175 0-7.559 3.303-7.559 7.377s3.384 7.377 7.559 7.377 7.559-3.303 7.559-7.377-3.384-7.377-7.559-7.377m-20.542 0c-4.175 0-7.559 3.303-7.559 7.377s3.384 7.377 7.559 7.377c4.174 0 7.559-3.303 7.559-7.377s-3.385-7.377-7.559-7.377m-20.569-.371c-4.175 0-7.559 3.303-7.559 7.377s3.384 7.377 7.559 7.377 7.559-3.303 7.559-7.377-3.384-7.377-7.559-7.377m-246.46-6.651h296.75c4.763 0 8.624 3.768 8.624 8.416v13.168c0 4.648-3.861 8.416-8.624 8.416H32.239c-4.763 0-8.624-3.768-8.624-8.416V31.144c0-4.648 3.861-8.416 8.624-8.416" class="fiforeground"/><path d="M125.85 260.65v24h33v62h-33v45h33v62h-33v24h66v-22h153v-21h33v-24h-33v-43h-66v-18h99v-24h-33v-20h33v-23h-99v-22z" class="fibackground" transform="translate(-79 -182)"/><path d="M34.96 20.72c-7.34 0-13.336 5.995-13.336 13.336v293.33c0 7.34 5.995 13.334 13.336 13.334h291.33c7.34 0 13.334-5.993 13.334-13.334V34.056c0-7.34-5.993-13.336-13.334-13.336zm0 4h291.33a9.305 9.305 0 0 1 9.334 9.336v293.33a9.303 9.303 0 0 1-9.334 9.334H34.96a9.305 9.305 0 0 1-9.336-9.334V34.056a9.307 9.307 0 0 1 9.336-9.336" class="fiforeground"/><path d="M23.62 48.73v4h314v-4z" class="fiforeground"/></svg>
diff --git a/subprojects/docs/src/components/Features/fi4.svg.license b/subprojects/docs/src/components/Features/fi4.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi4.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Features/fi5.svg b/subprojects/docs/src/components/Features/fi5.svg
new file mode 100644
index 00000000..65d61c3c
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi5.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="356" height="356" viewBox="0 0 356 356"><defs><clipPath id="refinery-fi5-clip0"><path d="M478.5 237.41c-41.747 0-75.591 33.844-75.591 75.591s33.844 75.591 75.591 75.591c41.748 0 75.591-33.844 75.591-75.591S520.248 237.41 478.5 237.41M323 160.001h311v306H323z" clip-rule="evenodd"/></clipPath></defs><g clip-path="url(#refinery-fi5-clip0)" transform="translate(-301 -146)"><path d="m544.17 208.9 24.346-20.43 19.45 16.32-15.892 27.525a125.2 125.2 0 0 1 25.251 43.735l31.786-.001 4.41 25.005-29.871 10.869a125.2 125.2 0 0 1-8.769 49.734l24.35 20.429-12.698 21.988-29.866-10.871a125.2 125.2 0 0 1-38.686 32.462l5.52 31.3-23.859 8.683-15.891-27.525a125.2 125.2 0 0 1-50.501 0l-15.89 27.525-23.859-8.683 5.52-31.3a125.2 125.2 0 0 1-38.686-32.462l-29.866 10.871-12.695-21.988 24.347-20.429a125.2 125.2 0 0 1-8.769-49.734l-29.866-10.869 4.409-25.005 31.782.001a125.2 125.2 0 0 1 25.251-43.735l-15.892-27.525 19.45-16.32 24.346 20.43a125.2 125.2 0 0 1 47.456-17.272l5.518-31.3h25.39l5.518 31.3A125.2 125.2 0 0 1 544.17 208.9" class="fibackground"/></g><path d="M164.01 262v4h12v-4z" class="fiforeground"/><g class="fiforeground"><path d="m177.02 261 11 3.501-11 3.5z"/><path d="M175.02 258.26v12.471l19.596-6.234zm4 5.47 2.404.766-2.404.766z"/></g><path d="M152.02 324v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m183.02 323 11 3.501-11 3.5z"/><path d="M181.02 320.26v12.471l19.596-6.234zm4 5.47 2.404.766-2.404.766z"/></g><path d="M157.02 150v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 148 10 4-10 4.001z"/><path d="M186.01 145.04v13.91l17.385-6.957zm4 5.908 2.617 1.047-2.617 1.045z"/></g><path d="M157.02 150v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 148 10 3.5-10 3.5z"/><path d="M186.01 145.18v12.637l18.055-6.318zm4 5.639 1.943.68-1.943.68z"/></g><path d="M157.02 129v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 128 10 3.5-10 3.5z"/><path d="M186.01 125.18v12.637l18.055-6.318zm4 5.639 1.943.68-1.943.68z"/></g><path d="M157.02 129v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 128 10 3.5-10 3.5z"/><path d="M186.01 125.18v12.637l18.055-6.318zm4 5.639 1.943.68-1.943.68z"/></g><g class="fiforeground"><path d="M141.88 311.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M130.12 309.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H130.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M141.88 311.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M130.12 309.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H130.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M141.88 249.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M130.12 247.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H130.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M141.88 249.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M130.12 247.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H130.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><path d="M134 266.02v31h4v-31z" class="fiforeground"/><g class="fiforeground"><path d="m139 297.02-3.501 11-3.5-11z"/><path d="m129.27 295.02 6.234 19.596 6.237-19.596zm5.469 4h1.531l-.766 2.404z"/></g><g class="fiforeground"><path d="M117.88 188.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M106.12 186.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H106.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M117.88 188.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755h-11.765c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M106.12 186.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H106.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><path d="m115.02 203.24-3.863 1.037 7.978 29.746 3.863-1.037z" class="fiforeground"/><g class="fiforeground"><path d="m124.49 232.34-.682 11.384-6.289-9.514z"/><path d="m126.66 229.68-12.334 3.309 11.127 16.832zm-4.326 5.303-.156 2.627-1.452-2.196z"/></g><g class="fiforeground"><path d="M74.88 127.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755H63.115c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M63.12 125.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H63.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M74.88 127.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755H63.115c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M63.12 125.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H63.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><path d="m81.96 149.63-3.469 1.992 15.336 26.707 3.469-1.992z" class="fiforeground"/><g class="fiforeground"><path d="m98.56 175.32 2.258 11.178-8.516-7.584z"/><path d="m99.97 172.21-11.072 6.357 15.066 13.418zm-2.822 6.232.521 2.582-1.967-1.752z"/></g><g class="fiforeground"><path d="M214.117 311.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M225.883 309.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/><path d="M214.117 311.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M225.883 309.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/></g><g class="fiforeground"><path d="M214.117 249.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M225.883 247.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/><path d="M214.117 249.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M225.883 247.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/></g><path d="M222 266.016v31h-4v-31z" class="fiforeground"/><g class="fiforeground"><path d="m216 297.016 4 11 4-11z"/><path d="M226.856 295.016 220 313.87l-6.855-18.854zm-5.713 4h-2.287l1.144 3.146z"/></g><g class="fiforeground"><path d="M237.47 188.016c-7.992 0-14.471 6.605-14.471 14.754v.491c0 8.149 6.479 14.754 14.471 14.754h12.059c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.754-14.471-14.754z"/><path d="M249.529 186.016c9.094 0 16.471 7.545 16.471 16.754v.492c0 9.208-7.377 16.754-16.471 16.754H237.47c-9.094 0-16.471-7.546-16.471-16.754v-.492c0-9.209 7.377-16.754 16.471-16.754zm0 4H237.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.088 5.58 12.754 12.471 12.754h12.059c6.89 0 12.471-5.666 12.471-12.754v-.492c0-7.089-5.58-12.754-12.471-12.754"/><path d="M237.47 188.016c-7.992 0-14.471 6.605-14.471 14.754v.491c0 8.149 6.479 14.754 14.471 14.754h12.059c7.992 0 14.471-6.605 14.471-14.754v-.491c0-8.149-6.479-14.754-14.471-14.754z"/><path d="M249.529 186.016c9.094 0 16.471 7.545 16.471 16.754v.492c0 9.208-7.377 16.754-16.471 16.754H237.47c-9.094 0-16.471-7.546-16.471-16.754v-.492c0-9.209 7.377-16.754 16.471-16.754zm0 4H237.47c-6.89 0-12.471 5.665-12.471 12.754v.492c0 7.088 5.58 12.754 12.471 12.754h12.059c6.89 0 12.471-5.666 12.471-12.754v-.492c0-7.089-5.58-12.754-12.471-12.754"/></g><path d="m244.588 204.283-7.98 29.745-3.864-1.036 7.98-29.745z" class="fiforeground"/><g class="fiforeground"><path d="m231.255 232.335.682 11.383 6.289-9.513z"/><path d="m241.425 232.993-11.126 16.83-1.206-20.139zm-6.398 2.425-1.609-.432.157 2.626z"/></g><g class="fiforeground"><path d="M281.117 127.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M292.883 125.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/><path d="M281.117 127.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M292.883 125.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/></g><path d="m277.267 151.63-15.336 26.706-3.47-1.992 15.337-26.706z" class="fiforeground"/><g class="fiforeground"><path d="m257.183 175.33-2.257 11.178 8.516-7.585z"/><path d="m266.845 178.571-15.068 13.418 3.996-19.776zm-6.806.705-1.444-.83-.522 2.58z"/></g><path d="M157.02 171v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 169 10 3.5-10 3.5z"/><path d="M186.01 166.18v12.637l18.055-6.318zm4 5.639 1.943.68-1.943.68z"/></g><path d="M157.02 171v4h31v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.01 169 10 3.5-10 3.5z"/><path d="M186.01 166.18v12.637l18.055-6.318zm4 5.639 1.943.68-1.943.68z"/></g><path d="M158.02 192v4h30v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.02 190 11 3.501-11 3.5z"/><path d="M186.02 187.26v12.471l19.596-6.234zm4 5.47 2.404.766-2.404.766z"/></g><path d="M158.02 191v4h30v-4z" class="fiforeground"/><g class="fiforeground"><path d="m188.02 190 11 3.501-11 3.5z"/><path d="M186.02 187.26v12.471l19.596-6.234zm4 5.47 2.404.766-2.404.766z"/></g><g class="fiforeground"><path d="M49.88 62.02c7.797 0 14.118 6.605 14.118 14.754v.491c0 8.149-6.321 14.755-14.118 14.755H38.115c-7.797 0-14.118-6.606-14.118-14.755v-.491c0-8.149 6.321-14.754 14.118-14.754z"/><path d="M38.12 60.02c-8.926 0-16.117 7.57-16.117 16.754v.492c0 9.183 7.19 16.754 16.117 16.754h11.766c8.927 0 16.119-7.57 16.119-16.754v-.492c0-9.183-7.193-16.754-16.119-16.754zm0 4h11.766c6.668 0 12.119 5.64 12.119 12.754v.492c0 7.115-5.452 12.754-12.119 12.754H38.12c-6.667 0-12.117-5.639-12.117-12.754v-.492c0-7.115 5.45-12.754 12.117-12.754"/></g><g class="fiforeground"><path d="M305.117 62.016c-7.797 0-14.118 6.605-14.118 14.754v.491c0 8.149 6.32 14.754 14.118 14.754h11.765c7.797 0 14.118-6.605 14.118-14.754v-.491c0-8.149-6.32-14.754-14.118-14.754z"/><path d="M316.883 60.016c8.927 0 16.117 7.57 16.117 16.754v.492c0 9.182-7.19 16.754-16.117 16.754h-11.766c-8.927 0-16.117-7.571-16.117-16.754v-.492c0-9.183 7.19-16.754 16.117-16.754zm0 4h-11.766c-6.667 0-12.117 5.64-12.117 12.754v.492c0 7.114 5.45 12.754 12.117 12.754h11.766c6.667 0 12.117-5.64 12.117-12.754v-.492c0-7.115-5.45-12.754-12.117-12.754"/></g></svg>
diff --git a/subprojects/docs/src/components/Features/fi5.svg.license b/subprojects/docs/src/components/Features/fi5.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/Features/fi5.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Features/index.module.css b/subprojects/docs/src/components/Features/index.module.css
new file mode 100644
index 00000000..20ad300b
--- /dev/null
+++ b/subprojects/docs/src/components/Features/index.module.css
@@ -0,0 +1,130 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7:global(.fibackground) {
8 fill: url(#fi-lg);
9}
10
11:global(.fiforeground) {
12 fill: #303846;
13}
14
15:global(.fiempty) {
16 fill: #fff;
17}
18
19[data-theme='dark'] :global(.fiforeground) {
20 fill: var(--ifm-font-color-base);
21}
22
23[data-theme='dark'] :global(.fiempty) {
24 fill: var(--ifm-background-color);
25}
26
27.lg {
28 position: absolute;
29 visibility: hidden;
30 top: 0;
31 left: 0;
32 width: 0;
33 height: 0;
34}
35
36.lg__start {
37 stop-color: var(--ifm-color-primary-lighter);
38}
39
40.lg__end {
41 stop-color: var(--ifm-color-primary);
42}
43
44[data-theme='dark'] .lg__start {
45 stop-color: var(--ifm-color-primary-light);
46}
47
48[data-theme='dark'] .lg__end {
49 stop-color: var(--ifm-color-primary-darker);
50}
51
52.feature__container {
53 display: flex;
54 flex-direction: column;
55 container-type: inline-size;
56 justify-content: stretch;
57}
58
59.feature {
60 display: flex;
61 flex-direction: column;
62 align-items: center;
63 margin-bottom: var(--ifm-leading);
64 gap: 1rem;
65}
66
67.feature__icon {
68 display: flex;
69 width: 100%;
70 max-width: 220px;
71 height: auto;
72 aspect-ratio: 1/1;
73}
74
75.feature__icon svg {
76 width: 100%;
77 height: 100%;
78}
79
80.feature__contents {
81 display: flex;
82 flex-direction: column;
83 align-items: center;
84 gap: 1rem;
85}
86
87.feature__title {
88 margin: 0;
89}
90
91.feature__text {
92 margin: 0;
93 text-align: center;
94}
95
96@container (min-width: 660px) {
97 .feature {
98 flex-direction: row;
99 }
100
101 .feature--even {
102 flex-direction: row-reverse;
103 }
104
105 .feature__icon {
106 flex-shrink: 0;
107 }
108
109 .feature__contents {
110 align-items: flex-start;
111 }
112
113 .feature--even .feature__contents {
114 align-items: flex-end;
115 }
116
117 .feature__title,
118 .feature__text {
119 text-align: left;
120 }
121
122 .feature--even .feature__title,
123 .feature--even .feature__text {
124 text-align: right;
125 }
126}
127
128.row--last {
129 margin-bottom: calc(-1 * var(--ifm-leading));
130}
diff --git a/subprojects/docs/src/components/Features/index.tsx b/subprojects/docs/src/components/Features/index.tsx
new file mode 100644
index 00000000..36fab14a
--- /dev/null
+++ b/subprojects/docs/src/components/Features/index.tsx
@@ -0,0 +1,114 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import clsx from 'clsx';
8
9import Fi1 from './fi1.svg';
10import Fi2 from './fi2.svg';
11import Fi3 from './fi3.svg';
12import Fi4 from './fi4.svg';
13import Fi5 from './fi5.svg';
14import styles from './index.module.css';
15
16function Feature({
17 icon,
18 title,
19 offset,
20 even,
21 children,
22}: {
23 icon: React.ReactNode;
24 title: string;
25 offset?: number;
26 even?: boolean;
27 children: React.ReactNode;
28}) {
29 return (
30 <div
31 className={clsx(
32 'col',
33 'col--4',
34 { [`col--offset-${offset}`]: offset !== undefined },
35 styles['feature__container'],
36 )}
37 >
38 <div
39 className={clsx(styles['feature'], {
40 [styles['feature--even']!]: even,
41 })}
42 >
43 <div className={styles['feature__icon']}>{icon}</div>
44 <div className={styles['feature__contents']}>
45 <h3 className={styles['feature__title']}>{title}</h3>
46 <p className={styles['feature__text']}>{children}</p>
47 </div>
48 </div>
49 </div>
50 );
51}
52
53Feature.defaultProps = {
54 offset: undefined,
55 even: false,
56};
57
58export default function Features() {
59 return (
60 <div className="container">
61 <svg xmlns="ttp://www.w3.org/2000/svg" className={styles['lg']}>
62 <defs>
63 <linearGradient
64 id="fi-lg"
65 x1="0"
66 y1="0"
67 x2="0"
68 y2="366"
69 gradientUnits="userSpaceOnUse"
70 >
71 <stop offset="0%" className={styles['lg__start']} />
72 <stop offset="100%" className={styles['lg__end']} />
73 </linearGradient>
74 </defs>
75 </svg>
76 <h2 className="sr-only">Features</h2>
77 <div className="row">
78 <Feature icon={<Fi1 />} title="Diverse graph generation">
79 Refinery provides a framework for the automated generation of graphs.
80 </Feature>
81 <Feature icon={<Fi2 />} title="Model with uncertainty" even>
82 Partial modeling allows us to explicitly represent unknown or
83 uncertain knowledge in our models. The Refinery framework enables us
84 to explore design alternatives systematically.
85 </Feature>
86 <Feature icon={<Fi3 />} title="Formal logic reasoning">
87 Refinery combines the mathematical precision of formal logic
88 structures with the expressiveness of graph-based models. Underlying
89 solver algorithms ensure formal correctness and completeness of
90 generation processes.
91 </Feature>
92 </div>
93 <div className={clsx('row', styles['row--last'])}>
94 <Feature
95 icon={<Fi4 />}
96 title="Advanced web-based editor"
97 offset={2}
98 even
99 >
100 Designers are supported with state-of-the-art web-based editors with
101 advanced IDE features and visualization techniques. The framework can
102 be applied as a simple command-line interface program or deployed on
103 the cloud.
104 </Feature>
105 <Feature icon={<Fi5 />} title="Powerful graph algorithms">
106 Refinery is equipped with powerful algorithms such as incremental
107 query evaluation, efficient graph isomorphism checking, and
108 version-controlled data structures to solve various modeling and graph
109 processing problems.
110 </Feature>
111 </div>
112 </div>
113 );
114}
diff --git a/subprojects/docs/src/components/UseCases/index.module.css b/subprojects/docs/src/components/UseCases/index.module.css
new file mode 100644
index 00000000..c8ffc0af
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/index.module.css
@@ -0,0 +1,104 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7:global(.uchighlight) {
8 fill: var(--refinery-highlight);
9}
10
11:global(.ucdraw) {
12 fill: #303846;
13}
14
15:global(.ucstroke) {
16 stroke: #303846;
17}
18
19[data-theme='dark'] :global(.ucdraw) {
20 fill: var(--ifm-color-emphasis-700);
21}
22
23[data-theme='dark'] :global(.ucstroke) {
24 stroke: var(--ifm-color-emphasis-700);
25}
26
27.use-case {
28 position: relative;
29 display: flex;
30 overflow: hidden;
31 flex-direction: column-reverse;
32 align-content: justify;
33 width: 100%;
34 background: var(--ifm-card-background-color);
35 box-shadow: 0 1.5px 3px 0 rgb(0 0 0 / 15%);
36 border: 1px solid var(--ifm-color-emphasis-200);
37 border-radius: var(--ifm-card-border-radius);
38 margin-bottom: 2rem;
39 transition: all var(--ifm-transition-fast) ease;
40 transition-property: border, box-shadow;
41 --ifm-link-color: var(--ifm-color-emphasis-800);
42 --ifm-link-hover-color: var(--ifm-color-emphasis-700);
43}
44
45.use-case:hover,
46.use-case:focus-within {
47 border-color: var(--ifm-color-primary);
48 box-shadow: 0 3px 6px 0 rgb(0 0 0 / 20%);
49}
50
51.use-case__content {
52 display: flex;
53 overflow: hidden;
54 flex-direction: column;
55 align-items: center;
56 padding: 1rem;
57}
58
59.use-case__content svg {
60 width: 100%;
61 max-width: 600px;
62 height: auto;
63 transform: scale(1);
64 transition: transform var(--ifm-transition-fast) ease;
65}
66
67.use-case:hover svg,
68.use-case:focus-within svg {
69 transform: scale(1.414);
70}
71
72.use-case__title {
73 margin: 0;
74 padding: 1rem;
75 background: var(--ifm-background-surface-color);
76 font-weight: 400;
77}
78
79.use-case__title b,
80.use-case__title span {
81 display: inline-block;
82}
83
84[data-theme='dark'] .use-case__title {
85 background: var(--ifm-color-emphasis-200);
86}
87
88.use-case__link:hover > * {
89 text-decoration: underline;
90}
91
92.use-case__link::before {
93 content: ' ';
94 position: absolute;
95 top: 0;
96 left: 0;
97 z-index: 99;
98 width: 100%;
99 height: 100%;
100}
101
102.row--bottom {
103 margin-bottom: -2rem;
104}
diff --git a/subprojects/docs/src/components/UseCases/index.tsx b/subprojects/docs/src/components/UseCases/index.tsx
new file mode 100644
index 00000000..c9570cc6
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/index.tsx
@@ -0,0 +1,106 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import Link from '@docusaurus/Link';
8import clsx from 'clsx';
9
10import styles from './index.module.css';
11import Uc1 from './uc1.svg';
12import Uc2 from './uc2.svg';
13import Uc3 from './uc3.svg';
14import Uc4 from './uc4.svg';
15import Uc5 from './uc5.svg';
16import Uc6 from './uc6.svg';
17
18function UseCase({
19 icon,
20 title,
21 href,
22}: {
23 icon: React.ReactNode;
24 title: React.ReactNode;
25 href: string;
26}) {
27 return (
28 <div className="col col--4">
29 <div className={styles['use-case']}>
30 <h3 className={styles['use-case__title']}>
31 <Link href={href} className={styles['use-case__link']!}>
32 {title}
33 </Link>
34 </h3>
35 <div className={styles['use-case__content']}>{icon}</div>
36 </div>
37 </div>
38 );
39}
40
41export default function UseCases() {
42 return (
43 <>
44 <div className="row">
45 <UseCase
46 icon={<Uc1 />}
47 title={
48 <>
49 <b>Scenario generation</b> for testing autonomous vechicles
50 </>
51 }
52 href="https://doi.org/10.1007/s10270-021-00884-z"
53 />
54 <UseCase
55 icon={<Uc2 />}
56 title={
57 <>
58 <b>Conformance checking</b> of modeling toolchains
59 </>
60 }
61 href="https://doi.org/10.1007/s10009-019-00530-6"
62 />
63 <UseCase
64 icon={<Uc3 />}
65 title={
66 <>
67 Synthesize distributed <b>communication networks</b>
68 </>
69 }
70 href="https://doi.org/10.1109/TSE.2020.3025732"
71 />
72 </div>
73 <div className={clsx('row', styles['row--bottom'])}>
74 <UseCase
75 icon={<Uc4 />}
76 title={
77 <>
78 <b>Execution time analysis</b> for <span>data-driven</span>{' '}
79 critical systems
80 </>
81 }
82 href="https://doi.org/10.1145/3471904"
83 />
84 <UseCase
85 icon={<Uc5 />}
86 title={
87 <>
88 <b>Generative architectures</b> with assured resilience
89 </>
90 }
91 href="https://doi.org/10.1145/3550355.3552448"
92 />
93 <UseCase
94 icon={<Uc6 />}
95 title={
96 <>
97 <b>Video game map generator</b> with <span>model-based</span>{' '}
98 techniques
99 </>
100 }
101 href="https://doi.org/10.1145/3417990.3422001"
102 />
103 </div>
104 </>
105 );
106}
diff --git a/subprojects/docs/src/components/UseCases/uc1.svg b/subprojects/docs/src/components/UseCases/uc1.svg
new file mode 100644
index 00000000..2b5504e5
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc1.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><path d="m223.98 61.834-4.984.18.048 1.332 4.961-.178.54.016.042-1.332-.562-.018zm3.006.094-.041 1.332 5.547.174.04-1.332zm-10.39.174-5.398.193-.044.004-.147.015.137 1.327.125-.014 5.377-.193zm18.335.074-.041 1.334 1.853.058 3.633.36.131-1.326-3.654-.364-.045-.002zm-26.312.387-5.52.574.14 1.326 5.519-.574zm34.277.275-.131 1.328 5.522.549.132-1.328zm-42.184.547-2.28.236-.044.008-3.211.555.226 1.314 3.19-.553 2.256-.234zm50.129.328-.22 1.314 5.472.92.22-1.314zm-58.03.879-5.468.947.229 1.313 5.468-.946zm65.87.44-.22 1.314 3.542.595 1.897.385.263-1.306-1.906-.387-.021-.004zm-73.734.98-5.393 1.312.315 1.295 5.392-1.312zm81.57.486-.266 1.307 5.438 1.103.265-1.306zm-89.295 1.393-3.906.95-.043.013-1.46.462.403 1.272 1.438-.455 3.882-.945zm97.086.19-.266 1.306 5.438 1.103.265-1.306zm7.79 1.581-.265 1.307 5.438 1.103.265-1.306zm-112.57.379-5.29 1.674.402 1.271 5.291-1.675zm120.37 1.203-.265 1.307 5.438 1.105.265-1.306zm-127.94 1.195-.866.274-.041.014-4.323 1.697.489 1.24 4.298-1.687.844-.268zm135.73.387-.263 1.307 5.438 1.105.265-1.306zm7.792 1.582-.264 1.307 1.156.236.266-1.307zm-150.99.893-5.166 2.027.486 1.24 5.168-2.027zm-7.383 3.058-5.018 2.371.57 1.206 5.018-2.372zm-7.187 3.397-2.377 1.125-.041.02-2.55 1.423.651 1.164 2.53-1.412 2.357-1.115zm-7.063 3.738-4.846 2.705.65 1.164 4.846-2.705zm-6.963 3.926-4.652 3.027.726 1.117 4.653-3.027zm-6.664 4.336-3.644 2.373-.038.025-.96.72.8 1.067.944-.707 3.625-2.361zm-6.56 4.562-4.438 3.334.8 1.065 4.438-3.334zm-6.356 4.776-.984.74-.035.03-3.27 2.82.871 1.01 3.252-2.807.967-.727zm-6.105 5.158-4.202 3.625.871 1.01 4.202-3.625zm-5.965 5.318-3.948 3.903.938.947 3.947-3.902zm-5.655 5.59-2.091 2.069-.032.033-1.726 1.953 1 .883 1.71-1.936 2.077-2.055zm-5.437 5.856-3.674 4.16 1 .88 3.674-4.16zm-5.281 6-3.385 4.4 1.057.813 3.384-4.399zm-4.848 6.302-2.834 3.686-.027.037-.5.748 1.11.74.487-.73 2.82-3.668zm-4.691 6.467-3.078 4.62 1.109.738 3.078-4.618zm-4.41 6.615-.846 1.27-.026.039-2 3.492 1.157.662 1.99-3.472.834-1.25zm-4.065 6.883-2.758 4.815 1.156.664 2.76-4.817zm-3.916 6.944-2.594 4.906 1.178.623 2.596-4.906zm-3.715 7.027-2.596 4.906 1.18.623 2.594-4.906zm-3.717 7.027-2.595 4.906 1.18.625 2.593-4.906z" class="ucdraw"/><g class="uchighlight"><path d="m240.37 55.167-.066 2.333-9.803-3.814 10-3.186-.065 2.333z"/><path d="m240.3 49.865-10 3.186a.667.667 0 0 0-.04 1.255l9.804 3.815a.667.667 0 0 0 .908-.602l.133-4.668.064-2.332a.667.667 0 0 0-.869-.654m-.488 1.555-.04 1.394-.066 2.334-.039 1.387-7.164-2.787z"/></g><g class="uchighlight"><path d="m130.34 78.475 1.156 1.987-11 2.038 7.533-8 1.156 1.987z"/><path d="M128.11 73.838a.67.67 0 0 0-.562.205l-7.534 8a.667.667 0 0 0 .608 1.113l11-2.039a.667.667 0 0 0 .455-.99l-1.156-1.986-1.155-1.989-1.156-1.988a.67.67 0 0 0-.5-.326zm-.203 1.77.707 1.214 1.154 1.989.68 1.168-8.063 1.494z"/></g><g class="uchighlight"><path d="m50.436 158.7 2.064 1.098-8 7.707 1.808-11 2.064 1.097z"/><path d="M46.012 155.9a.67.67 0 0 0-.361.49l-1.809 11a.667.667 0 0 0 1.121.588l8-7.707a.667.667 0 0 0-.15-1.068l-2.063-1.1-2.064-1.098-2.065-1.095a.67.67 0 0 0-.61-.01zm.805 1.621 1.242.662 2.064 1.098 1.254.666-5.892 5.676z"/></g><g class="uchighlight"><path d="M152.5 31.834c-10.52 0-20.61 4.18-28.049 11.617a39.674 39.674 0 0 0 0 56.098A39.667 39.667 0 1 0 152.5 31.834m0 1.332a38.337 38.337 0 0 1 27.107 65.439A38.336 38.336 0 0 1 114.166 71.5 38.335 38.335 0 0 1 152.5 33.166"/><path d="m162.7 60.635-25.027 10.097 24.341-8.45 3.626 9.433-22.578 8.92-3.938-9.971-1.448.073-.021-.03 4.694 11.674 25.022-9.915z"/><path d="M157.6 65.863a.67.67 0 0 0-.559.076l-10.902 7a.667.667 0 0 0 .46 1.22l13-1.958a.667.667 0 0 0 .515-.916l-2.098-5.041a.67.67 0 0 0-.416-.381zm-.508 1.627 1.463 3.518-9.072 1.367z"/></g><path d="m187.7 68.619-25.053 10.393 24.277-8.664 3.723 9.904-22.586 9.367-3.753-9.97 22.515-9.265-24.177 8.628.227.604 4.423 11.766 25.053-10.393z" class="ucdraw"/><g class="ucdraw"><path d="M86.871 166.16h4v1h-4zM93.46 165.34a.75.75 0 0 0-.628.3q-.23.3-.23.823 0 .519.23.822.232.3.628.301c.396.001.473-.1.627-.301q.23-.303.23-.822 0-.523-.23-.822a.75.75 0 0 0-.627-.301m1.167-2.044v.594a2.2 2.2 0 0 0-.447-.177 1.7 1.7 0 0 0-.447-.062q-.581 0-.89.436-.305.436-.35 1.317.172-.28.43-.43.26-.15.571-.15.656 0 1.035.44.38.44.38 1.199 0 .742-.396 1.192-.396.448-1.053.448-.754 0-1.153-.64-.398-.642-.398-1.859 0-1.142.489-1.822t1.312-.68q.22 0 .447.05.225.048.47.144M97.41 163.62q-.454 0-.682.496-.228.494-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.229-1.486 0-.995-.23-1.49-.227-.495-.684-.495m0-.516q.73 0 1.115.641.386.64.386 1.86 0 1.216-.386 1.858-.384.64-1.115.64-.73 0-1.114-.64-.386-.641-.386-1.858 0-1.22.386-1.86.385-.641 1.114-.641M115.87 166.16h4v1h-4zM121.1 163.1h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .791-.466 1.23-.465.44-1.314.439-.291 0-.595-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.37 2.37 0 0 0 1.208.319q.538 0 .85-.299.314-.299.314-.811t-.314-.81q-.312-.3-.85-.3-.252 0-.502.059t-.51.184zM125.1 163.1h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .791-.466 1.23-.465.44-1.313.439-.293 0-.596-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.36 2.36 0 0 0 1.208.319q.537 0 .85-.299.314-.299.314-.811t-.314-.81q-.312-.3-.85-.3-.252 0-.502.059-.249.058-.51.184zM144.91 166.16h3v1h-3zM150.11 163.1h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .791-.466 1.23-.465.44-1.314.439-.291 0-.595-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.36 2.36 0 0 0 1.208.319q.537 0 .85-.299.315-.299.314-.811 0-.512-.314-.81-.313-.3-.85-.3-.252 0-.502.059t-.51.184zM155.41 163.62q-.453 0-.682.496-.228.494-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.229-1.486 0-.995-.23-1.49-.227-.495-.684-.495m0-.516q.73 0 1.115.641.385.64.385 1.86 0 1.216-.385 1.858-.384.64-1.115.64-.73 0-1.114-.64-.385-.641-.385-1.858 0-1.22.385-1.86.384-.641 1.114-.641M172.88 166.16h4v1h-4zM180.37 163.7l-1.875 2.67h1.875zm-.195-.59h.934v3.26h.783v.562h-.783v1.179h-.739v-1.18h-2.478v-.652zM182.11 163.1h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .791-.466 1.23-.465.44-1.314.439-.291 0-.595-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.36 2.36 0 0 0 1.208.319q.538 0 .85-.299.314-.299.314-.811t-.314-.81q-.312-.3-.85-.3-.252 0-.502.059t-.51.184zM201.88 166.16h4v1h-4zM208.78 163.7l-1.406 2.67h1.406zm-.146-.59h.7v3.26h.588v.562h-.588v1.179h-.554v-1.18h-1.858v-.652zM212.42 163.62q-.453 0-.682.496-.228.494-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.228-1.486 0-.995-.228-1.49t-.685-.495m0-.516q.73 0 1.115.641.385.64.385 1.86 0 1.216-.385 1.858-.384.64-1.115.64-.73 0-1.114-.64-.386-.641-.386-1.858 0-1.22.386-1.86.384-.641 1.114-.641M230.88 166.16h4v1h-4zM237.98 165.41q.442.1.69.417.25.316.25.781 0 .713-.464 1.104-.465.39-1.32.39-.285 0-.59-.06c-.304-.06-.41-.1-.626-.179v-.629q.255.158.56.239t.64.08q.579 0 .882-.241.304-.243.304-.704 0-.426-.282-.666t-.785-.24h-.532v-.537h.556q.455 0 .695-.192.242-.192.242-.553 0-.371-.25-.57-.247-.198-.711-.199-.255 0-.545.059a5 5 0 0 0-.637.18v-.58a6 6 0 0 1 .658-.156q.306-.051.578-.051.703 0 1.11.337.41.338.41.911 0 .401-.217.677-.216.276-.616.382M240.11 163.1h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .791-.466 1.23-.465.44-1.314.439-.291 0-.595-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.37 2.37 0 0 0 1.208.319q.537 0 .85-.299.314-.299.314-.811t-.314-.81q-.313-.3-.85-.3-.252 0-.502.059t-.51.184zM65.673 155.55h2.231v.559h-3v-.559q.364-.391.992-1.05.63-.66.79-.852.306-.357.428-.607.123-.248.123-.488a.83.83 0 0 0-.264-.638q-.265-.247-.69-.247-.3 0-.634.108-.333.108-.713.33v-.672a5 5 0 0 1 .72-.243q.337-.082.616-.082.735 0 1.17.382.438.381.437 1.019 0 .303-.11.575a2.8 2.8 0 0 1-.396.639q-.079.096-.504.551-.422.456-1.196 1.275M70.965 153.41q.443.1.69.417.25.316.25.781 0 .713-.464 1.104-.465.39-1.319.39-.286 0-.59-.06-.305-.06-.627-.179v-.629q.255.158.56.239t.64.08q.579 0 .882-.242.304-.24.304-.703 0-.426-.282-.666t-.785-.24h-.532v-.537h.556q.454 0 .696-.192.24-.192.24-.553 0-.371-.248-.57-.249-.199-.712-.199-.255 0-.545.059a5 5 0 0 0-.637.18v-.58q.35-.104.658-.156.306-.051.578-.051.703 0 1.11.337.41.338.41.911 0 .401-.217.677t-.616.382zM74.408 151.62q-.454 0-.682.496-.228.495-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.228-1.486 0-.995-.228-1.49t-.685-.495m0-.516q.73 0 1.115.641.385.64.385 1.86 0 1.216-.385 1.858-.384.64-1.115.64-.73 0-1.114-.64-.386-.641-.386-1.858 0-1.22.386-1.86.385-.641 1.114-.641M65.673 126.64h2.231v.447h-3v-.447q.364-.314.992-.84.63-.528.79-.682.306-.285.428-.485a.74.74 0 0 0 .123-.39.61.61 0 0 0-.264-.51q-.265-.2-.69-.199-.3 0-.634.087-.333.087-.713.264v-.538q.386-.128.72-.194.337-.066.616-.066.735 0 1.17.306.438.305.437.815 0 .243-.11.46a2.2 2.2 0 0 1-.396.51q-.079.078-.504.442-.422.364-1.196 1.02M70.965 124.93q.443.08.69.334.25.252.25.625 0 .57-.464.883-.465.312-1.319.312-.286 0-.59-.048a5 5 0 0 1-.627-.144v-.503q.255.127.56.191.306.065.64.065.579 0 .882-.194a.63.63 0 0 0 .304-.562.61.61 0 0 0-.282-.533q-.282-.193-.785-.193h-.532v-.429h.556q.454 0 .696-.153a.5.5 0 0 0 .24-.443.51.51 0 0 0-.248-.456q-.249-.16-.712-.16-.255 0-.545.048-.29.046-.637.144v-.465q.35-.082.658-.124a4 4 0 0 1 .578-.04q.703 0 1.11.27.41.269.41.728a.74.74 0 0 1-.217.541q-.217.221-.616.306M73.1 123.09h2.462v.447h-1.888v.961a2 2 0 0 1 .547-.078q.775 0 1.23.36.453.36.453.975 0 .633-.466.984-.465.351-1.314.351-.291 0-.595-.042a6 6 0 0 1-.625-.126v-.534q.28.129.578.192t.63.063q.537 0 .85-.24a.77.77 0 0 0 .314-.648.77.77 0 0 0-.314-.649q-.312-.24-.85-.24a2.7 2.7 0 0 0-1.012.195zM65.673 97.645h2.231v.447h-3v-.447q.364-.314.992-.84.63-.528.79-.682.306-.285.428-.485a.74.74 0 0 0 .123-.39.61.61 0 0 0-.264-.51q-.265-.2-.69-.199-.3 0-.634.087-.333.087-.713.264v-.538a6 6 0 0 1 .72-.194q.337-.066.616-.066.735 0 1.17.306.438.305.437.815 0 .243-.11.46a2.2 2.2 0 0 1-.396.51q-.079.079-.504.442-.422.365-1.196 1.02M70.772 94.563l-1.406 2.135h1.406zm-.146-.471h.7v2.606h.588v.45h-.588v.943h-.554v-.942h-1.858v-.523zM74.408 94.627q-.454 0-.682.496-.228.494-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.228-1.486 0-.995-.228-1.49t-.685-.495m0-.516q.73 0 1.115.641.385.64.385 1.86 0 1.216-.385 1.858-.384.64-1.115.64-.73 0-1.114-.64-.386-.641-.386-1.858 0-1.22.386-1.86.385-.641 1.114-.641M70.772 65.707l-1.406 2.67h1.406zm-.146-.59h.7v3.26h.588v.562h-.588v1.179h-.554v-1.179h-1.858v-.653zM73.1 65.113h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.775 0 1.23.45.453.45.453 1.218 0 .792-.466 1.23-.465.44-1.314.439-.291 0-.595-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.37 2.37 0 0 0 1.208.319q.537 0 .85-.299.314-.299.314-.811 0-.511-.314-.81-.312-.3-.85-.3-.252 0-.502.059t-.51.184zM65.673 40.561h2.231v.559h-3v-.559q.364-.392.992-1.05.63-.66.79-.852.306-.358.428-.606.123-.249.123-.489a.83.83 0 0 0-.264-.638q-.265-.247-.69-.247-.3 0-.634.108a3.7 3.7 0 0 0-.713.33v-.672q.386-.16.72-.243.337-.082.616-.082.735 0 1.17.382.438.381.437 1.02 0 .302-.11.574a2.8 2.8 0 0 1-.396.639q-.079.096-.504.551-.422.457-1.196 1.275M69.1 36.115h2.462v.56h-1.888v1.2a1.6 1.6 0 0 1 .547-.097q.776 0 1.23.45t.453 1.218q0 .792-.466 1.23-.465.44-1.313.439-.293 0-.596-.053a4.5 4.5 0 0 1-.625-.157v-.668a2.37 2.37 0 0 0 1.208.319q.537 0 .85-.299.314-.299.314-.811t-.314-.81q-.312-.3-.85-.3-.252 0-.502.059-.249.058-.51.184zM74.408 36.632q-.454 0-.682.496-.228.495-.228 1.49 0 .99.228 1.485.23.496.682.496.457 0 .685-.496.228-.495.228-1.486 0-.995-.228-1.49t-.685-.495m0-.516q.73 0 1.115.64.385.642.385 1.861 0 1.216-.385 1.858-.384.64-1.115.64-.73 0-1.114-.64-.386-.641-.386-1.858 0-1.22.386-1.86.385-.641 1.114-.641M65.673 11.563h2.231v.559h-3v-.559q.364-.391.992-1.05.63-.66.79-.852.306-.358.428-.606.123-.249.123-.489a.83.83 0 0 0-.264-.638q-.265-.247-.69-.247-.3 0-.634.108-.333.109-.713.33v-.672q.386-.16.72-.243.337-.082.616-.082.735 0 1.17.382.438.381.437 1.02 0 .302-.11.574a2.8 2.8 0 0 1-.396.639q-.079.095-.504.551-.422.457-1.196 1.275M69.1 7.118h2.462v.559h-1.888v1.201a1.6 1.6 0 0 1 .547-.098q.776 0 1.23.45t.453 1.218q0 .792-.466 1.231-.465.438-1.313.438-.293 0-.596-.052a4.5 4.5 0 0 1-.625-.158v-.667a2.37 2.37 0 0 0 1.208.318q.537 0 .85-.298.314-.3.314-.812 0-.51-.314-.81-.312-.3-.85-.3-.252 0-.502.06-.249.058-.51.183zM73.1 7.118h2.462v.559h-1.888v1.201a1.6 1.6 0 0 1 .547-.098q.775 0 1.23.45.453.45.453 1.218 0 .792-.466 1.231-.465.438-1.314.438-.291 0-.595-.052a4.5 4.5 0 0 1-.625-.158v-.667a2.37 2.37 0 0 0 1.208.318q.537 0 .85-.298.314-.3.314-.812 0-.51-.314-.81-.312-.3-.85-.3-.252 0-.502.06-.25.058-.51.183z"/></g><path d="m223.23 40.834-14.213.56-.045.004-14.145 1.52-.045.006-14.008 2.467-.045.01-13.807 3.408-.043.011-13.547 4.33-.043.016-13.22 5.235-.042.017-12.836 6.113-.04.022-12.394 6.963-.037.023-11.893 7.783-.035.028-11.338 8.568-.035.027-10.73 9.313-.032.031-10.074 10.014-.032.033-9.37 10.67-.028.036-8.627 11.277-.025.037-7.842 11.834-.024.037-6.695 11.695-.012.02-15.924 30.07-.008.013-.002.002-.082.147-.326.58 1.162.654.1-.178.014.008.304-.574.008-.014.002-.004.317-.562-.014-.008 15.623-29.5 6.68-11.668 7.816-11.797 8.6-11.242 9.341-10.637 10.041-9.983 10.695-9.283 11.301-8.54 11.854-7.759 12.352-6.943 12.797-6.094 13.18-5.217 13.502-4.316 13.766-3.397 13.963-2.46 14.1-1.514 14.168-.559 14.176.397 14.113 1.351 14.002 2.301 40.814 8.272.655.13.263-1.306-.652-.133-40.828-8.273-.023-.004-14.037-2.307-.043-.006-14.16-1.355-.045-.002-14.22-.399h-.044zm.104 2.865-14.02.547-.045.004-13.951 1.492-.043.006-13.818 2.428-.045.01-13.621 3.355-.043.012-13.363 4.265-.041.016L141.3 60.99l-.041.018-12.664 6.025-.041.02-12.225 6.865-.037.023-11.732 7.672-.037.026-11.186 8.447-.036.027-10.586 9.182-.033.03-9.94 9.872-.03.034-9.249 10.52-.029.036-8.514 11.12-.025.038-7.74 11.668-.024.037-6.648 11.609-.01.02-15.31 28.907-.311.59 1.177.623.313-.588 15.303-28.898 6.633-11.582 7.716-11.63 8.485-11.087 9.219-10.486 9.908-9.841 10.553-9.153 11.148-8.418 11.695-7.648 12.186-6.842 12.623-6.004 13-5.14 13.32-4.252 13.576-3.344 13.773-2.42 13.906-1.488 13.975-.547 13.98.398 13.922 1.338 13.809 2.275 41.29 8.37.655.13.264-1.306-.653-.131-41.305-8.371-.023-.004-13.844-2.283-.045-.004-13.965-1.344-.045-.004-14.025-.396h-.045z" class="ucdraw"/><path d="m224.68 79.834-11.535.377-.045.002-11.482 1.158-.045.006-11.379 1.934-.045.007-11.223 2.702-.043.011-11.014 3.453-.043.014-10.758 4.194-.04.017-10.448 4.912-.04.022-10.095 5.61-.039.022-9.693 6.28-.037.025-9.246 6.924-.036.027-8.757 7.535-.034.03-8.23 8.111-.031.031-7.664 8.65-.03.034-7.062 9.15-.026.035-6.43 9.608-.032.052-12.188 22.414-.319.586 1.172.637.318-.586 12.174-22.387 6.4-9.562 7.036-9.114 7.633-8.615 8.199-8.078 8.725-7.506 9.209-6.894 9.656-6.256 10.055-5.588 10.406-4.893 10.715-4.175 10.97-3.44 11.18-2.69 11.333-1.925 11.439-1.153 11.49-.375 11.488.405 11.436 1.181 10.996 1.899 47.742 9.732.652.133.268-1.305-.655-.135-47.752-9.734-.02-.004-11.028-1.902-.045-.006-11.48-1.187-.045-.004-11.533-.407zm.104 2.879-11.34.363-.045.002-11.291 1.131-.045.006-11.188 1.894-.045.008-10.43 2.504-.014-.043-.615.194-.625.148.01.043-10.236 3.203-.043.016-10.578 4.113-.041.02-10.275 4.822-.041.02-9.928 5.51-.037.02-9.533 6.17-.037.026-9.096 6.8-.035.028-8.615 7.4-.033.032-8.098 7.968-.031.034-7.54 8.498-.029.035-6.949 8.99-.027.037-6.328 9.441-.032.053-11.414 20.982-.318.586 1.172.637.318-.586 11.398-20.955 6.3-9.397 6.921-8.957 7.51-8.464 8.064-7.936 8.582-7.371 9.059-6.774 9.496-6.144 9.887-5.486 10.236-4.805 10.535-4.098 10.789-3.377 10.99-2.635 11.145-1.886 11.244-1.125 11.297-.362 11.293.405 11.24 1.17 10.744 1.86 48.28 9.845.653.133.268-1.307-.655-.133-48.29-9.846-.02-.003-10.775-1.866-.043-.006-11.285-1.175-.045-.004-11.342-.407h-.045z" class="ucdraw"/><path d="m223.47 52.834-.848.031.05 1.332.825-.029.63.018.038-1.332-.65-.02zm3.17.092-.041 1.334 1.5.045.039-1.334zm-6.492.033-1.498.057.049 1.332 1.5-.057zm10.465.086-.04 1.332 1.499.045.04-1.332zm-14.438.062-1.498.057.05 1.332 1.499-.057zm18.41.057-.04 1.332 1.5.045.04-1.334zm-22.381.094-1.5.056.05 1.332 1.499-.056zm26.393.135-.13 1.326 1.493.146.129-1.328zm-30.402.138-1.49.157.138 1.326 1.492-.157zm34.359.246-.131 1.328 1.494.145.129-1.326zm-38.312.17-1.49.157.138 1.326 1.492-.157zm42.268.217-.13 1.326 1.493.145.13-1.326zm-46.221.2-1.49.156.138 1.326 1.492-.156zm50.22.197-.218 1.316 1.48.244.22-1.314zm-54.214.244-1.479.258.229 1.312 1.478-.256zm58.137.406-.219 1.316 1.48.245.22-1.315zm-62.053.275-1.479.258.229 1.313 1.478-.256zm65.975.375-.219 1.317 1.48.246.217-1.317zm-69.891.307-1.479.256.229 1.314 1.478-.257zm73.81.346-.216 1.314 1.393.233.074.013.242-1.312-.08-.014-.012-.002zm-77.726.336-1.205.209-.045.007-.268.067.317 1.295.246-.06 1.183-.206zm81.656.402-.264 1.307 1.47.296.264-1.306zm-85.578.469-1.46.355.317 1.295 1.457-.355zm89.475.318-.264 1.307 1.469.299.265-1.307zm-93.338.625-1.457.356.316 1.294 1.457-.355zm97.234.164-.266 1.307 1.47.297.264-1.307zm-101.1.78-1.457.355.316 1.295 1.457-.356zm104.99.007-.264 1.309 1.47.297.264-1.307zm3.896.79-.263 1.306 1.47.297.264-1.307zm-112.77.238-1.43.453.403 1.271 1.43-.453zm116.66.55-.263 1.307 1.468.297.266-1.307zm-120.45.65-1.431.454.404 1.27 1.43-.454zm124.35.137-.265 1.307 1.47.299.266-1.307zm3.897.79-.266 1.306 1.47.297.264-1.307zm-132.04.275-1.43.453.404 1.27 1.43-.454zm135.93.512-.264 1.308 1.47.297.264-1.306zm-139.72.687-.258.082-.041.016-1.145.449.487 1.24 1.125-.441.236-.074zm143.62.102-.264 1.306 1.47.297.264-1.306zm3.896.789-.263 1.306 1.468.297.266-1.306zm-151.26.562-1.396.547.488 1.242 1.396-.548zm-3.699 1.453-1.396.55.488 1.24 1.396-.55zm-3.7 1.453-1.396.55.489 1.24 1.394-.55zm-3.68 1.608-1.358.64.57 1.206 1.356-.641zm-3.595 1.7-1.357.64.57 1.207 1.358-.643zm-3.593 1.698-1.356.643.57 1.205 1.356-.643zm-3.612 1.762-1.31.732.65 1.164 1.309-.732zm-3.47 1.937-1.311.733.65 1.164 1.309-.732zm-3.471 1.94-1.31.73.65 1.165 1.31-.73zm-3.47 1.937-.704.393-.039.023-.582.38.727 1.117.564-.368.684-.38zm-3.4 2.145-1.257.816.727 1.12 1.257-.819zm-3.331 2.166-1.258.818.727 1.118 1.257-.817zm-3.332 2.168-1.258.818.727 1.118 1.257-.819zm-3.305 2.285-1.2.903.802 1.064 1.199-.9zm-3.178 2.389-1.2.9.802 1.067 1.2-.9zm-3.18 2.387-1.198.9.8 1.066 1.2-.9zm-3.204 2.425-1.135.98.87 1.009 1.136-.979zm-3.01 2.596-1.135.98.871 1.01 1.135-.98zm-3.01 2.598-1.135.98.871 1.008 1.135-.978zm-3.01 2.595-.986.854-.033.03-.14.138.937.947.123-.12.97-.839zm-2.92 2.762-1.066 1.053.937.95 1.067-1.056zm-2.826 2.793-1.068 1.055.937.95 1.069-1.056zm-2.828 2.795-1.066 1.055.937.947 1.067-1.055zm-2.799 2.883-.994 1.123 1 .883.992-1.123zm-2.633 2.978-.992 1.123.998.883.994-1.123zm-2.63 2.979-.995 1.123 1 .883.993-1.123zm-2.633 2.978-.192.217-.029.035-.738.96 1.056.814.725-.942.178-.2zm-2.47 3.172-.913 1.19 1.056.812.915-1.187zm-2.423 3.15-.914 1.19 1.057.813.914-1.188zm-2.424 3.151-.914 1.19 1.057.812.914-1.188zm-2.328 3.275-.832 1.248 1.11.739.832-1.248zm-2.205 3.307-.832 1.248 1.11.74.832-1.248zm-2.205 3.307-.832 1.248 1.11.74.831-1.248zm-2.187 3.37-.747 1.302 1.155.664.748-1.3zm-1.981 3.448-.748 1.299 1.156.666.748-1.3zm-1.982 3.445-.748 1.301 1.156.664.748-1.3zm-1.979 3.444c-.266.458-.492.913-.723 1.338l1.174.634c.246-.453.465-.896.701-1.302zm-1.885 3.53-.703 1.323 1.178.625.703-1.324zm-1.863 3.509-.703 1.326 1.178.625.703-1.326zm-1.863 3.512-.703 1.326 1.177.625.703-1.326zm-1.864 3.511-.703 1.324 1.178.625.703-1.324zm-1.863 3.512-.703 1.324 1.178.625.703-1.324zm-1.863 3.51-.703 1.326 1.177.625.704-1.326zm-1.863 3.511-.223.416 1.18.625.22-.416z" class="uchighlight"/><path d="m224.81 70.854-.045 1.332 1.5.05.045-1.333zm-2.52.045-1.497.053.044 1.332 1.5-.053zm6.493.086-.045 1.334 1.5.049.045-1.332zm-10.465.05-1.498.053.045 1.332 1.5-.052zm14.438.083-.045 1.332 1.5.048.045-1.332zm-18.41.054-1.498.051.045 1.334 1.5-.053zm22.426.102-.133 1.326 1.492.15.135-1.326zm-26.436.148-1.493.153.137 1.326 1.492-.152zm30.39.252-.132 1.327 1.492.15.133-1.326zm-34.343.153-1.493.152.135 1.326 1.492-.152zm38.299.246-.133 1.328 1.492.15.133-1.326zm-42.254.158-1.493.152.137 1.328 1.492-.154zm46.254.252-.225 1.314 1.479.252.224-1.314zm-50.24.25-1.479.254.225 1.314 1.478-.254zm54.158.416-.223 1.314 1.479.252.222-1.314zm-58.076.256-1.479.252.225 1.314 1.478-.252zm61.996.41-.225 1.314 1.479.252.224-1.314zm-65.914.26-1.479.254.225 1.314 1.478-.254zm69.732.384a.667.667 0 0 0-.254 1.31l.011.001.12.025.027.004.809.164.265-1.306-.662-.133-.008-.008-.012-.002-.275-.05-.01-.003zm-73.68.368-1.46.353.315 1.295 1.457-.351zm77.084.324-.266 1.306 1.47.297.266-1.306zm-80.947.61-1.46.353.315 1.295 1.457-.352zm84.842.18-.264 1.307 1.469.3.265-1.307zm-88.707.753-1.457.353.312 1.295 1.46-.351zm92.604.039-.266 1.306 1.47.3.266-1.307zm3.894.79-.263 1.308 1.468.298.266-1.306zm-100.39.173-1.431.449.4 1.272 1.432-.45zm104.29.619-.265 1.307 1.47.298.266-1.306zm-108.08.572-1.43.451.399 1.272 1.432-.451zm111.97.219-.263 1.307 1.468.298.266-1.306zm3.897.791-.266 1.307 1.47.298.266-1.306zm-119.66.184-1.432.449.4 1.271 1.432-.449zm123.55.607-.264 1.307 1.469.298.265-1.306zm-127.38.637-1.397.545.485 1.242 1.398-.545zm131.27.154-.266 1.307 1.47.298.264-1.306zm3.894.791-.265 1.307 1.47.298.266-1.306zm-138.87.5-1.396.545.484 1.242 1.398-.545zm142.77.291-.265 1.307 1.47.298.264-1.306zm3.895.791-.266 1.307.037.007.266-1.306zm-150.36.363-1.399.547.485 1.24 1.398-.544zm-3.737 1.485-1.357.638.568 1.206 1.358-.64zm-3.595 1.691-1.358.639.567 1.207 1.357-.639zm-3.598 1.694-1.357.638.568 1.207 1.357-.638zm-3.635 1.716-1.31.729.648 1.166 1.31-.729zm-3.474 1.932-1.311.728.648 1.166 1.311-.728zm-3.475 1.932-1.31.728.648 1.166 1.31-.728zm-3.473 1.931-.172.094-.037.023-1.095.711.724 1.12 1.076-.7.15-.082zm-3.38 2.174-1.26.816.726 1.12 1.258-.817zm-3.337 2.162-1.257.816.724 1.12 1.258-.817zm-3.335 2.162-.38.246-.036.028-.838.626.799 1.069.82-.615.361-.235zm-3.237 2.385-1.2.898.8 1.067 1.2-.899zm-3.181 2.38-1.202.901.8 1.067 1.2-.899zm-3.182 2.384-.566.423-.036.03-.601.515.87 1.012.583-.502.549-.41zm-3.078 2.584-1.139.976.87 1.012 1.138-.979zm-3.014 2.591-1.138.977.87 1.012 1.137-.979zm-3.013 2.592-.73.627-.034.031-.383.377.936.95.367-.362.713-.613zm-2.91 2.772-1.069 1.054.937.95 1.069-1.053zm-2.83 2.79-1.07 1.053.936.95 1.069-1.053zm-2.833 2.792-.865.851-.031.034-.19.212.998.885.176-.197.848-.836zm-2.726 2.949-.994 1.123.998.885.994-1.123zm-2.637 2.977-.994 1.12.998.885.994-1.123zm-2.637 2.974-.972 1.098-.028.035-.021.025 1.056.815.006-.008.957-1.08zm-2.533 3.117-.918 1.188 1.057.814.916-1.187zm-2.43 3.147-.916 1.187 1.055.815.916-1.188zm-2.43 3.146-.915 1.188 1.054.814.918-1.187zm-2.33 3.272-.835 1.248 1.11.74.833-1.246zm-2.21 3.305-.834 1.246 1.107.742.834-1.246zm-2.211 3.302-.834 1.248 1.107.74.834-1.246zm-2.117 3.416-.746 1.303 1.158.662.744-1.3zm-1.977 3.45-.746 1.302 1.158.662.746-1.3zm-1.975 3.449-.746 1.302 1.157.662.746-1.3zm-1.658 2.939-.701 1.326 1.178.623.7-1.326zm-1.86 3.514-.7 1.326 1.18.623.7-1.326zm-1.857 3.513-.7 1.327 1.179.623.701-1.327zm-1.857 3.514-.701 1.326 1.178.623.7-1.326zm-1.86 3.516-.7 1.324 1.18.625.7-1.326z" class="ucdraw"/><g class="uchighlight"><path d="m49.219 26.145-.53 1.225 1.378.593.529-1.222zm3.648 1.576-.529 1.225 1.377.595.53-1.224zm3.649 1.578-.53 1.223 1.378.596.529-1.225zm3.648 1.576-.527 1.225 1.377.594.527-1.223zm3.65 1.577-.529 1.224 1.377.594.53-1.223zm3.649 1.576-.53 1.225 1.378.595.529-1.224zm3.648 1.578-.529 1.223 1.377.595.53-1.224zm3.649 1.576-.528 1.225L75.61 39l.528-1.223zm3.65 1.576-.53 1.225 1.378.596.53-1.225zm3.649 1.578-.53 1.223 1.377.596.53-1.225zm3.648 1.577-.53 1.224 1.378.594.529-1.223zm3.648 1.576-.529 1.225 1.377.593.53-1.222zm3.65 1.576-.529 1.225 1.377.595.53-1.224zm3.65 1.578-.53 1.223 1.377.596.53-1.225zm3.648 1.576-.53 1.225 1.377.594.53-1.223zm3.648 1.577-.53 1.224 1.378.596.529-1.225zm3.648 1.578-.527 1.222 1.377.596.527-1.224zm3.65 1.576-.529 1.224 1.377.594.53-1.222zm3.65 1.576-.53 1.225 1.377.593.53-1.222zm3.648 1.576-.53 1.225 1.377.596.53-1.225zm3.648 1.578-.527 1.223 1.377.596.527-1.225zm3.65 1.577-.529 1.224 1.377.594.53-1.223zm3.649 1.576-.53 1.224 1.377.596.53-1.224zm3.648 1.578-.53 1.223 1.378.595.53-1.224zm3.649 1.576-.528 1.225 1.377.593.528-1.222zm3.65 1.576-.53 1.225 1.378.594.529-1.223zm3.648 1.576-.529 1.225 1.377.596.53-1.225zm3.649 1.579-.53 1.222 1.377.596.53-1.225zm3.648 1.576-.527 1.224 1.377.594.527-1.223z"/><path d="m151.89 71.234-.598 1.375 1.223.531.598-1.375zm-1.584 3.647-.598 1.374 1.223.532.598-1.375zm-1.584 3.644-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.375 1.223.532.598-1.375zm-1.584 3.647-.598 1.375 1.223.531.598-1.375zm-1.584 3.645-.598 1.376 1.225.532.598-1.377zm-1.584 3.646-.598 1.375 1.225.531.598-1.375zm-1.582 3.647-.598 1.374 1.223.532.598-1.375zm-1.584 3.644-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.375 1.223.532.598-1.375zm-1.584 3.647-.598 1.375 1.223.531.598-1.375zm-1.584 3.645-.598 1.376 1.223.532.598-1.377zm-1.584 3.646-.598 1.375 1.223.531.598-1.375zm-1.584 3.647-.598 1.374 1.223.532.598-1.375zm-1.584 3.644-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.375 1.223.532.598-1.375zm-1.584 3.647-.598 1.375 1.225.531.598-1.375zm-1.582 3.644-.598 1.377 1.223.532.598-1.377zm-1.584 3.647-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.375 1.223.532.598-1.375zm-1.584 3.645-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.377 1.223.532.598-1.377zm-1.584 3.647-.598 1.375 1.223.531.598-1.375zm-1.584 3.644-.598 1.377 1.223.532.598-1.377zm-1.584 3.647-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.375 1.223.532.598-1.375zm-1.584 3.645-.598 1.377 1.223.531.598-1.377zm-1.584 3.646-.598 1.377 1.225.532.598-1.377zm-1.582 3.647-.598 1.375 1.223.531.598-1.375z"/><path d="M157.6 65.863a.67.67 0 0 0-.559.076l-10.779 6.922-.521.078.035.233-.2.127.284.443.078.516.233-.036.127.2.437-.284 12.863-1.937a.667.667 0 0 0 .516-.916l-2.098-5.041a.67.67 0 0 0-.416-.381zm-.508 1.627 1.463 3.518-9.072 1.367z"/></g><path d="M182.07 74.215a.67.67 0 0 0-.588.102l-10.777 7.908-.5.086.043.25-.205.15.303.412.086.5.25-.043.15.205.412-.302 12.844-2.21a.667.667 0 0 0 .514-.886l-2.098-5.762a.67.67 0 0 0-.433-.41zm-.516 1.701 1.52 4.178-9.424 1.623z" class="ucdraw"/><path d="m162.7 60.635-.686 1.648 3.625 9.432-22.578 8.92-3.825-9.307 22.862-9.09-24.455 8.464 4.705 11.68 25.022-9.915z" class="uchighlight"/></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc1.svg.license b/subprojects/docs/src/components/UseCases/uc1.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc1.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/UseCases/uc2.svg b/subprojects/docs/src/components/UseCases/uc2.svg
new file mode 100644
index 00000000..8aec8169
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc2.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><path d="M90.592 52.826c-3.75 0-6.793 3.097-6.793 6.889V86.27c0 3.791 3.043 6.888 6.793 6.888h45.746c3.75 0 6.793-3.097 6.793-6.888V59.715c0-3.792-3.043-6.889-6.793-6.889zm0 1.332h45.746c3.018 0 5.46 2.474 5.46 5.557V86.27c0 3.082-2.442 5.556-5.46 5.556H90.592c-3.018 0-5.46-2.474-5.46-5.556V59.715c0-3.083 2.442-5.557 5.46-5.557" class="ucdraw"/><path d="M95.075 61.702h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zM95.075 69.702h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zM95.097 77.876h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zm3.855 0h.749v6.887h-.749zm3.856 0h.748v6.887h-.748zM94.559 106.83c-3.734 0-6.76 3.099-6.76 6.889v26.555c0 3.79 3.026 6.888 6.76 6.888h26.812c3.734 0 6.76-3.099 6.76-6.888v-26.555c0-3.79-3.026-6.889-6.76-6.889zm0 1.332h26.812c2.998 0 5.428 2.472 5.428 5.557v26.555c0 3.084-2.43 5.556-5.428 5.556H94.559c-2.998 0-5.428-2.472-5.428-5.556v-26.555c0-3.085 2.43-5.557 5.428-5.557M172.71 107.83c-3.81 0-6.918 3.09-6.918 6.889v26.555c0 3.798 3.107 6.888 6.918 6.888h27.5c3.81 0 6.916-3.09 6.916-6.888v-26.555c0-3.8-3.105-6.889-6.916-6.889zm0 1.332h27.5c3.093 0 5.582 2.481 5.582 5.557v26.555c0 3.075-2.489 5.556-5.582 5.556h-27.5c-3.093 0-5.584-2.481-5.584-5.556v-26.555c0-3.076 2.49-5.557 5.584-5.557" class="ucdraw"/><path d="M178.27 124.34v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.575 0v-.977h2.097v.977zm2.574 0v-.977h2.097v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zM178.27 133.34v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.575 0v-.977h2.097v.977zm2.574 0v-.977h2.097v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zM172.59 52.826c-3.747 0-6.79 3.097-6.79 6.889V86.27c0 3.791 3.043 6.888 6.79 6.888h27.756c3.747 0 6.787-3.097 6.787-6.888V59.715c0-3.792-3.04-6.889-6.787-6.889zm0 1.332h27.756c3.015 0 5.455 2.474 5.455 5.557V86.27c0 3.082-2.44 5.556-5.455 5.556H172.59c-3.015 0-5.457-2.474-5.457-5.556V59.715c0-3.083 2.442-5.557 5.457-5.557" class="ucdraw"/><path d="M176.67 69.344v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.575 0v-.977h2.097v.977zm2.574 0v-.977h2.097v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zM176.67 78.344v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.575 0v-.977h2.097v.977zm2.574 0v-.977h2.097v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977zm2.574 0v-.977h2.098v.977z" class="ucdraw"/><g class="ucdraw"><path d="M70.967 167.99c0 4.354 3.463 8 7.775 8h169.45c4.313 0 7.778-3.645 7.778-8h-4c0 2.273-1.71 4-3.778 4H78.742c-2.067 0-3.775-1.726-3.775-4zM78.742 15.992c-4.313 0-7.775 3.646-7.775 8h4c0-2.274 1.708-4 3.775-4h169.45c2.067 0 3.777 1.727 3.777 4h4c0-4.355-3.464-8-7.777-8z"/><path d="M79.242 17.826c-3.572 0-6.441 3.068-6.441 6.791v143.75c0 3.724 2.869 6.793 6.441 6.793h169.45c3.573 0 6.443-3.07 6.443-6.793V24.617c0-3.723-2.87-6.791-6.443-6.791zm0 1.332h169.45c2.809 0 5.111 2.416 5.111 5.459v143.75c0 3.044-2.302 5.46-5.111 5.46H79.242c-2.808 0-5.11-2.416-5.11-5.46V24.617c0-3.043 2.302-5.459 5.11-5.459"/><path d="M77.803 42.826v112.33h80.334V42.826zm1.334 1.334h77.666v109.67H79.137z"/><path d="M83.131 86.492c0 4.05 3.371 7.334 7.46 7.334h45.747c4.09 0 7.46-3.284 7.46-7.334h-2.667c0 2.578-2.115 4.666-4.793 4.666H90.592c-2.679 0-4.793-2.088-4.793-4.666zM90.592 52.158c-4.207 0-7.46 3.835-7.46 8.332h2.667c0-3.235 2.232-5.666 4.793-5.666h45.746c2.56 0 4.793 2.43 4.793 5.666h2.668c0-4.497-3.254-8.332-7.46-8.332zM87.131 140.49c0 4.046 3.352 7.334 7.428 7.334h26.812c4.075 0 7.427-3.288 7.427-7.334h-2.668c0 2.582-2.103 4.666-4.76 4.666H94.56c-2.657 0-4.76-2.084-4.76-4.666zM94.559 106.16c-4.076 0-7.428 3.29-7.428 7.334H89.8c0-2.581 2.104-4.666 4.76-4.666h26.812c2.657 0 4.76 2.085 4.76 4.666h2.668c0-4.045-3.352-7.334-7.428-7.334zM136.96 106.83c-3.398 0-6.168 2.768-6.168 6.166 0 3.397 2.77 6.166 6.168 6.166a6.176 6.176 0 0 0 6.166-6.166 6.176 6.176 0 0 0-6.166-6.166m0 1.332a4.823 4.823 0 0 1 4.832 4.834 4.824 4.824 0 0 1-4.832 4.834 4.824 4.824 0 0 1-4.834-4.834 4.824 4.824 0 0 1 4.834-4.834M156.8 42.826v112.33h94.334V42.826zm1.334 1.334H249.8v109.67h-91.666z"/></g><path d="M165.13 86.492c0 4.05 3.37 7.334 7.457 7.334h27.756c4.087 0 7.455-3.285 7.455-7.334h-2.668c0 2.579-2.112 4.666-4.787 4.666h-27.756c-2.675 0-4.79-2.087-4.79-4.666zM172.59 52.158c-4.205 0-7.457 3.836-7.457 8.332h2.668c0-3.236 2.232-5.666 4.79-5.666h27.755c2.557 0 4.787 2.43 4.787 5.666h2.668c0-4.496-3.25-8.332-7.455-8.332z" class="uchighlight"/><path d="M165.13 140.49c0 4.51 3.325 8.334 7.584 8.334h27.5c4.259 0 7.582-3.825 7.582-8.334h-2.666c0 3.223-2.271 5.668-4.916 5.668h-27.5c-2.645 0-4.918-2.445-4.918-5.668zM172.71 107.16c-4.144 0-7.584 3.27-7.584 7.334h2.666c0-2.562 2.158-4.666 4.918-4.666h27.5c2.76 0 4.916 2.104 4.916 4.666h2.666c0-4.064-3.438-7.334-7.582-7.334z" class="ucdraw"/><path d="m235.3 61.861-.121.014-.154.021-.153.026-.15.029-.15.031-.149.037-.148.04-.29.09-.126.044.443 1.258.111-.04.112-.036.115-.034.115-.03.115-.028.118-.026.119-.023.119-.02.12-.017.106-.012zm1.498.021-.184 1.32.086.012.12.02.119.023.117.026.115.027.115.031.114.034.113.037.111.039.111.043.006.002.504-1.235-.021-.007-.14-.055-.143-.05-.145-.046-.144-.045-.149-.039-.148-.037-.15-.031-.151-.03-.152-.025zm-4.27.987-.013.01-.121.084-.121.087-.118.09-.115.094-.113.094-.111.1-.108.099-.105.104-.104.105-.101.11-.006.007.996.885.076-.082.164-.164.086-.078.086-.076.09-.077.09-.072.091-.07.094-.069.096-.066.002-.002zm7.026.11-.787 1.075.088.065.091.07.09.072.09.077.086.076.086.078.082.082.082.084.078.084.078.086.014.017 1.017-.86-.023-.03-.098-.11-.101-.109-.104-.105-.105-.104-.11-.1-.109-.1-.113-.093-.115-.094-.118-.09zm-9.024 2.091-.058.113-.067.135-.064.135-.06.138-.057.14-.053.14-.05.142-.048.145-.043.146-.039.147-.004.012 1.295.314v.006l.032-.115.033-.114.037-.113.039-.111.04-.112.046-.107.047-.107.05-.108.051-.105.053-.098zm10.945.154-1.195.594.04.078.05.108.047.107.045.107.04.112.04.111.037.113.033.114.031.115.03.117.004.016 1.302-.283-.008-.034-.037-.148-.039-.147-.043-.146-.047-.145-.05-.142-.053-.14-.057-.14-.06-.138-.065-.135zm-11.678 2.707-.002.06.002.159.006.158.01.156.014.157.02.156.021.152.026.153.027.152.033.15.008.036 1.297-.317-.006-.02-.025-.117-.043-.238-.016-.12-.014-.122-.011-.121-.008-.123-.004-.125-.002-.125v-.045zm10.998.053-.002.133-.004.125-.008.123-.011.121-.014.121-.016.121-.019.12-.023.118-.026.118-.017.07 1.294.316.022-.086.033-.15.03-.152.025-.153.021-.152.018-.156.014-.157.01-.156.006-.158.001-.15zm-9.134 2.213-1.18.623.059.111.074.131.074.127.08.127.082.123.084.123.088.12.09.116.092.116.095.113.022.023.996-.884-.01-.012-.074-.088-.145-.184-.068-.093-.067-.096-.125-.195-.117-.203zm8.578.047-.026.05-.056.102-.06.102-.126.195-.066.096-.069.093-.144.184-.074.088-.045.05.996.886.056-.063.096-.113.092-.116.09-.117.088-.119.084-.123.082-.123.08-.127.074-.127.074-.13.033-.065zm-7.051 1.705-.76 1.096.086.06.123.082.127.078.129.076.129.073.133.07.133.066.136.063.137.06.14.057.044.018.474-1.248-.03-.01-.108-.045-.108-.047-.105-.049-.106-.052-.103-.055-.102-.057-.2-.12-.097-.065zm5.506.031-.03.02-.097.064-.2.121-.1.057-.104.055-.106.052-.105.05-.108.046-.11.045-.077.03.474 1.245.092-.035.14-.057.138-.06.134-.063.135-.066.133-.07.129-.073.129-.076.125-.078.125-.082.043-.03zm-3.348.807-.15 1.324.137.016.156.013.156.01.158.006.16.002.159-.002.158-.006.156-.01.156-.013.084-.01-.15-1.324-.068.007-.123.01-.123.008-.124.006-.125.002-.125-.002-.125-.006-.123-.008-.123-.01zM236.12 69.712h-.395v-2.776l.005-.19.012-.179q-.076.079-.142.135l-.163.134-.408.327-.213-.276.967-.744h.337z" class="uchighlight"/><g class="ucdraw"><path d="M95.082 93.492v14h.748v-14z"/><path d="m92.902 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="m92.902 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654zM142.92 61.49v.748h10.692v-.748z"/><path d="M152.87 61.494v20h.748v-20z"/><path d="M144.53 81.115v.748h9.077v-.748z"/><path d="m148.84 77.887-6.21 3.106 6.21 3.105zm-.748 1.21v3.79l-3.791-1.894z"/><path d="m148.84 77.887-6.21 3.106 6.21 3.105zm-.748 1.21v3.79l-3.791-1.894zM174.08 93.492v15h.748v-15z"/><path d="m170.9 102.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="m170.9 102.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/></g><g class="uchighlight"><path d="M209.71 66.859v1.26h.09v-1.26zm1.422 0v1.26h1.334v-1.26zm2.668 0v1.26h1.332v-1.26zm2.666 0v1.26h1.334v-1.26zm2.666 0v1.26h1.334v-1.26zm2.668 0v1.26h1.332v-1.26zm2.666 0v1.26h1.334v-1.26zm2.666 0v1.26h1.334v-1.26z"/><path d="m213.84 64.932-7.348 3.06 7.348 3.061zm-.748 1.121v3.879l-4.652-1.94z"/><path d="m213.84 64.932-7.348 3.06 7.348 3.061zm-.748 1.121v3.879l-4.652-1.94z"/><path d="m213.84 64.932-7.348 3.06 7.348 3.061zm-.748 1.121v3.879l-4.652-1.94z"/></g><g class="uchighlight"><path d="m207.46 67.992 6-2.5v5z"/><path d="m213.84 64.932-7.348 3.06 7.348 3.061zm-.748 1.121v3.879l-4.652-1.94z"/></g><path d="M173.79 93.492v15h1.332v-15z" class="ucdraw"/><path d="m170.9 102.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z" class="ucdraw"/><path d="m173.96 108.49-2.5-6h5z" class="ucdraw"/><path d="m170.9 102.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654zM152.87 61.494v20h.748v-20zM148.84 77.887l-6.21 3.106 6.21 3.105zm-.748 1.21v3.79l-3.791-1.894z" class="ucdraw"/><path d="m143.47 80.992 5-2.5v5z" class="ucdraw"/><path d="m148.84 77.887-6.21 3.106 6.21 3.105zm-.748 1.21v3.79l-3.791-1.894zM94.791 93.492v14h1.332v-14z" class="ucdraw"/><path d="m92.902 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z" class="ucdraw"/><path d="m95.964 107.49-2.5-6h5z" class="ucdraw"/><path d="m92.902 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z" class="ucdraw"/><g class="uchighlight"><path d="m235.3 74.861-.121.014-.154.021-.153.026-.15.029-.15.031-.149.037-.148.04-.29.09-.126.044.443 1.258.111-.04.112-.036.115-.034.115-.03.115-.028.118-.026.119-.023.119-.02.12-.017.106-.012zm1.498.021-.184 1.32.086.013.12.019.119.023.117.026.115.027.115.031.114.034.113.037.111.039.111.043.006.002.504-1.235-.021-.007-.14-.055-.143-.05-.145-.046-.144-.045-.149-.039-.148-.037-.15-.031-.151-.03-.152-.025zm-4.27.987-.013.01-.121.084-.121.087-.118.09-.115.094-.113.094-.111.1-.108.099-.105.104-.104.105-.101.11-.006.007.996.885.076-.082.164-.164.086-.078.086-.076.09-.077.09-.072.091-.07.094-.069.096-.066.002-.002zm7.026.11-.787 1.075.088.065.091.07.09.072.09.077.086.076.086.078.082.082.082.084.078.084.078.086.014.017 1.017-.86-.023-.03-.098-.11-.101-.109-.104-.105-.105-.104-.11-.1-.109-.1-.113-.093-.115-.094-.118-.09zm-9.024 2.091-.058.113-.067.135-.064.135-.06.138-.057.14-.053.14-.05.142-.048.145-.043.146-.039.147-.004.012 1.295.314v.006l.032-.115.033-.114.037-.113.039-.111.04-.112.046-.107.047-.107.05-.108.051-.105.053-.098zm10.945.154-1.195.594.04.078.05.108.047.107.045.107.04.112.04.111.037.113.033.114.031.115.03.117.004.016 1.302-.283-.008-.034-.037-.148-.039-.147-.043-.146-.047-.145-.05-.142-.053-.14-.057-.14-.06-.138-.065-.135zm-11.678 2.707-.002.06.002.159.006.158.01.156.014.157.02.156.021.152.026.153.027.152.033.15.008.036 1.297-.317-.006-.02-.025-.117-.043-.238-.016-.12-.014-.122-.011-.121-.008-.123-.004-.125-.002-.125v-.045zm10.998.053-.002.133-.004.125-.008.123-.011.121-.014.121-.016.121-.019.12-.023.118-.026.118-.017.07 1.294.316.022-.086.033-.15.03-.152.025-.153.021-.152.018-.156.014-.157.01-.156.006-.158.001-.15zm-9.134 2.213-1.18.623.059.111.074.131.074.127.08.127.082.123.084.123.088.12.09.116.092.116.095.113.022.023.996-.884-.01-.012-.074-.088-.145-.184-.068-.093-.067-.096-.125-.195-.117-.203zm8.578.047-.026.05-.056.102-.06.102-.126.195-.066.096-.069.093-.144.184-.074.088-.045.05.996.886.056-.063.096-.113.092-.116.09-.117.088-.119.084-.123.082-.123.08-.127.074-.127.074-.13.033-.065zm-7.051 1.705-.76 1.096.086.06.123.082.127.078.129.076.129.073.133.07.133.066.136.063.137.06.14.057.044.018.474-1.248-.03-.01-.108-.045-.108-.047-.105-.049-.106-.052-.103-.055-.102-.057-.2-.12-.097-.065zm5.506.031-.03.02-.097.064-.2.121-.1.057-.104.055-.106.052-.105.05-.108.046-.11.045-.077.03.474 1.245.092-.035.14-.057.138-.06.134-.063.135-.066.133-.07.129-.073.129-.076.125-.078.125-.082.043-.03zm-3.348.807-.15 1.324.137.016.156.013.156.01.158.006.16.002.159-.002.158-.006.156-.01.156-.013.084-.01-.15-1.324-.068.007-.123.01-.123.008-.124.006-.125.002-.125-.002-.125-.006-.123-.008-.123-.01zM237.02 82.653h-2.339v-.34l.955-.969q.266-.268.446-.478.184-.21.279-.413.095-.205.095-.449 0-.303-.18-.461-.179-.162-.472-.162-.256 0-.454.088t-.405.25l-.218-.274q.142-.12.308-.212.169-.093.359-.147.193-.054.41-.054.33 0 .569.115t.369.327q.131.213.131.506 0 .283-.112.527-.112.242-.315.481-.202.237-.473.503l-.777.774v.017h1.824zM212.75 79.895v1.19h.717v-1.19zm2.05 0v1.19h1.333v-1.19zm2.667 0v1.19h1.334v-1.19zm2.666 0v1.19h1.334v-1.19zm2.668 0v1.19h1.332v-1.19zm2.666 0v1.19h1.334v-1.19zm2.666 0v1.19h1.334v-1.19z"/><path d="m212.82 77.561-6.21 3.106 6.21 3.105zm-.748 1.209v3.793l-3.791-1.896z"/><path d="m212.82 77.561-6.21 3.106 6.21 3.105zm-.748 1.209v3.793l-3.791-1.896z"/><path d="m212.82 77.561-6.21 3.106 6.21 3.105zm-.748 1.209v3.793l-3.791-1.896z"/></g><g class="uchighlight"><path d="m207.45 80.666 5-2.5v5z"/><path d="m212.82 77.561-6.21 3.106 6.21 3.105zm-.748 1.209v3.793l-3.791-1.896z"/></g><g class="ucdraw"><path d="M107.08 93.492v14h.748v-14z"/><path d="m103.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="m103.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="M106.79 93.492v14h1.332v-14z"/><path d="m103.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/></g><path d="m106.96 107.49-2.5-6h5z" class="ucdraw"/><path d="m103.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z" class="ucdraw"/><g class="ucdraw"><path d="M118.08 93.492v14h.748v-14z"/><path d="m114.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="m114.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/><path d="M117.79 93.492v14h1.332v-14z"/><path d="m114.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z"/></g><path d="m117.96 107.49-2.5-6h5z" class="ucdraw"/><path d="m114.9 101.12 3.063 7.348 3.06-7.348zm1.121.748h3.881l-1.94 4.654z" class="ucdraw"/><g class="ucdraw"><path d="M137.08 92.508v15h.748v-15z"/><path d="m136.96 92.535.338 1.117h-.684zm0 0-.346.83-2.716 6.518h6.123zm.34 1.125 1.658 5.475h-3.94z"/><path d="m136.96 92.535.338 1.117h-.684zm0 0-.346.83-2.716 6.518h6.123zm.34 1.125 1.658 5.475h-3.94z"/><path d="M136.79 92.508v15h1.332v-15z"/><path d="m136.96 92.535.338 1.117h-.684zm0 0-.346.83-2.716 6.518h6.123zm.34 1.125 1.658 5.475h-3.94z"/></g><path d="m136.96 93.508-2.5 6h5z" class="ucdraw"/><path d="m136.96 92.535.338 1.117h-.684zm0 0-.346.83-2.716 6.518h6.123zm.34 1.125 1.658 5.475h-3.94z" class="ucdraw"/></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc2.svg.license b/subprojects/docs/src/components/UseCases/uc2.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc2.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/UseCases/uc3.svg b/subprojects/docs/src/components/UseCases/uc3.svg
new file mode 100644
index 00000000..cd463317
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc3.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><g class="ucdraw"><path d="M45.86 158.243a.67.67 0 0 0-.402.314l-5.26 9.301a.667.667 0 0 0 .252.908.667.667 0 0 0 .908-.251l5.26-9.301a.667.667 0 0 0-.252-.908.67.67 0 0 0-.506-.063"/><path d="M45.958 158.243a.67.67 0 0 0-.506.06.667.667 0 0 0-.256.909l5.223 9.3a.667.667 0 0 0 .908.256.667.667 0 0 0 .254-.908l-5.22-9.3a.67.67 0 0 0-.403-.317M46.27 140.213c-3.088 0-5.923.478-8.174 1.295s-3.957 1.95-4.668 3.478l-.031.065-.227.988-.013-.002-.026.18-.039.164.006.025.014-.072v.06c0 2.609 1.56 4.928 3.953 6.551s5.639 2.6 9.213 2.6 6.82-.976 9.213-2.6c2.392-1.623 3.953-3.942 3.953-6.55v-.053l-.008-.051-.092-.582h-.064a4 4 0 0 0-1.016-1.936c-.716-.745-1.703-1.372-2.89-1.894-2.376-1.045-5.575-1.666-9.104-1.666m0 2.242c3.055 0 5.821.513 7.762 1.303.97.395 1.73.86 2.217 1.328s.685.9.685 1.316-.2.847-.685 1.315-1.247.935-2.217 1.33c-1.941.79-4.707 1.303-7.762 1.303s-5.817-.513-7.758-1.303c-.97-.395-1.73-.862-2.217-1.33s-.687-.899-.687-1.315.202-.848.687-1.316 1.247-.933 2.217-1.328c1.94-.79 4.703-1.303 7.758-1.303m-11.809 3.906.002.01-.012.051v-.004zm23.137 1.752c-.49 1.392-1.342 2.702-2.855 3.729-2.132 1.447-5.136 2.371-8.465 2.371s-6.333-.924-8.465-2.371c-1.503-1.02-2.354-2.319-2.848-3.7.135.18.237.374.405.536.66.636 1.558 1.164 2.638 1.603 2.16.88 5.06 1.4 8.262 1.4s6.104-.52 8.264-1.4c1.08-.44 1.978-.967 2.638-1.603.177-.17.286-.375.426-.565"/><path d="M45.778 134.213a.667.667 0 0 0-.666.666v14.449a.667.667 0 0 0 .666.668.667.667 0 0 0 .666-.668v-14.449a.667.667 0 0 0-.666-.666M50.202 154.253l-4.844 3.465a.59.59 0 0 0-.137.824.59.59 0 0 0 .825.137l4.845-3.465a.59.59 0 0 0 .137-.824.59.59 0 0 0-.826-.137"/><path d="M41.653 154.033a.595.595 0 0 0-.836.096.595.595 0 0 0 .096.836l4.654 3.689a.595.595 0 0 0 .834-.098.595.595 0 0 0-.096-.836z"/></g><g class="ucdraw"><path d="M55 64v16h16V64zm1 1h14v14H56z"/><path d="M62.539 64.5v15.117h1V64.5z"/><path d="M55.5 72v1h15.117v-1zM36.237 63.422v8.156h8.156v-8.156zm.51.51h7.136v7.136h-7.136z"/><path d="M40.08 63.677v7.706h.51v-7.706z"/><path d="M36.492 67.5v.51h7.706v-.51zM22.466 51.849v8.156h8.156v-8.156zm.51.51h7.136v7.136h-7.136z"/><path d="M26.309 52.104v7.706h.51v-7.706z"/><path d="M22.721 55.927v.51h7.706v-.51zM143.75 57.236v8.156h8.156v-8.156zm.51.51h7.136v7.136h-7.136z"/><path d="M147.59 57.491v7.706h.51V57.49z"/><path d="M144 61.314v.51h7.706v-.51zM274.51 58.005v8.156h8.156v-8.156zm.51.51h7.136v7.136h-7.136z"/><path d="M278.35 58.26v7.706h.51V58.26z"/><path d="M274.76 62.083v.51h7.706v-.51zM156 48v16h16V48zm1 1h14v14h-14z"/><path d="M163.65 48.5v15.117h1V48.5z"/><path d="M156.5 56v1h15.119v-1zM84.646 9.985c-5.073 2.633-11.259 5.953-16.332 8.586l.79.45c4.682-2.431 11.198-5.864 15.881-8.295zm-41.323.13-.338.741c4.682 2.431 11.198 5.864 15.881 8.294l.789-.448c-5.073-2.634-11.259-5.953-16.332-8.587m21.372 6.714.034 10.956 7.379 3.555-.031-10.828zm-1.363.121-7.251 3.668-.014 4.195c-2.346 1.217-6.549 3.409-8.883 4.621l.339.737c2.332-1.21 6.197-3.24 8.542-4.456l-.018 5.684 7.253-3.541zm8.866 8.343.002.9c2.342 1.215 5.882 3.029 8.223 4.244l.339-.737c-2.341-1.216-6.223-3.191-8.564-4.407m-8.195 3.53-7.302 3.58 7.394 2.582 7.302-2.582zM184.65 8.984c-5.08 2.739-11.26 6.191-16.34 8.93l.79.468c4.69-2.528 11.2-6.099 15.88-8.627zm-41.327.136-.338.771c4.682 2.528 11.198 6.097 15.881 8.625l.789-.466c-5.073-2.739-11.259-6.191-16.332-8.93m21.372 6.982.034 11.394 7.381 3.697-.03-11.261zm-1.363.126-7.251 3.815-.014 4.362c-2.346 1.266-6.549 3.546-8.883 4.806l.339.767c2.332-1.258 6.197-3.369 8.542-4.634l-.018 5.911 7.253-3.683zm8.868 8.677v.936c2.34 1.264 5.88 3.15 8.22 4.414l.34-.767c-2.34-1.264-6.22-3.319-8.56-4.583m-8.197 3.671-7.302 3.723 7.394 2.685 7.305-2.685z"/></g><path d="M311.65 8.984c-5.08 2.739-11.26 6.191-16.34 8.93l.79.468c4.69-2.528 11.2-6.099 15.88-8.627zm-41.33.136-.34.771c4.69 2.528 11.2 6.097 15.89 8.625l.78-.466c-5.07-2.739-11.25-6.191-16.33-8.93m21.38 6.982.03 11.394 7.38 3.697-.03-11.261zm-1.37.126-7.25 3.815-.01 4.362c-2.35 1.266-6.55 3.546-8.89 4.806l.34.767c2.33-1.258 6.2-3.369 8.54-4.634l-.01 5.911 7.25-3.683zm8.87 8.677v.936c2.34 1.264 5.88 3.15 8.22 4.414l.34-.767c-2.34-1.264-6.22-3.319-8.56-4.583m-8.2 3.671-7.3 3.723 7.39 2.685 7.31-2.685z" class="uchighlight"/><g class="ucdraw"><path d="M187.8 165.99a.54.54 0 0 0-.327.256l-4.279 7.566a.54.54 0 0 0 .205.739.54.54 0 0 0 .739-.205l4.278-7.566a.54.54 0 0 0-.204-.74.54.54 0 0 0-.412-.05"/><path d="M187.88 165.99a.54.54 0 0 0-.412.05.54.54 0 0 0-.208.738l4.247 7.566a.54.54 0 0 0 .74.208.54.54 0 0 0 .206-.739l-4.246-7.566a.54.54 0 0 0-.327-.257M187.72 151.33c-2.612 0-5.01.416-6.911 1.128-1.902.712-3.343 1.705-3.94 3.016l-.023.052-.186.857-.012-.002-.02.144-.017.065v.09c0 2.251 1.309 4.261 3.329 5.674s4.765 2.262 7.788 2.262 5.769-.85 7.788-2.262c2.02-1.413 3.329-3.423 3.329-5.674v-.56h-.129a3.46 3.46 0 0 0-.856-1.692c-.602-.645-1.435-1.192-2.439-1.647-2.008-.91-4.714-1.45-7.701-1.45zm0 1.878c2.586 0 4.926.445 6.571 1.134.823.345 1.47.752 1.885 1.163s.592.8.592 1.18-.177.77-.592 1.181-1.062.819-1.885 1.163c-1.645.69-3.985 1.135-6.571 1.135-2.582 0-4.921-.445-6.565-1.135-.822-.344-1.468-.751-1.883-1.163s-.592-.8-.592-1.18.177-.77.592-1.18c.415-.412 1.06-.819 1.883-1.164 1.644-.69 3.983-1.134 6.565-1.134m9.641 4.852c-.396 1.276-1.137 2.474-2.466 3.404-1.807 1.263-4.35 2.068-7.167 2.068s-5.36-.805-7.167-2.068c-1.322-.925-2.06-2.116-2.46-3.385.129.19.238.39.409.558.555.55 1.313 1.01 2.226 1.392 1.825.766 4.279 1.22 6.984 1.22 2.71 0 5.164-.454 6.99-1.22.914-.383 1.673-.841 2.228-1.392.177-.175.291-.38.423-.577"/><path d="M187.73 146.45a.54.54 0 0 0-.542.542v11.754a.54.54 0 0 0 .542.543.54.54 0 0 0 .542-.543v-11.754a.54.54 0 0 0-.542-.542M191.4 163.47a.47.47 0 0 0-.345.092l-3.687 2.847a.467.467 0 0 0-.085.655.467.467 0 0 0 .657.086l3.686-2.847a.467.467 0 0 0 .084-.655.47.47 0 0 0-.31-.178"/><path d="M184.1 163.38a.47.47 0 0 0-.316.178.473.473 0 0 0 .08.664l3.714 2.928a.473.473 0 0 0 .664-.08.473.473 0 0 0-.078-.663l-3.716-2.929a.47.47 0 0 0-.348-.098zM302.58 174.86a.67.67 0 0 0-.402.314l-5.26 9.301a.667.667 0 0 0 .252.908.667.667 0 0 0 .908-.251l5.26-9.301a.667.667 0 0 0-.252-.908.67.67 0 0 0-.506-.063"/><path d="M302.17 174.92a.667.667 0 0 0-.256.908l5.22 9.301a.667.667 0 0 0 .909.256.667.667 0 0 0 .254-.908l-5.219-9.301a.667.667 0 0 0-.908-.256M302.99 156.83c-3.084 0-5.919.478-8.17 1.295s-3.96 1.951-4.674 3.478l-.03.065-.222.988h-.013l-.024.16-.023.08v.116c0 2.609 1.562 4.927 3.955 6.55s5.639 2.6 9.21 2.6 6.819-.976 9.212-2.6c2.393-1.623 3.955-3.941 3.955-6.55v-.686h-.162c-.157-.702-.472-1.37-1.016-1.935-.717-.745-1.704-1.372-2.892-1.895-2.377-1.045-5.577-1.666-9.106-1.666zm0 2.242c3.057 0 5.822.513 7.762 1.303.97.395 1.729.86 2.215 1.328s.687.9.687 1.316-.202.847-.687 1.315-1.245.935-2.215 1.33c-1.94.79-4.705 1.303-7.762 1.303-3.051 0-5.816-.513-7.758-1.303-.97-.395-1.732-.862-2.219-1.33s-.687-.9-.687-1.315.2-.848.687-1.316 1.248-.933 2.22-1.328c1.94-.79 4.705-1.303 7.757-1.303m11.33 5.656c-.49 1.393-1.343 2.704-2.857 3.73-2.133 1.448-5.136 2.372-8.463 2.372s-6.331-.924-8.463-2.371c-1.5-1.018-2.35-2.314-2.846-3.691.134.176.233.368.399.527.66.636 1.56 1.164 2.64 1.603 2.16.88 5.061 1.4 8.26 1.4 3.203 0 6.104-.52 8.264-1.4 1.08-.44 1.978-.967 2.638-1.603.178-.171.287-.376.428-.567"/><path d="M302.5 150.83a.667.667 0 0 0-.666.666v14.449a.667.667 0 0 0 .666.668.667.667 0 0 0 .666-.668v-14.449a.667.667 0 0 0-.666-.666M306.29 170.64a.55.55 0 0 0-.39.148l-3.897 3.672a.546.546 0 0 0-.024.772.546.546 0 0 0 .772.023l3.896-3.672a.546.546 0 0 0 .024-.771.55.55 0 0 0-.381-.172"/><path d="M298.56 170.88a.57.57 0 0 0-.799.098.57.57 0 0 0 .098.799l4.476 3.51a.57.57 0 0 0 .8-.098.57.57 0 0 0-.096-.8zM64 136v16h16v-16zm1 1h14v14H65z"/><path d="M71.539 136.73v15.119h1V136.73z"/><path d="M64.5 143v1h15.117v-1zM165 134v17h16v-17zm1 1h14v15h-14z"/><path d="M172.35 134.85v15.579h1V134.85z"/><path d="M165.5 142v1h15.119v-1zM282.24 137.06v16h16v-16zm1 1h14v14h-14z"/><path d="M289.9 137.56v15.117h1V137.56z"/><path d="M282.74 145.06v1h15.119v-1zM71.748 136.02l-.624-3.951 1.317-.208.624 3.952zm-.832-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.623-3.951 1.317-.208.623 3.951zm-.831-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.624-3.951 1.317-.208.624 3.951zm-.831-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.624-3.951 1.317-.208.624 3.951zm-.832-5.268-.624-3.951 1.318-.208.623 3.951zm-2.564-3.419 1.802-5.684 3.466 4.852zM80.949 135.72l2.956-2.694-.898-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.956 2.694zm3.942-3.593 2.956-2.694-.898-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.956 2.694zm3.942-3.593 2.956-2.694-.898-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.957 2.694zm3.942-3.593 2.956-2.694-.898-.985-2.957 2.694zm3.941-3.592 2.957-2.694-.898-.986-2.957 2.694zm3.942-3.593 2.957-2.694-.898-.985-2.957 2.694zm3.942-3.592 2.957-2.694-.898-.986-2.957 2.695zm3.942-3.592 2.957-2.695-.898-.985-2.957 2.694zm3.942-3.593 2.957-2.694-.899-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.956 2.694zm3.942-3.593 2.956-2.694-.898-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.956 2.694zm3.942-3.593 2.956-2.694-.898-.986-2.956 2.695zm3.942-3.592 2.956-2.695-.898-.985-2.956 2.694zm3.942-3.593 2.956-2.694-.898-.985-2.957 2.694zm3.942-3.592 2.956-2.694-.898-.986-2.957 2.694zm4.053-.988 2.145-5.563-5.738 1.621z"/><path d="m71.838 78.926 3.447 2.03-.677 1.149-3.446-2.031zm4.596 2.707 3.446 2.03-.677 1.149-3.446-2.03zm4.595 2.707 3.446 2.03-.677 1.149-3.446-2.03zm4.595 2.707 3.446 2.03-.676 1.149-3.447-2.03zm4.595 2.707 3.447 2.031-.677 1.148-3.447-2.03zm4.595 2.707 3.447 2.031-.677 1.149-3.446-2.031zm4.596 2.708 3.446 2.03-.677 1.149-3.446-2.031zm4.595 2.707 3.446 2.03-.677 1.149-3.446-2.031zm4.595 2.707 3.446 2.03-.676 1.149-3.447-2.03zm4.595 2.707 3.447 2.03-.677 1.149-3.447-2.03zm4.595 2.707 3.447 2.03-.677 1.149-3.446-2.03zm4.596 2.707 3.446 2.031-.677 1.148-3.446-2.03zm4.595 2.707 3.446 2.031-.677 1.149-3.446-2.031zm4.595 2.708 3.446 2.03-.676 1.149-3.447-2.031zm4.595 2.707 3.447 2.03-.677 1.149-3.447-2.031zm4.595 2.707 3.447 2.03-.677 1.149-3.446-2.03zm4.596 2.707 3.446 2.03-.677 1.149-3.446-2.03zm4.595 2.707 3.446 2.03-.677 1.149-3.446-2.03zm4.595 2.707 3.446 2.031-.676 1.148-3.447-2.03zm4.595 2.707 1.862 1.097-.677 1.149-1.862-1.097zm1.729-1.303 3.241 5.005-5.949-.409zM171.56 57.242l3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.358-.119-1.328-3.984.358zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.358-.119-1.328-3.984.358zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.358-.119-1.328-3.984.358zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.358-.119-1.328-3.984.358zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.358-.119-1.328-3.984.358zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.312-.476 3.984-.357-.119-1.328-3.984.357zm5.312-.477 3.984-.357-.119-1.328-3.984.357zm5.313-.476 3.984-.357-.12-1.328-3.983.357zm5.312-.476 2.157-.194-.12-1.328-2.156.194zm1.008 1.917 5.074-3.132-5.55-2.18zM291.66 52.391l-.077 3.94 1.333.026.076-3.94zm-.102 5.255-.076 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.94 1.333.026.076-3.94zm-.102 5.254-.076 3.941 1.333.025.076-3.94zm-.102 5.255-.076 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.941 1.334.025.076-3.94zm-.101 5.255-.077 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.941 1.333.025.076-3.94zm-.102 5.255-.076 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.941 1.333.025.076-3.94zm-.102 5.255-.076 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.941 1.333.025.077-3.94zm-.102 5.255-.077 3.94 1.333.026.076-3.941zm-.102 5.254-.076 3.94 1.333.026.076-3.94zm-.102 5.255-.076 3.94 1.333.025.076-3.94zm-.102 5.254-.055 2.856 1.333.025.055-2.856zm-2.03 1.505 2.565 5.305 2.768-5.205z"/></g><path d="m164.7 64.152.48 3.895-1.328.155-.474-3.895zm.63 5.193.48 3.895-1.326.155-.474-3.895zm.64 5.193.47 3.894-1.32.155-.48-3.894zm.63 5.192.47 3.895-1.32.155-.48-3.895zm.63 5.193.47 3.895-1.32.155-.47-3.895zm.63 5.193.48 3.895-1.33.155-.47-3.895zm.63 5.193.48 3.894-1.33.155-.47-3.894zm.64 5.192.47 3.895-1.32.155-.48-3.895zm.63 5.193.47 3.895-1.32.155-.48-3.895zm.63 5.193.47 3.895-1.32.155-.47-3.895zm.63 5.193.48 3.894-1.33.155-.47-3.894zm.63 5.192.48 3.895-1.33.155-.47-3.895zm.64 5.193.42 3.5-1.32.156-.43-3.501zm2.25 1.97-2.02 5.503-3.27-4.883z" class="uchighlight"/><g class="uchighlight"><path d="M283.17 37.166v16.668h16.668V37.166zm1.668 1.668h13.332v13.332h-13.332z"/><path d="M290.63 38v15.117h1.666V38z"/><path d="M284 45.166v1.668h15.119v-1.668z"/></g><path fill="none" class="ucstroke" stroke-miterlimit="8" stroke-width="2.667" d="M12.36 125.2c93.532-4.036 205.07-3.996 298.18.107"/><g fill="none" class="ucstroke" stroke-dasharray="0.733, 1.466" stroke-width=".733"><path d="M12.449 46.04c94.703-4.242 208.29-4.2 302.56.112M14.63 59.887c93.532-4.036 205.07-3.996 298.18.107M12.449 73.04c94.703-4.242 208.29-4.2 302.56.112"/></g></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc3.svg.license b/subprojects/docs/src/components/UseCases/uc3.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc3.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/UseCases/uc4.svg b/subprojects/docs/src/components/UseCases/uc4.svg
new file mode 100644
index 00000000..fa647e00
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc4.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><g class="ucdraw"><path d="m148.57 31.054 4.688 2.603 5.132 2.557 5.512 2.481 5.884 2.408 12.724 4.54 13.935 4.171 14.966 3.752 15.83 3.288 16.529 2.779 17.025 2.22.173-1.322-17.001-2.217-16.48-2.77-15.778-3.278-14.91-3.738-13.873-4.151-12.663-4.52-5.835-2.387-5.466-2.46-5.082-2.533-4.662-2.589zm1.295-2.331 4.636 2.574 5.032 2.507 5.421 2.44 5.785 2.368 12.602 4.497 13.811 4.133 14.854 3.724 15.728 3.266 16.43 2.763 16.976 2.214.173-1.322-16.951-2.21-16.381-2.756-15.675-3.255-14.8-3.71-13.748-4.114-12.54-4.476-5.736-2.347-5.377-2.42-4.982-2.482-4.61-2.56zM283.37 139.42l-.943.943 10.135 10.135-10.135 10.135.943.944 10.135-10.135 10.135 10.135.943-.944-10.135-10.135 10.135-10.135-.943-.943-10.135 10.135z"/><path d="M293.5 134.83c-8.644 0-15.666 7.022-15.666 15.666s7.022 15.666 15.666 15.666 15.666-7.022 15.666-15.666-7.022-15.666-15.666-15.666m0 1.332c7.924 0 14.334 6.41 14.334 14.334s-6.41 14.334-14.334 14.334-14.334-6.41-14.334-14.334 6.41-14.334 14.334-14.334M288.83 20.5v14.834h-14.334v1.332h14.334V51.5h1.332V36.666h14.334v-1.332h-14.334V20.5z"/><path d="M289.5 19.834c-8.656 0-15.666 7.257-15.666 16.166s7.01 16.166 15.666 16.166S305.166 44.909 305.166 36s-7.01-16.166-15.666-16.166m0 1.332c7.912 0 14.334 6.623 14.334 14.834s-6.422 14.834-14.334 14.834S275.166 44.211 275.166 36s6.422-14.834 14.334-14.834M260.67 98.014l-.937.059.17 2.662.935-.06zm-3.598.227-2.662.17.17 2.66 2.66-.168zm-5.322.337-2.662.168.168 2.663 2.662-.168zm-5.324.338-2.66.168.168 2.66 2.662-.167zm-5.322.336-2.66.168.167 2.662 2.66-.168zm-5.323.338-2.662.168.17 2.662 2.66-.17zm-5.322.336-2.662.17.168 2.66 2.662-.168zm-5.322.338-2.662.168.168 2.662 2.662-.17zm-5.325.336-2.66.17.168 2.66 2.662-.168zm-5.322.338-2.66.168.168 2.662 2.66-.168zm-5.322.338-2.662.168.17 2.66 2.66-.168zm-5.322.336-2.663.168.168 2.662 2.663-.168zm-5.323.338-2.662.168.168 2.662 2.662-.17zm-5.324.335-2.66.17.168 2.66 2.662-.167zm-5.322.338-2.662.168.17 2.662 2.66-.17zm-5.323.336-2.662.17.17 2.66 2.66-.168zm-5.322.338-2.662.168.168 2.662 2.662-.168zm-5.324.338-2.66.168.168 2.66 2.662-.168zM147.86 104.96q-1.335.086-2.678.19l.207 2.658c.878-.07 1.759-.129 2.643-.186zm-5.346.408q-1.34.118-2.672.25l.262 2.653q1.322-.133 2.637-.247zm-5.34.53q-1.33.145-2.665.31l.328 2.647q1.31-.162 2.63-.307zm-5.32.648q-1.336.176-2.658.367l.379 2.64c.878-.125 1.753-.249 2.625-.363zm-5.31.766q-1.325.203-2.649.425l.442 2.631q1.304-.22 2.615-.422zm-5.291.88q-1.318.234-2.64.485l.503 2.619q1.297-.246 2.605-.479zm-5.266.998q-1.319.264-2.627.543l.553 2.608q1.299-.277 2.594-.535zm-5.246 1.116q-1.312.292-2.617.603l.617 2.594q1.288-.306 2.58-.596zm-5.223 1.236q-1.3.322-2.601.664l.681 2.58q1.278-.337 2.565-.656zm-5.19 1.36a188 188 0 0 0-2.585.73l.746 2.56q1.268-.367 2.547-.718zm-5.155 1.492q-1.286.387-2.567.796l.813 2.54q1.259-.403 2.527-.786zm-5.116 1.628q-1.278.427-2.545.872l.883 2.515q1.25-.44 2.502-.857zm-5.072 1.78q-1.262.465-2.518.953l.967 2.484q1.23-.48 2.47-.933zm-5.014 1.947q-1.247.508-2.486 1.045l1.06 2.445q1.21-.522 2.432-1.02zm-4.947 2.137q-1.226.558-2.443 1.15l1.168 2.397q1.181-.576 2.382-1.121zm-4.853 2.355q-1.206.621-2.39 1.278l1.294 2.334a84 84 0 0 1 2.314-1.239zm-4.735 2.63q-1.17.694-2.308 1.437l1.453 2.234q1.096-.713 2.218-1.38zm-4.558 2.97-.18.129-.557.406-.004.004q-.729.54-1.449 1.12l1.676 2.073q.661-.533 1.357-1.048l.004-.004.004-.002.53-.387.165-.12zm-4.282 3.45a32 32 0 0 0-1.966 1.968l1.972 1.795a29 29 0 0 1 1.801-1.803zm-3.746 4.153c-.553.771-1.064 1.59-1.496 2.463l2.39 1.184c.357-.72.79-1.42 1.272-2.092zm-2.517 5.239c-.237.98-.343 2.001-.288 3.029l2.663-.143a7.9 7.9 0 0 1 .216-2.261z"/><path d="m49.393 145.39.448-3.534 1.747-3.75 2.798-3.573 3.727-3.431 4.582-3.31 5.371-3.189 6.095-3.054 6.755-2.9 7.346-2.722 7.867-2.517 8.32-2.287 8.72-2.031 18.227-3.159 9.44-1.082 9.478-.703.098 1.33-9.45.701-9.376 1.075-18.152 3.145-8.657 2.017-8.268 2.272-7.812 2.5-7.286 2.699-6.688 2.871-6.018 3.016-5.278 3.134-4.469 3.228-3.59 3.304-2.636 3.367-1.594 3.423-.422 3.328zm2.646.335.395-3.121 1.442-3.095 2.475-3.161 3.452-3.177 4.356-3.148 5.186-3.078 5.941-2.977 6.62-2.842 7.226-2.677 7.757-2.483 8.216-2.257 8.595-2.003 18.076-3.132 9.311-1.067 9.424-.699.099 1.329-9.398.697-9.246 1.06-18.001 3.119-8.532 1.988-8.164 2.244-7.702 2.464-7.166 2.655-6.553 2.814-5.863 2.937-5.094 3.024-4.243 3.066-3.314 3.05-2.314 2.955-1.289 2.768-.369 2.915zM152.58 146.4l-.951.072.2 2.659.95-.07zm-3.61.271-2.66.2.2 2.658 2.66-.2zm-5.318.397-2.66.2.2 2.66 2.66-.2zm-5.318.398-2.66.2.199 2.66 2.658-.2zm-5.32.399-2.659.2.2 2.66 2.658-.2zm-5.319.398-2.658.2.2 2.66 2.657-.2zm-5.318.399-2.658.199.199 2.658 2.658-.197zm-5.319.398-2.658.2.2 2.658 2.658-.2zm-5.318.399-2.658.199.199 2.658 2.658-.2zm-5.318.398-2.659.2.2 2.657 2.658-.199zm-5.319.397-2.658.199.2 2.66 2.657-.2zm-5.318.398-2.658.2.199 2.66 2.658-.2zm-5.319.398-2.658.2.2 2.66 2.658-.2zm-5.318.399-2.658.199.197 2.66 2.66-.199zm-5.318.398-2.66.2.199 2.658 2.66-.198zm-5.319.399-2.66.199.2 2.658 2.66-.2zm-5.318.398-2.66.2.199 2.658 2.66-.2zm-5.319.399-2.66.199.2 2.658 2.66-.2z"/><path d="m61.466 156.57 91.366-6.84-.1-1.33-91.366 6.84zm-.2-2.66 91.367-6.84-.1-1.33-91.365 6.841zM169.46 107.13l91.422-5.789-.084-1.33-91.422 5.788zm-.169-2.661 91.422-5.79-.084-1.33-91.422 5.789z"/><path d="m61.443 144.44-20.203 1.078 1.08 20.201 20.201-1.078zm-1.26 1.402.938 17.537-17.541.938-.938-17.54z"/></g><path d="M48.713 159.189q.002.031-.012.055-.01.024-.045.042-.034.017-.092.028-.058.012-.148.016-.086.005-.145 0t-.095-.018-.053-.037q-.013-.022-.014-.054l-.362-6.771q-.001-.028.01-.052.014-.024.048-.041t.092-.029.144-.015q.09-.005.149 0t.095.018.048.037q.017.022.019.05zm3.679-.196q.001.03-.013.055-.01.024-.045.042-.034.017-.092.028t-.148.016q-.086.004-.145 0-.059-.005-.094-.019t-.053-.036q-.013-.023-.015-.054l-.361-6.772q-.002-.027.009-.051.014-.024.048-.042.034-.017.093-.028t.143-.016q.09-.005.15 0 .058.005.094.019t.049.036q.017.023.018.05zm3.678-.197q.002.031-.013.056-.01.024-.044.041-.035.018-.093.029-.058.01-.147.015-.086.005-.145 0-.06-.005-.095-.018-.036-.014-.053-.037-.013-.022-.015-.054l-.361-6.771q-.002-.027.009-.051.014-.025.049-.042.034-.018.092-.029.058-.01.144-.015.09-.005.148 0 .06.005.095.018.036.014.05.037.016.022.018.05zM171.68 136.59l-20.201 1.078 1.078 20.203 20.201-1.078zm-1.262 1.402.938 17.54-17.54.937-.937-17.54z" class="ucdraw"/><path d="M158.948 151.19q.001.031-.013.055-.01.024-.045.042-.034.017-.092.028t-.148.016q-.086.005-.145 0t-.094-.019-.053-.036q-.013-.023-.015-.054l-.361-6.771q-.002-.028.009-.052.014-.024.048-.042.035-.017.093-.028t.143-.016q.09-.004.15 0 .058.005.094.019t.049.036q.017.023.018.05zm3.678-.197q.002.032-.013.056-.01.024-.044.041-.035.018-.093.029-.058.01-.147.016-.086.004-.145 0-.06-.005-.095-.019t-.053-.036q-.013-.023-.014-.054l-.362-6.772q-.002-.027.009-.051.014-.024.049-.042.034-.017.092-.028t.144-.016q.09-.005.148 0 .06.005.095.018.036.014.05.037.016.023.017.05zm3.678-.196q.002.031-.012.056-.01.023-.045.041t-.092.028q-.058.012-.148.016-.086.005-.145 0t-.095-.018-.053-.037q-.012-.022-.014-.054l-.362-6.771q-.001-.027.01-.052.014-.024.048-.041t.092-.029.144-.015q.09-.005.149 0t.095.018q.035.014.049.037.016.022.018.05z" class="ucdraw"/><path d="m170.09 94.295-21.535 1.15 1.15 21.533 21.533-1.15zm-2.521 2.805.865 16.209-16.207.865-.867-16.207z" class="uchighlight"/><path d="M156.725 109.189q.002.03-.013.055-.01.024-.044.042-.035.017-.093.028t-.148.016q-.085.004-.144 0-.06-.005-.095-.019t-.053-.036q-.013-.023-.015-.054l-.361-6.772q-.002-.027.009-.051.014-.024.048-.042.035-.017.093-.028t.144-.016q.09-.005.148 0 .06.005.095.019t.05.036q.016.023.017.05zm3.678-.197q.002.031-.012.056-.01.024-.045.041t-.092.029-.148.015q-.086.005-.145 0t-.095-.018-.053-.036q-.013-.023-.014-.054l-.362-6.772q-.001-.027.01-.051.014-.025.048-.042t.092-.029q.058-.01.144-.015.09-.005.149 0t.095.018q.035.014.048.037.017.022.019.05zm3.679-.196q.001.031-.013.055-.01.024-.045.042-.034.017-.092.028t-.148.016q-.086.005-.145 0t-.094-.019-.053-.036q-.013-.023-.015-.054l-.361-6.771q-.002-.028.009-.052.014-.024.048-.042.035-.017.093-.028t.143-.016q.09-.004.15 0 .058.005.094.019t.049.036q.017.023.018.05z" class="uchighlight"/><path d="m279.75 88.199-20.203 1.08 1.078 20.201 20.203-1.078zm-1.262 1.402.938 17.54-17.54.937-.937-17.54z" class="ucdraw"/><path d="M267.019 102.188q.001.03-.013.055-.01.024-.045.042-.034.017-.092.028t-.148.016q-.086.005-.145 0t-.095-.019-.052-.036q-.013-.023-.015-.054l-.362-6.772q-.001-.027.01-.05.014-.025.048-.043t.092-.028q.059-.011.144-.016.09-.004.149 0 .059.005.095.019t.049.036q.017.023.018.05zm3.678-.197q.002.031-.013.056-.01.024-.044.041-.035.018-.093.029-.058.01-.148.016-.085.004-.144 0-.06-.006-.095-.019-.036-.014-.053-.036-.013-.023-.015-.054l-.361-6.772q-.002-.027.009-.051.014-.024.048-.042.035-.017.093-.028.058-.012.144-.016.09-.005.148 0 .06.005.095.018.036.014.05.037.016.022.017.05zm3.678-.196q.002.031-.012.055-.01.024-.045.042-.034.017-.092.028-.059.011-.148.016-.086.005-.145 0t-.095-.018-.053-.037q-.013-.023-.014-.054l-.362-6.771q-.001-.028.01-.052.014-.024.048-.041t.092-.029.144-.016q.09-.004.149 0 .059.005.095.019.035.014.048.036.017.023.019.05z" class="ucdraw"/><g class="ucdraw"><path d="m42.316 154.25-2.662.143.142 2.662 2.663-.14zm-5.324.285-2.664.143.142 2.662 2.662-.143zm-5.326.283-2.664.143.142 2.664 2.664-.143zm-5.327.286-2.662.142.14 2.662 2.665-.142z"/><path d="m23.642 154.58 18.697-.998.072 1.331-18.698.998zm.142 2.663 18.698-.998.071 1.331-18.698.998zM171.7 148.72l9.476-.697 9.438-1.064 18.228-3.09 8.72-1.983 8.321-2.23 7.869-2.452 7.347-2.65 6.757-2.82 6.098-2.97 5.374-3.098 4.587-3.216 3.734-3.333 2.809-3.474 1.762-3.659.456-3.443-1.322-.175-.427 3.232-1.604 3.327-2.643 3.27-3.595 3.209-4.474 3.136-5.282 3.046-6.022 2.932-6.691 2.794-7.289 2.628-7.815 2.436-8.27 2.215-8.659 1.97-18.155 3.077-9.375 1.057-9.45.695zm-.196-2.66 9.425-.693 9.313-1.05 18.08-3.065 8.599-1.955 8.22-2.202 7.761-2.42 7.23-2.606 6.625-2.766 5.946-2.895 5.19-2.993 4.362-3.058 3.456-3.085 2.478-3.065 1.444-2.996.4-3.02-1.322-.175-.372 2.809-1.284 2.663-2.313 2.862-3.316 2.96-4.25 2.979-5.099 2.94-5.869 2.858-6.56 2.738-7.17 2.586-7.709 2.402-8.169 2.189-8.537 1.941-18.008 3.054-9.25 1.042-9.398.692z"/><path d="M269.25 110.02a9.2 9.2 0 0 1-.719 2.209l2.422 1.12c.413-.893.738-1.848.92-2.853zm-1.941 4.305c-.472.673-1 1.318-1.567 1.941l1.973 1.795a22 22 0 0 0 1.777-2.207zm-3.372 3.74q-.34.31-.712.63-.614.53-1.243 1.026l1.649 2.096q.685-.54 1.34-1.104l.004-.004q.377-.325.767-.683zm-4.04 3.21-.102.073q-1.015.711-2.057 1.373l1.43 2.252a58 58 0 0 0 2.164-1.445l.004-.004.106-.074zm-4.395 2.811q-1.127.663-2.283 1.287l1.27 2.344a77 77 0 0 0 2.365-1.332zm-4.611 2.506a95 95 0 0 1-2.364 1.156l1.14 2.413a98 98 0 0 0 2.427-1.19zm-4.762 2.26q-1.202.54-2.418 1.049l1.033 2.457a113 113 0 0 0 2.475-1.072zm-4.865 2.053q-1.225.49-2.46.957l.942 2.494q1.26-.476 2.51-.977zm-4.942 1.875q-1.245.448-2.496.875l.864 2.523q1.272-.435 2.537-.89zm-5.01 1.717q-1.254.411-2.519.804l.791 2.545q1.281-.397 2.56-.816zm-5.056 1.574q-1.267.378-2.541.736l.723 2.567q1.291-.365 2.58-.749zm-5.1 1.441q-1.278.345-2.56.672l.66 2.584q1.3-.331 2.598-.682zm-5.134 1.317q-1.285.314-2.578.613l.601 2.597q1.305-.301 2.611-.62zm-5.166 1.197q-1.29.284-2.592.555l.545 2.61q1.31-.27 2.623-.562zm-5.192 1.082q-1.3.257-2.603.498l.484 2.623q1.32-.244 2.637-.506zm-5.213.969q-1.31.229-2.613.441l.424 2.633c.886-.143 1.767-.295 2.644-.447zm-5.24.857q-1.305.201-2.621.387l.375 2.64a217 217 0 0 0 2.654-.392zm-5.248.746q-1.318.174-2.63.33l.314 2.649q1.335-.16 2.662-.334zm-5.27.633q-1.311.146-2.636.275l.264 2.653q1.329-.13 2.668-.278zm-5.277.52q-1.32.116-2.64.214l.199 2.66q1.341-.1 2.676-.218zm-5.29.402q-1.316.087-2.645.158l.144 2.662q1.334-.071 2.68-.16z"/></g><path d="m57.244 77.292 18.898-.506 18.395.444 8.826.549 8.457.749 8.026.938 7.524 1.116 6.95 1.282 6.306 1.439 5.598 1.588 4.828 1.737 4.011 1.897 3.158 2.112 2.244 2.474.824 2.454-1.264.424-.735-2.189-1.947-2.145-2.94-1.967-3.863-1.828-4.724-1.699-5.52-1.566-6.246-1.425-6.899-1.273-7.48-1.109-7.987-.933-8.421-.746-8.784-.546-18.335-.443-18.865.505zm.071 2.666 18.831-.504 18.276.441 8.741.544 8.384.742 7.949.929 7.436 1.103 6.849 1.263 6.184 1.411 5.443 1.545 4.62 1.661 3.716 1.759 2.722 1.82 1.649 1.817.646 1.925-1.264.424-.557-1.66-1.351-1.489-2.504-1.674-3.569-1.689L145 88.702l-5.365-1.523-6.123-1.397-6.799-1.254-7.392-1.096-7.91-.924-8.348-.74-8.699-.541-18.216-.439-18.797.502z" class="uchighlight"/><path d="M75.953 77.453q-1.337.001-2.678.014l.026 2.668q1.326-.013 2.656-.014zm2.676.008-.016 2.666q1.331.006 2.656.025l.037-2.666q-1.34-.019-2.677-.025m-8.026.039q-1.343.02-2.677.05l.06 2.667q1.331-.032 2.655-.051zm13.377.031-.05 2.666q1.324.025 2.654.063l.072-2.666q-1.334-.037-2.676-.063m-18.73.092q-1.337.04-2.677.092l.105 2.666q1.325-.052 2.652-.092zm24.08.057-.09 2.664q1.326.046 2.653.103l.115-2.666a306 306 0 0 0-2.678-.101m-29.428.148q-1.342.06-2.675.13l.14 2.665q1.33-.072 2.653-.13zm34.777.078-.132 2.662q1.328.065 2.652.143l.156-2.662q-1.339-.078-2.676-.143m5.346.307-.174 2.662q1.328.086 2.649.183l.197-2.66a303 303 0 0 0-2.672-.185m5.34.392-.217 2.659q1.325.107 2.645.226l.242-2.656q-1.337-.12-2.67-.229m5.336.48-.264 2.655q1.322.131 2.639.274l.29-2.653a269 269 0 0 0-2.665-.275m5.326.577-.31 2.648q1.316.154 2.632.323l.338-2.645q-1.329-.17-2.66-.326m5.316.68-.363 2.642q1.312.18 2.621.38l.399-2.638q-1.327-.2-2.657-.384m5.305.799-.426 2.63q1.308.213 2.61.444l.465-2.627q-1.325-.234-2.649-.447m5.29.931-.5 2.62q1.3.246 2.591.517l.547-2.611q-1.32-.275-2.639-.526m5.265 1.096-.588 2.601q1.286.29 2.566.608l.645-2.588q-1.308-.325-2.623-.621m5.232 1.303-.703 2.572q1.27.345 2.523.728l.782-2.55a88 88 0 0 0-2.602-.75m5.178 1.584-.861 2.523q1.233.42 2.443.898l.98-2.48a58 58 0 0 0-2.562-.942m5.082 2.021-1.12 2.422q.731.338 1.45.72l.4.217.387.22.01.005 1.361-2.293-.033-.02-.43-.242-.439-.24-.006-.002a32 32 0 0 0-1.58-.787m4.85 2.89-1.67 2.079c.628.504 1.177 1.043 1.58 1.629l2.197-1.51c-.606-.881-1.352-1.59-2.107-2.197" class="uchighlight"/><g class="ucdraw"><path d="m38.299 78.971-2.664.143.142 2.662 2.663-.143zm-5.326.283-2.664.143.142 2.664 2.664-.143zm-5.326.285-2.662.143.14 2.662 2.664-.143zm-5.327.286-2.662.14.143 2.664 2.662-.142z"/><path d="m19.622 79.3 18.698-.998.071 1.331-18.698.998zm.142 2.663 18.698-.998.071 1.331-18.698.998zM298.19 96.01l-2.662.143.142 2.662 2.663-.143zm-5.324.283-2.664.143.142 2.664 2.662-.143zm-5.326.285-2.665.143.143 2.662 2.664-.143zm-5.327.286-2.662.14.14 2.664 2.665-.142z"/><path d="m279.52 96.339 18.698-.998.071 1.331-18.698.999zm.142 2.663 18.698-.998.071 1.331-18.697.999zM57.424 69.154l-20.201 1.078 1.078 20.203 20.201-1.08zm-1.262 1.402.938 17.54-17.54.937-.937-17.54z"/></g><path d="M44.151 84.065q.002.035-.014.062-.012.027-.05.047-.04.02-.104.032-.066.012-.167.018-.096.005-.163 0-.066-.006-.106-.021t-.06-.04q-.014-.027-.016-.062l-.407-7.618q-.002-.03.01-.058.016-.027.055-.046t.104-.032.162-.018q.1-.005.167 0 .066.005.107.02.04.016.055.042.019.025.02.056zm4.138-.221q.002.035-.014.062-.012.027-.05.047t-.104.032-.167.018q-.096.005-.163 0-.066-.006-.106-.021t-.06-.04q-.014-.027-.016-.062l-.407-7.618q-.001-.03.01-.057.016-.028.055-.047t.104-.032.162-.018q.1-.005.167 0 .066.005.107.02.04.016.055.042.019.025.02.056zm4.138-.22q.002.034-.014.061-.012.027-.05.047t-.104.032-.167.018q-.096.005-.162 0-.067-.006-.107-.021t-.06-.04q-.014-.026-.016-.062l-.407-7.618q-.001-.03.01-.057.017-.028.055-.047t.104-.032q.065-.013.162-.018.1-.005.167 0 .066.005.107.02.04.016.055.042.019.025.02.056z" class="ucdraw"/><g class="ucdraw"><path d="m23.644 31.374 259.65 29.056-.148 1.325-259.65-29.056zm-.296 2.651 259.65 29.055-.149 1.325L23.209 35.35z"/><path d="m23.631 32.42-.297 2.65 2.65.297.297-2.65zm5.299.594-.295 2.648 2.648.297.297-2.65zm5.3.592-.296 2.65 2.65.297.297-2.65zm5.301.593-.296 2.65 2.65.298.297-2.65zm5.301.594-.297 2.648 2.65.297.296-2.65zm5.299.592-.297 2.65 2.65.297.297-2.65zm5.3.594-.296 2.65 2.65.297.297-2.65zm5.302.593-.297 2.649 2.65.297.297-2.65zm5.3.592-.296 2.65 2.65.297.295-2.65zm5.3.594-.298 2.65 2.65.297.298-2.65zm5.3.594-.297 2.65 2.65.295.297-2.648zm5.3.591-.296 2.65 2.65.298.297-2.65zm5.3.594-.295 2.65 2.648.297.297-2.65zm5.3.594-.296 2.65 2.65.295.297-2.648zm5.301.592-.297 2.65 2.65.297.298-2.65zm5.301.593-.297 2.65 2.65.298.295-2.65zm5.299.594-.295 2.65 2.648.296.297-2.649zm5.3.592-.296 2.65 2.65.297.297-2.65zm5.301.594-.296 2.65 2.65.297.297-2.65zm5.301.594-.297 2.65 2.65.297.296-2.65zm5.299.591-.297 2.65 2.65.298.297-2.65zm5.3.594-.296 2.65 2.65.297.297-2.65zm5.302.594-.297 2.65 2.65.297.297-2.65zm5.298.594-.295 2.648 2.65.297.296-2.65zm5.301.591-.297 2.65 2.65.298.298-2.65zm5.301.594-.297 2.65 2.65.297.297-2.65zm5.3.594-.296 2.648 2.65.297.297-2.65zm5.3.592-.295 2.65 2.648.297.297-2.65zm5.3.593-.296 2.65 2.65.298.297-2.65zm5.301.594-.297 2.649 2.65.296.298-2.65zm5.301.592-.297 2.65 2.65.297.295-2.65zm5.299.594-.297 2.65 2.65.297.297-2.65zm5.3.593-.296 2.65 2.65.296.297-2.649zm5.301.592-.296 2.65 2.65.298.297-2.65zm5.301.594-.297 2.65 2.65.297.296-2.65zm5.299.594-.297 2.65 2.65.295.297-2.648zm5.3.592-.296 2.65 2.65.297.297-2.65zm5.302.593-.297 2.65 2.65.298.297-2.65zm5.298.594-.295 2.65 2.649.295.297-2.648zm5.301.592-.297 2.65 2.65.297.298-2.65zm5.301.594-.297 2.65 2.65.297.297-2.65zm5.3.593-.296 2.65 2.65.298.297-2.65zm5.3.592-.295 2.65 2.648.297.297-2.65zm5.3.594-.296 2.65 2.65.297.297-2.65zm5.301.594-.297 2.65 2.65.297.298-2.65zm5.301.593-.297 2.649 2.65.297.295-2.65zm5.299.592-.297 2.65 2.65.298.297-2.65zm5.3.594-.296 2.65 2.65.297.297-2.65zm5.301.594-.296 2.648 2.65.297.297-2.65zm7.023 4.093-2.956-.877z"/><path d="m150.25 27.859-1.352 2.299q1.165.684 2.346 1.34l1.293-2.334q-1.152-.638-2.287-1.305m4.607 2.559-1.24 2.359q1.193.627 2.399 1.229l1.189-2.387q-1.18-.588-2.348-1.201m4.725 2.359-1.148 2.406q1.216.58 2.441 1.135l1.102-2.428q-1.204-.545-2.395-1.113m4.815 2.19-1.067 2.443q1.233.537 2.473 1.055l1.025-2.461q-1.22-.51-2.431-1.037zm4.884 2.04-.996 2.473q1.248.502 2.498.985l.963-2.487q-1.238-.477-2.465-.97m4.944 1.91-.932 2.499q1.256.468 2.52.922l.9-2.512q-1.246-.447-2.488-.908m4.99 1.792-.875 2.52q1.268.44 2.539.864l.846-2.529a227 227 0 0 1-2.51-.855m5.031 1.685-.822 2.537q1.274.413 2.553.813l.796-2.545q-1.266-.396-2.527-.805m5.065 1.588-.774 2.55q1.282.39 2.566.767l.75-2.56q-1.273-.372-2.543-.757m5.095 1.496-.73 2.565q1.288.367 2.58.72l.707-2.57a281 281 0 0 1-2.557-.715m5.121 1.41-.69 2.577q1.295.345 2.593.68l.666-2.583a303 303 0 0 1-2.569-.674m5.143 1.33-.649 2.588q1.298.325 2.6.641l.629-2.592q-1.29-.312-2.58-.637m5.164 1.254-.611 2.596q1.3.308 2.61.606l.589-2.602q-1.293-.293-2.588-.6m5.182 1.182-.575 2.604 1.03.226 1.584.342.56-2.608-1.578-.34zm5.197 1.111-.537 2.614 1.705.35.914.18.523-2.612-.908-.184zm5.217 1.045-.512 2.617.463.09 2.017.385.153.027.484-2.62-.144-.028-2.006-.38zm5.222.979-.472 2.623 1.285.232 1.346.236.46-2.627-1.34-.234zm5.243.916-.45 2.629.127.021 2.07.344.446.07.424-2.633-.44-.07-2.056-.34zm5.246.851-.412 2.635 1.078.168 1.562.236.4-2.636-1.556-.237zm5.254.791-.375 2.64 2.097.298.55.076.362-2.643-.54-.074zm5.271.729-.351 2.644 1.05.139 1.6.205.34-2.644-1.596-.205zm5.28.672-.329 2.646.022.004 2.146.254.494.057.301-2.649-.486-.056-2.135-.254z"/></g></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc4.svg.license b/subprojects/docs/src/components/UseCases/uc4.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc4.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/UseCases/uc5.svg b/subprojects/docs/src/components/UseCases/uc5.svg
new file mode 100644
index 00000000..1b7d769d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc5.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><g class="ucdraw"><path d="M74.833 30.833h70.334l-.173 22.149-8.136.058-.23 13.697 8.539.057v26.587l-8.056.116.289 13.35 7.767.173c-.058.519-.23 22.046-.058 22.854l-7.456.173-.23 13.524 7.744-.058v20.655H74.833v-20.367l8.735.173-.346-14.043-8.39.115.059-22.68 8.48-.231-.057-13.812-8.481-.173.029-26.53c2.096.599 5.263.068 7.88.145l-.144-13.581-7.736.086c-.03-.317-.03-22.437-.03-22.437zm.778 22.006h7.65V67.11h-7.65v25.955h7.904v14.271h-7.904v22.682h8.158v14.272h-8.158v19.099h68.778V144.29h-7.052v-14.272h7.052v-22.682h-7.305V93.065h7.305V67.11h-7.559V52.839h7.559V31.611H75.611zm.778-20.45h67.222v19.672h-7.559v15.827h7.559v24.399h-7.305v15.827h7.305v21.127h-7.051v15.827h7.051v17.543H76.389v-17.543h8.157v-15.827h-8.157v-21.127h7.904V92.287h-7.904V67.888h7.65V52.061h-7.65zm.778 18.894h7.65v17.383h-7.65v22.843h7.903v17.382h-7.903v19.572h8.157v17.382h-8.157v15.988h65.666v-15.988h-7.051v-17.382h7.051v-19.572h-7.305V91.509h7.305V68.666h-7.559V51.283h7.559V33.167H77.167zM186.83 39.833h70.334s0 33.466.029 34.072c-.058-.692-7.9.144-7.9.144l.058 13.275h7.813v34.843H186.83v-15.33l8.39.115-.116-13.534-8.274.057-.058-26.79h7.905l.058-13.245-7.905-.086zm.778 13.031h7.559v14.34h-7.559v26.07h7.813v14.34h-7.813v13.775h68.778V88.102h-7.813V73.761h7.813v-33.15h-68.778zm.778-11.475h67.222v31.595h-7.813v15.895h7.813v31.732h-67.222v-12.219h7.813V92.496h-7.813V67.982h7.559V52.086h-7.559zm.778 9.919h7.559V68.76h-7.559v22.958h7.813v17.452h-7.813v10.663h65.666V89.657h-7.813V72.206h7.813V42.167h-65.666zM152.5 59.834v1.332h28.883v-1.332zM152.5 99.834v1.332h28.883v-1.332z"/></g><g class="uchighlight"><path d="M186.83 126.83h70.334v12.024l-7.66-.014v13.163h7.66c.014.158 0 12.16 0 12.16H186.83v-12.612l9.05.021.022-13.084-9.065-.021zm.778 10.996h8.791v14.402h-8.791v11.158h68.778v-10.735h-7.559v-14.402h7.559v-10.641h-68.778zm.778-9.44h67.222v9.085h-7.559v15.958h7.559v9.179h-67.222v-9.602h8.791v-15.958h-8.791zm.778 7.884h8.791v17.514h-8.791v8.046h65.666v-7.623h-7.559v-17.514h7.559v-7.529h-65.666z"/><path d="M202.58 134.22h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.753 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zM205.58 140.22h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.753 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zM205.58 147.22h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.753 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zM202.58 153.22h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534zm2.753 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.535v4.92h-.535zm2.754 0h.534v4.92h-.534zm2.754 0h.534v4.92h-.534z"/></g><g class="ucdraw"><path d="M152.93 136.83v1.34h14.256v8.115h15.594v-1.34h-14.256v-8.115zM40.183 59.805v1.332h28.883v-1.332zM40.231 99.84v1.32h28.393v-1.32zM40.5 136.83v1.332h28.883v-1.332zM264.5 80.834v1.332h28.883v-1.332zM264.5 146.83v1.332h28.883v-1.332z"/><path d="M67.291 51.404v17.02h17.15v-17.02zm1.998 2h13.152v13.021H69.289zM135.35 51.404v17.02h17.15v-17.02zm1.998 2h13.154v13.021h-13.154zM179.37 51.568v17.02h17.15v-17.02zm1.998 1.998h13.152v13.021h-13.152zM247.35 72.168v17.021h17.152V72.168zm2 2h13.152v13.021H249.35zM67.545 91.555v17.021h17.15V91.555zm2 2h13.152v13.023H69.545zM135.61 91.555v17.021h17.15V91.555zm2 2h13.152v13.023H137.61zM179.7 91.555v17.021h17.15V91.555zm2 2h13.152v13.023H181.7zM67.799 128.44v17.02h17.152v-17.02zm2 2h13.152v13.021H69.799zM135.86 128.44v17.02h17.15v-17.02zm2 2h13.152v13.021H137.86zM180.68 136.5v17.021h17.15V136.5zm2 2h13.152v13.021H182.68zM247.6 136.92v17.021h17.152V136.92zm2 2h13.152v13.021H249.6zM23.5 51.5v17.176h17.48V51.5zm2 2h13.48v13.176H25.5zM23.76 92.062v17.174h17.48V92.062zm2 2h13.48v13.174H25.76zM24.02 129.32v17.176H41.5V129.32zm2 2H39.5v13.176H26.02zM300.62 73.146c-4.592 0-8.334 3.742-8.334 8.334s3.742 8.334 8.334 8.334 8.334-3.742 8.334-8.334-3.742-8.334-8.334-8.334m0 1.668a6.653 6.653 0 0 1 6.666 6.666 6.653 6.653 0 0 1-6.666 6.666 6.653 6.653 0 0 1-6.666-6.666 6.653 6.653 0 0 1 6.666-6.666M300.44 139.09c-4.592 0-8.332 3.74-8.332 8.332s3.74 8.334 8.332 8.334 8.334-3.742 8.334-8.334-3.742-8.332-8.334-8.332m0 1.666a6.655 6.655 0 0 1 6.668 6.666 6.657 6.657 0 0 1-6.668 6.668 6.655 6.655 0 0 1-6.666-6.668 6.653 6.653 0 0 1 6.666-6.666M90.833 44.833h39.334v103.33H90.833zm.778 102.56h37.778V45.613H91.611zm.778-101h36.222v100.22H92.389zm.778 99.444h34.666V47.171H93.167zM204.83 51.833h37.334v58.334H204.83zm.778 57.556h35.778V52.611h-35.778zm.778-56h34.222v55.222h-34.222zm.778 54.444h32.666V54.167h-32.666z"/></g></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc5.svg.license b/subprojects/docs/src/components/UseCases/uc5.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc5.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/UseCases/uc6.svg b/subprojects/docs/src/components/UseCases/uc6.svg
new file mode 100644
index 00000000..063d89a8
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc6.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="328" height="189" viewBox="0 0 328 189"><g class="ucdraw"><path d="M199.54 12.877c-4.198-.201-8.359.317-12.461 1.111-3.585.692-6.178 3-7.383 6.471-.808 2.325-1.483 4.683-2.162 7.02v.002c-1.406 4.865-3.309 9.424-6.834 12.993-6.266 6.343-13.344 11.724-20.828 16.599-1.386.902-2.792 1.2-4.473.818-3.488-.794-6.882-1.867-10.229-3.14h-.002c-5.267-2.001-9.84-5.37-14.738-8.337-4.2-2.544-8.728-2.99-13.279-1.047-7.172 3.07-12.17 8.202-13.318 16.305-.777 5.49-2.148 10.835-3.92 16.107h-.001c-.945 2.818-1.407 5.763-1.15 8.829.388 4.62 2.67 8.345 5.091 11.863 4.239 6.162 3.381 13.213-.613 18.758-2.613 3.627-6.373 6.054-10.014 8.79-2.563 1.926-5.166 3.938-6.946 6.848-3.084 5.033-4.566 10.538-4.445 16.441 0 .348.003.692 0 1.028-.071 6.845 2.72 12.404 8.89 15.637 6.602 3.456 13.332 6.653 20.042 9.857 3.314 1.58 6.867 1.698 10.35.658a474 474 0 0 0 10.83-3.379c6.848-2.223 11.473-6.816 13.447-13.828 1.75-6.209 1.104-12.262-1.106-18.176-1.328-3.548-2.74-6.95-2.474-10.664.195-2.723 1.364-5.325 1.949-8.207.69-3.388.817-6.089.035-8.435s-2.472-4.254-5.182-6.04l-.002-.001c-.75-.49-1.48-1.051-2.238-1.606h-.002c-2.14-1.557-3.924-3.71-4.861-6.111s-1.046-5.029.148-7.674c1.053-2.33 2.614-4.35 4.49-6.014 3.957-3.503 7.603-7.275 10.766-11.533 2.458-3.31 4.908-6.57 7.899-9.338 1.556-1.44 2.699-2.156 3.857-2.347s2.46.12 4.38 1.004c2.802 1.29 5.341 3.03 7.776 4.968v-.002c3.468 2.771 7.376 4.62 11.8 5.276 5.863.87 11.074-.554 14.73-5.717 1.98-2.788 3.44-5.798 4.73-8.871l.002-.002a64 64 0 0 1 6.59-11.893v-.002c3.587-5.115 4.901-11.119 7.341-16.465 1.29-2.83 3.13-5.568 3.864-8.898v-.002c.53-2.44.303-4.546-.762-6.15-1.065-1.606-2.916-2.625-5.375-3.048a34 34 0 0 0-4.209-.457zm-4.266 2.135c2.55-.161 5.105-.074 7.664.38 2.299.408 3.73 1.282 4.448 2.495.717 1.213.79 2.887.039 5.094h-.002v.002c-.335.998-.78 1.977-1.272 2.943v.002c-1.716 3.384-3.332 6.836-4.548 10.465-1.434 4.22-3.326 8.166-5.883 11.791h.002c-2.821 3.991-4.757 8.401-6.639 12.785-1.18 2.75-2.649 5.312-4.582 7.537v.002c-2.744 3.16-6.187 4.449-10.352 3.924-3.88-.49-7.44-1.819-10.572-4.186v-.002c-2.876-2.166-5.765-4.4-9.148-5.978-2.32-1.08-4.295-1.525-6.18-1.205s-3.6 1.388-5.445 3.123c-4.187 3.934-7.096 8.776-10.684 12.934-2.213 2.563-4.69 4.885-7.2 7.217-3.064 2.847-5.25 5.912-6.03 9.271-.782 3.359-.109 6.972 2.37 10.736 1.807 2.74 4.606 4.32 7.008 6.049 3.657 2.63 4.547 5.942 3.637 10.213-.634 2.975-1.904 5.915-2.174 9.222-.36 4.434 1.048 8.415 2.514 12.3 1.662 4.397 2.594 8.818 1.754 13.46h-.002c-1.4 7.756-5.422 13.028-13.041 15.49-3.528 1.142-7.09 2.212-10.637 3.362-2.828.917-5.542.78-8.168-.485-6.817-3.277-13.68-6.43-20.285-10.018-2.906-1.579-4.85-3.455-6.116-6.1-1.263-2.64-1.84-6.091-1.841-10.774.157-4.331 1.812-10.478 6.347-15.512 1.673-1.859 3.834-3.3 5.961-4.854 3.19-2.333 6.424-4.712 8.88-8.058 4.695-6.398 4.953-14.844.63-21.492v-.002c-2.319-3.56-4.588-6.987-4.867-11.221-.161-2.433.263-4.796 1.05-7.084a91.3 91.3 0 0 0 4.016-16.523c1.09-7.442 5.569-11.923 12.223-14.938 3.84-1.74 7.305-1.275 10.848.855 3.896 2.34 7.624 5.086 11.87 7.096 4.701 2.224 9.61 3.8 14.704 4.795 1.97.385 3.865-.07 5.496-1.088 6.965-4.35 13.287-9.518 19.361-14.96 4.788-4.278 7.496-9.76 9.178-15.77h.002c.647-2.338 1.343-4.666 2.154-6.934v-.002c1.015-2.822 2.99-4.624 5.871-5.242 2.544-.544 5.09-.954 7.641-1.115z"/><path d="M199.07 13.869c-3.976-.185-7.904.355-11.777 1.19-3.287.708-5.682 2.921-6.826 6.117-.829 2.323-1.524 4.69-2.186 7.05v.002c-1.628 5.878-4.21 11.107-8.754 15.193-6.025 5.432-12.273 10.567-19.123 14.873-1.39.873-2.9 1.23-4.521.91-4.975-.975-9.76-2.521-14.344-4.704-4.08-1.945-7.765-4.665-11.721-7.059-3.8-2.3-7.855-2.843-12.055-.932h-.002c-6.89 3.145-11.8 8.099-12.953 16.014a91 91 0 0 1-3.947 16.34v.002c-.83 2.426-1.291 4.98-1.118 7.631.306 4.673 2.76 8.336 5.063 11.9 4.021 6.22 3.784 14.068-.584 20.057-2.297 3.15-5.388 5.46-8.567 7.799v-.002c-2.087 1.534-4.33 3.024-6.146 5.057-4.776 5.335-6.507 11.783-6.672 16.42v.025c0 4.817.58 8.485 1.967 11.402s3.59 5.038 6.662 6.719c6.64 3.63 13.507 6.8 20.285 10.086v.002c2.916 1.407 6.036 1.562 9.135.55 3.517-1.148 7.072-2.217 10.609-3.372 8.004-2.604 12.447-8.453 13.904-16.582.89-4.976-.11-9.675-1.813-14.215-1.453-3.879-2.76-7.659-2.426-11.758v-.002c.246-3.041 1.476-5.933 2.145-9.09.977-4.622-.137-8.705-4.15-11.61l2.018-.117c-2.477-1.79-5.08-3.31-6.656-5.72-2.321-3.547-2.873-6.73-2.178-9.739.696-3.008 1.13-5.478 4.066-8.225l.002-.002c2.506-2.342 4.551-4.995 6.818-7.636 3.677-4.288 7.566-7.48 11.566-11.264 1.74-1.646 2.77-2.465 4.279-2.82.865-.203 2.014-.724 4.19.298 3.204 1.502 6.646 2.896 9.52 5.075 3.327 2.524 7.108 3.946 11.178 4.463h.002c4.526.574 8.5-.928 11.496-4.389 2.047-2.361 3.57-5.063 4.789-7.914l.002-.002c1.868-4.391 3.761-8.725 6.48-12.586l.002-.002c2.631-3.756 6.455-7.184 7.902-11.52v-.084c1.194-3.548 1.79-7.281 3.494-10.65.508-1.003.085-2.286.45-3.376v-.002c.822-2.438.807-4.57-.162-6.217s-2.827-2.689-5.354-3.14a31 31 0 0 0-3.99-.444zm-3.879 1.373c2.541-.162 5.086-.073 7.635.383 2.293.41 3.722 1.285 4.44 2.504.717 1.219.793 2.9.046 5.115v.002c-.335 1.002-.782 1.985-1.273 2.955-1.717 3.398-3.322 6.862-4.547 10.502v.002c-1.413 4.235-3.305 8.195-5.854 11.834-2.82 4.005-4.747 8.432-6.619 12.83-1.18 2.76-2.636 5.337-4.568 7.565h-.002c-2.745 3.172-6.169 4.465-10.322 3.939-3.87-.491-7.409-1.827-10.541-4.203-2.866-2.173-5.745-4.414-9.123-5.998h-.002c-2.31-1.085-4.282-1.532-6.162-1.211-1.882.321-3.592 1.394-5.432 3.135-4.174 3.95-7.075 8.807-10.654 12.98h.002c-2.207 2.57-4.676 4.903-7.18 7.242-3.055 2.858-5.233 5.933-6.011 9.303-.779 3.369-.11 6.993 2.361 10.77 1.798 2.748 4.59 4.337 6.99 6.072 3.646 2.639 4.534 5.966 3.627 10.254-.633 2.986-1.901 5.937-2.17 9.258-.362 4.444 1.046 8.438 2.506 12.336 1.657 4.418 2.585 8.85 1.75 13.512-1.395 7.785-5.412 13.077-13.006 15.547v.002c-3.517 1.149-7.072 2.218-10.609 3.373-2.815.919-5.519.78-8.14-.485-6.794-3.292-13.64-6.456-20.227-10.057v.002c-2.897-1.584-4.836-3.468-6.098-6.123-1.26-2.651-1.835-6.115-1.836-10.816.157-4.347 1.808-10.518 6.33-15.57l.002-.002c1.667-1.865 3.821-3.309 5.942-4.867v-.002c3.181-2.342 6.406-4.728 8.855-8.086 4.682-6.42 4.94-14.893.627-21.564-2.31-3.573-4.575-7.012-4.854-11.264-.16-2.442.264-4.817 1.05-7.115a92 92 0 0 0 4.003-16.578c1.087-7.466 5.555-11.967 12.188-14.994 3.83-1.743 7.28-1.277 10.812.86 3.886 2.351 7.605 5.103 11.838 7.12 4.69 2.234 9.583 3.815 14.66 4.811 1.969.388 3.862-.067 5.49-1.09 6.946-4.366 13.25-9.553 19.305-15.012 4.777-4.297 7.475-9.794 9.147-15.826v-.002c.658-2.349 1.345-4.686 2.156-6.96 1.016-2.835 2.98-4.641 5.852-5.26 2.538-.547 5.076-.96 7.617-1.121zm-2.293 5.875c-1.636.185-3.236.75-4.33 1.736-2.97 2.676-2.72 6.35-3.074 8.965l-.008.047v.045c.01 2.28.321 4.051.642 5.767v.002c.145.766.337 1.567.762 2.282s1.124 1.326 2.106 1.582c1.881.493 3.498-.418 4.673-1.635l.002-.002c3.589-3.667 5.29-8.247 6.002-13.13.268-1.832-.335-3.79-2.168-4.888-1.283-.765-2.97-.957-4.607-.771zm.149 1.324c1.42-.16 2.868.051 3.775.592 1.367.819 1.743 2.1 1.531 3.55-.687 4.704-2.287 8.971-5.637 12.394h-.002v.002c-1.004 1.04-2.045 1.626-3.38 1.275h-.002v-.002c-.659-.171-1.01-.492-1.295-.972s-.463-1.135-.598-1.846v-.002c-.317-1.698-.605-3.354-.617-5.502.372-2.818.168-5.86 2.638-8.086.786-.71 2.166-1.242 3.586-1.402zM113.152 51.59c-6.431.01-12.085 4.366-13.713 10.857-1.622 6.477-3.418 12.985-4.1 19.75-.615 6.096 2.027 9.877 6.215 12.523 2.396 1.514 5.674 1.332 7.666-.357l.002-.002c1.465-1.239 2.933-2.516 4.285-3.918v-.002c2.92-3.035 6.136-5.718 9.486-8.297v.002c3.36-2.582 6.564-5.454 8.444-9.543V72.6c2.184-4.75 1.429-9.347-2.352-12.705-2.017-1.792-4.195-3.426-6.428-4.916-2.837-1.894-5.933-3.638-9.506-3.39zm.07 1.326c3.093-.223 5.91 1.315 8.695 3.174 2.19 1.462 4.32 3.06 6.281 4.803 3.413 3.031 4.036 6.783 2.026 11.152-1.738 3.782-4.74 6.503-8.045 9.043-3.378 2.6-6.647 5.326-9.633 8.43-1.299 1.346-2.73 2.597-4.186 3.828h-.002c-1.446 1.227-4.227 1.427-6.093.248-3.938-2.49-6.173-5.58-5.6-11.262.668-6.617 2.442-13.063 4.068-19.561 1.488-5.931 6.6-9.854 12.44-9.854h.023zm-4.049 58.46c-1.392-.058-2.848.608-4.184 2.208v.002c-1.798 2.16-3.47 4.387-5.343 6.373-3.26 3.47-7.167 6.23-11.205 8.805v-.002c-6.225 3.963-10.221 9.45-10.965 17-.523 5.29.511 10.23 4.502 14.172 3.43 3.393 7.664 5.357 11.67 7.518 9.664 5.217 19.422 4.614 28.922-.229 5.91-3.013 8.945-8.157 9.035-14.816v-.025l-.002-.026c-.077-1.185.003-2.428-.242-3.752v-.002c-.557-3.004-1.748-5.734-3.139-8.334-2.712-5.074-4.929-10.289-5.973-15.938v-.002c-.349-1.9-1.29-3.558-2.537-4.93-2.08-2.293-4.265-4.544-6.795-6.427-1.115-.83-2.352-1.538-3.744-1.596zm-.055 1.331c.96.04 1.982.573 3.002 1.332 2.417 1.8 4.545 3.984 6.604 6.254v.002h.002c1.113 1.224 1.916 2.657 2.213 4.276 1.078 5.836 3.358 11.183 6.107 16.326 1.357 2.536 2.483 5.138 3.004 7.949.204 1.104.142 2.276.22 3.55-.086 6.271-2.77 10.818-8.306 13.64-9.215 4.697-18.382 5.263-27.684.242-4.05-2.185-8.144-4.106-11.365-7.291-3.681-3.637-4.61-8.048-4.111-13.094.704-7.155 4.374-12.201 10.352-16.006h.002c4.073-2.597 8.077-5.415 11.459-9.014 1.941-2.06 3.629-4.309 5.398-6.433 1.16-1.39 2.145-1.772 3.104-1.733zM245.93 61.027c-1.196.263-2.419.796-3.633 1.59-1.799 1.176-3.226 2.719-4.508 4.334-4.492 5.666-9.389 10.664-15.918 13.979h-.002c-6.484 3.292-12.342 7.587-17.28 13.035v.002a2103 2103 0 0 1-10.249 11.26c-3.974 4.332-8.046 8.596-11.957 13.014-3.712 4.19-5.47 9.185-4.047 14.859.711 2.854 1.712 5.645 3.29 8.201.865 1.413 1.897 2.752 3.214 3.877 4.4 3.78 9.604 5.156 14.885 4.973 7.63.148 14.258-1.904 19.982-6.46 3.322-2.643 5.877-5.94 8.305-9.279v-.002c2.357-3.264 5.22-5.847 8.932-7.427 2.078-.883 4.169-1.766 6.21-2.768 2.992-1.462 4.679-4.03 5.215-7.248.487-2.924.278-5.823-.199-8.639v-.002c-1.101-6.686-.427-12.722 4.1-18.029h-.002c2.076-2.422 3.294-5.307 4.031-8.324 1.334-5.464 1.332-10.942-1.438-16.139l-.002-.002c-1.353-2.553-3.245-4.212-5.459-4.78-1.106-.283-2.274-.288-3.47-.024zm.803 2.027a4.3 4.3 0 0 1 1.802.028c1.138.267 2.214 1.032 3.23 2.345 3.786 4.912 3.922 10.357 2.343 16.221-.906 3.36-2.747 6.235-4.865 9.012v.002c-3.614 4.749-3.97 10.144-3.393 15.631v.002c.322 3.05.75 6.028.527 9.008-.257 3.361-1.708 5.667-4.724 7.12-1.9.919-3.87 1.747-5.84 2.579-4.03 1.704-7.118 4.525-9.629 7.977-2.44 3.352-4.975 6.57-8.203 9.105-5.272 4.149-11.283 6.001-17.98 6.022h-.002c-5.162.027-9.75-1.296-13.668-4.71-1.946-1.697-3.238-3.876-4.297-6.275l-.002-.002c-2.175-4.984-3.173-9.712.037-14.549v-.002c1.234-1.861 2.568-3.616 4.1-5.187l.002-.002c6.477-6.63 12.534-13.606 18.736-20.432h-.002c5.264-5.788 11.279-10.614 18.227-14.107 6.844-3.441 11.906-8.68 16.492-14.455h.002v-.002c1.435-1.815 3.052-3.396 5.064-4.537.728-.412 1.406-.67 2.043-.791z"/><path d="M247.2 61.941c-.81.152-1.641.47-2.478.943-2.227 1.258-3.98 2.989-5.503 4.899-4.573 5.745-9.532 10.846-16.178 14.182-7.171 3.598-13.377 8.563-18.764 14.488-6.266 6.883-12.38 13.89-18.863 20.525-1.628 1.66-3.023 3.495-4.298 5.416-3.54 5.318-2.41 10.744-.147 15.916v.002c1.102 2.511 2.512 4.898 4.684 6.787 4.211 3.664 9.224 5.095 14.662 5.066 7.002-.021 13.409-1.984 18.957-6.345 3.422-2.68 6.048-6.025 8.527-9.428v-.002c2.43-3.33 5.341-5.974 9.15-7.578v.002c1.99-.837 3.99-1.679 5.95-2.623 3.413-1.64 5.232-4.528 5.515-8.27.237-3.189-.213-6.27-.53-9.302v-.002c-.573-5.377-.23-10.352 3.146-14.777 2.181-2.85 4.16-5.896 5.144-9.532 1.65-6.124 1.506-12.175-2.605-17.486l-.002-.002c-1.149-1.485-2.488-2.486-4.006-2.841a5.7 5.7 0 0 0-2.362-.038zm.236 1.307a4.3 4.3 0 0 1 1.82.027c1.15.27 2.235 1.04 3.257 2.361h.002c3.826 4.942 3.962 10.424 2.373 16.324-.916 3.382-2.778 6.274-4.916 9.069v.002c-3.644 4.776-4.002 10.205-3.415 15.727.322 3.067.753 6.065.53 9.064-.257 3.385-1.717 5.705-4.764 7.168h-.002c-1.92.925-3.898 1.757-5.889 2.594-4.07 1.714-7.18 4.552-9.71 8.021-2.46 3.377-5.014 6.616-8.272 9.166h-.002c-5.311 4.175-11.381 6.042-18.14 6.063h-.001c-5.203.027-9.831-1.305-13.78-4.74-1.966-1.711-3.277-3.905-4.335-6.317v-.002c-2.195-5.015-3.207-9.773.033-14.643v-.002c1.245-1.874 2.59-3.64 4.14-5.22h.003c6.517-6.67 12.643-13.69 18.896-20.56v-.001c5.293-5.823 11.367-10.677 18.375-14.193 6.895-3.461 11.997-8.732 16.623-14.543 1.457-1.828 3.083-3.42 5.115-4.569h.002c.733-.415 1.414-.675 2.057-.797zm-2.332 12.043c-.471.013-.973.137-1.482.351-1.68.706-2.702 2.065-3.473 3.45l-.002.001c-1.664 3.023-4.093 4.69-7.502 5.287-1.486.261-2.9.702-4.297 1.051-5.306 1.335-10.055 3.8-13.842 7.817-5.789 6.128-11.604 12.244-17.186 18.58-3.322 3.778-6.798 7.539-9.59 11.865-2.568 3.976-3.746 8.368-2.443 13.146 1.738 6.362 6.786 9.838 12.982 9.841 6 .086 10.3-1.362 14.232-3.808 4.323-2.69 7.502-6.459 10.086-10.646 3.257-5.282 7.478-9.431 12.867-12.562 4.06-2.365 6.77-5.993 6.99-10.95.162-3.69.073-7.395-.486-11.071-.413-2.735-.698-5.359-.28-7.729.42-2.37 1.504-4.497 3.96-6.398 1.618-1.259 2.314-3.342 2.084-5.482-.119-1.082-.536-1.976-1.317-2.432a2.4 2.4 0 0 0-1.302-.31zm.63 1.463c.304.178.572.589.663 1.423.19 1.77-.374 3.35-1.576 4.286-2.692 2.085-3.99 4.585-4.455 7.218-.466 2.635-.142 5.394.275 8.16.542 3.566.632 7.185.473 10.815-.2 4.51-2.548 7.654-6.328 9.855-5.57 3.237-9.969 7.558-13.332 13.014-2.516 4.076-5.557 7.666-9.654 10.215-3.768 2.344-7.73 3.692-13.51 3.61h-.01c-5.72 0-10.105-2.996-11.707-8.862-1.197-4.387-.154-8.31 2.277-12.072 2.708-4.198 6.133-7.912 9.47-11.707 5.56-6.31 11.364-12.417 17.155-18.547 3.593-3.81 8.084-6.152 13.197-7.437 1.444-.361 2.829-.789 4.203-1.03 3.73-.654 6.602-2.623 8.437-5.955l.002-.002c.709-1.272 1.526-2.32 2.824-2.865.792-.333 1.29-.297 1.594-.12z"/><path d="M244.53 74.836c-.468.014-.966.14-1.47.354-1.672.706-2.683 2.063-3.444 3.447v.002c-1.646 3.02-4.045 4.683-7.412 5.28-1.477.26-2.871.701-4.246 1.05-5.258 1.333-9.948 3.797-13.693 7.81-5.719 6.122-11.484 12.231-16.996 18.563-3.294 3.777-6.718 7.534-9.489 11.854h-.002c-2.538 3.972-3.702 8.36-2.41 13.133 1.727 6.357 6.719 9.836 12.852 9.836h.002c5.936.083 10.184-1.365 14.074-3.808 4.283-2.688 7.421-6.455 9.975-10.64 3.216-5.275 7.398-9.42 12.717-12.546l.002-.002c4.03-2.364 6.698-5.99 6.918-10.938v-.002c.161-3.687.071-7.389-.477-11.061-.412-2.733-.7-5.357-.287-7.725s1.487-4.491 3.918-6.39c1.608-1.26 2.292-3.34 2.063-5.475-.113-1.08-.522-1.972-1.297-2.43a2.37 2.37 0 0 0-1.297-.312zm-.414 2.315c.237-.108.282-.144.564.068.28.212.368.415.397.715s-.045.694-.186 1.091c-.55 1.538-1.581 2.78-2.841 3.895-2.326 2.043-3.131 4.743-3.225 7.623-.083 2.471.296 4.855.533 7.19.33 3.228.634 6.42.496 9.615v.002c-.19 4.657-2.567 7.806-6.443 10.096-5.1 3.017-9.29 6.95-12.428 12.049-2.725 4.422-5.85 8.42-10.281 11.146h-.002c-3.478 2.144-7.259 3.407-9.528 3.449-4.458-.003-7.883-1.129-10.246-3.08-2.364-1.953-3.71-4.743-3.95-8.258v-.002c-.206-2.876.963-5.558 2.595-8.076v-.002c2.72-4.235 6.127-7.998 9.473-11.828 5.528-6.342 11.314-12.49 17.084-18.64 3.393-3.614 7.646-5.845 12.453-7.116 1.73-.458 3.452-.917 5.181-1.291 3.119-.674 5.645-2.303 7.295-5.147v-.002h.002c.634-1.103 1.228-2.152 2.123-2.828l.002-.002h.002c.364-.28.693-.56.93-.668zm-53.225 65.426.088.058q-.045-.028-.088-.058"/><path d="M245.22 75.859a1.93 1.93 0 0 0-.922.154c-.523.234-.898.59-1.197.815-1.196.892-1.862 2.13-2.496 3.209-1.478 2.518-3.622 3.88-6.485 4.492-1.78.38-3.54.843-5.279 1.299-5.042 1.313-9.587 3.652-13.205 7.455-5.82 6.118-11.664 12.245-17.266 18.582-3.364 3.798-6.85 7.588-9.68 11.924-1.718 2.623-3.06 5.578-2.836 8.855v.002c.266 3.795 1.797 6.97 4.479 9.153s6.47 3.365 11.195 3.367h.012c2.728-.049 6.615-1.397 10.295-3.629 4.727-2.872 8.013-7.043 10.809-11.527l.002-.002c3.05-4.88 7.1-8.63 12.09-11.539 4.173-2.434 6.94-6.061 7.16-11.137v-.002c.132-3.299-.173-6.542-.514-9.762-.252-2.37-.614-4.677-.537-6.97.097-2.656.78-4.874 2.801-6.631l.002-.002c1.37-1.195 2.586-2.61 3.248-4.424v-.004l.002-.002c.18-.505.312-1.069.252-1.662a2.35 2.35 0 0 0-.932-1.645h-.002v-.002a1.9 1.9 0 0 0-.996-.367zm-.379 1.371c.24-.108.294-.144.582.068.285.213.37.414.4.71s-.04.685-.181 1.08c-.559 1.527-1.601 2.764-2.871 3.873-2.336 2.032-3.154 4.721-3.258 7.59v.001c-.083 2.461.295 4.831.543 7.157.34 3.208.635 6.385.508 9.566-.201 4.63-2.594 7.763-6.5 10.04-5.15 3.004-9.38 6.916-12.55 11.987h-.001c-2.744 4.402-5.897 8.378-10.37 11.096-3.518 2.135-7.33 3.393-9.622 3.435-4.5-.003-7.96-1.125-10.346-3.068-2.388-1.944-3.746-4.718-3.99-8.213-.196-2.859.981-5.528 2.623-8.033v-.002c2.75-4.214 6.185-7.959 9.56-11.77 5.579-6.311 11.414-12.427 17.234-18.547 3.422-3.597 7.716-5.819 12.574-7.084h.002c1.74-.456 3.48-.912 5.219-1.283 3.137-.67 5.695-2.29 7.357-5.121h-.002c.647-1.1 1.241-2.143 2.145-2.817h.002c.37-.279.7-.558.941-.666zm-16.184 17.633a8.5 8.5 0 0 0-2.933.15c-3.473.776-5.543 3.304-7.024 6.11v.002h-.002c-1.92 3.647-3.93 7.015-7.512 9.082-4.838 2.793-8.774 6.555-12.145 10.916-1.94 2.511-4.384 4.865-5.652 8.09-1.112 2.812-1.074 5.577.045 7.646 1.116 2.065 3.355 3.35 6.248 3.18 5.741.01 9.691-2.687 12.996-6.117 3.51-3.636 5.42-8.18 7.556-12.44v-.001c.757-1.501 1.732-2.806 2.922-4.028v-.002c2.149-2.206 5.091-3.441 7.797-5.285 4.11-2.796 5.655-7.565 3.889-12.207v-.002c-1.093-2.894-3.392-4.735-6.186-5.094zm-.152 1.295c2.33.262 4.142 1.76 5.09 4.271l.002.002c1.572 4.132.276 8.138-3.393 10.633-2.554 1.74-5.61 3-8.002 5.457-1.269 1.302-2.334 2.726-3.158 4.362-2.163 4.313-4.034 8.705-7.324 12.113-3.19 3.31-6.715 5.73-12.06 5.713h-.02l-.022.002c-2.488.152-4.124-.848-5.01-2.487-.886-1.638-.977-3.998.021-6.521v-.002c1.132-2.88 3.448-5.15 5.467-7.764 3.29-4.257 7.095-7.885 11.756-10.576 3.938-2.271 6.087-5.929 8.027-9.615h-.002c1.4-2.652 3.15-4.763 6.137-5.43a7.6 7.6 0 0 1 2.49-.158zM186.84 67.453l-1.672 1.094 1.094 1.674 1.674-1.096zm2.191 3.346-1.673 1.095 1.095 1.674 1.674-1.096zm2.19 3.347-1.674 1.096 1.096 1.674 1.674-1.096zm2.191 3.348-1.674 1.094 1.096 1.674 1.672-1.094zm2.19 3.346-1.674 1.095 1.096 1.674 1.673-1.095zm2.19 3.347-1.673 1.096 1.094 1.674 1.674-1.096zm2.19 3.348-1.673 1.096 1.095 1.672 1.674-1.094zm2.19 3.348-1.674 1.094 1.096 1.674 1.674-1.096zm2.191 3.346-1.673 1.095.728 1.114 1.672-1.096zM180.99 136l-1.291.121.186 1.99 1.29-.119zm-3.281.307-1.992.183.185 1.992 1.99-.185zm-3.985.369-1.99.185.184 1.993 1.992-.186zm-3.982.37-1.992.186.185 1.99 1.993-.185zm-3.982.37-1.993.186.186 1.992 1.992-.186zm-3.983.371-1.992.186.186 1.99 1.99-.186zm-3.984.37-1.99.185.185 1.992 1.99-.186zm-3.983.37-1.99.186.184 1.99 1.992-.184zm-3.982.37-1.992.185.185 1.992 1.993-.185zm-3.982.37-1.993.186.186 1.99 1.992-.183zm-3.983.372-1.992.183.186 1.992 1.99-.185zm-3.984.369-1.99.185.185 1.993 1.99-.186zm-3.983.37-1.99.185.592 1.829 1.584-.023zM103.03 131.25l4 17 4-17zM107 60l4.5 18 4.5-18z"/><path d="m196 118 4 17 4-17zM242 51l4.5 18 4.5-18z"/></g><g class="uchighlight"><path d="m191.84 61.453-1.672 1.094 1.094 1.674 1.674-1.096zm2.191 3.346-1.673 1.095 1.095 1.674 1.674-1.096zm2.19 3.347-1.674 1.096 1.096 1.674 1.674-1.096zm2.191 3.348-1.674 1.094 1.096 1.674 1.672-1.094zm2.19 3.346-1.674 1.095 1.096 1.674 1.673-1.095zm2.19 3.347-1.673 1.096 1.094 1.674 1.674-1.096zm2.19 3.348-1.673 1.096 1.095 1.672 1.674-1.094zm2.19 3.348-1.674 1.094 1.096 1.674 1.674-1.096zm2.191 3.346-1.673 1.095.728 1.114 1.672-1.096zM206.46 71.865l4.5 18 4.5-18zM181.54 42.385l4.5 18 4.5-18z"/></g><g class="ucdraw"><path d="M36.51 30.38v.332h256v-.332zM36.51 56.38v.332h256v-.332zM36.51 82.38v.332h256v-.332zM36.51 108.38v.332h256v-.332zM36.51 134.38v.332h256v-.332zM36.51 160.38v.332h256v-.332z"/></g></svg>
diff --git a/subprojects/docs/src/components/UseCases/uc6.svg.license b/subprojects/docs/src/components/UseCases/uc6.svg.license
new file mode 100644
index 00000000..15aca74d
--- /dev/null
+++ b/subprojects/docs/src/components/UseCases/uc6.svg.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
diff --git a/subprojects/docs/src/components/Video/cover-background.png b/subprojects/docs/src/components/Video/cover-background.png
new file mode 100644
index 00000000..11369ae3
--- /dev/null
+++ b/subprojects/docs/src/components/Video/cover-background.png
Binary files differ
diff --git a/subprojects/docs/src/components/Video/cover-background.png.license b/subprojects/docs/src/components/Video/cover-background.png.license
new file mode 100644
index 00000000..50ad65f2
--- /dev/null
+++ b/subprojects/docs/src/components/Video/cover-background.png.license
@@ -0,0 +1,9 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
4
5Modified image based on "Low Angle Shot of Manufacturing Plant under Blue Sky"
6available under the CC-1.0 license at
7https://www.pexels.com/photo/low-angle-shot-of-manufacturing-plant-under-blue-sky-257700/
8Original image "Bulgaria, Vratsa, Abandoned image. Free for use." by "2427999" also available at
9https://pixabay.com/photos/bulgaria-vratsa-abandoned-industry-1351947/
diff --git a/subprojects/docs/src/components/Video/cover.svg b/subprojects/docs/src/components/Video/cover.svg
new file mode 100644
index 00000000..632175d9
--- /dev/null
+++ b/subprojects/docs/src/components/Video/cover.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720"><path fill="#fff" fill-opacity=".4" d="M0 95h1280v530H0z"/><path class="videocolor" fill-opacity=".702" d="M0 171h1205v378H0z"/><path class="videohighlight" d="M1261 95h19v530h-19z"/><path class="videocolor" d="M0 171h19v378H0z"/><g class="videotitle"><path fill-rule="evenodd" d="M1143 387.09v26.293c-69.4 3.985-192.57 26.695-256 95.615v-31.074c64.343-63.744 180.16-88.046 256-90.834M887 211c55.152 64.14 162.7 91.63 256 98.004V335.3c-93.3 6.374-200.85 33.866-256 98.003v-30.676c22.983-34.262 96.979-66.532 153.51-80.476-56.535-13.944-130.53-46.214-153.51-80.475z"/><text font-size="72" font-weight="800" transform="translate(31 439)">Video</text><text font-size="72" font-weight="800" transform="translate(31 525)">introduction</text></g><path class="videoplay" fill-rule="evenodd" d="M593.12 284.23v151.53l131.07-75.765zm46.879-85.227c88.918 0 161 72.082 161 161s-72.082 161-161 161-161-72.082-161-161 72.082-161 161-161"/></svg>
diff --git a/subprojects/docs/src/components/Video/cover.svg.license b/subprojects/docs/src/components/Video/cover.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/components/Video/cover.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/components/Video/index.module.css b/subprojects/docs/src/components/Video/index.module.css
new file mode 100644
index 00000000..4fbd1b80
--- /dev/null
+++ b/subprojects/docs/src/components/Video/index.module.css
@@ -0,0 +1,79 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7:global(.videocolor) {
8 fill: var(--ifm-color-primary);
9}
10
11:global(.videohighlight) {
12 fill: var(--refinery-highlight);
13}
14
15[data-theme='dark'] :global(.videocolor) {
16 fill: var(--ifm-color-primary-darker);
17}
18
19.video__container {
20 position: relative;
21 width: 100%;
22 height: auto;
23 aspect-ratio: 560/315;
24 box-shadow: var(--ifm-global-shadow-lw);
25 transition: box-shadow var(--ifm-button-transition-duration) var(--ifm-transition-timing-default);
26}
27
28.video__container:hover,
29.video__container:focus-within {
30 box-shadow: var(--ifm-global-shadow-md);
31}
32
33.video,
34.video__button,
35.video__image,
36.video__svg,
37.video__svg > svg {
38 position: absolute;
39 top: 0;
40 left: 0;
41 width: 100%;
42 height: 100%;
43}
44
45.video__svg text {
46 font-family: var(--ifm-font-family-base);
47}
48
49.video__button {
50 margin: 0;
51 padding: 0;
52 border: none;
53 cursor: pointer;
54 background-size: cover;
55}
56
57.video__cover {
58 z-index: 1;
59}
60
61:global(.videoplay) {
62 fill: rgb(255 255 255 / 40%);
63 transition: fill var(--ifm-button-transition-duration) var(--ifm-transition-timing-default);
64}
65
66.video__container:hover :global(.videoplay),
67.video__container:focus-within :global(.videoplay) {
68 fill: rgb(255 255 255 / 70%);
69}
70
71:global(.videotitle) * {
72 fill: #303846;
73 transition: fill var(--ifm-button-transition-duration) var(--ifm-transition-timing-default);
74}
75
76.video__container:hover :global(.videotitle) *,
77.video__container:focus-within :global(.videotitle) * {
78 fill: #21252b;
79}
diff --git a/subprojects/docs/src/components/Video/index.tsx b/subprojects/docs/src/components/Video/index.tsx
new file mode 100644
index 00000000..bd36eaa4
--- /dev/null
+++ b/subprojects/docs/src/components/Video/index.tsx
@@ -0,0 +1,62 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import { useState } from 'react';
8
9import coverBackground from './cover-background.png?sizes[]=1920&sizes[]=1288&sizes[]=1108&&sizes[]=644&sizes[]=322&placeholder=true&rl';
10import Cover from './cover.svg';
11import styles from './index.module.css';
12
13export default function Video() {
14 const [started, setStarted] = useState(false);
15 return (
16 <>
17 <h2 className="sr-only">Check out the intro video</h2>
18 <div className="container">
19 <div className={styles['video__container']}>
20 {started ? (
21 <iframe
22 width="560"
23 height="315"
24 src="https://www.youtube-nocookie.com/embed/Qy_3udNsWsM?autoplay=1"
25 title="YouTube video player"
26 frameBorder="0"
27 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
28 referrerPolicy="strict-origin-when-cross-origin"
29 allowFullScreen
30 className={styles['video']}
31 />
32 ) : (
33 <button
34 type="button"
35 aria-label="Video introduction"
36 title="Play video (requires acceping cookies from YouTube)"
37 onClick={() => setStarted(true)}
38 className={styles['video__button']}
39 style={{
40 backgroundImage: `url("${coverBackground.placeholder}")`,
41 }}
42 >
43 <img
44 alt=""
45 src={coverBackground.src}
46 srcSet={coverBackground.srcSet}
47 width={coverBackground.width}
48 height={coverBackground.height}
49 sizes="(min-width: 1440px) 1288px, (min-width: 1140px) 1108px, calc(100vw - 32px)"
50 loading="lazy"
51 className={styles['video__image']}
52 />
53 <div className={styles['video__svg']}>
54 <Cover />
55 </div>
56 </button>
57 )}
58 </div>
59 </div>
60 </>
61 );
62}
diff --git a/subprojects/docs/src/css/custom.css b/subprojects/docs/src/css/custom.css
new file mode 100644
index 00000000..30384369
--- /dev/null
+++ b/subprojects/docs/src/css/custom.css
@@ -0,0 +1,142 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7@import '@fontsource-variable/open-sans/wdth.css';
8@import '@fontsource-variable/open-sans/wdth-italic.css';
9@import '@fontsource-variable/jetbrains-mono/wght.css';
10@import '@fontsource-variable/jetbrains-mono/wght-italic.css';
11
12@import './sr-only.css';
13
14:root {
15 --ifm-font-family-base: 'Open Sans Variable',
16 'Open Sans',
17 'Roboto',
18 'Helvetica',
19 'Arial',
20 sans-serif;
21 --ifm-font-family-monospace: 'JetBrains Mono Variable',
22 'JetBrains Mono',
23 'Cascadia Code',
24 'Fira Code',
25 monospace;
26 --ifm-code-font-size: 95%;
27 --ifm-background-surface-color: #f5f5f5;
28 --refinery-outer-border-color: rgb(0 0 0 / 0.21);
29 --ifm-font-color-base: #19202b;
30 --ifm-color-content-secondary: #696c77;
31 --ifm-color-primary: #038a99;
32 --ifm-color-primary-dark: #037c8a;
33 --ifm-color-primary-darker: #037582;
34 --ifm-color-primary-darkest: #02616b;
35 --ifm-color-primary-light: #0398a8;
36 --ifm-color-primary-lighter: #039fb0;
37 --ifm-color-primary-lightest: #04b3c7;
38 --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
39 --ifm-blockquote-color: var(--ifm-color-content-secondary);
40 --ifm-card-background-color: #fff;
41 /* elevation=4 shadow from Material UI. */
42 --ifm-global-shadow-md: 0px 2px 4px -1px rgba(0,0,0,0.2),
43 0px 4px 5px 0px rgba(0,0,0,0.14),
44 0px 1px 10px 0px rgba(0,0,0,0.12) !important;
45 /* elevation=8 shadow from Material UI. */
46 --ifm-global-shadow-tl: 0px 5px 5px -3px rgba(0,0,0,0.2),
47 0px 8px 10px 1px rgba(0,0,0,0.14),
48 0px 3px 14px 2px rgba(0,0,0,0.12) !important;
49 --refinery-highlight: #ca1243;
50}
51
52[data-theme='dark'] {
53 --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
54 --ifm-background-color: #21252b !important;
55 --ifm-background-surface-color: #282c34 !important;
56 --refinery-outer-border-color: transparent;
57 --ifm-font-color-base: #ebebff !important;
58 --ifm-color-content-secondary: #abb2bf !important;
59 --ifm-color-primary: #56b6c2;
60 --ifm-color-primary-dark: #43acb9;
61 --ifm-color-primary-darker: #3fa2af;
62 --ifm-color-primary-darkest: #348690;
63 --ifm-color-primary-light: #6bbfc9;
64 --ifm-color-primary-lighter: #75c3cd;
65 --ifm-color-primary-lightest: #94d1d8;
66 --ifm-card-background-color: var(--ifm-background-surface-color) !important;
67 --refinery-highlight: #e06c75;
68}
69
70code {
71 font-feature-settings: 'liga', 'calt';
72}
73
74.navbar {
75 --ifm-navbar-background-color: var(--ifm-background-surface-color) !important;
76 --ifm-navbar-shadow: 0 1px var(--refinery-outer-border-color) !important;
77}
78
79[data-theme='dark'] .navbar {
80 --ifm-navbar-shadow: var(--ifm-global-shadow-lw) !important;
81}
82
83.button, .navbar__link, .footer__link-item {
84 text-transform: uppercase;
85 font-variation-settings: 'wdth' 87.5;
86}
87
88.button--play::before {
89 content: '▶';
90 display: inline-block;
91 transform: translatey(-0.1em);
92 padding-right: 1ch;
93}
94
95.button, .navbar__link {
96 font-weight: 600;
97}
98
99.navbar__link--try-now {
100 color: var(--ifm-color-primary);
101 background: rgb(3 138 153 / 12%);
102 transition: background var(--ifm-button-transition-duration) var(--ifm-transition-timing-default);
103}
104
105.navbar__inner .navbar__link--try-now {
106 margin: 0 0.75rem 0 0.5rem;
107 padding: 0.25rem 1.25rem;
108 border-radius: 50em;
109}
110
111.navbar__link--try-now:hover,
112.navbar__link--try-now:active {
113 color: var(--ifm-color-primary);
114 background: rgb(3 138 153 / 16%);
115}
116
117[data-theme="dark"] .navbar__link--try-now {
118 background: rgb(86 182 194 / 16%);
119}
120
121[data-theme="dark"] .navbar__link--try-now:hover,
122[data-theme="dark"] .navbar__link--try-now:active {
123 background: rgb(86 182 194 / 24%);
124}
125
126.menu {
127 --ifm-menu-color: var(--ifm-color-content-secondary) !important;
128}
129
130.footer {
131 --ifm-footer-background-color: var(--ifm-background-surface-color) !important;
132 --ifm-footer-link-color: var(--ifm-color-content-secondary) !important;
133 box-shadow: 0 -1px var(--refinery-outer-border-color);
134}
135
136.hero__title {
137 font-weight: 800;
138}
139
140.markdown svg {
141 max-width: 100%;
142}
diff --git a/subprojects/docs/src/css/sr-only.css b/subprojects/docs/src/css/sr-only.css
new file mode 100644
index 00000000..dbd65fd3
--- /dev/null
+++ b/subprojects/docs/src/css/sr-only.css
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2010–2021 Kitty Giraudel
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * This snippet was copied from
7 * https://kittygiraudel.com/snippets/sr-only-class/
8 */
9
10.sr-only {
11 border: 0 !important;
12 clip: rect(1px, 1px, 1px, 1px) !important;
13 -webkit-clip-path: inset(50%) !important;
14 clip-path: inset(50%) !important;
15 height: 1px !important;
16 overflow: hidden !important;
17 margin: -1px !important;
18 padding: 0 !important;
19 position: absolute !important;
20 width: 1px !important;
21 white-space: nowrap !important;
22}
diff --git a/subprojects/docs/src/develop/contributing/commands.md b/subprojects/docs/src/develop/contributing/commands.md
new file mode 100644
index 00000000..abfea704
--- /dev/null
+++ b/subprojects/docs/src/develop/contributing/commands.md
@@ -0,0 +1,172 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 1
5title: Build commands
6---
7
8# Building from the command line
9
10## Gradle commands
11
12We use [Gradle](https://gradle.org/) to manage the compilation and tests of Refinery.
13
14Java code is built directly by Gradle.
15We use the [frontend-gradle-plugin](https://siouan.github.io/frontend-gradle-plugin/) to manage a [Node.js](https://nodejs.org/en) and [Yarn](https://yarnpkg.com/) installation, which in turn is used to build TypeScript code (including this documentation website).
16Typically, Yarn commands are issued by Gradle and you don't need to work with the TypeScript build system directly if you're only working on the Java parts of Refinery.
17
18### `build`
19
20```bash posix2windows
21./gradlew build
22```
23
24Compile all code, run all tests, and produce all build artifacts.
25
26You should run this command before submitting a [Pull request](https://github.com/graphs4value/refinery/pulls) to make sure that all code builds and tests pass on your local machine.
27This will also be run by GitHub Actions for each commit or pull requests.
28
29### `publishToMavenLocal`
30
31
32```bash posix2windows
33./gradlew publishToMavenLocal
34```
35
36Publishes the Refinery Java artifacts to the [Maven local repository](https://www.baeldung.com/maven-local-repository).
37
38Build tools, such as Gradle, will be able to consume such artifacts, which enables you to use the latest version of Refinery -- possibly including your own modification -- in other Java projects.
39
40For example, in Gradle, you may set
41
42```kotlin title="build.gradle.kts"
43repositories {
44 mavenLocal()
45}
46
47dependencies {
48 implementation("tools.refinery:refinery-generator:0.0.0-SNAPSHOT")
49}
50```
51
52to add a dependency on Refinery to your Java project.
53
54### `serve`
55
56```bash posix2windows
57./gradlew serve
58```
59
60Starts the Refinery backend and web interface on port 1312.
61
62This task is ideal for running the Refinery backend if you don't intend to work on the frontend.
63The Refinery frontend TypeScript projects is automatically built before the server starts.
64The server will use the latest build output of the frontend as static assets.
65
66The behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker).
67However, the default value of `REFINERY_LISTEN_PORT` is `1312`.
68
69### `serveBackend`
70
71```bash posix2windows
72./gradlew serveBackend
73```
74
75Starts the Refinery backend on port 1312.
76
77This task is ideal for running the Refinery backend if you're working on the frontend.
78No static assets will be build.
79You'll need to use [`yarnw frontend dev`](#frontend-dev)
80
81Like [`gradlew serve`](#serve), the behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker).
82However, the default value of `REFINERY_LISTEN_PORT` is `1312`.
83
84## Yarn commands
85
86We provide a `yarnw` wrapper script to invoke the Yarn distribution installed by frontend-gradle-plugin directly.
87The following commands can only be run once [`gradlew build`](#build) has installed the necessary Node.js and Yarn packages.
88
89### `docs dev`
90
91```bash posix2windows
92./yarn docs dev
93```
94
95Builds and serves this documentation in development mode on port 3000.
96Saved changes to most documentation sources are immediately reflected in the browse without reloading.
97
98You can set the port with the `-p` option, e.g. to use port 1313, use
99
100```bash posix2windows
101./yarn docs dev -p 1313
102```
103
104:::note
105
106Running this command for the first time may generate error messages like
107```
108ERROR failed to read input source map: failed to parse inline source map url
109```
110which can be safely ignored.
111
112:::
113
114### `frontend dev`
115
116```bash posix2windows
117./yarn frontend dev
118```
119
120Builds and serves the refinery frontend on port 1313.
121Saved changes to most source files are immediately reflected in the browser without reload.
122
123Before running this command, you need to start [`gradlew serveBackend`](#servebackend) to provide a backend for the frontend to connect to.
124The development server of the frontend will proxy all WebSocket connections to the backend.
125
126The following environmental variables influence the behavior of this command:
127
128#### `REFINERY_LISTEN_HOST`
129
130Hostname to listen at for incoming HTTP connections.
131
132**Default value:** `localhost`
133
134#### `REFINERY_LISTEN_PORT`
135
136TCP port to listen at for incoming HTTP connections.
137
138**Default value:** `1313`
139
140#### `REFINERY_BACKEND_HOST`
141
142Hostname of the Refinery backend.
143
144This should match the `REFINERY_LISTEN_HOST` passed to [`gradlew serveBackend`](#servebackend).
145
146**Default value:** `127.0.0.1` (connect to `localhost` over IPv4 only)
147
148#### `REFINERY_LISTEN_PORT`
149
150TCP port of the Refinery backend.
151
152This should match the `REFINERY_LISTEN_PORT` passed to [`gradlew serveBackend`](#servebackend).
153
154**Default value:** `1312`
155
156#### `REFINERY_PUBLIC_HOST`
157
158Publicly visible hostname of the Refinery instance.
159
160If you use a reverse proxy in front of the development server, you must set this variable.
161Otherwise, connections to the development server will fail due to cross-origin protection.
162
163**Default value:** equal to `REFINERY_LISTEN_HOST`
164
165#### `REFINERY_PUBLIC_PORT`
166
167Publicly visible port of the Refinery instance.
168
169If you use a reverse proxy in front of the development server, you must set this variable.
170Otherwise, connections to the development server will fail due to cross-origin protection.
171
172**Default value:** equal to `REFINERY_LISTEN_PORT`
diff --git a/subprojects/docs/src/develop/contributing/ide-setup.md b/subprojects/docs/src/develop/contributing/ide-setup.md
new file mode 100644
index 00000000..742035e0
--- /dev/null
+++ b/subprojects/docs/src/develop/contributing/ide-setup.md
@@ -0,0 +1,94 @@
1---
2SPDX-FileCopyrightText: 2021-2023 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 2
5title: IDE setup
6---
7
8# Setting up the development environment
9
10## IntelliJ IDEA
11
12We prefer [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a Java development environment.
13No special preparations should be necessary for importing the project as a Gradle project into IDEA:
14
151. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later).
16
172. Clone the project git repository and open it in IntelliJ IDEA. Make sure to _open_ the project instead of creating a _new_ one in the same directory.
18
193. IntelliJ IDEA should build and index the project. If there are errors, it is likely that the `JAVA_HOME` was incorrectly set:
20 * In _Project Structure > Project settings > Project > SDK_, a Java 21 compatible JDK should be selected.
21 * In _Project Structure > Project settings > Project > Language level_, either _SDK default_ or _21_ should be selected.
22 * Make sure that each module in _Project Structure > Project settings > Module_ uses the _Project default_ language level in _Sources > Language level_ and the _Project SDK_ in _Dependencies > Module SDK._
23 * In _Settings > Gradle settings > Gralde Projects > Gradle_, the _Distribution_ should be set to _Wrapper_ and the _Gradle JVM_ should be set to _Project SDK._
24
254. We recommend installing the latest _SonarLint_ plugin in _Settings > Plugins_ to get real-time code quality analysis in your IDE.
26
27:::note
28
29You'll need [Eclipse](#eclipse) to edit Xtext (`*.xtext`) and MWE2 (`*.mwe2`) files and Ecore class diagrams (`*.aird`, `*.ecore`, `*.genmodel`).
30If you do not plan on making changes to such files, feel free to skip the Eclipse installation steps below.
31
32You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery.
33
34:::
35
36## Eclipse
37
381. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version.
39
402. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package.
41
423. Launch Eclipse and create a new workspace.
43
444. Open _Help > Eclipse Marketplace_ and install the following software:
45 * _EclEmma Java Code Coverage_
46 * _EcoreTools : Ecore Diagram Editor_
47 * _Sirius_ (ignore the warning during installation about the solution _Sirius_ not being available)
48 * _SonarLint_
49
505. Open _Window > Preferences_ and set the following preferences:
51 * _General > Workspace > Text file encoding_ should be _UTF-8_.
52 * _General > Workspace > New text file line delimiter_ should be _Unix_.
53 * Add the JDK 21 to _Java > Installed JREs_.
54 * Make sure JDK 21 is selected for _JavaSE-21_ at _Java > Installed JREs > Execution Environments_.
55 * Set _Gradle > Java home_ to the `JAVA_HOME` directory (the directory which contains the `bin` directory) of JDK 21. Here, Buildship will show a yellow warning sign, which can be safely ignored.
56 * Set _Java > Compiler > JDK Compliance > Compiler compliance level_ to _21_.
57
586. Clone the project Git repository but _do not_ import it into Eclipse yet.
59
607. Open a new terminal and run
61 ```bash posix2windows
62 ./gradlew prepareEclipse
63 ```
64 in the cloned repository.
65 * This should complete without any compilation errors.
66 * To troubleshoot any error, see the [instructions about compiling Refinery](/develop/contributing#compiling).
67
688. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse.
69 * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_.
70 * If you have previously imported the project into Eclipse, this step will likely fail. In that case, you should remove the projects from Eclipse, run `git clean -fxd` in the repository, and start over from step 8.
71
72## VS Code
73
74We recommend [VSCodium](https://github.com/VSCodium/vscodium) or [Visual Studio Code](https://code.visualstudio.com/) to work with the parts of Refinery that are written is TypeScript.
75
761. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later).
77
782. Install the following VS Code extensions:
79 * _EditorConfig for VS Code_ [[Open VSX](https://open-vsx.org/extension/EditorConfig/EditorConfig)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)]
80 * _ZipFS - a zip file system_ [[Open VSX](https://open-vsx.org/extension/arcanis/vscode-zipfs)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)]
81 * _ESLint_ [[Open VSX](https://open-vsx.org/extension/dbaeumer/vscode-eslint)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)]
82 * _XState VSCode_ [[Open VSX](https://open-vsx.org/extension/statelyai/stately-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)]
83
843. Clone the project Git repository but _do not_ import it into VS Code yet.
85
864. Run
87 ```bash posix2windows
88 ./gradlew installFrontend
89 ```
90 to install all required Node.js tooling.
91
925. Open the repository with _Open Folder&hellip;_ in VS Code.
93 * When asked, select that you _Trust_ the folder.
94 * When asked, enable using the TypeScript and ESLint tooling specified in the repository.
diff --git a/subprojects/docs/src/develop/contributing/index.md b/subprojects/docs/src/develop/contributing/index.md
new file mode 100644
index 00000000..aa0bdb2f
--- /dev/null
+++ b/subprojects/docs/src/develop/contributing/index.md
@@ -0,0 +1,59 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 1
5title: Contributing
6---
7
8import TabItem from '@theme/TabItem';
9import Tabs from '@theme/Tabs';
10
11# Contributing to Refinery
12
13You can clone the refinery repository from GitHub at https://github.com/graphs4value/refinery.
14If you want to contribute code, we recommend [forking](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the repository on GitHub so that you can submit a [pull request](https://github.com/graphs4value/refinery/pulls) later.
15
16## Required tools
17
18Refinery is written in Java and TypeScript. To build Refinery, you'll need a **Java 21** compatible **Java Development Kit (JDK).** We recommend the [Adoptium Java 21 JDK](https://adoptium.net/) or the [Amazon Corretto Java 21 JDK](https://aws.amazon.com/corretto/).
19
20## Compiling Refinery {#compiling}
21
22To build Refinery, run the command
23```bash posix2windows
24./gradlew build
25```
26in the cloned repository.
27
28This should complete without any compilation errors.
29
30If you get any errors about the JVM version, check whether the `JAVA_HOME` environment variable is set to the location of **JDK 21**. You can query the variable with
31<Tabs groupId="posix2windows">
32 <TabItem value="posix" label="Linux or macOS">
33 ```bash
34 echo $JAVA_HOME
35 ```
36 </TabItem>
37 <TabItem value="windows" label="Windows (PowerShell)">
38 ```bash
39 echo $Env:JAVA_HOME
40 ```
41 </TabItem>
42</Tabs>
43To set the `JAVA_HOME` environmental variable, use
44<Tabs groupId="posix2windows">
45 <TabItem value="posix" label="Linux or macOS">
46 ```bash
47 export JAVA_HOME=/java/path/here
48 ```
49 </TabItem>
50 <TabItem value="windows" label="Windows (PowerShell)">
51 ```bash
52 $Env:JAVA_HOME="C:\java\path\here"
53 ```
54 </TabItem>
55</Tabs>
56
57If the build fails with a `Host name must not be empty` error, you [might need to remove the empty proxy configuration from your global `gradle.properties` file](https://stackoverflow.com/a/62128323).
58
59For further information, see the [supported build commands](/develop/contributing/commands) and the [instructions for setting up an IDE](/develop/contributing/ide-setup).
diff --git a/subprojects/docs/src/develop/index.md b/subprojects/docs/src/develop/index.md
new file mode 100644
index 00000000..4537889e
--- /dev/null
+++ b/subprojects/docs/src/develop/index.md
@@ -0,0 +1,13 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 0
5---
6
7# Programming guide
8
9:::warning
10
11Under construction
12
13:::
diff --git a/subprojects/docs/src/develop/javadoc.md b/subprojects/docs/src/develop/javadoc.md
new file mode 100644
index 00000000..225e9de0
--- /dev/null
+++ b/subprojects/docs/src/develop/javadoc.md
@@ -0,0 +1,42 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4description: API documentation for Refinery components automatically generated by Javadoc
5sidebar_position: 999
6---
7
8# Javadoc
9
10Here you can find API documentation for Refinery components automatically generated by Javadoc. We recommend reading the [Programming guide](/develop) first to understand how to use these components.
11
12# Refinery
13
14* [`tools.refinery:refinery-generator`](pathname://refinery-generator)
15* [`tools.refinery:refinery-generator-cli`](pathname://refinery-generator-cli)
16* [`tools.refinery:refinery-language`](pathname://refinery-language)
17* [`tools.refinery:refinery-language-ide`](pathname://refinery-language-ide)
18* [`tools.refinery:refinery-language-model`](pathname://refinery-language-model)
19* [`tools.refinery:refinery-language-semantics`](pathname://refinery-language-semantics)
20* [`tools.refinery:refinery-language-web`](pathname://refinery-language-web)
21* [`tools.refinery:refinery-logic`](pathname://refinery-logic)
22* [`tools.refinery:refinery-store`](pathname://refinery-store)
23* [`tools.refinery:refinery-store-dse`](pathname://refinery-store-dse)
24* [`tools.refinery:refinery-store-dse-visualization`](pathname://refinery-store-dse-visualization)
25* [`tools.refinery:refinery-store-query`](pathname://refinery-store-query)
26* [`tools.refinery:refinery-store-query-interpreter`](pathname://refinery-store-query-interpreter)
27* [`tools.refinery:refinery-store-reasoning`](pathname://refinery-store-reasoning)
28* [`tools.refinery:refinery-store-reasoning-scope`](pathname://refinery-store-reasoning-scope)
29* [`tools.refinery:refinery-store-reasoning-smt`](pathname://refinery-store-reasoning-smt)
30
31# Interpreter
32
33:::note
34
35The _Refinery Interpreter_ is modified version of [VIATRA&trade;](https://eclipse.dev/viatra/) specifically for use in Refinery. If you're interested in learning about [VIATRA&trade;](https://eclipse.dev/viatra/), we recommend the [VIATRA&trade; documentation](https://eclipse.dev/viatra/documentation/index.html) and [source code](https://github.com/eclipse-viatra/org.eclipse.viatra) instead. Eclipse&reg;, VIATRA&trade; and &lsquo;Eclipse VIATRA&trade;&rsquo; are trademarks of Eclipse Foundation, Inc.
36
37:::
38
39* [`tools.refinery.interpreter:refinery-interpreter`](pathname://refinery-interpreter)
40* [`tools.refinery.interpreter:refinery-interpreter-localsearch`](pathname://refinery-interpreter-localsearch)
41* [`tools.refinery.interpreter:refinery-interpreter-rete`](pathname://refinery-interpreter-rete)
42* [`tools.refinery.interpreter:refinery-interpreter-rete-recipes`](pathname://refinery-interpreter-rete-recipes)
diff --git a/subprojects/docs/src/learn/docker.md b/subprojects/docs/src/learn/docker.md
new file mode 100644
index 00000000..0df87da8
--- /dev/null
+++ b/subprojects/docs/src/learn/docker.md
@@ -0,0 +1,175 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 100
5sidebar_label: Docker
6---
7
8# Running in Docker
9
10:::note
11
12Refinery can run as a cloud-based _Graph Solver as a Service_ without local installation.
13If you're just looking to try Refinery, our [online demo](https://refinery.services/) provides a seamless experience without installation.
14
15:::
16
17:::info
18
19Installing Refinery as a Docker container can support more advanced use cases, such as when generating models with more resources or a longer timeout.
20
21:::
22
23To generate larger models with a longer timeout, you can use our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery) on either `amd64` or `arm64` machines:
24
25```shell
26docker run --rm -it -p 8888:8888 ghcr.io/graphs4value/refinery
27```
28
29Once Docker pulls and starts the container, you can navigate to http://localhost:8888 to open the model generation interface and start editing.
30
31Alternatively, you can follow the [instructions to set up a local development environment](/develop/contributing) and compile and run Refinery from source.
32
33## Updating
34
35To take advantage of the latest updates, you can simply re-pull our Docker container from the GitHub Container Registry:
36
37```shell
38docker pull ghcr.io/graphs4value/refinery
39```
40
41Restart the container to make sure that you're running the last pulled version.
42
43## Environmental variables
44
45The Docker container supports the following environmental variables to customize its behavior.
46Customizing these variable should only be needed if you want to _increase resource limits_ or _expose you Refinery instance over the network_ for others.
47
48Notes for **local-only instances** are highlighted with the :arrow_right: arrow emoji.
49
50Important security notices for **public instances** are highlighted with the :warning: warning emoji.
51
52### Networking
53
54#### `REFINERY_LISTEN_HOST`
55
56Hostname to listen at for incoming HTTP connections.
57
58**Default value:** `0.0.0.0` (accepts connections on any IP address)
59
60#### `REFINERY_LISTEN_PORT`
61
62TCP port to listen at for incoming HTTP connections.
63
64Refinery doesn't support HTTPS connections out of the box, so there's no point in setting this to `443`. Use a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) instead if you wish to expose Refinery to encrypted connections.
65
66If you change this value, don't forget to adjust the `-p 8888:8888` option of the `docker run` command to [expose](https://docs.docker.com/reference/cli/docker/container/run/#publish) the selected port.
67
68**Default value:** `8888`
69
70#### `REFINERY_PUBLIC_HOST`
71
72Publicly visible hostname of the Refinery instance.
73
74:arrow_right: For installations only accessed locally (i.e., `localhost:8888`) without any reverse proxy, you can safely leave this empty.
75
76:warning: You should set this to the publicly visible hostname of your Refinery instance if you wish to expose Refinery over the network. Most likely, this will be the hostname of a reverse proxy that terminates TLS connections. Our online demo sets this to [refinery.services](https://refinery.services/).
77
78**Default value:** _empty_
79
80#### `REFINERY_PUBLIC_PORT`
81
82Publicly visible port of the Refinery instance.
83
84:arrow_right: For installations only accessed locally (i.e., `localhost:8888`), this value is ignored because `REFINERY_PUBLC_HOST` is not set.
85
86**Default value:** `443`
87
88#### `REFINERY_ALLOWED_ORIGINS`
89
90Comma-separated list of allowed origins for incoming WebSocket connections. If this variable is empty, all incoming WebSocket connections are accepted.
91
92:arrow_right: For installations only accessed locally (i.e., `localhost:8888`) without any reverse proxy, you can safely leave this empty.
93
94:warning: The value inferred from `REFINERY_PUBLIC_HOST` and `REFINERY_PUBLIC_PORT` should be suitable for instances exposed over the network. For security reasons, public instances should never leave this empty.
95
96**Default value:** equal to `REFINERY_PUBLIC_HOST:REFINERY_PUBLIC_PORT` if they are both set, _empty_ otherwise
97
98### Timeouts
99
100#### `REFINERY_SEMANTICS_TIMEOUT_MS`
101
102Timeout for partial model semantics calculation in milliseconds.
103
104:arrow_right: Increase this if you have a slower machine and the editor times out before showing a preview of your partial model in the _Graph_ or _Table_ views.
105
106:warning: Increasing this timeout may increase server load. Excessively large timeout may allow users to overload you server by entering extremely complex partial models.
107
108**Default value:** `1000`
109
110#### `REFINERY_SEMANTICS_WARMUP_TIMEOUT_MS`
111
112Timeout for partial model semantics calculation in milliseconds when the server first start.
113
114Due to various initialization tasks, the first partial model semantics generation may take longer the `REFINERY_SEMANTICS_TIMEOUT_MS` and display a timeout error. This setting increases the timeout for the first generation, leading to seamless use even after server start (especially in auto-scaling setups).
115
116**Default value:** equal to 2 &times; `REFINERY_SEMANTICS_TIMEOUT`
117
118#### `REFINERY_MODEL_GENERATION_TIMEOUT_SEC`
119
120Timeout for model generation in seconds.
121
122:arrow_right: Adjust this value if you're generating very large models (> 10000 nodes) and need more time to complete a generation. Note that some _unsatisfiable_ model generation problems cannot be detected by Refinery and will result in model generation running for an arbitrarily long time without producing any solution.
123
124:warning: Long running model generation will block a [_model generation thread_](#refinery_model_generation_thread_count). Try to balance the number of threads and the timeout to avoid exhausting system resources, but keep the wait time for a free model generation thread for users reasonably short. Auto-scaling to multiple instances may help with bursty demand.
125
126**Default value:** `600` (10 minutes)
127
128### Threading
129
130:warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation.
131
132#### `REFINERY_XTEXT_THREAD_COUNT`
133
134Number of threads used for non-blocking text editing operations. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread.
135
136:warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation.
137
138**Default value:** `1`
139
140#### `REFINERY_XTEXT_LOCKING_THREAD_COUNT`
141
142Number of threads used for text editing operations that lock the document. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread.
143
144
145**Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT`
146
147#### `REFINERY_XTEXT_SEMANTICS_THREAD_COUNT`
148
149Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread.
150
151Must be at least as large as `REFINERY_XTEXT_THREAD_COUNT`.
152
153:warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation.
154
155**Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT`
156
157#### `REFINERY_MODEL_GENERATION_THREAD_COUNT`
158
159Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread.
160
161:warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. Each model generation task may also demand a large amount of memory in addition to CPU time.
162
163**Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT`
164
165### Libraries
166
167#### `REFINERY_LIBRARY_PATH`
168
169Modules (`.refinery` files) in this directory or colon-separated list of directories will be exposed to user via Refinery's `import` mechanism.
170
171:arrow_right: Use this in conjunction with the [mount volume (-v)](https://docs.docker.com/reference/cli/docker/container/run/#volume) option of `docker run` to work with multi-file projects in Refinery.
172
173:warning: Make sure you only expose files that you want to make public. It's best to expose a directory that contains nothing other that `.refinery` files to minimize potential information leaks.
174
175**Default value:** _empty_ (no directories are exposed)
diff --git a/subprojects/docs/src/learn/index.md b/subprojects/docs/src/learn/index.md
new file mode 100644
index 00000000..bb28df57
--- /dev/null
+++ b/subprojects/docs/src/learn/index.md
@@ -0,0 +1,11 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4sidebar_position: 0
5---
6
7# Introduction
8
9Various software and systems engineering scenarios rely on the systematic construction of consistent graph models. However, **automatically generating a diverse set of consistent graph models** for complex domain specifications is challenging. First, the graph generation problem must be specified with mathematical precision. Moreover, graph generation is a computationally complex task, which necessitates specialized logic solvers.
10
11**Refinery is a novel open-source software framework** to automatically synthesize a diverse set of consistent domain-specific graph models. The framework offers an expressive high-level specification language using partial models to succinctly formulate a wide range of graph generation challenges. It also provides a modern cloud-based architecture for a scalable _Graph Solver as a Service,_ which uses logic reasoning rules to efficiently synthesize a diverse set of solutions to graph generation problems by partial model refinement. Applications include system-level architecture synthesis, test generation for modeling tools or traffic scenario synthesis for autonomous vehicles.
diff --git a/subprojects/docs/src/learn/language/_category_.yml b/subprojects/docs/src/learn/language/_category_.yml
new file mode 100644
index 00000000..f5a6f896
--- /dev/null
+++ b/subprojects/docs/src/learn/language/_category_.yml
@@ -0,0 +1,10 @@
1# SPDX-FileCopyrightText: 2024 The Refinery Authors
2#
3# SPDX-License-Identifier: EPL-2.0
4
5position: 2
6label: Language reference
7link:
8 type: generated-index
9 slug: /language
10 description: Learn more about the Refinery partial modeling language!
diff --git a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg b/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg
new file mode 100644
index 00000000..197f4b48
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg
@@ -0,0 +1,227 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="395pt" height="226pt" viewBox="-6 -6 407.4700012207031 238.39999389648438" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-sdYDAL2PsHukjJUpNyUhU"><style>.refinery-sdYDAL2PsHukjJUpNyUhU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-outline{stroke:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-header{fill:rgb(53, 161, 173);}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-bg{fill:#fff;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-h .node-header{fill:#e06c75;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-i .node-header{fill:#98c379;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-j .node-header{fill:#c678dd;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-n .node-header{fill:#abcc94;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-line{stroke:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-arrow{fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN text{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR text{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-TRUE{fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-UNKNOWN{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-ERROR{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU text.label-UNKNOWN{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU 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-sdYDAL2PsHukjJUpNyUhU-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-sdYDAL2PsHukjJUpNyUhU-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-sdYDAL2PsHukjJUpNyUhU-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 class="graph" transform="scale(1 1) rotate(0) translate(4 222.4)">
4<!-- n3 -->
5<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
6
7<rect stroke="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="151" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-0)" class="node-header"/>
9<text text-anchor="start" x="181.53" y="-117.8" font-size="12.00">v1</text>
10<use x="161.952" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
11<g><text text-anchor="start" x="177.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
12</g>
13<polyline points="155.95,-110.2 219.98,-110.2" class="node-outline"/>
14<rect fill="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-0"><rect stroke="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n4 -->
17<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
18
19<rect stroke="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="109" y="-222" width="74" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-1)" class="node-header"/>
21<text text-anchor="start" x="141.09" y="-202.6" font-size="12.00">r1</text>
22<use x="119.78" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
23<g><text text-anchor="start" x="135.78" y="-179.2" font-size="12.00" class="label label-TRUE">Region</text>
24</g>
25<polyline points="113.78,-195 180.15,-195" class="node-outline"/>
26<rect fill="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-outline"/>
27<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-1"><rect stroke="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n3&#45;&gt;n4 -->
29<g class="edge edge-TRUE">
30
31<path fill="none" d="M182.6,-133.43C179.13,-141.63 174.65,-151.02 170,-159.88" class="edge-line"/>
32<polygon points="166.95,-158.15 165.24,-168.61 173.1,-161.5 166.95,-158.15" class="edge-line edge-arrow"/>
33<text text-anchor="middle" x="158.39" y="-155.04" font-size="10.50">region</text>
34</g>
35<!-- n8 -->
36<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
37
38<rect stroke="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-bg"/>
39<rect stroke="none" x="49" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-2)" class="node-header"/>
40<text text-anchor="start" x="90.42" y="-33" font-size="12.00">t1</text>
41<use x="59.8559" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
42<g><text text-anchor="start" x="75.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
43</g>
44<polyline points="53.86,-25.4 138.08,-25.4" class="node-outline"/>
45<rect fill="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-outline"/>
46<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-2"><rect stroke="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
47<!-- n3&#45;&gt;n8 -->
48<g class="edge edge-TRUE">
49
50<path fill="none" stroke-width="2" d="M155.95,-84.92C145.63,-76.18 134.29,-66.11 124.25,-56.77" class="edge-line"/>
51<polygon stroke-width="2" points="126.48,-54.66 118.02,-50.88 122.27,-59.11 126.48,-54.66" class="edge-line edge-arrow"/>
52<text text-anchor="start" x="35.36" y="-71.18" font-weight="bold" font-size="10.50">outgoingTransition</text>
53</g>
54<!-- n3&#45;&gt;n8 -->
55
56<!-- n9 -->
57<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
58
59<rect stroke="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/>
60<rect stroke="none" x="151" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-3)" class="node-header"/>
61<text text-anchor="start" x="192.42" y="-33" font-size="12.00">t2</text>
62<use x="161.856" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
63<g><text text-anchor="start" x="177.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
64</g>
65<polyline points="155.86,-25.4 240.08,-25.4" class="node-outline"/>
66<rect fill="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/>
67<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-3"><rect stroke="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
68<!-- n3&#45;&gt;n9 -->
69<g class="edge edge-TRUE">
70
71<path fill="none" stroke-width="2" d="M172.38,-85.14C171.61,-77.31 172.15,-68.38 173.81,-59.86" class="edge-line"/>
72<polygon stroke-width="2" points="176.73,-60.8 175.99,-51.56 170.81,-59.24 176.73,-60.8" class="edge-line edge-arrow"/>
73<text text-anchor="start" x="72.62" y="-58.26" font-weight="bold" font-size="10.50">outgoingTransition</text>
74</g><g class="edge edge-TRUE">
75
76<path fill="none" d="M184.63,-85.14C185,-77.31 185.9,-68.38 187.14,-59.86" class="edge-line"/>
77<polygon points="190.57,-60.56 188.77,-50.12 183.67,-59.4 190.57,-60.56" class="edge-line edge-arrow"/>
78<text text-anchor="middle" x="233.31" y="-70.26" font-size="10.50">incomingTransition</text>
79</g>
80<!-- n3&#45;&gt;n9 -->
81
82<!-- n10 -->
83<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
84
85<rect stroke="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/>
86<rect stroke="none" x="253" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-4)" class="node-header"/>
87<text text-anchor="start" x="294.42" y="-33" font-size="12.00">t3</text>
88<use x="263.856" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
89<g><text text-anchor="start" x="279.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
90</g>
91<polyline points="257.86,-25.4 342.08,-25.4" class="node-outline"/>
92<rect fill="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/>
93<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-4"><rect stroke="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
94<!-- n3&#45;&gt;n10 -->
95
96<!-- n4&#45;&gt;n3 -->
97<g class="edge edge-TRUE">
98
99<path fill="none" stroke-width="2" d="M152.26,-169.94C155.72,-161.75 160.2,-152.37 164.84,-143.5" class="edge-line"/>
100<polygon stroke-width="2" points="167.39,-145.24 168.88,-136.09 162.01,-142.31 167.39,-145.24" class="edge-line edge-arrow"/>
101<text text-anchor="start" x="119.24" y="-143.04" font-weight="bold" font-size="10.50">vertices</text>
102</g>
103<!-- n5 -->
104<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
105
106<rect stroke="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-bg"/>
107<rect stroke="none" x="69" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-5)" class="node-header"/>
108<text text-anchor="start" x="99.53" y="-117.8" font-size="12.00">v2</text>
109<use x="79.9516" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
110<g><text text-anchor="start" x="95.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
111</g>
112<polyline points="73.95,-110.2 137.98,-110.2" class="node-outline"/>
113<rect fill="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-outline"/>
114<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-5"><rect stroke="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
115<!-- n4&#45;&gt;n5 -->
116<g class="edge edge-TRUE">
117
118<path fill="none" stroke-width="2" d="M129.42,-169.94C124.87,-161.93 120.16,-152.77 116.09,-144.08" class="edge-line"/>
119<polygon stroke-width="2" points="118.93,-142.93 112.57,-136.19 113.34,-145.43 118.93,-142.93" class="edge-line edge-arrow"/>
120<text text-anchor="start" x="78.34" y="-156.05" font-weight="bold" font-size="10.50">vertices</text>
121</g>
122<!-- n5&#45;&gt;n4 -->
123<g class="edge edge-TRUE">
124
125<path fill="none" d="M123.6,-133.43C128.21,-141.54 132.96,-150.81 137.06,-159.59" class="edge-line"/>
126<polygon points="133.79,-160.84 141.05,-168.55 140.18,-157.99 133.79,-160.84" class="edge-line edge-arrow"/>
127<text text-anchor="middle" x="117.57" y="-142.24" font-size="10.50">region</text>
128</g>
129<!-- n5&#45;&gt;n8 -->
130<g class="edge edge-TRUE">
131
132<path fill="none" d="M97.06,-85.14C95.54,-77.31 94.28,-68.38 93.46,-59.86" class="edge-line"/>
133<polygon points="96.97,-59.85 92.75,-50.14 89.99,-60.37 96.97,-59.85" class="edge-line edge-arrow"/>
134<text text-anchor="middle" x="47.13" y="-57.66" font-size="10.50">incomingTransition</text>
135</g>
136<!-- n5&#45;&gt;n9 -->
137
138<!-- n5&#45;&gt;n10 -->
139
140<!-- n6 -->
141<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
142
143<rect stroke="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-bg"/>
144<rect stroke="none" x="263" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-6)" class="node-header"/>
145<text text-anchor="start" x="293.53" y="-117.8" font-size="12.00">v3</text>
146<use x="273.952" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
147<g><text text-anchor="start" x="289.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
148</g>
149<polyline points="267.95,-110.2 331.98,-110.2" class="node-outline"/>
150<rect fill="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-outline"/>
151<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-6"><rect stroke="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE">
152
153<path fill="none" d="M293.84,-85.14C293.27,-77.31 293.09,-68.38 293.3,-59.86" class="edge-line"/>
154<polygon points="296.78,-60.29 293.76,-50.14 289.79,-59.96 296.78,-60.29" class="edge-line edge-arrow"/>
155<text text-anchor="middle" x="340.34" y="-70.26" font-size="10.50">incomingTransition</text>
156</g>
157<!-- n7 -->
158<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
159
160<rect stroke="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
161<rect stroke="none" x="262" y="-222" width="74" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-7)" class="node-header"/>
162<text text-anchor="start" x="294.09" y="-202.6" font-size="12.00">r2</text>
163<use x="272.78" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/>
164<g><text text-anchor="start" x="288.78" y="-179.2" font-size="12.00" class="label label-TRUE">Region</text>
165</g>
166<polyline points="266.78,-195 333.15,-195" class="node-outline"/>
167<rect fill="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-outline"/>
168<clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-7"><rect stroke="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
169<!-- n6&#45;&gt;n7 -->
170<g class="edge edge-TRUE">
171
172<path fill="none" d="M306.1,-133.43C306.67,-141.27 306.84,-150.2 306.63,-158.72" class="edge-line"/>
173<polygon points="303.14,-158.27 306.16,-168.42 310.14,-158.6 303.14,-158.27" class="edge-line edge-arrow"/>
174<text text-anchor="middle" x="291.03" y="-142.02" font-size="10.50">region</text>
175</g>
176<!-- n6&#45;&gt;n8 -->
177
178<!-- n6&#45;&gt;n9 -->
179
180<!-- n6&#45;&gt;n10 -->
181<g class="edge edge-TRUE">
182
183<path fill="none" d="M213.57,-48.63C214.33,-56.56 213.75,-65.61 212.03,-74.21" class="edge-line"/>
184<polygon points="208.7,-73.11 209.51,-83.67 215.47,-74.91 208.7,-73.11" class="edge-line edge-arrow"/>
185<text text-anchor="middle" x="198.62" y="-70.03" font-size="10.50">target</text>
186</g>
187<!-- n6&#45;&gt;n10 -->
188
189<!-- n7&#45;&gt;n6 -->
190<g class="edge edge-TRUE">
191
192<path fill="none" stroke-width="2" d="M293.84,-169.94C293.28,-162.19 293.09,-153.38 293.29,-144.95" class="edge-line"/>
193<polygon stroke-width="2" points="296.34,-145.33 293.69,-136.45 290.22,-145.05 296.34,-145.33" class="edge-line edge-arrow"/>
194<text text-anchor="start" x="251.69" y="-155.87" font-weight="bold" font-size="10.50">vertices</text>
195</g><g class="edge edge-TRUE">
196
197<path fill="none" stroke-width="2" d="M281.6,-85.14C279.9,-77.39 279.35,-68.58 279.95,-60.15" class="edge-line"/>
198<polygon stroke-width="2" points="282.96,-60.72 281.14,-51.63 276.9,-59.87 282.96,-60.72" class="edge-line edge-arrow"/>
199<text text-anchor="start" x="179.61" y="-58.47" font-weight="bold" font-size="10.50">outgoingTransition</text>
200</g><g class="edge edge-TRUE">
201
202<path fill="none" d="M318.37,-48.63C320.07,-56.47 320.6,-65.4 319.95,-73.92" class="edge-line"/>
203<polygon points="316.51,-73.24 318.55,-83.64 323.44,-74.24 316.51,-73.24" class="edge-line edge-arrow"/>
204<text text-anchor="middle" x="305.62" y="-57.22" font-size="10.50">target</text>
205</g>
206<g class="edge edge-TRUE">
207
208<path fill="none" d="M127.91,-48.63C138.46,-57.54 150.06,-67.86 160.27,-77.38" class="edge-line"/>
209<polygon points="157.7,-79.76 167.36,-84.09 162.51,-74.68 157.7,-79.76" class="edge-line edge-arrow"/>
210<text text-anchor="middle" x="165.07" y="-69.83" font-size="10.50">source</text>
211</g><g class="edge edge-TRUE">
212
213<path fill="none" d="M104.91,-48.63C106.42,-56.47 107.67,-65.4 108.49,-73.92" class="edge-line"/>
214<polygon points="104.98,-73.91 109.19,-83.63 111.96,-73.4 104.98,-73.91" class="edge-line edge-arrow"/>
215<text text-anchor="middle" x="122.29" y="-69.82" font-size="10.50">target</text>
216</g><g class="edge edge-TRUE">
217
218<path fill="none" d="M201.3,-48.63C200.91,-56.47 200.01,-65.4 198.77,-73.92" class="edge-line"/>
219<polygon points="195.34,-73.2 197.13,-83.64 202.24,-74.36 195.34,-73.2" class="edge-line edge-arrow"/>
220<text text-anchor="middle" x="183.25" y="-57.22" font-size="10.50">source</text>
221</g><g class="edge edge-TRUE">
222
223<path fill="none" d="M306.1,-48.63C306.67,-56.47 306.84,-65.4 306.63,-73.92" class="edge-line"/>
224<polygon points="303.14,-73.47 306.16,-83.62 310.14,-73.8 303.14,-73.47" class="edge-line edge-arrow"/>
225<text text-anchor="middle" x="323.2" y="-57.22" font-size="10.50">source</text>
226</g></g>
227</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license b/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg b/subprojects/docs/src/learn/language/classes/InvalidInstance.svg
new file mode 100644
index 00000000..fb9dd37d
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/InvalidInstance.svg
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="74pt" height="72pt" viewBox="-6 -6 86 84.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-S3CluI8WDJspDI9OUqv4H"><style>.refinery-S3CluI8WDJspDI9OUqv4H .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-outline{stroke:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-header{fill:rgb(53, 161, 173);}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-bg{fill:#fff;}.refinery-S3CluI8WDJspDI9OUqv4H .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-S3CluI8WDJspDI9OUqv4H .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-h .node-header{fill:#e06c75;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-i .node-header{fill:#98c379;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-j .node-header{fill:#c678dd;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-n .node-header{fill:#abcc94;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-S3CluI8WDJspDI9OUqv4H .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-line{stroke:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-arrow{fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN text{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR text{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-TRUE{fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-UNKNOWN{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-ERROR{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H text.label-UNKNOWN{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H 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-S3CluI8WDJspDI9OUqv4H-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-S3CluI8WDJspDI9OUqv4H-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-S3CluI8WDJspDI9OUqv4H-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 class="graph" transform="translate(4, 68.4000015258789)">
4<!-- n0 -->
5
6<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE">
7
8<rect stroke="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-bg"/>
9<rect stroke="none" x="-4" y="-68" width="74" height="27" clip-path="url(#refinery-S3CluI8WDJspDI9OUqv4H-clip-0)" class="node-header"/>
10<text text-anchor="start" x="14.75" y="-48.6" font-size="12.00">invalid</text>
11<use x="6" y="-35" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-S3CluI8WDJspDI9OUqv4H-icon-ERROR"/>
12<g><text text-anchor="start" x="21.81" y="-25.2" font-size="12.00" class="label label-ERROR">Region</text>
13</g>
14<use x="6" y="-19" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-S3CluI8WDJspDI9OUqv4H-icon-ERROR"/>
15<g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-ERROR">State</text>
16</g>
17<polyline points="0,-41 66,-41" class="node-outline"/>
18<rect fill="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-outline"/>
19<clipPath id="refinery-S3CluI8WDJspDI9OUqv4H-clip-0"><rect stroke="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g></g>
20</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license b/subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg b/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg
new file mode 100644
index 00000000..b28c295a
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg
@@ -0,0 +1,229 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="802pt" height="157pt" viewBox="-6 -6 814.3800048828125 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-MZ1i4PkeOsY_2x7-6CJ29"><style>.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-outline{stroke:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-header{fill:rgb(53, 161, 173);}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-bg{fill:#fff;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-i .node-header{fill:#98c379;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-line{stroke:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-arrow{fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN text{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR text{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-TRUE{fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-UNKNOWN{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-ERROR{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-UNKNOWN{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 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-MZ1i4PkeOsY_2x7-6CJ29-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-MZ1i4PkeOsY_2x7-6CJ29-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-MZ1i4PkeOsY_2x7-6CJ29-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 class="graph" transform="translate(4, 153.1999969482422)">
4<!-- n0 -->
5
6<!-- n0&#45;&gt;n0 -->
7
8<!-- n0&#45;&gt;n1 -->
9
10<!-- n0&#45;&gt;n1 -->
11
12<!-- n3 -->
13<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
14
15<rect stroke="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
16<rect stroke="none" x="80" y="-145" width="72" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-0)" class="node-header"/>
17<text text-anchor="start" x="109.83" y="-125.6" font-size="12.00">v1</text>
18<use x="90.2516" y="-111.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
19<g><text text-anchor="start" x="106.25" y="-102.2" font-size="12.00" class="label label-TRUE">Vertex</text>
20</g>
21<polyline points="84.25,-118 148.28,-118" class="node-outline"/>
22<rect fill="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-outline"/>
23<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-0"><rect stroke="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
24<!-- n1&#45;&gt;n3 -->
25<g class="edge edge-TRUE">
26
27<path fill="none" d="M127.79,-92.67C135,-81.99 144.45,-69.17 153.6,-57.6" class="edge-line"/>
28<polygon points="156.32,-59.81 159.88,-49.83 150.87,-55.41 156.32,-59.81" class="edge-line edge-arrow"/>
29<text text-anchor="middle" x="190.06" y="-60.95" font-size="10.50">outgoingTransition</text>
30</g>
31<!-- n5 -->
32<g class="edge edge-TRUE">
33
34<path fill="none" d="M172.8,-48.65C165.6,-59.32 156.15,-72.13 147,-83.71" class="edge-line"/>
35<polygon points="144.27,-81.51 140.72,-91.49 149.72,-85.91 144.27,-81.51" class="edge-line edge-arrow"/>
36<text text-anchor="middle" x="140.38" y="-74.06" font-size="10.50">source</text>
37</g>
38<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
39
40<rect stroke="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/>
41<rect stroke="none" x="36" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-1)" class="node-header"/>
42<text text-anchor="start" x="76.72" y="-33" font-size="12.00">t1</text>
43<use x="46.1559" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
44<g><text text-anchor="start" x="62.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
45</g>
46<polyline points="40.16,-25.4 124.38,-25.4" class="node-outline"/>
47<rect fill="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/>
48<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-1"><rect stroke="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE">
49
50<path fill="none" d="M96.97,-48.65C101.54,-58.79 106.27,-70.87 110.06,-81.98" class="edge-line"/>
51<polygon points="106.64,-82.78 113.04,-91.23 113.3,-80.64 106.64,-82.78" class="edge-line edge-arrow"/>
52<text text-anchor="middle" x="89.38" y="-60.85" font-size="10.50">source</text>
53</g><g class="edge edge-TRUE">
54
55<path fill="none" d="M101.52,-92.67C96.95,-82.52 92.23,-70.44 88.44,-59.33" class="edge-line"/>
56<polygon points="91.87,-58.54 85.47,-50.09 85.2,-60.68 91.87,-58.54" class="edge-line edge-arrow"/>
57<text text-anchor="middle" x="46.32" y="-74.16" font-size="10.50">outgoingTransition</text>
58</g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
59
60<rect stroke="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/>
61<rect stroke="none" x="138" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-2)" class="node-header"/>
62<text text-anchor="start" x="178.72" y="-33" font-size="12.00">t2</text>
63<use x="148.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
64<g><text text-anchor="start" x="164.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
65</g>
66<polyline points="142.16,-25.4 226.38,-25.4" class="node-outline"/>
67<rect fill="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/>
68<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-2"><rect stroke="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n5 -->
69<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
70
71<rect stroke="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-bg"/>
72<rect stroke="none" x="196" y="-153" width="244" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-3)" class="node-header"/>
73<text text-anchor="start" x="312.83" y="-133.4" font-size="12.00">v2</text>
74<use x="206.766" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
75<g><text text-anchor="start" x="222.77" y="-110" font-size="12.00" class="label label-TRUE">Vertex</text>
76</g>
77<use x="206.766" y="-103.8" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-ERROR"/>
78<g><text text-anchor="start" x="222.4" y="-94" font-size="12.00" class="label label-ERROR">outgoingTransition::invalidMultiplicity</text>
79</g>
80<polyline points="200.77,-125.8 437.77,-125.8" class="node-outline"/>
81<rect fill="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-outline"/>
82<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-3"><rect stroke="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
83
84<rect stroke="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/>
85<rect stroke="none" x="273" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-4)" class="node-header"/>
86<text text-anchor="start" x="313.72" y="-33" font-size="12.00">t3</text>
87<use x="283.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
88<g><text text-anchor="start" x="299.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
89</g>
90<polyline points="277.16,-25.4 361.38,-25.4" class="node-outline"/>
91<rect fill="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/>
92<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-4"><rect stroke="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
93<!-- n4&#45;&gt;n5 -->
94
95<!-- n5&#45;&gt;n4 -->
96<g class="edge edge-TRUE">
97
98<path fill="none" d="M325.25,-48.62C325.82,-56.19 326.07,-64.85 326.01,-73.38" class="edge-line"/>
99<polygon points="322.51,-73.22 325.75,-83.31 329.51,-73.4 322.51,-73.22" class="edge-line edge-arrow"/>
100<text text-anchor="middle" x="309.51" y="-57.13" font-size="10.50">source</text>
101</g>
102<!-- n6&#45;&gt;n5 -->
103<g class="edge edge-TRUE">
104
105<path fill="none" d="M312.82,-84.82C312.47,-76.84 312.42,-68.21 312.68,-60.07" class="edge-line"/>
106<polygon points="316.18,-60.3 313.21,-50.13 309.19,-59.94 316.18,-60.3" class="edge-line edge-arrow"/>
107<text text-anchor="middle" x="358.85" y="-70.18" font-size="10.50">outgoingTransition</text>
108</g>
109<!-- n7 -->
110<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
111
112<rect stroke="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/>
113<rect stroke="none" x="400" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-5)" class="node-header"/>
114<text text-anchor="start" x="440.72" y="-33" font-size="12.00">t4</text>
115<use x="410.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
116<g><text text-anchor="start" x="426.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
117</g>
118<polyline points="404.16,-25.4 488.38,-25.4" class="node-outline"/>
119<rect fill="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/>
120<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-5"><rect stroke="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
121<!-- n8 -->
122<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
123
124<rect stroke="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-bg"/>
125<rect stroke="none" x="489" y="-153" width="245" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-6)" class="node-header"/>
126<text text-anchor="start" x="605.83" y="-133.4" font-size="12.00">v3</text>
127<use x="499.766" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
128<g><text text-anchor="start" x="515.77" y="-110" font-size="12.00" class="label label-TRUE">Vertex</text>
129</g>
130<polyline points="493.77,-125.8 730.77,-125.8" class="node-outline"/><use x="499.766" y="-103.8" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-ERROR"/>
131
132<rect fill="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-outline"/><g><text text-anchor="start" x="515.4" y="-94" font-size="12.00" class="label label-ERROR">outgoingTransition::invalidMultiplicity</text>
133</g>
134
135<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-6"><rect stroke="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g>
136<!-- n7&#45;&gt;n8 -->
137<g class="edge edge-TRUE">
138
139<path fill="none" d="M488.13,-45.15C507.42,-55.16 530.62,-67.65 551.73,-79.34" class="edge-line"/>
140<polygon points="549.83,-82.28 560.27,-84.08 553.23,-76.16 549.83,-82.28" class="edge-line edge-arrow"/>
141<text text-anchor="middle" x="508.37" y="-55.19" font-size="10.50">source</text>
142</g>
143<!-- n8&#45;&gt;n7 -->
144<g class="edge edge-TRUE">
145
146<path fill="none" d="M548.69,-84.82C530.15,-74.94 510.25,-64.06 492.94,-54.34" class="edge-line"/>
147<polygon points="494.83,-51.38 484.4,-49.51 491.38,-57.48 494.83,-51.38" class="edge-line edge-arrow"/>
148<text text-anchor="middle" x="469.61" y="-70.25" font-size="10.50">outgoingTransition</text>
149</g>
150<!-- n9 -->
151<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
152
153<rect stroke="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/>
154<rect stroke="none" x="502" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-7)" class="node-header"/>
155<text text-anchor="start" x="542.72" y="-33" font-size="12.00">t5</text>
156<use x="512.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
157<g><text text-anchor="start" x="528.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
158</g>
159<polyline points="506.16,-25.4 590.38,-25.4" class="node-outline"/>
160<rect fill="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/>
161<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-7"><rect stroke="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
162<!-- n8&#45;&gt;n9 -->
163<g class="edge edge-TRUE">
164
165<path fill="none" d="M583.79,-84.82C577.32,-76.19 570.67,-66.81 564.85,-58.09" class="edge-line"/>
166<polygon points="567.9,-56.37 559.52,-49.89 562.03,-60.18 567.9,-56.37" class="edge-line edge-arrow"/>
167<text text-anchor="middle" x="617.13" y="-69.99" font-size="10.50">outgoingTransition</text>
168</g>
169<!-- n10 -->
170<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
171
172<rect stroke="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/>
173<rect stroke="none" x="604" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-8)" class="node-header"/>
174<text text-anchor="start" x="644.72" y="-33" font-size="12.00">t6</text>
175<use x="614.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
176<g><text text-anchor="start" x="630.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
177</g>
178<polyline points="608.16,-25.4 692.38,-25.4" class="node-outline"/>
179<rect fill="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-outline"/>
180<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-8"><rect stroke="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
181<!-- n8&#45;&gt;n10 -->
182<g class="edge edge-TRUE">
183
184<path fill="none" d="M618.89,-84.82C622.03,-76.47 625.8,-67.41 629.66,-58.94" class="edge-line"/>
185<polygon points="632.78,-60.52 633.9,-49.99 626.45,-57.53 632.78,-60.52" class="edge-line edge-arrow"/>
186<text text-anchor="middle" x="579.94" y="-57.16" font-size="10.50">outgoingTransition</text>
187</g>
188<!-- n11 -->
189<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
190
191<rect stroke="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/>
192<rect stroke="none" x="706" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-9)" class="node-header"/>
193<text text-anchor="start" x="746.72" y="-33" font-size="12.00">t7</text>
194<use x="716.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/>
195<g><text text-anchor="start" x="732.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
196</g>
197<polyline points="710.16,-25.4 794.38,-25.4" class="node-outline"/>
198<rect fill="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-outline"/>
199<clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-9"><rect stroke="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
200<!-- n8&#45;&gt;n11 -->
201<g class="edge edge-TRUE">
202
203<path fill="none" d="M654,-84.82C668.76,-74.99 685.52,-64.17 700.97,-54.48" class="edge-line"/>
204<polygon points="702.47,-57.67 709.12,-49.42 698.78,-51.73 702.47,-57.67" class="edge-line edge-arrow"/>
205<text text-anchor="middle" x="728.4" y="-57.01" font-size="10.50">outgoingTransition</text>
206</g>
207<!-- n9&#45;&gt;n8 -->
208<g class="edge edge-TRUE">
209
210<path fill="none" d="M570.65,-48.62C577.04,-56.79 583.96,-66.23 590.31,-75.41" class="edge-line"/>
211<polygon points="587.33,-77.25 595.84,-83.57 593.13,-73.33 587.33,-77.25" class="edge-line edge-arrow"/>
212<text text-anchor="middle" x="567.43" y="-69.5" font-size="10.50">source</text>
213</g>
214<!-- n10&#45;&gt;n8 -->
215<g class="edge edge-TRUE">
216
217<path fill="none" d="M646.51,-48.62C643.79,-56.54 640.22,-65.64 636.38,-74.54" class="edge-line"/>
218<polygon points="633.29,-72.88 632.41,-83.44 639.68,-75.73 633.29,-72.88" class="edge-line edge-arrow"/>
219<text text-anchor="middle" x="656.03" y="-57.56" font-size="10.50">source</text>
220</g>
221<!-- n11&#45;&gt;n8 -->
222<g class="edge edge-TRUE">
223
224<path fill="none" d="M722.36,-48.62C708.84,-57.83 692.35,-68.64 676.28,-78.88" class="edge-line"/>
225<polygon points="674.75,-75.7 668.17,-84.01 678.5,-81.62 674.75,-75.7" class="edge-line edge-arrow"/>
226<text text-anchor="middle" x="711.22" y="-70.14" font-size="10.50">source</text>
227</g>
228</g>
229</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license b/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg b/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg
new file mode 100644
index 00000000..95ba8def
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg
@@ -0,0 +1,29 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="172pt" height="57pt" viewBox="-6 -6 184.24000549316406 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-8UTvxM6Gq4184FIveUdov"><style>.refinery-8UTvxM6Gq4184FIveUdov .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .node .node-outline{stroke:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .node .node-header{fill:rgb(53, 161, 173);}.refinery-8UTvxM6Gq4184FIveUdov .node .node-bg{fill:#fff;}.refinery-8UTvxM6Gq4184FIveUdov .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-8UTvxM6Gq4184FIveUdov .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-8UTvxM6Gq4184FIveUdov .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-h .node-header{fill:#e06c75;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-i .node-header{fill:#98c379;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-j .node-header{fill:#c678dd;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-n .node-header{fill:#abcc94;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-8UTvxM6Gq4184FIveUdov .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge .edge-line{stroke:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge .edge-arrow{fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN text{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR text{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .icon-TRUE{fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .icon-UNKNOWN{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .icon-ERROR{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov text.label-UNKNOWN{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov 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-8UTvxM6Gq4184FIveUdov-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-8UTvxM6Gq4184FIveUdov-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-8UTvxM6Gq4184FIveUdov-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 class="graph" transform="translate(4, 52.79999923706055)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="5.5" y="-42.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-52" width="86" height="27" clip-path="url(#refinery-8UTvxM6Gq4184FIveUdov-clip-0)" class="node-header"/>
9<text text-anchor="start" x="5" y="-33" font-size="12.00">Region::new</text>
10<use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-8UTvxM6Gq4184FIveUdov-icon-TRUE"/>
11<g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">Region</text>
12</g>
13<polyline points="0,-25.4 78.19,-25.4" class="node-outline"/>
14<rect fill="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-8UTvxM6Gq4184FIveUdov-clip-0"><rect stroke="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n2 -->
17
18<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="100.5" y="-42.5" width="69" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
19
20<rect stroke="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-bg"/>
21<rect stroke="none" x="91" y="-52" width="76" height="27" clip-path="url(#refinery-8UTvxM6Gq4184FIveUdov-clip-1)" class="node-header"/>
22<text text-anchor="start" x="100.95" y="-33" font-size="12.00">State::new</text>
23
24
25
26
27<use x="101.954" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-8UTvxM6Gq4184FIveUdov-icon-TRUE"/><g><text text-anchor="start" x="117.95" y="-9.6" font-size="12.00" class="label label-TRUE">State</text>
28</g><polyline points="95.95,-25.4 164.24,-25.4" class="node-outline"/><rect fill="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-8UTvxM6Gq4184FIveUdov-clip-1"><rect stroke="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g></g>
29</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license b/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg b/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg
new file mode 100644
index 00000000..cdf365f0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="230pt" height="104pt" viewBox="-6 -6 242.10000610351562 116.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-snWM44tZVFiopoyqQzHDw"><style>.refinery-snWM44tZVFiopoyqQzHDw .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .node .node-outline{stroke:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .node .node-header{fill:rgb(53, 161, 173);}.refinery-snWM44tZVFiopoyqQzHDw .node .node-bg{fill:#fff;}.refinery-snWM44tZVFiopoyqQzHDw .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-snWM44tZVFiopoyqQzHDw .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-snWM44tZVFiopoyqQzHDw .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-h .node-header{fill:#e06c75;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-i .node-header{fill:#98c379;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-j .node-header{fill:#c678dd;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-n .node-header{fill:#abcc94;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-snWM44tZVFiopoyqQzHDw .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge .edge-line{stroke:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge .edge-arrow{fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN text{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR text{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .icon-TRUE{fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .icon-UNKNOWN{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .icon-ERROR{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw text.label-UNKNOWN{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw 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-snWM44tZVFiopoyqQzHDw-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-snWM44tZVFiopoyqQzHDw-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-snWM44tZVFiopoyqQzHDw-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 class="graph" transform="translate(4, 100.4000015258789)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="5.5" y="-66.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-76" width="86" height="27" clip-path="url(#refinery-snWM44tZVFiopoyqQzHDw-clip-0)" class="node-header"/>
9<text text-anchor="start" x="5" y="-56.8" font-size="12.00">Region::new</text>
10<use x="6" y="-43" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/>
11<g><text text-anchor="start" x="22" y="-33.4" font-size="12.00" class="label label-TRUE">Region</text>
12</g>
13<polyline points="0,-49.2 78.19,-49.2" class="node-outline"/>
14<rect fill="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-snWM44tZVFiopoyqQzHDw-clip-0"><rect stroke="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n1 -->
17<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="101.5" y="-90.5" width="127" height="97" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
18
19<rect stroke="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="92" y="-100" width="134" height="27" clip-path="url(#refinery-snWM44tZVFiopoyqQzHDw-clip-1)" class="node-header"/>
21<text text-anchor="start" x="129.95" y="-80.6" font-size="12.00">State::new</text>
22<use x="102.096" y="-67" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/>
23<g><text text-anchor="start" x="118.01" y="-58.2" font-style="italic" font-size="12.00" class="label label-TRUE">CompositeElement</text>
24</g>
25<use x="102.096" y="-51" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/>
26<g><text text-anchor="start" x="118.1" y="-42.2" font-style="italic" font-size="12.00" class="label label-TRUE">Vertex</text>
27</g>
28<use x="102.096" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/>
29<g><text text-anchor="start" x="118.1" y="-26.2" font-style="italic" font-size="12.00" class="label label-TRUE">RegularState</text>
30</g>
31<use x="102.096" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/>
32<g><text text-anchor="start" x="118.1" y="-9.2" font-size="12.00" class="label label-TRUE">State</text>
33</g>
34<polyline points="96.1,-73 222.1,-73" class="node-outline"/>
35<rect fill="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-outline"/>
36<clipPath id="refinery-snWM44tZVFiopoyqQzHDw-clip-1"><rect stroke="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
37</g>
38</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license b/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg b/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg
new file mode 100644
index 00000000..56a4d956
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg
@@ -0,0 +1,69 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="236pt" height="142pt" viewBox="-6 -6 247.8800048828125 153.60000610351562" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-aUMOWvqBGMJmEq5FBgfQD"><style>.refinery-aUMOWvqBGMJmEq5FBgfQD .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-outline{stroke:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-header{fill:rgb(53, 161, 173);}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-bg{fill:#fff;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-h .node-header{fill:#e06c75;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-i .node-header{fill:#98c379;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-j .node-header{fill:#c678dd;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-n .node-header{fill:#abcc94;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-line{stroke:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-arrow{fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN text{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR text{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-TRUE{fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-UNKNOWN{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-ERROR{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD text.label-UNKNOWN{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD 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-aUMOWvqBGMJmEq5FBgfQD-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-aUMOWvqBGMJmEq5FBgfQD-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-aUMOWvqBGMJmEq5FBgfQD-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 class="graph" transform="translate(4, 137.60000610351562)">
4<!-- n0 -->
5
6<!-- n1 -->
7
8<!-- n1&#45;&gt;n0 -->
9<!-- n1&#45;&gt;n0 -->
10
11<!-- n1&#45;&gt;n0 -->
12
13<!-- n1&#45;&gt;n0 -->
14
15
16<!-- n3 -->
17<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
18
19<rect stroke="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="42" y="-137" width="72" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-0)" class="node-header"/>
21<text text-anchor="start" x="72.39" y="-117.8" font-size="12.00">v1</text>
22<use x="52.8135" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/>
23<g><text text-anchor="start" x="68.81" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
24</g>
25<polyline points="46.81,-110.2 110.84,-110.2" class="node-outline"/>
26<rect fill="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/>
27<clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-0"><rect stroke="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<g class="edge edge-TRUE">
29
30<path fill="none" d="M114.46,-48.63C110.99,-56.83 106.51,-66.22 101.86,-75.08" class="edge-line"/>
31<polygon points="98.81,-73.35 97.1,-83.81 104.96,-76.7 98.81,-73.35" class="edge-line edge-arrow"/>
32<text text-anchor="middle" x="89.45" y="-70.24" font-size="10.50">source</text>
33</g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
34
35<rect stroke="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/>
36<rect stroke="none" x="124" y="-137" width="72" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-1)" class="node-header"/>
37<text text-anchor="start" x="154.39" y="-117.8" font-size="12.00">v2</text>
38<use x="134.814" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/>
39<g><text text-anchor="start" x="150.81" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
40</g>
41<polyline points="128.81,-110.2 192.84,-110.2" class="node-outline"/>
42<rect fill="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/>
43<clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-1"><rect stroke="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
44
45<rect stroke="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/>
46<rect stroke="none" x="73" y="-52" width="92" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-2)" class="node-header"/>
47<text text-anchor="start" x="114.28" y="-33" font-size="12.00">t1</text>
48<use x="83.7178" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/>
49<g><text text-anchor="start" x="99.72" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
50</g>
51<polyline points="77.72,-25.4 161.94,-25.4" class="node-outline"/>
52<rect fill="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/>
53<clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-2"><rect stroke="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE">
54
55<path fill="none" d="M84.13,-85.14C87.58,-76.95 92.06,-67.57 96.71,-58.7" class="edge-line"/>
56<polygon points="99.76,-60.41 101.47,-49.96 93.61,-57.06 99.76,-60.41" class="edge-line edge-arrow"/>
57<text text-anchor="middle" x="46.32" y="-57.24" font-size="10.50">outgoingTransition</text>
58</g><g class="edge edge-TRUE">
59
60<path fill="none" d="M137.47,-48.63C142.07,-56.74 146.83,-66.01 150.92,-74.79" class="edge-line"/>
61<polygon points="147.65,-76.04 154.91,-83.75 154.04,-73.19 147.65,-76.04" class="edge-line edge-arrow"/>
62<text text-anchor="middle" x="132.53" y="-57.44" font-size="10.50">target</text>
63</g><g class="edge edge-TRUE">
64
65<path fill="none" d="M143.28,-85.14C138.68,-77.04 133.92,-67.77 129.82,-58.99" class="edge-line"/>
66<polygon points="133.08,-57.72 125.81,-50.01 126.69,-60.57 133.08,-57.72" class="edge-line edge-arrow"/>
67<text text-anchor="middle" x="180.75" y="-70.04" font-size="10.50">incomingTransition</text>
68</g></g>
69</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license b/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg b/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg
new file mode 100644
index 00000000..81ab4a0c
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="117pt" height="57pt" viewBox="-6 -6 128.5 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-4mmABsPwhJURILrHpcKRU"><style>.refinery-4mmABsPwhJURILrHpcKRU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .node .node-outline{stroke:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .node .node-header{fill:rgb(53, 161, 173);}.refinery-4mmABsPwhJURILrHpcKRU .node .node-bg{fill:#fff;}.refinery-4mmABsPwhJURILrHpcKRU .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-4mmABsPwhJURILrHpcKRU .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-4mmABsPwhJURILrHpcKRU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-h .node-header{fill:#e06c75;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-i .node-header{fill:#98c379;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-j .node-header{fill:#c678dd;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-n .node-header{fill:#abcc94;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-4mmABsPwhJURILrHpcKRU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge .edge-line{stroke:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge .edge-arrow{fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN text{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR text{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .icon-TRUE{fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .icon-UNKNOWN{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .icon-ERROR{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU text.label-UNKNOWN{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU 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-4mmABsPwhJURILrHpcKRU-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-4mmABsPwhJURILrHpcKRU-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-4mmABsPwhJURILrHpcKRU-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 class="graph" transform="translate(4, 52.79999923706055)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-p"><rect stroke="none" x="5.5" y="-42.5" width="80" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-52" width="87" height="27" clip-path="url(#refinery-4mmABsPwhJURILrHpcKRU-clip-0)" class="node-header"/>
9<text text-anchor="start" x="5" y="-33" font-size="12.00">Person::new</text>
10<use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-4mmABsPwhJURILrHpcKRU-icon-TRUE"/>
11<g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">Person</text>
12</g>
13<polyline points="0,-25.4 79.01,-25.4" class="node-outline"/>
14<rect fill="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-4mmABsPwhJURILrHpcKRU-clip-0"><rect stroke="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n0&#45;&gt;n0 -->
17<g class="edge edge-UNKNOWN">
18
19<path fill="none" stroke-dasharray="5,2" d="M78.75,-33.47C89.14,-32.95 97.01,-29.93 97.01,-24.4 97.01,-21.12 94.24,-18.72 89.81,-17.2" class="edge-line"/>
20<polygon points="90.68,-13.8 80.24,-15.58 89.52,-20.7 90.68,-13.8" class="edge-line edge-arrow"/>
21<text text-anchor="middle" x="93.87" y="-36.59" font-size="10.50">friend</text>
22</g>
23</g>
24</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license b/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg b/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg
new file mode 100644
index 00000000..fac74815
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg
@@ -0,0 +1,43 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="104pt" height="142pt" viewBox="-6 -6 116.04000091552734 153.60000610351562" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-KS4S5srdaYqLoF1P1dM89"><style>.refinery-KS4S5srdaYqLoF1P1dM89 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-outline{stroke:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-header{fill:rgb(53, 161, 173);}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-bg{fill:#fff;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-i .node-header{fill:#98c379;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-line{stroke:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-arrow{fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN text{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR text{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-TRUE{fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-UNKNOWN{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-ERROR{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 text.label-UNKNOWN{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 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-KS4S5srdaYqLoF1P1dM89-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-KS4S5srdaYqLoF1P1dM89-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-KS4S5srdaYqLoF1P1dM89-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 class="graph" transform="translate(4, 137.60000610351562)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="15.5" y="-42.5" width="76" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="6" y="-52" width="83" height="27" clip-path="url(#refinery-KS4S5srdaYqLoF1P1dM89-clip-0)" class="node-header"/>
9<text text-anchor="start" x="15.1" y="-33" font-size="12.00">Vertex::new</text>
10<use x="16.0957" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KS4S5srdaYqLoF1P1dM89-icon-TRUE"/>
11<g><text text-anchor="start" x="32.1" y="-9.6" font-size="12.00" class="label label-TRUE">Vertex</text>
12</g>
13<polyline points="10.1,-25.4 85.94,-25.4" class="node-outline"/>
14<rect fill="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-KS4S5srdaYqLoF1P1dM89-clip-0"><rect stroke="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n1 -->
17<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="5.5" y="-127.5" width="97" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
18
19<rect stroke="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="-4" y="-137" width="104" height="27" clip-path="url(#refinery-KS4S5srdaYqLoF1P1dM89-clip-1)" class="node-header"/>
21<text text-anchor="start" x="5" y="-117.8" font-size="12.00">Transition::new</text>
22<use x="6" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KS4S5srdaYqLoF1P1dM89-icon-TRUE"/>
23<g><text text-anchor="start" x="22" y="-94.4" font-size="12.00" class="label label-TRUE">Transition</text>
24</g>
25<polyline points="0,-110.2 96.04,-110.2" class="node-outline"/>
26<rect fill="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-outline"/>
27<clipPath id="refinery-KS4S5srdaYqLoF1P1dM89-clip-1"><rect stroke="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n1&#45;&gt;n0 -->
29<g class="edge edge-UNKNOWN">
30
31<path fill="none" stroke-dasharray="5,2" d="M41.9,-85.14C41.32,-77.31 41.14,-68.38 41.35,-59.86" class="edge-line"/>
32<polygon points="44.84,-60.29 41.81,-50.14 37.85,-59.96 44.84,-60.29" class="edge-line edge-arrow"/>
33<text text-anchor="middle" x="24.78" y="-70.26" font-size="10.50">source</text>
34</g>
35<!-- n1&#45;&gt;n0 -->
36<g class="edge edge-UNKNOWN">
37
38<path fill="none" stroke-dasharray="5,2" d="M54.14,-85.14C54.71,-77.31 54.9,-68.38 54.68,-59.86" class="edge-line"/>
39<polygon points="58.19,-59.96 54.23,-50.14 51.2,-60.29 58.19,-59.96" class="edge-line edge-arrow"/>
40<text text-anchor="middle" x="40.18" y="-57.66" font-size="10.50">target</text>
41</g>
42</g>
43</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license b/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/classes/index.md b/subprojects/docs/src/learn/language/classes/index.md
new file mode 100644
index 00000000..73108039
--- /dev/null
+++ b/subprojects/docs/src/learn/language/classes/index.md
@@ -0,0 +1,212 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4description: Metamodeling in Refinery
5sidebar_position: 0
6---
7
8# Classes and references
9
10Refinery supports _metamodeling_ to describe the desired structure of generated models.
11
12The metamodeling facilities are inspired by object-oriented software and the [Eclipse Modeling Foundation](https://eclipse.dev/modeling/emf/) (EMF) Core, a lightweight framework for data models.
13The textual syntax in Refinery for defining metamodels is largely compatible with [Xcore](https://wiki.eclipse.org/Xcore), a textual syntax for EMF metamodels.
14
15## Classes
16
17Classes are declared with the `class` keyword.
18
19Like in many programming languages, class members are specified between curly braces `{}`.
20If a class has no members, the declaration may be terminated with a `.` instead.
21
22```refinery
23% Class with no members.
24class Region {}
25
26% Alternative syntax without curly braces.
27class State.
28```
29
30By default, a _new object_ is added to the partial model to represent the instances of a class.
31For example, the new objects `Region::new` and `State::new` represent potential instances of the classes `Region` and `State`, respectively:
32
33import NewObjectsSimple from './NewObjectsSimple.svg';
34
35<NewObjectsSimple />
36
37As you can see, no new objects represent potential nodes that are instanceof of both `Region` and `State`.
38In fact, such instances are not permitted at all.
39Each node must the instance of a _single most-specific class:_
40
41import InvalidInstance from './InvalidInstance.svg';
42
43<InvalidInstance />
44
45### Inheritance
46
47Like in object-oriented programming languages, classes may declare _superclasses_ with the `extends` keyword.
48The inheritance hierarchy may not contain any cycles (a class cannot be a superclass of itself), but _multiple inheritance_ is allowed.
49
50Classes that can't be instantiated directly (i.e., a subclass must be instantiated instead) can be marked with the `abstract` keyword.
51Such classes do not have a _new object,_ since there are no direct instances to represent.
52
53```refinery
54abstract class CompositeElement.
55class Region.
56abstract class Vertex.
57abstract class RegularState extends Vertex.
58class State extends RegularState, CompositeElement.
59```
60
61Notice that the new object `State::new` is an instance of `CompositeElement`, `Vertex`, `RegularState`, and `State` as well.
62
63import NewObjectsWithInheritance from './NewObjectsWithInheritance.svg';
64
65<NewObjectsWithInheritance />
66
67## References
68
69The graph structure of model generated by Refinery is determined by the _references_ of the metamodel, which will appear as labeled edges between nodes (class instances).
70
71References are declared as class members by providing the _target type,_ and optional _multiplicity,_ and the name of the reference:
72
73```refinery
74class Vertex.
75class Transition {
76 Vertex[1] source
77 Vertex[1] target
78}
79```
80
81import ReferencesSimple from './ReferencesSimple.svg';
82
83<ReferencesSimple />
84
85You may add the `refers` keyword for compatibility with [Xcore](https://wiki.eclipse.org/Xcore). The following specification is equivalent:
86
87```refinery
88class Vertex.
89class Transition {
90 refers Vertex[1] source
91 refers Vertex[1] target
92}
93```
94
95### Opposite constraints
96
97The `opposite` keywords specifies that two references are in an _opposite_ relationship, i.e., if one reference is present in a direction, the other must be present between the same nodes in the opposite direction.
98
99```
100class Vertex {
101 Transition[] outgoingTransition opposite source
102 Transition[] incomingTransition opposite target
103}
104class Transition {
105 Vertex[1] source opposite outgoingTransition
106 Vertex[1] target opposite incomingTransition
107}
108```
109
110import ReferencesOppositeInstance from './ReferencesOppositeInstance.svg';
111
112<ReferencesOppositeInstance />
113
114Opposites must be declared in pairs: it is a specification error to declare the `opposite` for one direction but not the other.
115
116Unlike in EMF, references that are the `opposite` of themselves are also supported.
117These must always be present in both directions between two nodes.
118Thus, they correspond to undirected graph edges.
119
120```refinery
121class Person {
122 Person[] friend opposite friend
123}
124```
125
126import ReferencesOppositeSelf from './ReferencesOppositeSelf.svg';
127
128<ReferencesOppositeSelf />
129
130### Multiplicity
131
132_Multiplicity constrains_ can be provided after the reference type in square braces.
133They specify how many _outgoing_ references should exist for any given instance of the class.
134
135:::info
136
137To control the number of _incoming_ references, add an `opposite` reference with multiplicity constraint.
138
139:::
140
141A multiplicity constraint is of the form `[n..m]`, where the non-negative integer `n` is the _lower_ bound of outgoing references,
142and `m` is a positive integer or `*` corresponding to the _upper_ bound of outgoing references.
143The value of `*` represent a reference with _unbounded_ upper multiplicity.
144
145If `n` = `m`, the shorter form `[n]` may be used.
146The bound `[0..*]` may be abbreviated as `[]`.
147If the multiplicity constraint is omitted, the bound `[0..1]` is assumed.
148
149---
150
151In the following model, the node `v1` satisfies all multiplicity constraints of `outgoingTransition`.
152The node `v2` violates the lower bound constraint, while `v3` violates the upper bound constraint.
153All `Transition` instances satisfy the multiplicity constrains associated with `source`.
154
155```refinery
156class Vertex {
157 Transition[2..3] outgoingTransition opposite source
158}
159class Transition {
160 Vertex[1] source opposite outgoingTransition
161}
162```
163
164import MultiplicityConstraintsInstance from './MultiplicityConstraintsInstance.svg';
165
166<MultiplicityConstraintsInstance />
167
168### Containment hierarchy
169
170To structure models and ensure their connectedness, Refinery supports _containment_ constraints.
171
172References may be marked as _containment_ references with the `contains` keyword.
173
174Classes that are the _target type_ of at least one _containment_ reference are considered `contained`.
175An instance of a `contained` class must have exactly 1 incoming containment reference.
176Instances of classes that are not `contained` must _not_ have any incoming containment references.
177
178Containment references have to form a _forest_, i.e., they must not contain any cycles.
179The _roots_ of the forest are instances of classes that are not `contained`, while `contained` classes for the internal nodes and leaves of the trees.
180
181Opposites of _containment_ references have to be marked with the `container` keyword.
182They must not specify any multiplicity constraint, since the multiplicity is already implied by the containment hierarchy.
183
184---
185
186In the following model, the instances of `Region` are the roots of the containment hierarchy.
187The classes `Vertex` are `Transition` are both considered `contained`.
188
189```refinery
190class Region {
191 contains Vertex[] vertices opposite region
192}
193
194class Vertex {
195 container Region region opposite vertices
196 contains Transition[] outgoingTransition opposite source
197 Transition[] incomingTransition opposite target
198}
199
200class Transition {
201 container Vertex source opposite outgoingTransition
202 Vertex[1] target opposite incomingTransition
203}
204```
205
206Containment edges are show with **thick** lines:
207
208import ContainmentInstance from './ContainmentInstance.svg';
209
210<ContainmentInstance />
211
212Containment edges form trees, while non-containment references, such as `target`, may point across the containment hierarchy.
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsError.svg b/subprojects/docs/src/learn/language/logic/AssertionsError.svg
new file mode 100644
index 00000000..8ddc65f3
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/AssertionsError.svg
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="72pt" height="72pt" viewBox="-6 -6 84 84.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-t1ihtn3yDar9Rl2Fph8SJ"><style>.refinery-t1ihtn3yDar9Rl2Fph8SJ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-outline{stroke:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-header{fill:rgb(53, 161, 173);}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-bg{fill:#fff;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-h .node-header{fill:#e06c75;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-i .node-header{fill:#98c379;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-j .node-header{fill:#c678dd;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-n .node-header{fill:#abcc94;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-line{stroke:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-arrow{fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN text{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR text{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-TRUE{fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-UNKNOWN{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-ERROR{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-UNKNOWN{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ 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-t1ihtn3yDar9Rl2Fph8SJ-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-t1ihtn3yDar9Rl2Fph8SJ-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-t1ihtn3yDar9Rl2Fph8SJ-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 class="graph" transform="translate(4, 68.4000015258789)">
4<!-- n3 -->
5<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE">
6
7<rect stroke="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-68" width="72" height="27" clip-path="url(#refinery-t1ihtn3yDar9Rl2Fph8SJ-clip-0)" class="node-header"/>
9<text text-anchor="start" x="25.56" y="-48.6" font-size="12.00">v1</text>
10
11
12<use x="6" y="-35" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-ERROR"/>
13<g><text text-anchor="start" x="21.99" y="-25.2" font-size="12.00" class="label label-ERROR">Vertex</text>
14</g>
15<use x="6" y="-19" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-ERROR"/>
16<g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-ERROR">State</text>
17</g>
18<polyline points="0,-41 64,-41" class="node-outline"/><rect fill="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-t1ihtn3yDar9Rl2Fph8SJ-clip-0"><rect stroke="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
19</g>
20</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsError.svg.license b/subprojects/docs/src/learn/language/logic/AssertionsError.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/AssertionsError.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg b/subprojects/docs/src/learn/language/logic/AssertionsExample.svg
new file mode 100644
index 00000000..26b3d1ff
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/AssertionsExample.svg
@@ -0,0 +1,99 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="250pt" height="157pt" viewBox="-6 -6 262.17999267578125 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-yow0X_ZN3HQDi30KE0Sac"><style>.refinery-yow0X_ZN3HQDi30KE0Sac .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-outline{stroke:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-header{fill:rgb(53, 161, 173);}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-bg{fill:#fff;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-h .node-header{fill:#e06c75;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-i .node-header{fill:#98c379;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-j .node-header{fill:#c678dd;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-n .node-header{fill:#abcc94;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-line{stroke:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-arrow{fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN text{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR text{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-TRUE{fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-UNKNOWN{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-ERROR{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac text.label-UNKNOWN{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac 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-yow0X_ZN3HQDi30KE0Sac-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-yow0X_ZN3HQDi30KE0Sac-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-yow0X_ZN3HQDi30KE0Sac-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 class="graph" transform="translate(4, 153.1999969482422)">
4<!-- n3 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="6.5" y="-143.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-3" y="-153" width="86" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-0)" class="node-header"/>
9<text text-anchor="start" x="6.07" y="-133.4" font-size="12.00">Region::new</text>
10<use x="7.07291" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
11<g><text text-anchor="start" x="23.07" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
12</g>
13<polyline points="1.07,-125.8 79.26,-125.8" class="node-outline"/>
14<rect fill="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-0"><rect stroke="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n4 -->
17<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="14.5" y="-58.5" width="69" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
18
19<rect stroke="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="5" y="-68" width="76" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-1)" class="node-header"/>
21<text text-anchor="start" x="14.03" y="-48.6" font-size="12.00">State::new</text>
22<use x="15.027" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
23<g><text text-anchor="start" x="31.01" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
24</g>
25<use x="15.027" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
26<g><text text-anchor="start" x="31.03" y="-9.2" font-size="12.00" class="label label-TRUE">State</text>
27</g>
28<polyline points="9.03,-41 77.31,-41" class="node-outline"/>
29<rect fill="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-outline"/>
30<clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-1"><rect stroke="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
31<!-- n3&#45;&gt;n4 -->
32<g class="edge edge-UNKNOWN">
33
34<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M40.94,-100.47C41.19,-92.98 41.47,-84.45 41.75,-76.02" class="edge-line"/>
35<polygon stroke-width="2" points="44.81,-76.18 42.04,-67.34 38.69,-75.98 44.81,-76.18" class="edge-line edge-arrow"/>
36<text text-anchor="start" x="0" y="-86.89" font-weight="bold" font-size="10.50">vertices</text>
37</g>
38<!-- n8 -->
39<!-- n4 -->
40<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
41
42<rect stroke="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/>
43<rect stroke="none" x="92" y="-68" width="71" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-2)" class="node-header"/>
44<text text-anchor="start" x="121.73" y="-48.6" font-size="12.00">v1</text>
45<use x="102.169" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
46<g><text text-anchor="start" x="118.15" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
47</g>
48<use x="102.169" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-UNKNOWN"/>
49<g><text text-anchor="start" x="118.17" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text>
50</g>
51<polyline points="96.17,-41 160.17,-41" class="node-outline"/>
52<rect fill="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-outline"/>
53<clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-2"><rect stroke="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n0&#45;&gt;n3 -->
54<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
55
56<rect stroke="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
57<rect stroke="none" x="92" y="-153" width="74" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-3)" class="node-header"/>
58<text text-anchor="start" x="124.29" y="-133.4" font-size="12.00">r1</text>
59<use x="102.982" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
60<g><text text-anchor="start" x="118.98" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
61</g>
62<polyline points="96.98,-125.8 163.36,-125.8" class="node-outline"/>
63<rect fill="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
64<clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-3"><rect stroke="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
65
66<rect stroke="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-bg"/>
67<rect stroke="none" x="174" y="-60" width="72" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-4)" class="node-header"/>
68<text text-anchor="start" x="203.73" y="-40.8" font-size="12.00">v2</text>
69<use x="184.154" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/>
70<g><text text-anchor="start" x="200.15" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text>
71</g>
72
73
74<polyline points="178.15,-33.2 242.18,-33.2" class="node-outline"/><rect fill="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-4"><rect stroke="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g>
75<g class="edge edge-UNKNOWN">
76
77<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M107.76,-100.47C99.59,-91.95 90.1,-82.07 80.95,-72.55" class="edge-line"/>
78<polygon stroke-width="2" points="83.41,-70.68 75.14,-66.49 78.99,-74.93 83.41,-70.68" class="edge-line edge-arrow"/>
79<text text-anchor="start" x="48.91" y="-86.57" font-weight="bold" font-size="10.50">vertices</text>
80</g>
81<!-- n2&#45;&gt;n0 -->
82
83<!-- n3&#45;&gt;n0 -->
84
85<!-- n3&#45;&gt;n4 -->
86
87<g class="edge edge-TRUE">
88
89<path fill="none" stroke-width="2" d="M129.65,-100.47C129.49,-92.98 129.3,-84.45 129.11,-76.02" class="edge-line"/>
90<polygon stroke-width="2" points="132.18,-76.02 128.92,-67.34 126.05,-76.15 132.18,-76.02" class="edge-line edge-arrow"/>
91<text text-anchor="start" x="129.26" y="-86.89" font-weight="bold" font-size="10.50">vertices</text>
92</g>
93<g class="edge edge-TRUE">
94
95<path fill="none" stroke-width="2" d="M150.77,-100.47C160.29,-89.69 171.74,-76.72 182.04,-65.06" class="edge-line"/>
96<polygon stroke-width="2" points="184.14,-67.31 187.63,-58.72 179.55,-63.25 184.14,-67.31" class="edge-line edge-arrow"/>
97<text text-anchor="start" x="128.85" y="-69.81" font-weight="bold" font-size="10.50">vertices</text>
98</g></g>
99</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license b/subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg b/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg
new file mode 100644
index 00000000..2ab002bf
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg
@@ -0,0 +1,129 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="327pt" height="157pt" viewBox="-6 -6 339.3299865722656 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-p_2c2ycZZXd-I3Xg-BBah"><style>.refinery-p_2c2ycZZXd-I3Xg-BBah .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-outline{stroke:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-header{fill:rgb(53, 161, 173);}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-bg{fill:#fff;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-h .node-header{fill:#e06c75;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-i .node-header{fill:#98c379;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-j .node-header{fill:#c678dd;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-n .node-header{fill:#abcc94;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-line{stroke:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-arrow{fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN text{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR text{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-TRUE{fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-UNKNOWN{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-ERROR{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah text.label-UNKNOWN{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah 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-p_2c2ycZZXd-I3Xg-BBah-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-p_2c2ycZZXd-I3Xg-BBah-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-p_2c2ycZZXd-I3Xg-BBah-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 class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)">
4<!-- n0 -->
5
6<!-- n2 -->
7<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="5.5" y="-58.5" width="69" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
8
9<rect stroke="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/>
10<rect stroke="none" x="-4" y="-68" width="76" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-0)" class="node-header"/>
11<text text-anchor="start" x="5" y="-48.6" font-size="12.00">State::new</text>
12<use x="6" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
13<g><text text-anchor="start" x="21.99" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
14</g>
15<use x="6" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
16<g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-TRUE">State</text>
17</g>
18<polyline points="0,-41 68.28,-41" class="node-outline"/>
19<rect fill="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-outline"/>
20<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-0"><rect stroke="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
21<!-- n0&#45;&gt;n2 -->
22
23<!-- n3 -->
24<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
25
26<rect stroke="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
27<rect stroke="none" x="80" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-1)" class="node-header"/>
28<text text-anchor="start" x="112.26" y="-133.4" font-size="12.00">r1</text>
29<use x="90.9551" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
30<g><text text-anchor="start" x="106.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
31</g>
32<polyline points="84.96,-125.8 151.33,-125.8" class="node-outline"/>
33<rect fill="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
34<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-1"><rect stroke="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
35<!-- n3&#45;&gt;n2 -->
36<g class="edge edge-UNKNOWN">
37
38<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M96.51,-100.47C88.69,-92.04 79.64,-82.27 70.89,-72.83" class="edge-line"/>
39<polygon stroke-width="2" points="73.24,-70.86 65.04,-66.53 68.75,-75.03 73.24,-70.86" class="edge-line edge-arrow"/>
40<text text-anchor="start" x="38.02" y="-86.32" font-weight="bold" font-size="10.50">vertices</text>
41</g>
42<!-- n4 -->
43<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
44
45<rect stroke="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/>
46<rect stroke="none" x="82" y="-68" width="71" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-2)" class="node-header"/>
47<text text-anchor="start" x="111.71" y="-48.6" font-size="12.00">v1</text>
48<use x="92.1416" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
49<g><text text-anchor="start" x="108.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
50</g>
51<use x="92.1416" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/>
52<g><text text-anchor="start" x="108.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text>
53</g>
54<polyline points="86.14,-41 150.14,-41" class="node-outline"/>
55<rect fill="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-outline"/>
56<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-2"><rect stroke="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
57<!-- n3&#45;&gt;n4 -->
58<g class="edge edge-TRUE">
59
60<path fill="none" stroke-width="2" d="M118.14,-100.47C118.14,-92.98 118.14,-84.45 118.14,-76.02" class="edge-line"/>
61<polygon stroke-width="2" points="121.2,-76.09 118.14,-67.34 115.08,-76.09 121.2,-76.09" class="edge-line edge-arrow"/>
62<text text-anchor="start" x="118.14" y="-86.89" font-weight="bold" font-size="10.50">vertices</text>
63</g>
64<!-- n5 -->
65<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
66
67<rect stroke="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
68<rect stroke="none" x="164" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-3)" class="node-header"/>
69<text text-anchor="start" x="196.26" y="-133.4" font-size="12.00">r2</text>
70<use x="174.955" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
71<g><text text-anchor="start" x="190.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
72</g>
73<polyline points="168.96,-125.8 235.33,-125.8" class="node-outline"/>
74<rect fill="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
75<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-3"><rect stroke="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
76<!-- n6 -->
77<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
78
79<rect stroke="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/>
80<rect stroke="none" x="166" y="-68" width="72" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-4)" class="node-header"/>
81<text text-anchor="start" x="195.71" y="-48.6" font-size="12.00">v2</text>
82<use x="176.142" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
83<g><text text-anchor="start" x="192.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
84</g>
85<use x="176.142" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/>
86<g><text text-anchor="start" x="192.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text>
87</g>
88<polyline points="170.14,-41 234.14,-41" class="node-outline"/>
89<rect fill="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/>
90<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-4"><rect stroke="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
91<!-- n5&#45;&gt;n6 -->
92<g class="edge edge-TRUE">
93
94<path fill="none" stroke-width="2" d="M202.14,-100.47C202.14,-92.98 202.14,-84.45 202.14,-76.02" class="edge-line"/>
95<polygon stroke-width="2" points="205.2,-76.09 202.14,-67.34 199.08,-76.09 205.2,-76.09" class="edge-line edge-arrow"/>
96<text text-anchor="start" x="160.61" y="-86.89" font-weight="bold" font-size="10.50">vertices</text>
97</g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
98
99<rect stroke="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/>
100<rect stroke="none" x="250" y="-68" width="72" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-5)" class="node-header"/>
101<text text-anchor="start" x="279.71" y="-48.6" font-size="12.00">v3</text>
102<use x="260.142" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
103<g><text text-anchor="start" x="276.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
104</g>
105<use x="260.142" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/>
106<g><text text-anchor="start" x="276.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text>
107</g>
108<polyline points="254.14,-41 318.14,-41" class="node-outline"/>
109<rect fill="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/>
110<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-5"><rect stroke="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g>
111<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k">
112
113<rect stroke="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
114<rect stroke="none" x="248" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-6)" class="node-header"/>
115<text text-anchor="start" x="280.26" y="-133.4" font-size="12.00">r3</text>
116<use x="258.955" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/>
117<g><text text-anchor="start" x="274.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
118</g>
119<polyline points="252.96,-125.8 319.33,-125.8" class="node-outline"/>
120<rect fill="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
121<clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-6"><rect stroke="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n7&#45;&gt;n8 -->
122<g class="edge edge-TRUE">
123
124<path fill="none" stroke-width="2" d="M286.14,-100.47C286.14,-92.98 286.14,-84.45 286.14,-76.02" class="edge-line"/>
125<polygon stroke-width="2" points="289.2,-76.09 286.14,-67.34 283.08,-76.09 289.2,-76.09" class="edge-line edge-arrow"/>
126<text text-anchor="start" x="244.61" y="-86.89" font-weight="bold" font-size="10.50">vertices</text>
127</g>
128</g>
129</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license b/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/MultiObjects.svg b/subprojects/docs/src/learn/language/logic/MultiObjects.svg
new file mode 100644
index 00000000..a5232575
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/MultiObjects.svg
@@ -0,0 +1,81 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="508pt" height="57pt" viewBox="-6 -6 519.5700073242188 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-meygjgDtG1kB8-FV4IATS"><style>.refinery-meygjgDtG1kB8-FV4IATS .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .node .node-outline{stroke:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .node .node-header{fill:rgb(53, 161, 173);}.refinery-meygjgDtG1kB8-FV4IATS .node .node-bg{fill:#fff;}.refinery-meygjgDtG1kB8-FV4IATS .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-meygjgDtG1kB8-FV4IATS .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-meygjgDtG1kB8-FV4IATS .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-h .node-header{fill:#e06c75;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-i .node-header{fill:#98c379;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-j .node-header{fill:#c678dd;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-n .node-header{fill:#abcc94;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-meygjgDtG1kB8-FV4IATS .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge .edge-line{stroke:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge .edge-arrow{fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN text{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR text{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .icon-TRUE{fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .icon-UNKNOWN{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .icon-ERROR{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS text.label-UNKNOWN{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS 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-meygjgDtG1kB8-FV4IATS-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-meygjgDtG1kB8-FV4IATS-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-meygjgDtG1kB8-FV4IATS-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 class="graph" transform="translate(4, 52.79999923706055)">
4<!-- n0 -->
5<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE">
6
7<rect stroke="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-52" width="67" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-0)" class="node-header"/>
9<text text-anchor="start" x="6.58" y="-33" font-size="12.00">node [1]</text>
10
11
12<polyline points="0,-25.4 59.74,-25.4" class="node-outline"/><use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-TRUE"/>
13<rect fill="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-outline"/><g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">exists</text>
14</g>
15<clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-0"><rect stroke="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n0&#45;&gt;n0 -->
17<g class="edge edge-TRUE">
18
19<path fill="none" d="M59.32,-33.55C69.51,-33.64 77.74,-30.6 77.74,-24.4 77.74,-20.82 74.99,-18.29 70.7,-16.81" class="edge-line"/>
20<polygon points="71.2,-13.35 60.82,-15.46 70.25,-20.28 71.2,-13.35" class="edge-line edge-arrow"/>
21<text text-anchor="middle" x="75.93" y="-36.7" font-size="10.50">equals</text>
22</g>
23<!-- n1 -->
24<g class="node node-IMPLICIT node-exists-UNKNOWN node-equalsSelf-TRUE">
25
26<rect stroke="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-bg"/>
27<rect stroke="none" x="91" y="-52" width="108" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-1)" class="node-header"/>
28<text text-anchor="start" x="100.38" y="-33" font-size="12.00">removable [0..1]</text>
29
30
31<use x="101.384" y="-19.2" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-UNKNOWN"/>
32<g><text text-anchor="start" x="117.38" y="-9.6" font-size="12.00" class="label label-UNKNOWN">exists</text>
33</g>
34<polyline points="95.38,-25.4 196.36,-25.4" class="node-outline"/><rect fill="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-1"><rect stroke="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
35<!-- n1&#45;&gt;n1 -->
36<g class="edge edge-TRUE">
37
38<path fill="none" d="M196.34,-33.23C206.82,-32.3 214.36,-29.36 214.36,-24.4 214.36,-21.46 211.7,-19.22 207.35,-17.7" class="edge-line"/>
39<polygon points="208.31,-14.32 197.83,-15.86 206.98,-21.2 208.31,-14.32" class="edge-line edge-arrow"/>
40<text text-anchor="middle" x="213.45" y="-36.29" font-size="10.50">equals</text>
41</g>
42<!-- n2 -->
43<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-UNKNOWN"><rect stroke="none" x="237.5" y="-42.5" width="70" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
44
45<rect stroke="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-bg"/>
46<rect stroke="none" x="228" y="-52" width="77" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-2)" class="node-header"/>
47<text text-anchor="start" x="237.01" y="-33" font-size="12.00">multi [1..*]</text>
48<use x="238.011" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-TRUE"/>
49<g><text text-anchor="start" x="254.01" y="-9.6" font-size="12.00" class="label label-TRUE">exists</text>
50</g>
51<polyline points="232.01,-25.4 301.73,-25.4" class="node-outline"/>
52<rect fill="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-outline"/>
53<clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-2"><rect stroke="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
54<!-- n2&#45;&gt;n2 -->
55<g class="edge edge-UNKNOWN">
56
57<path fill="none" d="M301.69,-33.52C311.86,-33.21 319.73,-30.17 319.73,-24.4 319.73,-21.07 317.1,-18.64 312.91,-17.13" class="edge-line" stroke-dasharray="5,2"/>
58<polygon points="313.62,-13.7 303.19,-15.52 312.48,-20.61 313.62,-13.7" class="edge-line edge-arrow"/>
59<text text-anchor="middle" x="318.3" y="-36.66" font-size="10.50">equals</text>
60</g>
61<!-- n3 -->
62<g class="node node-IMPLICIT node-exists-UNKNOWN node-equalsSelf-UNKNOWN"><rect stroke="none" x="342.5" y="-42.5" width="130" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
63
64<rect stroke="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-bg"/>
65<rect stroke="none" x="333" y="-52" width="137" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-3)" class="node-header"/>
66<text text-anchor="start" x="342.25" y="-33" font-size="12.00">removableMulti [0..*]</text>
67<use x="343.251" y="-19.2" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-UNKNOWN"/>
68<g><text text-anchor="start" x="359.25" y="-9.6" font-size="12.00" class="label label-UNKNOWN">exists</text>
69</g>
70<polyline points="337.25,-25.4 466.49,-25.4" class="node-outline"/>
71<rect fill="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-outline"/>
72<clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-3"><rect stroke="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
73<!-- n3&#45;&gt;n3 -->
74<g class="edge edge-UNKNOWN">
75
76<path fill="none" stroke-dasharray="5,2" d="M466.3,-32.9C477.06,-31.71 484.49,-28.88 484.49,-24.4 484.49,-21.74 481.87,-19.66 477.49,-18.16" class="edge-line"/>
77<polygon points="478.28,-14.75 467.78,-16.2 476.89,-21.61 478.28,-14.75" class="edge-line edge-arrow"/>
78<text text-anchor="middle" x="483.44" y="-35.94" font-size="10.50">equals</text>
79</g>
80</g>
81</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/MultiObjects.svg.license b/subprojects/docs/src/learn/language/logic/MultiObjects.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/MultiObjects.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg b/subprojects/docs/src/learn/language/logic/ObjectScopes.svg
new file mode 100644
index 00000000..440dfb19
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/ObjectScopes.svg
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="260pt" height="157pt" viewBox="-6 -6 271.9100036621094 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery--ID4ju8v8LPQmawWdGqBG"><style>.refinery--ID4ju8v8LPQmawWdGqBG .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-outline{stroke:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-header{fill:rgb(53, 161, 173);}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-bg{fill:#fff;}.refinery--ID4ju8v8LPQmawWdGqBG .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery--ID4ju8v8LPQmawWdGqBG .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-g .node-header{fill:#e5c07b;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-h .node-header{fill:#e06c75;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-i .node-header{fill:#98c379;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-j .node-header{fill:#c678dd;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-k .node-header{fill:#80a7f4;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-m .node-header{fill:#e78b8f;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-n .node-header{fill:#abcc94;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-p .node-header{fill:#92c0e9;}.refinery--ID4ju8v8LPQmawWdGqBG .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-line{stroke:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-arrow{fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN text{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-arrow{fill:none;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR text{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-line{stroke:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-TRUE{fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-UNKNOWN{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-ERROR{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG text.label-UNKNOWN{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG 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--ID4ju8v8LPQmawWdGqBG-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--ID4ju8v8LPQmawWdGqBG-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--ID4ju8v8LPQmawWdGqBG-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 class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="74.5" y="-143.5" width="117" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="65" y="-153" width="124" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-0)" class="node-header"/>
9<text text-anchor="start" x="74.26" y="-133.4" font-size="12.00">Region::new [0..70]</text>
10<use x="75.2588" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/>
11<g><text text-anchor="start" x="91.26" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
12</g>
13<polyline points="69.26,-125.8 185.44,-125.8" class="node-outline"/>
14<rect fill="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-0"><rect stroke="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n2 -->
17<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="143.5" y="-58.5" width="114" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
18
19<rect stroke="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="134" y="-68" width="121" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-1)" class="node-header"/>
21<text text-anchor="start" x="143.78" y="-48.6" font-size="12.00">State::new [0..120]</text>
22<use x="144.782" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/>
23<g><text text-anchor="start" x="160.77" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
24</g>
25<use x="144.782" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/>
26<g><text text-anchor="start" x="160.78" y="-9.2" font-size="12.00" class="label label-TRUE">State</text>
27</g>
28<polyline points="138.78,-41 251.91,-41" class="node-outline"/>
29<rect fill="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-outline"/>
30<clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-1"><rect stroke="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="5.5" y="-50.5" width="121" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
31
32<rect stroke="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-bg"/>
33<rect stroke="none" x="-4" y="-60" width="128" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-2)" class="node-header"/>
34<text text-anchor="start" x="5" y="-40.8" font-size="12.00">Vertex::new [0..120]</text>
35<use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/>
36<g><text text-anchor="start" x="22" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text>
37</g>
38<polyline points="0,-33.2 120.7,-33.2" class="node-outline"/>
39<rect fill="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-outline"/>
40<clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-2"><rect stroke="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g>
41<!-- n0&#45;&gt;n2 -->
42<g class="edge edge-UNKNOWN">
43
44<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M110.09,-100.47C102.28,-89.9 92.91,-77.23 84.42,-65.75" class="edge-line"/>
45<polygon stroke-width="2" points="87.01,-64.1 79.34,-58.89 82.08,-67.74 87.01,-64.1" class="edge-line edge-arrow"/>
46<text text-anchor="start" x="52.27" y="-82.58" font-weight="bold" font-size="10.50">vertices</text>
47</g>
48<!-- n3 -->
49
50<!-- n3&#45;&gt;n2 -->
51<g class="edge edge-UNKNOWN">
52
53<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M144.86,-100.47C150.99,-92.3 158.07,-82.87 164.95,-73.7" class="edge-line"/>
54<polygon stroke-width="2" points="167.38,-75.57 170.18,-66.73 162.48,-71.89 167.38,-75.57" class="edge-line edge-arrow"/>
55<text text-anchor="start" x="116.95" y="-86.47" font-weight="bold" font-size="10.50">vertices</text>
56</g>
57</g>
58</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license b/subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg b/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg
new file mode 100644
index 00000000..6f988065
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="246pt" height="157pt" viewBox="-6 -6 258.2899932861328 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-J5TgkNWMX1Aj-K6cbPdd9"><style>.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-outline{stroke:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-header{fill:rgb(53, 161, 173);}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-bg{fill:#fff;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-i .node-header{fill:#98c379;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-line{stroke:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-arrow{fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN text{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR text{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-TRUE{fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-UNKNOWN{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-ERROR{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-UNKNOWN{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 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-J5TgkNWMX1Aj-K6cbPdd9-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-J5TgkNWMX1Aj-K6cbPdd9-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-J5TgkNWMX1Aj-K6cbPdd9-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 class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)">
4<!-- n0 -->
5<g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="74.5" y="-143.5" width="117" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
6
7<rect stroke="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="65" y="-153" width="124" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-0)" class="node-header"/>
9<text text-anchor="start" x="74.69" y="-133.4" font-size="12.00">Region::new [0..70]</text>
10<use x="75.6895" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/>
11<g><text text-anchor="start" x="91.69" y="-110" font-size="12.00" class="label label-TRUE">Region</text>
12</g>
13<polyline points="69.69,-125.8 185.87,-125.8" class="node-outline"/>
14<rect fill="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-0"><rect stroke="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n2 -->
17<g class="node node-NEW node-exists-TRUE node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="150.5" y="-58.5" width="94" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
18
19<rect stroke="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="141" y="-68" width="101" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-1)" class="node-header"/>
21<text text-anchor="start" x="150.27" y="-48.6" font-size="12.00">State::new [20]</text>
22<use x="151.268" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/>
23<g><text text-anchor="start" x="167.25" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text>
24</g>
25<use x="151.268" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/>
26<g><text text-anchor="start" x="167.27" y="-9.2" font-size="12.00" class="label label-TRUE">State</text>
27</g>
28<polyline points="145.27,-41 238.29,-41" class="node-outline"/>
29<rect fill="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-outline"/>
30<clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-1"><rect stroke="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-NEW node-exists-TRUE node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="5.5" y="-50.5" width="128" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/>
31
32<rect stroke="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-bg"/>
33<rect stroke="none" x="-4" y="-60" width="135" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-2)" class="node-header"/>
34<text text-anchor="start" x="5" y="-40.8" font-size="12.00">Vertex::new [30..100]</text>
35<use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/>
36<g><text text-anchor="start" x="22" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text>
37</g>
38<polyline points="0,-33.2 127.56,-33.2" class="node-outline"/>
39<rect fill="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-outline"/>
40<clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-2"><rect stroke="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g>
41<!-- n0&#45;&gt;n2 -->
42<g class="edge edge-UNKNOWN">
43
44<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M111.3,-100.47C103.83,-89.9 94.88,-77.23 86.77,-65.75" class="edge-line"/>
45<polygon stroke-width="2" points="89.5,-64.3 81.95,-58.93 84.5,-67.84 89.5,-64.3" class="edge-line edge-arrow"/>
46<text text-anchor="start" x="54.2" y="-82.58" font-weight="bold" font-size="10.50">vertices</text>
47</g>
48<!-- n3 -->
49
50<!-- n3&#45;&gt;n2 -->
51<g class="edge edge-UNKNOWN">
52
53<path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M144.26,-100.47C150.03,-92.3 156.69,-82.87 163.17,-73.7" class="edge-line"/>
54<polygon stroke-width="2" points="165.51,-75.7 168.06,-66.78 160.51,-72.16 165.51,-75.7" class="edge-line edge-arrow"/>
55<text text-anchor="start" x="115.55" y="-86.47" font-weight="bold" font-size="10.50">vertices</text>
56</g>
57</g>
58</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license b/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/logic/index.md b/subprojects/docs/src/learn/language/logic/index.md
new file mode 100644
index 00000000..e366e9b8
--- /dev/null
+++ b/subprojects/docs/src/learn/language/logic/index.md
@@ -0,0 +1,256 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4description: Four-valued logic abstraction
5sidebar_position: 1
6---
7
8# Partial modeling
9
10Refinery allow precisely expressing _unknown,_ _uncertain_ or even _contradictory_ information using [four-valued logic](https://en.wikipedia.org/wiki/Four-valued_logic#Belnap).
11During model generation, unknown aspects of the partial model get _refined_ into concrete (true or false) facts until the generated model is completed, or a contradiction is reached.
12
13The _Belnap--Dunn four-valued logic_ supports the following truth values:
14
15* `true` values correspond to facts known about the model, e.g., that a node is the instance of a given class or there is a reference between two nodes.
16* `false` values correspond to facts that are known not to hold, e.g., that a node is _not_ an instance of a given class or there is _no_ reference between two nodes.
17* `unknown` values express uncertain properties and design decisions yet to be made. During model refinement, `unknown` values are gradually replaced with `true` and `false` values until a consistent and concrete model is derived.
18* `error` values represent contradictions and validation failures in the model. One a model contains an error value, it can't be refined into a consistent model anymore.
19
20## Assertions
21
22_Assertions_ express facts about a partial model. An assertion is formed by a _symbol_ and an _argument list_ of _nodes_ in parentheses.
23Possible symbols include [classes](../classes/#classes), [references](../classes/#references), and [predicates](../predicates).
24Nodes appearing in the argument list are automatically added to the model.
25
26A _negative_ assertion with a `false` truth value is indicated by prefixing it with `!`.
27
28---
29
30Consider the following metamodel:
31
32```refinery
33class Region {
34 contains Vertex[] vertices
35}
36class Vertex.
37class State extends Vertex.
38```
39
40Along with the following set of assertions:
41
42```refinery
43Region(r1).
44Vertex(v1).
45Vertex(v2).
46!State(v2).
47vertices(r1, v1).
48vertices(r1, v2).
49!vertices(Region::new, v1).
50!vertices(Region::new, v2).
51```
52
53import AssertionsExample from './AssertionsExample.svg';
54
55<AssertionsExample />
56
57It is `true` that `r1` is an instance of the class `Region`, while `v1` and `v2` are instances of the class `Vertex`.
58We also assert that `v2` is _not_ an instance of the class `State`, but it is unknown whether `v1` is an instance of the class `State`.
59Types that are `unknown` are shown in a lighter color and with an outlined icon.
60
61It is `true` that there is a `vertices` reference between `r1` and `v1`, as well as `r1` and `v2`, but there is no such reference from `Region::new` to the same vertices.
62As no information is provided, it is `unknown` whether `State::new` is a vertex of any `Region` instance.
63References that are `unknown` are shown in a lighter color and with a dashed line.
64
65### Propagation
66
67Refinery can automatically infer some facts about the partial model based on the provided assertions by information _propagation._
68The set of assertions in the [example above](#assertions) is equivalent to the following:
69
70```refinery
71vertices(r1, v1).
72vertices(r1, v2).
73!State(v2).
74```
75
76By the type constraints of the `vertices` reference, Refinery can infer that `r1` is a `Region` instance and `v1` and `v2` are `Vertex` instances.
77Since `State` is a subclass of `Vertex`, it is still unknown whether `v1` is a `State` instance,
78but `v2` is explicitly forbidden from being such by the negative assertion `!State(v2)`.
79We may omit `!vertices(Region::new, v1)` and `!vertices(Region::new, v2)`, since `v1` and `v2` may be a target of only one [containment](../classes/#containment-hierarchy) reference.
80
81Contradictory assertions lead to `error` values in the partial model:
82
83```refinery
84State(v1).
85!Vertex(v1).
86```
87
88import AssertionsError from './AssertionsError.svg';
89
90<AssertionsError />
91
92### Default assertions
93
94Assertions marked with the `default` keyword have _lower priority_ that other assertions.
95They may contain wildcard arguments `*` to specify information about _all_ nodes in the graph.
96However, they can be overridden by more specific assertions that are not marked with the `default` keyword.
97
98---
99
100To make sure that the reference `vertices` is `false` everywhere except where explicitly asserted, we may add a `default` assertion:
101
102```refinery
103default !vertices(*, *).
104vertices(r1, v1).
105vertices(r2, v2).
106vertices(r3, v3).
107?vertices(r1, State::new).
108```
109
110import DefaultAssertions from './DefaultAssertions.svg';
111
112<DefaultAssertions />
113
114We can prefix an assertion with `?` to explicitly assert that some fact about the partial model is `unknown`.
115This is useful for overriding negative `default` assertions.
116
117## Multi-objects
118
119The special symbols `exists` and `equals` control the _number of graph nodes_ represented by an object in a partial model.
120
121By default, `exists` is `true` for all objects.
122An object `o` with `?exists(o)` (i.e., `exists(o)` explicitly set to `unknown`) may be _removed_ from the partial model.
123Therefore, it represents _at least 0_ graph nodes.
124
125By default, `equals` is `true` for its _diagonal_, i.e., we have `equals(o, o)` for all object `o`.
126For off-diagonal pairs, i.e., `(p, q)` with `p` not equal to `q`, we always have `!equals(p, q)`: distinct objects can never be _merged._
127If we set a _diagonal_ entry to `unknown` by writing `?equals(o, o)`, the object `o` becomes a **multi-object:** it can be freely _split_ into multiple graph nodes.
128Therefore, multi-objects represent _possibly more than 1_ graph nodes.
129
130| `exists(o)` | `equals(o, o)` | Number of nodes | Description |
131|:-:|:-:|-:|:-|
132| `true` | `true` | `1` | graph node |
133| `unknown` | `true` | `0..1` | removable graph node |
134| `true` | `unknown` | `1..*` | multi-object |
135| `unknown` | `unknown` | `0..*` | removable multi-object |
136
137In the Refinery web UI, `?exists(o)` is represented with a _dashed_ border, while `?equals(o, o)`
138
139```refinery
140node(node).
141
142node(removable).
143?exists(removable).
144
145node(multi).
146?equals(multi, multi).
147
148node(removableMulti).
149?exists(removableMulti).
150?equals(removableMulti, removableMulti).
151```
152
153import MultiObjects from './MultiObjects.svg';
154
155<MultiObjects />
156
157import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg';
158import LabelIcon from '@material-icons/svg/svg/label/baseline.svg';
159import LabelOutlineIcon from '@material-icons/svg/svg/label/outline.svg';
160
161:::info
162
163You may use the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" />&nbsp;_filter panel_ icon in Refinery to toggle the visibility of special symbols like `exists` and `equals`.
164You may either show <LabelOutlineIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Unknown value icon" />&nbsp;_both true and unknown_ values or <LabelIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="True value icon" />&nbsp;_just true_ values.
165The _object scopes_ toggle will also show the number of graph nodes represented by an object in square brackets after its name, like in the figure above.
166:::
167
168By default, a **new object** `C::new` is added for each non-`abstract` [class](../classes#classes) `C` with `?exists(C::new)` and `?equals(C::new, C::new)`.
169This multi-object represents all potential instances of the class.
170To assert that no new instances of `C` should be added to the generated model, you may write `!exists(C::new)`.
171
172You may use the `multi` keyword to quickly defined a (removable) multi-object:
173
174```refinery
175multi removableMulti.
176% This is equivalent to:
177% ?exists(removableMulti).
178% ?equals(removableMulti, removableMulti).
179```
180
181## Type scopes
182
183_Type scopes_ offer finer-grained control over the number of graph nodes in the generated model (as represented by the multi-objects) that `exists` or `equals` assertions.
184
185A _type scope constraint_ is formed by a unary symbol (a [class](../classes/#classes) or a [predicate](../predicates) with a single parameter) and _scope range._
186Ranges have a form similar to [multiplicity constraints](../classes#multiplicity): a range `n..m` indicates a lower bound of `n` and an upper bound of `m`.
187While an upper bound of `*` indicates a possibly unbounded number of objects, generated models will always be finite.
188Like for multiplicity constraints, the case `n..n` can be abbreviated as `n`.
189
190The number of nodes in the generated model can be controlled using the `node` special symbol.
191For example, we may write the following to generate a model with at least 100 at and most 120 nodes:
192
193```refinery
194scope node = 100..200.
195```
196
197A `scope` declaration may prescribe type scope constraint for any number of symbols, separated by `,`.
198Multiple `scope` declarations are also permitted.
199Multiple ranges provided for the same symbol will be intersected, i.e., they influence the generated model simultaneously.
200
201In other words,
202```
203scope Region = 10, State = 80..120.
204scope State = 100..150.
205% Equivalent to:
206scope Region = 10, State = 100..120.
207```
208
209The _object scopes_ option in the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" />&nbsp;_filter panel_ may help in exploring the effects of object scopes.
210
211---
212
213Consider the example
214
215```refinery
216class Region {
217 contains Vertex[] vertices
218}
219class Vertex.
220class State extends Vertex.
221scope node = 100..120, Vertex = 50..*.
222```
223
224import ObjectScopes from './ObjectScopes.svg';
225
226<ObjectScopes />
227
228Notice that Refinery could determine that there can be no more than 70 `Region` instances in the generated model, since at least 50 of the `100..120` nodes in the model must be `Vertex` instances.
229However, since `State` is a subclass of `Vertex` (i.e., `State::new` is also an instance of `Vertex`), the range `50..*` is shared between both `Vertex::new` and `State::new`, resulting in both representing `0..120` nodes.
230Nevertheless, every generated model will obey the scope constraint exactly, i.e., will have between 100 and 120 node, at least 50 of which are `Vertex` instances.
231
232By providing more information, Refinery can determine more precise ranges for multi-objects.
233For example, we may strengthen the scope constraints as follows:
234
235```refinery
236scope node = 100..120, Vertex = 50..*, State = 20.
237```
238
239import StrongerObjectScopes from './StrongerObjectScopes.svg';
240
241<StrongerObjectScopes />
242
243### Incremental scopes
244
245We may specify an _incremental_ object scope with the `+=` operator to determine the number of new instances to be added to the model.
246This is only allowed for symbol that are classes with no subclasses, as it directly influences the number of nodes represented by the corresponding `::new` object.
247
248For example, to ensure that between 5 and 7 `State` instances are added to the model, we may write:
249
250```refinery
251State(s1).
252State(s2).
253scope State += 5..7.
254```
255
256Since `s1` and `s2` are also instances of the `State` class, the generated concrete model will have between 7 and 9 `State` instances altogether.
diff --git a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg
new file mode 100644
index 00000000..be9465b8
--- /dev/null
+++ b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg
@@ -0,0 +1,76 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="216pt" height="226pt" viewBox="-6 -6 227.8699951171875 238.39999389648438" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-9k5t5y1ScYnYvNXEZbWT4"><style>.refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(53, 161, 173);}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#fff;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#98c379;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 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-9k5t5y1ScYnYvNXEZbWT4-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-9k5t5y1ScYnYvNXEZbWT4-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-9k5t5y1ScYnYvNXEZbWT4-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 class="graph" transform="translate(4, 222.39999389648438)">
4<!-- n2 -->
5<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i">
6
7<rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="60" y="-52" width="92" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0)" class="node-header"/>
9<text text-anchor="start" x="76.52" y="-33" font-size="12.00">transition1</text>
10<use x="70.7485" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/>
11<g><text text-anchor="start" x="86.75" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text>
12</g>
13<polyline points="64.75,-25.4 148.97,-25.4" class="node-outline"/>
14<rect fill="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/>
15<clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0"><rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
16<!-- n3 -->
17<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
18
19<rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="70" y="-222" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1)" class="node-header"/>
21<text text-anchor="start" x="85.98" y="-202.6" font-size="12.00">vertex1</text>
22<use x="80.8442" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/>
23<g><text text-anchor="start" x="96.84" y="-179.2" font-size="12.00" class="label label-TRUE">Vertex</text>
24</g>
25<polyline points="74.84,-195 138.87,-195" class="node-outline"/>
26<rect fill="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-outline"/>
27<clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1"><rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g>
28<!-- n2&#45;&gt;n3 -->
29<g class="edge edge-TRUE">
30
31<path fill="none" d="M111.71,-48.78C113.97,-77.25 114.21,-125.59 112.45,-158.38" class="edge-line"/>
32<polygon points="108.96,-157.98 111.8,-168.19 115.95,-158.44 108.96,-157.98" class="edge-line edge-arrow"/>
33<text text-anchor="middle" x="97.12" y="-99.47" font-size="10.50">source</text>
34</g>
35<!-- n4 -->
36<g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h">
37
38<rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/>
39<rect stroke="none" x="139" y="-137" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2)" class="node-header"/>
40<text text-anchor="start" x="154.98" y="-117.8" font-size="12.00">vertex2</text>
41<use x="149.844" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/>
42<g><text text-anchor="start" x="165.84" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text>
43</g>
44<polyline points="143.84,-110.2 207.87,-110.2" class="node-outline"/>
45<rect fill="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/>
46<clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2"><rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g>
47<!-- n2&#45;&gt;n4 -->
48<g class="edge edge-TRUE">
49
50<path fill="none" d="M132.35,-48.63C140.1,-57.19 148.46,-67.04 155.81,-76.23" class="edge-line"/>
51<polygon points="152.92,-78.21 161.83,-83.94 158.44,-73.91 152.92,-78.21" class="edge-line edge-arrow"/>
52<text text-anchor="middle" x="133.35" y="-57.15" font-size="10.50">target</text>
53</g>
54<!-- n3&#45;&gt;n2 -->
55<g class="edge edge-TRUE">
56
57<path fill="none" stroke-width="2" d="M102.01,-169.7C99.75,-141.26 99.5,-92.92 101.27,-60.1" class="edge-line"/>
58<polygon stroke-width="2" points="104.3,-60.73 101.81,-51.8 98.18,-60.33 104.3,-60.73" class="edge-line edge-arrow"/>
59<text text-anchor="start" x="0" y="-113.73" font-weight="bold" font-size="10.50">outgoingTransition</text>
60</g>
61<!-- n4&#45;&gt;n2 -->
62<!-- n4&#45;&gt;n2 -->
63<g class="edge edge-TRUE">
64
65<path fill="none" d="M150.52,-85.14C142.77,-76.59 134.41,-66.75 127.05,-57.54" class="edge-line"/>
66<polygon points="129.93,-55.54 121.01,-49.82 124.41,-59.86 129.93,-55.54" class="edge-line edge-arrow"/>
67<text text-anchor="middle" x="87.8" y="-70.33" font-size="10.50">incomingTransition</text>
68</g><g class="edge edge-TRUE">
69
70<path fill="none" d="M126.08,-169.94C133.27,-161.31 141.57,-151.34 149.31,-142.06" class="edge-line"/>
71<polygon points="151.82,-144.51 155.53,-134.59 146.44,-140.03 151.82,-144.51" class="edge-line edge-arrow"/>
72<text text-anchor="middle" x="116.57" y="-154.94" font-size="10.50">neighbors</text>
73</g>
74
75</g>
76</svg> \ No newline at end of file
diff --git a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/language/predicates/index.md b/subprojects/docs/src/learn/language/predicates/index.md
new file mode 100644
index 00000000..261054c1
--- /dev/null
+++ b/subprojects/docs/src/learn/language/predicates/index.md
@@ -0,0 +1,284 @@
1---
2SPDX-FileCopyrightText: 2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4description: Model queries and model validation
5sidebar_position: 2
6---
7
8# Graph predicates
9
10Graph predicates are logic expressions that can be used to query for interesting model fragments, as well as for validating the consistency of models. They are evaluated on partial models according to [four-valued logic](../logic) semantics.
11
12Predicates in Refinery are written in [Disjunctive Normal Form](https://en.wikipedia.org/wiki/Disjunctive_normal_form) (DNF) as an _OR_ of _ANDs_, i.e., a _disjunction_ of _clauses_ formed as a _conjunction_ of positive or negated logic _literals._
13This matches the syntax and semantics of logical query languages, such as [Datalog](https://en.wikipedia.org/wiki/Datalog), and logical programming languages, such as [Prolog](https://en.wikipedia.org/wiki/Prolog).
14
15import Link from '@docusaurus/Link';
16
17<details>
18<summary>Example metamodel</summary>
19
20In the examples on this page, we will use the following metamodel as illustration:
21
22```refinery
23abstract class CompositeElement {
24 contains Region[] regions
25}
26
27class Region {
28 contains Vertex[] vertices opposite region
29}
30
31abstract class Vertex {
32 container Region region opposite vertices
33 contains Transition[] outgoingTransition opposite source
34 Transition[] incomingTransition opposite target
35}
36
37class Transition {
38 container Vertex source opposite outgoingTransition
39 Vertex[1] target opposite incomingTransition
40}
41
42abstract class Pseudostate extends Vertex.
43
44abstract class RegularState extends Vertex.
45
46class Entry extends Pseudostate.
47
48class Exit extends Pseudostate.
49
50class Choice extends Pseudostate.
51
52class FinalState extends RegularState.
53
54class State extends RegularState, CompositeElement.
55
56class Statechart extends CompositeElement.
57```
58
59<p>
60 <Link
61 href="https://refinery.services/#/1/KLUv_WAEAiUIAOIKIR5gadMGg1ajk9jLoipJ58vc0vAE5opt1YaDpyOCAAdaCjMohSdgl4rj1yTo8UCgpTDHCIAE-o3Jr28mGO9AEoDcR-tLGh4liE2Z3IOX50z-FksLaNWLpLXd1QiUII2vNjCMBWOVEgTzjhG0eHVMIyIyFOjoxcrBv83FkgftlmJ0K_0eVDQgEBSCrXYvD1Q2wlwGXecz2HjRADQOLMh6iIYIWBPuFBBCI2igVgiHAFH4uclAydd4TFayN-oOpjzxgd0FlTzkN6QZ8CQDXBN4EPjB5VJZCANQlJA3wDd_PVyUA5eA0gaeAcgENsm4YnCogWihMAMkA8-CoB-gm9HJC0AB"
62 className="button button--lg button--primary button--play"
63 >Try in Refinery</Link>
64</p>
65
66</details>
67
68[Assertions](../logic/#assertions) about graph predicates can prescribe where the predicate should (for positive assertions) or should not (for negative assertions) hold.
69When generating consistent models
70
71## Atoms
72
73An _atom_ is formed by a _symbol_ and _argument list_ of variables.
74Possible symbols include [classes](../classes/#classes), [references](../classes/#references), and [predicates](../predicates).
75We may write a basic graph query as a conjunction (AND) of atoms.
76
77The `pred` keyword defines a graph predicate. After the _predicate name_, a _parameter list_ of variables is provided. The atoms of the graph predicate are written after the `<->` operator, and a full stop `.` terminates the predicate definition.
78
79The following predicate `entryInRegion` will match pairs of `Region` instances `r` and `Entry` instances `e` such that `e` is a vertex in `r`.
80
81```refinery
82pred entryInRegion(r, e) <->
83 Region(r),
84 vertices(r, e),
85 Entry(e).
86```
87
88We may write unary symbols that act as _parameter types_ directly in the parameter list. The following definition is equivalent to the previous one:
89
90```refinery
91pred entryInRegion(Region r, Entry e) <->
92 vertices(r, e).
93```
94
95import TableIcon from '@material-icons/svg/svg/table_chart/baseline.svg';
96
97:::info
98
99You may display the result of graph predicate matching in the <TableIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Table view icon" />&nbsp;_table view_ of the Refinery web UI.
100
101:::
102
103## Quantification
104
105Variables not appearing in the parameter list are _existentially quantified._
106
107The following predicate matches `Region` instances with two entries:
108
109```refinery
110pred multipleEntriesInRegion(Region r) <->
111 entryInRegion(r, e1),
112 entryInRegion(r, e2),
113 e1 != e2.
114```
115
116Existentially quantified variables that appear only once in the predicate should be prefixed with `_`. This shows that the variable is intentionally used only once (as opposite to the second reference to the variable being omitted by mistake).
117
118```refinery
119pred regionWithEntry(Region r) <->
120 entryInRegion(r, _e).
121```
122
123Alternatively, you may use a single `_` whenever a variable occurring only once is desired. Different occurrences of `_` are considered distinct variables.
124
125```refinery
126pred regionWithEntry(Region r) <->
127 entryInRegion(r, _).
128```
129
130## Negation
131
132Negative literals are written by prefixing the corresponding atom with `!`.
133
134Inside negative literals, quantification is _universal:_ the literal matches if there is no assignment of the variables solely appearing in it that satisfies the corresponding atom.
135
136The following predicate matches `Region` instances that have no `Entry`:
137
138```refinery
139pred regionWithoutEntry(Region r) <->
140 !entryInRegion(r, _).
141```
142
143In a graph predicate, all parameter variables must be _positively bound,_ i.e., appear in at least one positive literal (atom).
144Negative literals may further constrain the predicate match one it has been established by the positive literals.
145
146## Object equality
147
148The operators `a == b` and `a != b` correspond to the literals `equals(a, b)` and `!equals(a, b)`, respectively.
149See the section about [multi-objects](../logic/#multi-objects) for more information about the `equals` symbol.
150
151## Transitive closure
152
153The `+` operator forms the [transitive closure](https://en.wikipedia.org/wiki/Transitive_closure) of symbols with exactly 2 parameters.
154The transitive closure `r+(a, b)` holds if either `r(a, b)` is `true`, or there is a sequence of objects `c1`, `c2`, &hellip;, `cn` such that `r(a, c1)`, `r(c1, c2)`, `r(c2, c3)`, &hellip;, `r(cn, b)`.
155In other words, there is a path labelled with `r` in the graph from `a` to `b`.
156
157Transitive closure may express queries about graph reachability:
158
159```refinery
160pred neighbors(Vertex v1, Vertex v2) <->
161 Transition(t),
162 source(t, v1),
163 target(t, v2).
164
165pred cycle(Vertex v) <->
166 neighbors+(v, v).
167```
168
169## Disjunction
170
171Disjunction (OR) of _clauses_ formed by a conjunction (AND) of literals is denoted by `;`.
172
173```refinery
174pred regionWithInvalidNumberOfEntries(Region r) <->
175 !entryInRegion(r, _)
176;
177 entryInRegion(r, e1),
178 entryInRegion(r, e2),
179 e1 != e2.
180```
181
182Every clause of a disjunction must bind every parameter variable of the graph predicate _positively._
183_Type annotations_ on parameter are applied in all clauses.
184Therefore, the previous graph pattern is equivalent to the following:
185
186```refinery
187pred regionWithInvalidNumberOfEntries(r) <->
188 Region(r),
189 !entryInRegion(r, _)
190;
191 Region(r),
192 entryInRegion(r, e1),
193 entryInRegion(r, e2),
194 e1 != e2.
195```
196
197## Derived features
198
199Graph predicates may act as _derived types_ and _references_ in metamodel.
200
201A graph predicate with exactly 1 parameters can be use as if it was a class: you may use it as a [_parameter type_](#atoms) in other graph patterns, as a _target type_ of a (non-containment) [reference](../classes/#references), or in a [_scope constraint_](../logic#type-scopes).
202
203_Derived references_ are graph predicates with exactly 2 parameters, which correspond the source and target node of the reference.
204
205import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg';
206import LabelIcon from '@material-icons/svg/svg/label/baseline.svg';
207import LabelOutlineIcon from '@material-icons/svg/svg/label/outline.svg';
208
209:::info
210
211You may use the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" />&nbsp;_filter panel_ icon in Refinery to toggle the visibility of graph predicates with 1 or 2 parameters.
212You may either show <LabelOutlineIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Unknown value icon" />&nbsp;_both true and unknown_ values or <LabelIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="True value icon" />&nbsp;_just true_ values.
213
214:::
215
216---
217
218For example, we may replace the reference `neighbors` in the class `Vertex`:
219
220```refinery
221class Vertex {
222 Vertex[] neighbors
223}
224```
225
226with the graph predicate `neighbors` as follows:
227
228
229```refinery
230class Vertex {
231 contains Transition[] outgoingTransition opposite source
232 Transition[] incomingTransition opposite target
233}
234
235class Transition {
236 container Vertex source opposite outgoingTransition
237 Vertex[1] target opposite incomingTransition
238}
239
240pred neighbors(Vertex v1, Vertex v2) <->
241 Transition(t),
242 source(t, v1),
243 target(t, v2).
244```
245
246Since `neighbors` is now computed based on the `Transition` instances and their `source` and `target` references present in the model, the assertion
247
248```refinery
249neighbors(vertex1, vertex2).
250```
251
252will only be satisfied if a corresponding node `transition1` is present in the generated model that also satisfies
253
254```refinery
255Transition(transition1).
256source(transition1, vertex1).
257target(transition1, vertex2).
258```
259
260import DerivedFeature from './DerivedFeature.svg';
261
262<DerivedFeature />
263
264## Error predicates
265
266A common use-case for graph predicates is _model validation_, where a predicate highlights _errors_ in the model.
267Such predicates are called _error predicates._
268In a consistent generated model, an error predicates should have no matches.
269
270You can declare error predicates with the `error` keyword:
271
272```refinery
273error regionWithoutEntry(Region r) <->
274 !entryInRegion(r, _).
275```
276
277This is equivalent to asserting that the error predicate is `false` everywhere:
278
279```refinery
280pred regionWithoutEntry(Region r) <->
281 !entryInRegion(r, _).
282
283!regionWithoutEntry(*).
284```
diff --git a/subprojects/docs/src/learn/tutorials/_category_.yml b/subprojects/docs/src/learn/tutorials/_category_.yml
new file mode 100644
index 00000000..adf8293f
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/_category_.yml
@@ -0,0 +1,11 @@
1# SPDX-FileCopyrightText: 2024 The Refinery Authors
2#
3# SPDX-License-Identifier: EPL-2.0
4
5position: 1
6label: Tutorials
7link:
8 type: generated-index
9 slug: /tutorials
10 title: Tutorial overview
11 description: Try Refinery in practical partial modeling challenges!
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..1e20393a
--- /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 class="graph" transform="translate(4, 238)">
4<!-- n0 -->
5<g 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"/>
6
7<rect stroke="none" x="0" y="-234" width="99.3" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-238" width="107" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-0)" 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" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
11<g><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" 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 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"/>
18
19<rect stroke="none" x="20.24" y="-48.8" width="58.82000000000001" height="48.8" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="16" y="-52" width="66" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-1)" 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" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
23<g><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" 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 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 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"/>
37
38<rect stroke="none" x="49.4" y="-149.2" width="56.50000000000001" height="64.39999999999999" rx="12" ry="12" class="node-bg"/>
39<rect stroke="none" x="45" y="-153" width="64" height="27" clip-path="url(#refinery-_27O8Mh6OPBYQczvSCWrX-clip-2)" 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" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
42<g><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" class="icon icon-TRUE" href="#refinery-_27O8Mh6OPBYQczvSCWrX-icon-TRUE"/>
45<g><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" 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 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 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 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>
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
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..6375bfd6
--- /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 class="graph" transform="translate(4, 438.79998779296875)">
4<!-- n0 -->
5<g 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"/>
6
7<rect stroke="none" x="21.89" y="-434.8" width="99.3" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="17" y="-438" width="107" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-0)" 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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
11<g><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" 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 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"/>
18
19<rect stroke="none" x="29.14" y="-48.8" width="58.81" height="48.8" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="25" y="-52" width="66" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-1)" 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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
23<g><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" 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 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 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"/>
37
38<rect stroke="none" x="43.29" y="-350" width="56.50000000000001" height="64.39999999999998" rx="12" ry="12" class="node-bg"/>
39<rect stroke="none" x="39" y="-354" width="64" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-2)" 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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
42<g><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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
45<g><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" 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 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 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 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" class="node-bg"/>
67<rect stroke="none" x="96" y="-253" width="73" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-3)" 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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
70<g><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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
73<g><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" 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 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 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 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 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 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" class="node-bg"/>
112<rect stroke="none" x="57" y="-153" width="54" height="27" clip-path="url(#refinery-qAo8dBdD08mqlzpuHY9q_-clip-4)" 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" class="icon icon-TRUE" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-TRUE"/>
115<g><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" class="icon icon-UNKNOWN" href="#refinery-qAo8dBdD08mqlzpuHY9q_-icon-UNKNOWN"/>
118<g><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" 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 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 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 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>
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
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..0d020a71
--- /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 class="graph" transform="translate(4, 84.4000015258789)">
4<!-- n0 -->
5<g 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"/>
6
7<rect stroke="none" x="0" y="-72.4" width="99.3" height="64.4" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="-4" y="-76" width="107" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-0)" 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" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="6" y="-43" width="12" height="12" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
11<g><text text-anchor="start" x="21.76" y="-17.2" font-size="12.00" class="label label-TRUE">FileSystem</text>
12</g><g><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" 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 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"/>
21
22<rect stroke="none" x="135.15" y="-72.4" width="59" height="64.4" rx="12" ry="12" class="node-bg"/>
23<rect stroke="none" x="131" y="-76" width="67" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-1)" 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" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="141.151" y="-43" width="12" height="12" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
26<g><text text-anchor="start" x="157.15" y="-17.2" font-size="12.00" class="label label-TRUE">File</text>
27</g><g><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" 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 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 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"/>
41
42<rect stroke="none" x="230.15" y="-80.4" width="58.99999999999997" height="80.4" rx="12" ry="12" class="node-bg"/>
43<rect stroke="none" x="226" y="-84" width="66" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-2)" 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" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="236.151" y="-51" width="12" height="12" class="icon icon-UNKNOWN" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-UNKNOWN"/>
46<g><text text-anchor="start" x="252.15" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
47</g><g><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" 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" class="node-outline"/><g><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 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 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" class="node-bg"/>
69<rect stroke="none" x="321" y="-84" width="73" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-3)" 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" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="331.133" y="-51" width="12" height="12" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
72<g><text text-anchor="start" x="347.13" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
73</g><g><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" 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" class="node-outline"/><g><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 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 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 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" class="node-bg"/>
101<rect stroke="none" x="422" y="-84" width="67" height="27" clip-path="url(#refinery-KGyg4OhNwvGOkw-tAzU-g-clip-4)" 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" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/><use x="432.151" y="-51" width="12" height="12" class="icon icon-TRUE" href="#refinery-KGyg4OhNwvGOkw-tAzU-g-icon-TRUE"/>
104<g><text text-anchor="start" x="448.15" y="-25.2" font-size="12.00" class="label label-TRUE">File</text>
105</g><g><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" 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" class="node-outline"/><g><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 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>
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
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..d6701bdd
--- /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 class="graph" transform="translate(4, 354)">
4<!-- n0 -->
5<g 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"/>
6
7<rect stroke="none" x="35.76" y="-350" width="99.30000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/>
8<rect stroke="none" x="31" y="-354" width="107" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-0)" 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" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
11<g><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" 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 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"/>
18
19<rect stroke="none" x="0" y="-56.6" width="58.81" height="48.800000000000004" rx="12" ry="12" class="node-bg"/>
20<rect stroke="none" x="-4" y="-60" width="66" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-1)" 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" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
23<g><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" 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 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"/>
30
31<rect stroke="none" x="57.16" y="-265.2" width="56.5" height="64.39999999999998" rx="12" ry="12" class="node-bg"/>
32<rect stroke="none" x="53" y="-269" width="64" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-2)" 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" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
35<g><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" 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" class="node-outline"/><g><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 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 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" class="node-bg"/>
55<rect stroke="none" x="97" y="-168" width="73" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-3)" 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" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/>
58<g><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" 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" class="node-outline"/><g><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 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 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 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 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" class="node-bg"/>
92<rect stroke="none" x="72" y="-68" width="127" height="27" clip-path="url(#refinery-D5mYxKifz-hFmWmmvYTA9-clip-4)" 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" class="icon icon-TRUE" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-TRUE"/><use x="82.9072" y="-35" width="12" height="12" class="icon icon-ERROR" href="#refinery-D5mYxKifz-hFmWmmvYTA9-icon-ERROR"/>
95<g><text text-anchor="start" x="98.91" y="-9.2" font-size="12.00" class="label label-TRUE">File</text>
96</g><g><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" 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 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 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 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 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>
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/src/learn/tutorials/file-system/index.md b/subprojects/docs/src/learn/tutorials/file-system/index.md
new file mode 100644
index 00000000..365d0fba
--- /dev/null
+++ b/subprojects/docs/src/learn/tutorials/file-system/index.md
@@ -0,0 +1,209 @@
1---
2SPDX-FileCopyrightText: 2023-2024 The Refinery Authors
3SPDX-License-Identifier: EPL-2.0
4description: Introduction to classes, references, and error predicates
5sidebar_position: 0
6sidebar_label: File system
7---
8
9# File system tutorial
10
11The goal of this tutorial is to give a brief overview of the partial modeling and model generation features of the Refinery framework. The running example will be the modeling of files, directories, and repositories.
12
13## Partial models
14
15### Types and relations
16
17- First, let us introduce some basic types: `Dir`, `File`, and `FileSystem`, along with the relations between them: `element` and `root`. There is a `scope` expression at the end, which we will ignore for now.
18
19```refinery
20class FileSystem {
21 contains File[1] root
22}
23
24class File.
25
26class Dir extends File {
27 contains File[] element
28}
29
30scope node = 10.
31```
32
33import Link from '@docusaurus/Link';
34
35<p>
36 <Link
37 href="https://refinery.services/#/1/KLUv_SDT_QMAQkcXGnBL2-ikxOa10ZNeN1bwnxijfsojpwHQAxAE5pzBk5uCd8F5EjAGJrUNQBWIbdRU7tkRB-VsG_aVuMlSEWzzTShXE8h-eBHzK_cK11NoD9P_2_GFrS61RRmuipYUCwA046ljtvEqgDAGQyDQwsIqKACEt2LiANXAaUxBAQ=="
38 className="button button--lg button--primary button--play"
39 >Try in Refinery</Link>
40</p>
41
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:
44
45import Fig1 from './fig1.svg';
46
47<Fig1 title="Initial model" />
48
49- Add some statements about a partial model:
50
51```refinery
52class FileSystem {
53 contains File[1] root
54}
55
56class File.
57
58class Dir extends File {
59 contains File[] element
60}
61
62Dir(resources).
63element(resources, img).
64File(img).
65
66scope node = 10.
67```
68
69import Fig2 from './fig2.svg';
70
71<Fig2 title="Partial model extended with new facts" />
72
73### Partial models
74
75- Notice that the instance model elements are coexisting with ```<type>::new``` nodes representing the prototypes of newly created objects.
76
77- Check the disabled `equals` and `exist` predicates. check the visual annotation of those predicates in the visualization (dashed line, shadow).
78
79import Fig3 from './fig3.svg';
80
81<Fig3 title="Object existence and equality" />
82
83### Information merging
84
85- For the object `img`, we didn't specify if it is a directory or not. Therefore, it will typically be a folder.
86
87- If we want to state that img is not a directory, we need to a negative statement:
88
89```refinery
90!Dir(img).
91```
92
93- Statements are merged with respect to the refinement relation of 4-valued logic.
94
95- If we add, a statement both negatively and positively, it will create an inconsistency:
96
97```refinery
98element(resources, img).
99!element(resources, img).
100```
101
102- Inconsistent models parts in a partial model typically make the problem trivially unsatisfiable.
103
104import Fig4 from './fig4.svg';
105
106<Fig4 title="Inconsistent partial model" />
107
108- However, the model can be saved if the inconsistent part may not exist...
109
110```refinery
111!File(File::new).
112```
113
114### Default values
115
116- A large amount of statements can be expressed by using `*`.
117- The `default` keyword defines lower priority statements that need to be considered unless other statement specifies otherwise. No information merging is happening.
118
119## Constraints
120
121Let's extend the metamodel with a new class `SymLink`:
122
123```refinery
124class FileSystem {
125 contains File[1] root
126}
127
128class File.
129
130class Dir extends File {
131 contains File[0..10] element
132}
133
134class SymLink extends File {
135 File[1] target
136}
137
138Dir(resources).
139element(resources, img).
140element(resources, link).
141target(link, img).
142
143scope node = 10.
144```
145
146- Add some simple constraints:
147
148```refinery
149% Simple constraints:
150pred hasReference(f) <-> target(_, f).
151error pred selfLoop(s) <-> target(s, s).
152target(x,x).
153```
154
155- There are no empty directories in a git repository, so let's forbid them!
156
157```refinery
158error pred emptyDir(d) <-> Dir(d), !element(d,_).
159```
160
161- End result:
162
163```refinery
164class FileSystem {
165 contains File[1] root
166}
167
168class File.
169
170class Dir extends File {
171 contains File[0..10] element
172}
173
174class SymLink extends File {
175 File[1] target
176}
177
178Dir(resources).
179element(resources, img).
180!Dir(img).
181element(resources, link).
182target(link,img).
183
184% Simple constraints:
185pred hasReference(f) <-> target(_, f).
186error pred selfLoop(s) <-> target(s, s).
187
188% Object equality with ==:
189error pred emptyDir(d) <-> Dir(d), !element(d, _).
190pred importantFile(f) <-> target(l1, f), target(l2, f), l1 != l2.
191
192% Transitive closure, and
193pred containsFile(fs, file) <->
194 FileSystem(fs),
195 root(fs, file)
196;
197 FileSystem(fs),
198 root(fs, rootDir),
199 element+(rootDir, file).
200
201% Predicate reuse
202error conflictBetweenTwoFileSystem(fs1, fs2, l, t) <->
203 containsFile(fs1, l),
204 containsFile(fs2, t),
205 fs1 != fs2,
206 target(l, t).
207
208scope node = 40..50, FileSystem = 2, importantFile = 1..*.
209```
diff --git a/subprojects/docs/src/pages/index.module.css b/subprojects/docs/src/pages/index.module.css
new file mode 100644
index 00000000..367b78ed
--- /dev/null
+++ b/subprojects/docs/src/pages/index.module.css
@@ -0,0 +1,41 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7.page {
8 margin-bottom: 2rem;
9}
10
11.buttons {
12 display: flex;
13 flex-direction: row;
14 flex-wrap: wrap;
15 gap: 1rem;
16}
17
18.section {
19 padding: 2rem 0;
20}
21
22@media (max-width: 576px) {
23 .hero, .section {
24 padding-top: 2rem;
25 padding-bottom: 2rem;
26 }
27
28 .button {
29 flex-grow: 1;
30 }
31}
32
33.section__title {
34 text-align: center;
35 margin-bottom: var(--ifm-leading);
36}
37
38.section--video {
39 padding: 4rem 0;
40 background: var(--ifm-background-surface-color);
41}
diff --git a/subprojects/docs/src/pages/index.tsx b/subprojects/docs/src/pages/index.tsx
new file mode 100644
index 00000000..cb0bfeb8
--- /dev/null
+++ b/subprojects/docs/src/pages/index.tsx
@@ -0,0 +1,243 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import Link from '@docusaurus/Link';
8import Layout from '@theme/Layout';
9import clsx from 'clsx';
10
11import styles from './index.module.css';
12
13import Features from '@site/src/components/Features';
14import UseCases from '@site/src/components/UseCases';
15import Video from '@site/src/components/Video';
16
17function Hero() {
18 return (
19 <header className={clsx('hero', 'hero--dark', styles['hero'])}>
20 <div className="container">
21 <h1 className="hero__title">Refinery</h1>
22 <p className="hero__subtitle">
23 An efficient graph solver for generating well-formed models
24 </p>
25 <div className={styles['buttons']}>
26 <Link
27 href="https://refinery.services/"
28 className={clsx(
29 'button',
30 'button--lg',
31 'button--primary',
32 styles['button'],
33 )}
34 >
35 Try online
36 </Link>
37 <Link
38 to="/learn/docker"
39 className={clsx(
40 'button',
41 'button--lg',
42 'button--secondary',
43 styles['button'],
44 )}
45 >
46 Try in Docker
47 </Link>
48 <Link
49 to="/learn/tutorials/file-system"
50 className={clsx(
51 'button',
52 'button--lg',
53 'button--secondary',
54 styles['button'],
55 )}
56 >
57 Tutorial
58 </Link>
59 </div>
60 </div>
61 </header>
62 );
63}
64
65function Publication({
66 doi,
67 pdf,
68 video,
69 children,
70}: {
71 doi?: string;
72 pdf?: string;
73 video?: string;
74 children?: React.ReactNode;
75}) {
76 return (
77 <li>
78 {children}
79 {doi && (
80 <>
81 {' '}
82 [<Link href={`https://doi.org/${doi}`}>doi</Link>]
83 </>
84 )}
85 {pdf && (
86 <>
87 {' '}
88 [<Link href={pdf}>pdf</Link>]
89 </>
90 )}
91 {video && (
92 <>
93 {' '}
94 [<Link href={video}>video</Link>]
95 </>
96 )}
97 </li>
98 );
99}
100
101Publication.defaultProps = {
102 doi: undefined,
103 pdf: undefined,
104 video: undefined,
105 children: undefined,
106};
107
108function Publications() {
109 return (
110 <div className="row">
111 <div className="col col--6">
112 <h3>Tool demonstration</h3>
113 <ul>
114 <Publication
115 doi="10.1145/3639478.3640045"
116 pdf="pathname:///papers/icse24-demo.pdf"
117 video="https://youtu.be/Qy_3udNsWsM"
118 >
119 K. Marussy, A. Ficsor, O. Semeráth, D. Varró: &ldquo;Refinery: Graph
120 Solver as a Service&rdquo; <em>ICSE 2024 Demonstrations</em>
121 </Publication>
122 </ul>
123 <h3>Partial model specification language</h3>
124 <ul>
125 <Publication
126 doi="10.5381/jot.2020.19.3.a12"
127 pdf="https://www.jot.fm/issues/issue_2020_03/article12.pdf"
128 video="https://www.youtube.com/watch?v=ggTbv_s5t2A"
129 >
130 K. Marussy, O. Semeráth, A. Babikian, D. Varró:{' '}
131 <em>
132 A Specification Language for Consistent Model Generation based on
133 Partial Models.
134 </em>{' '}
135 J. Object Technol. <b>19</b>(3): 3:1-22 (2020)
136 </Publication>
137 </ul>
138 <h3>Diverse and realistic graph generation</h3>
139 <ul>
140 <Publication
141 doi="10.1007/s10009-019-00530-6"
142 pdf="https://link.springer.com/content/pdf/10.1007/s10009-019-00530-6.pdf?pdf=button"
143 >
144 O. Semeráth, R. Farkas, G. Bergmann, D. Varró:{' '}
145 <em>
146 Diversity of graph models and graph generators in mutation
147 testing.
148 </em>{' '}
149 Int. J. Softw. Tools Technol. Transf. <b>22</b>(1): 57-78 (2020)
150 </Publication>
151 <Publication
152 doi="10.1007/s10270-021-00884-z"
153 pdf="https://link.springer.com/content/pdf/10.1007/s10270-021-00884-z.pdf?pdf=button"
154 >
155 O. Semeráth, A. Babikian, B. Chen, C. Li, K. Marussy, G. Szárnyas,
156 D. Varró:{' '}
157 <em>
158 Automated generation of consistent, diverse and structurally
159 realistic graph models.
160 </em>{' '}
161 Softw. Syst. Model. <b>20</b>(5): 1713-1734 (2021)
162 </Publication>
163 </ul>
164 </div>
165 <div className="col col--6">
166 <h3>Consistent graph generation techniques</h3>
167 <ul>
168 <Publication
169 doi="10.1145/3180155.3180186"
170 pdf="https://dl.acm.org/doi/pdf/10.1145/3180155.3180186"
171 >
172 O. Semeráth, A. Nagy, D. Varró: &ldquo;A graph solver for the
173 automated generation of consistent domain-specific models.&rdquo;{' '}
174 <em>ICSE 2018:</em> 969-980
175 </Publication>
176 <Publication
177 doi="10.1109/TSE.2020.3025732"
178 pdf="https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9201551"
179 >
180 K. Marussy, O. Semeráth, D. Varró:{' '}
181 <em>
182 Automated Generation of Consistent Graph Models With Multiplicity
183 Reasoning.
184 </em>{' '}
185 IEEE Trans. Softw. Eng. <b>48</b>(5): 1610-1629 (2022)
186 </Publication>
187 <Publication
188 doi="10.1007/s10270-021-00918-6"
189 pdf="https://link.springer.com/content/pdf/10.1007/s10270-021-00918-6.pdf?pdf=button"
190 >
191 A. Babikian, O. Semeráth, A. Li, K. Marussy, D. Varró:{' '}
192 <em>
193 Automated generation of consistent models using qualitative
194 abstractions and exploration strategies.
195 </em>{' '}
196 Softw. Syst. Model. <b>21</b>(5): 1763-1787 (2022)
197 </Publication>
198 </ul>
199 <h3>Correctness proofs</h3>
200 <ul>
201 <Publication
202 doi="10.1007/978-3-319-75396-6_16"
203 pdf="https://inf.mit.bme.hu/sites/default/files/publications/fmhe-model-generation.pdf"
204 >
205 D. Varró, O. Semeráth, G. Szárnyas, Á. Horváth: &ldquo;Towards the
206 Automated Generation of Consistent, Diverse, Scalable and Realistic
207 Graph Models.&rdquo;{' '}
208 <em>Graph Transformation, Specifications, and Nets</em> 2018:
209 285-312
210 </Publication>
211 </ul>
212 </div>
213 </div>
214 );
215}
216
217export default function Home() {
218 return (
219 <Layout>
220 <div className={styles['page']}>
221 <Hero />
222 <section className={styles['section']}>
223 <Features />
224 </section>
225 <section className={clsx(styles['section'], styles['section--video'])}>
226 <Video />
227 </section>
228 <section className={styles['section']}>
229 <div className="container">
230 <h2 className={styles['section__title']}>Explore use-cases</h2>
231 <UseCases />
232 </div>
233 </section>
234 <section className={styles['section']}>
235 <div className="container">
236 <h2 className={styles['section__title']}>Related publications</h2>
237 <Publications />
238 </div>
239 </section>
240 </div>
241 </Layout>
242 );
243}
diff --git a/subprojects/docs/src/pages/license.md b/subprojects/docs/src/pages/license.md
new file mode 100644
index 00000000..3ce07e47
--- /dev/null
+++ b/subprojects/docs/src/pages/license.md
@@ -0,0 +1,100 @@
1---
2SPDX-FileCopyrightText: 2017, Eclipse.org Foundation, Inc.
3SPDX-License-Identifier: LicenseRef-EPL-Steward
4title: License
5---
6
7# Eclipse Public License - v 2.0
8
9THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
10
11## 1. DEFINITIONS
12
13“Contribution” means:
14
15 * a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and
16 * b) in the case of each subsequent Contributor:
17 * i) changes to the Program, and
18 * ii) additions to the Program;
19 where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works.
20
21“Contributor” means any person or entity that Distributes the Program.
22
23“Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
24
25“Program” means the Contributions Distributed in accordance with this Agreement.
26
27“Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors.
28
29“Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship.
30
31“Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof.
32
33“Distribute” means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy.
34
35“Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files.
36
37“Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor.
38
39## 2. GRANT OF RIGHTS
40
41 * a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works.
42 * b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
43 * c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
44 * d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
45 * e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3).
46
47## 3. REQUIREMENTS
48
493.1 If a Contributor Distributes the Program in any form, then:
50
51 * a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and
52 * b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license:
53 * i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
54 * ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
55 * iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and
56 * iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3.
57
583.2 When the Program is Distributed as Source Code:
59
60 * a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and
61 * b) a copy of this Agreement must be included with each copy of the Program.
62
633.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (‘notices’) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices.
64
65## 4. COMMERCIAL DISTRIBUTION
66
67Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
68
69For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
70
71## 5. NO WARRANTY
72
73EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
74
75## 6. DISCLAIMER OF LIABILITY
76
77EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
78
79## 7. GENERAL
80
81If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
82
83If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
84
85All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
86
87Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version.
88
89Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement.
90
91## Exhibit A – Form of Secondary Licenses Notice
92
93“This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: \{name license(s), version(s), and exceptions or additional permissions here\}.”
94
95> Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses.
96>
97> If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
98>
99> You may add additional accurate notices of copyright ownership.
100
diff --git a/subprojects/docs/src/plugins/loadersPlugin.ts b/subprojects/docs/src/plugins/loadersPlugin.ts
new file mode 100644
index 00000000..8b52667c
--- /dev/null
+++ b/subprojects/docs/src/plugins/loadersPlugin.ts
@@ -0,0 +1,117 @@
1/*
2 * Copyright (c) 2016, Jeremy Stucki
3 * Copyright (c) Facebook, Inc. and its affiliates.
4 * Copyright (c) 2024 The Refinery Authors
5 *
6 * SPDX-License-Identifier: BSD-3-Clause AND MIT AND EPL-2.0
7 */
8
9import type { Plugin } from '@docusaurus/types';
10// @ts-expect-error No typings available for `responsive-loader`.
11import sharp from 'responsive-loader/sharp';
12
13export default function loadersPlugin(): Plugin {
14 return {
15 name: 'refinery-loaders-plugin',
16 configureWebpack(config, isServer) {
17 let svgoDisabled = false;
18 const rules = [...(config.module?.rules ?? [])];
19 rules.forEach((rule) => {
20 // Compare with
21 // https://github.com/facebook/docusaurus/blob/73016d4936164ba38d4b86ec2aa8c168b5904a21/packages/docusaurus-utils/src/webpackUtils.ts#L128-L166
22 if (
23 typeof rule !== 'object' ||
24 rule === null ||
25 !('test' in rule) ||
26 !(rule.test instanceof RegExp) ||
27 !rule.test.test('.svg') ||
28 !('oneOf' in rule)
29 ) {
30 return;
31 }
32 const {
33 oneOf: [svgLoader],
34 } = rule;
35 if (
36 typeof svgLoader !== 'object' ||
37 svgLoader === null ||
38 !('use' in svgLoader) ||
39 typeof svgLoader.use !== 'object' ||
40 svgLoader.use === null ||
41 !(0 in svgLoader.use)
42 ) {
43 return;
44 }
45 // Skip SVGR when importing SVG files with ?url.
46 svgLoader.resourceQuery = { not: /[?&]url$/ };
47 const {
48 use: [loader],
49 } = svgLoader;
50 if (
51 typeof loader !== 'object' ||
52 loader === null ||
53 !('options' in loader)
54 ) {
55 return;
56 }
57
58 loader.options = {
59 ...(typeof loader.options === 'object' ? loader.options : {}),
60 svgo: true,
61 svgoConfig: {
62 plugins: [
63 {
64 name: 'preset-default',
65 params: {
66 overrides: {
67 removeTitle: false,
68 removeViewBox: false,
69 // Disable SVGO, because it interferes styling figures exported from Refinery with CSS.
70 inlineStyles: false,
71 cleanupIds: false,
72 },
73 },
74 },
75 ],
76 },
77 };
78 svgoDisabled = true;
79 });
80 if (!svgoDisabled) {
81 throw new Error('Failed to disable SVGO.');
82 }
83 return {
84 mergeStrategy: {
85 'module.rules': 'replace',
86 },
87 module: {
88 rules: [
89 // Configuration based on
90 // https://github.com/dazuaz/responsive-loader/blob/ef2c806fcd36f06f6be8a0b97e09f40c3d86d3ac/README.md
91 {
92 test: /\.(png|jpe?g)$/,
93 resourceQuery: /[?&]rl$/,
94 use: [
95 {
96 loader: 'responsive-loader',
97 options: {
98 /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment --
99 * No typings available for `responsive-loader`.
100 */
101 adapter: sharp,
102 format: 'webp',
103 // See
104 // https://github.com/facebook/docusaurus/blob/c745021b01a8b88d34e1d772278d7171ad8acdf5/packages/docusaurus-plugin-ideal-image/src/index.ts#L62-L66
105 emitFile: !isServer,
106 name: 'assets/images/[name].[hash:hex:7].[width].[ext]',
107 },
108 },
109 ],
110 },
111 ...rules,
112 ],
113 },
114 };
115 },
116 };
117}
diff --git a/subprojects/docs/src/plugins/remarkPosix2Windows.ts b/subprojects/docs/src/plugins/remarkPosix2Windows.ts
new file mode 100644
index 00000000..784802f2
--- /dev/null
+++ b/subprojects/docs/src/plugins/remarkPosix2Windows.ts
@@ -0,0 +1,169 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 The Refinery Authors
4 *
5 * SPDX-License-Identifier: EPL-2.0
6 *
7 * This file is based on
8 * https://github.com/facebook/docusaurus/blob/e4ecffe41878728acff55a8370bd7440706c02f7/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts
9 * but was changed to conver shell commands to POSIX to Windows syntax.
10 */
11
12import type { Code, Literal } from 'mdast';
13import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx';
14import type { Transformer } from 'unified';
15import type { Node, Parent } from 'unist';
16import { visit } from 'unist-util-visit';
17
18function isLiteral(node: Node): node is Literal {
19 return node.type === 'mdxjsEsm';
20}
21
22function isTabImport(node: Node): boolean {
23 return isLiteral(node) && node.value.includes('@theme/Tabs');
24}
25
26function isParent(node: Node): node is Parent {
27 return 'children' in node && Array.isArray(node.children);
28}
29
30function isCode(node: Node): node is Code {
31 return node.type === 'code';
32}
33
34function isPosix2Windows(node: Node): node is Code {
35 return isCode(node) && node.meta === 'posix2windows';
36}
37
38function createTabItem(
39 code: string,
40 node: Code,
41 value: string,
42 label: string,
43): MdxJsxFlowElement {
44 return {
45 type: 'mdxJsxFlowElement',
46 name: 'TabItem',
47 attributes: [
48 {
49 type: 'mdxJsxAttribute',
50 name: 'value',
51 value,
52 },
53 {
54 type: 'mdxJsxAttribute',
55 name: 'label',
56 value: label,
57 },
58 ],
59 children: [
60 {
61 type: node.type,
62 lang: node.lang,
63 value: code,
64 },
65 ],
66 };
67}
68
69function transformNode(node: Code): MdxJsxFlowElement[] {
70 const posixCode = node.value;
71 const windowsCode = posixCode.replaceAll(
72 /(^\w*)\.\//gm,
73 (_substring, prefix: string) => `${prefix}.\\`,
74 );
75 return [
76 {
77 type: 'mdxJsxFlowElement',
78 name: 'Tabs',
79 attributes: [
80 {
81 type: 'mdxJsxAttribute',
82 name: 'groupId',
83 value: 'posix2windows',
84 },
85 ],
86 children: [
87 createTabItem(posixCode, node, 'posix', 'Linux or macOS'),
88 createTabItem(windowsCode, node, 'windows', 'Windows (PowerShell)'),
89 ],
90 },
91 ];
92}
93
94function createImportNode(): MdxjsEsm {
95 return {
96 type: 'mdxjsEsm',
97 value:
98 "import Tabs from '@theme/Tabs'\nimport TabItem from '@theme/TabItem'",
99 data: {
100 estree: {
101 type: 'Program',
102 body: [
103 {
104 type: 'ImportDeclaration',
105 specifiers: [
106 {
107 type: 'ImportDefaultSpecifier',
108 local: { type: 'Identifier', name: 'Tabs' },
109 },
110 ],
111 source: {
112 type: 'Literal',
113 value: '@theme/Tabs',
114 raw: "'@theme/Tabs'",
115 },
116 },
117 {
118 type: 'ImportDeclaration',
119 specifiers: [
120 {
121 type: 'ImportDefaultSpecifier',
122 local: { type: 'Identifier', name: 'TabItem' },
123 },
124 ],
125 source: {
126 type: 'Literal',
127 value: '@theme/TabItem',
128 raw: "'@theme/TabItem'",
129 },
130 },
131 ],
132 sourceType: 'module',
133 },
134 },
135 };
136}
137
138export default function remarkPosix2Windows(): Transformer {
139 return (root) => {
140 let transformed = false;
141 let alreadyImported = false;
142 visit(root, (node) => {
143 if (isTabImport(node)) {
144 alreadyImported = true;
145 }
146 if (isParent(node)) {
147 let index = 0;
148 while (index < node.children.length) {
149 const child = node.children[index];
150 if (child !== undefined && isPosix2Windows(child)) {
151 const result = transformNode(child);
152 node.children.splice(index, 1, ...result);
153 index += result.length;
154 transformed = true;
155 } else {
156 index += 1;
157 }
158 }
159 }
160 });
161 if (transformed && !alreadyImported) {
162 if (isParent(root)) {
163 root.children.unshift(createImportNode());
164 } else {
165 throw new Error("Cannot import '@theme/Tabs'");
166 }
167 }
168 };
169}
diff --git a/subprojects/docs/src/plugins/swcMinifyPlugin.ts b/subprojects/docs/src/plugins/swcMinifyPlugin.ts
new file mode 100644
index 00000000..ecac654b
--- /dev/null
+++ b/subprojects/docs/src/plugins/swcMinifyPlugin.ts
@@ -0,0 +1,41 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import type { Plugin } from '@docusaurus/types';
8import TerserPlugin from 'terser-webpack-plugin';
9
10/**
11 * A Docusarus plugin that replaces the built-in Javascript minifier with swc.
12 *
13 * See
14 * https://github.com/facebook/docusaurus/issues/4765#issuecomment-1679863984
15 * but we use swc instead of esbuild.
16 *
17 * @returns The Docusarus plugin.
18 */
19export default function swcMinifyPlugin(): Plugin {
20 return {
21 name: 'refinery-swc-minify-plugin',
22 configureWebpack: (config) => ({
23 mergeStrategy: {
24 'optimization.minimizer': 'replace',
25 },
26 optimization: {
27 minimizer:
28 config.optimization?.minimizer?.map((plugin) => {
29 // `instanceof` seems to be broken, because a different version of
30 // `TerserPlguin` is coming from Docusaurus than the one we import.
31 if (plugin?.constructor.name === TerserPlugin.name) {
32 return new TerserPlugin({
33 minify: TerserPlugin.swcMinify,
34 });
35 }
36 return plugin;
37 }) ?? [],
38 },
39 }),
40 };
41}
diff --git a/subprojects/docs/src/types.d.ts b/subprojects/docs/src/types.d.ts
new file mode 100644
index 00000000..658763e7
--- /dev/null
+++ b/subprojects/docs/src/types.d.ts
@@ -0,0 +1,39 @@
1/*
2 * Copyright (c) 2016, Jeremy Stucki
3 * Copyright (c) 2024 The Refinery Authors
4 *
5 * SPDX-License-Identifier: BSD-3-Clause AND EPL-2.0
6 *
7 * Typings for `ResponsiveImageOutput` copied from
8 * https://github.com/dazuaz/responsive-loader/blob/ef2c806fcd36f06f6be8a0b97e09f40c3d86d3ac/README.md
9 */
10
11declare module '*?url' {
12 const url: string;
13 export default url;
14}
15
16declare module '*&url' {
17 const url: string;
18 export default url;
19}
20
21interface ResponsiveImageOutput {
22 src: string;
23 srcSet: string;
24 placeholder: string | undefined;
25 images: { path: string; width: number; height: number }[];
26 width: number;
27 height: number;
28 toString: () => string;
29}
30
31declare module '*?rl' {
32 const src: ResponsiveImageOutput;
33 export default src;
34}
35
36declare module '*&rl' {
37 const src: ResponsiveImageOutput;
38 export default src;
39}
diff --git a/subprojects/docs/static/.nojekyll b/subprojects/docs/static/.nojekyll
new file mode 100644
index 00000000..cfd3d41b
--- /dev/null
+++ b/subprojects/docs/static/.nojekyll
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors
2
3SPDX-License-Identifier: CC0-1.0
diff --git a/subprojects/docs/static/CNAME b/subprojects/docs/static/CNAME
new file mode 100644
index 00000000..4e43c2cd
--- /dev/null
+++ b/subprojects/docs/static/CNAME
@@ -0,0 +1 @@
refinery.tools \ No newline at end of file
diff --git a/subprojects/docs/static/CNAME.license b/subprojects/docs/static/CNAME.license
new file mode 100644
index 00000000..f1dad64a
--- /dev/null
+++ b/subprojects/docs/static/CNAME.license
@@ -0,0 +1,4 @@
1SPDX-FileCopyrightText: 2021 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: CC0-1.0
4
diff --git a/subprojects/docs/static/apple-touch-icon.png b/subprojects/docs/static/apple-touch-icon.png
new file mode 100644
index 00000000..de8549e7
--- /dev/null
+++ b/subprojects/docs/static/apple-touch-icon.png
Binary files differ
diff --git a/subprojects/docs/static/apple-touch-icon.png.license b/subprojects/docs/static/apple-touch-icon.png.license
new file mode 100644
index 00000000..e5db6ccd
--- /dev/null
+++ b/subprojects/docs/static/apple-touch-icon.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/favicon-96x96.png b/subprojects/docs/static/favicon-96x96.png
new file mode 100644
index 00000000..353fe18a
--- /dev/null
+++ b/subprojects/docs/static/favicon-96x96.png
Binary files differ
diff --git a/subprojects/docs/static/favicon-96x96.png.license b/subprojects/docs/static/favicon-96x96.png.license
new file mode 100644
index 00000000..e5db6ccd
--- /dev/null
+++ b/subprojects/docs/static/favicon-96x96.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/favicon.png b/subprojects/docs/static/favicon.png
new file mode 100644
index 00000000..18e67636
--- /dev/null
+++ b/subprojects/docs/static/favicon.png
Binary files differ
diff --git a/subprojects/docs/static/favicon.png.license b/subprojects/docs/static/favicon.png.license
new file mode 100644
index 00000000..e5db6ccd
--- /dev/null
+++ b/subprojects/docs/static/favicon.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/favicon.svg b/subprojects/docs/static/favicon.svg
new file mode 100644
index 00000000..b5d1d217
--- /dev/null
+++ b/subprojects/docs/static/favicon.svg
@@ -0,0 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><style>@media(prefers-color-scheme:dark){#a{fill:#ebebff}#b{fill:#56b6c2}}</style><path d="M447.98 179.335c-139.95-9.583-301.272-50.91-384-147.336v46.117C98.45 129.623 209.442 178.137 294.243 199.1c-84.796 20.963-195.791 69.476-230.265 120.985v46.117c82.73-96.422 244.053-137.752 384.002-147.334z" fill="#35373e" id="a"/><path d="M447.98 296.729c-113.755 4.192-287.485 40.727-384 136.557v46.716c95.14-103.612 279.898-137.754 384-143.745z" fill="#038a99" id="b"/></svg>
diff --git a/subprojects/docs/static/favicon.svg.license b/subprojects/docs/static/favicon.svg.license
new file mode 100644
index 00000000..e5db6ccd
--- /dev/null
+++ b/subprojects/docs/static/favicon.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/icon-192x192.png b/subprojects/docs/static/icon-192x192.png
new file mode 100644
index 00000000..2417b8df
--- /dev/null
+++ b/subprojects/docs/static/icon-192x192.png
Binary files differ
diff --git a/subprojects/docs/static/icon-192x192.png.license b/subprojects/docs/static/icon-192x192.png.license
new file mode 100644
index 00000000..a73a3364
--- /dev/null
+++ b/subprojects/docs/static/icon-192x192.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/icon-512x512.png b/subprojects/docs/static/icon-512x512.png
new file mode 100644
index 00000000..22f553c6
--- /dev/null
+++ b/subprojects/docs/static/icon-512x512.png
Binary files differ
diff --git a/subprojects/docs/static/icon-512x512.png.license b/subprojects/docs/static/icon-512x512.png.license
new file mode 100644
index 00000000..a73a3364
--- /dev/null
+++ b/subprojects/docs/static/icon-512x512.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/icon-any.svg b/subprojects/docs/static/icon-any.svg
new file mode 100644
index 00000000..9b51b758
--- /dev/null
+++ b/subprojects/docs/static/icon-any.svg
@@ -0,0 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h512v512H0z" fill="#282c34"/><path d="M387.985 218.608c-96.215-6.674-207.125-35.454-264-102.609v32.118c23.698 35.87 100.005 69.657 158.306 84.257-58.297 14.599-134.606 48.385-158.307 84.258v32.117c56.876-67.153 167.786-95.936 264-102.609zm0 81.752c-78.207 2.92-197.646 28.364-264 95.103v32.535c65.409-72.159 192.43-95.936 264-100.108z" fill="#181a1f"/><path d="M387.985 202.606c-96.215-6.674-207.125-35.455-264-102.609v32.117c23.698 35.871 100.005 69.658 158.306 84.258-58.297 14.599-134.606 48.384-158.307 84.257v32.117c56.876-67.152 167.786-95.935 264-102.608z" fill="#ebebff"/><path d="M387.985 284.362c-78.207 2.92-197.646 28.364-264 95.103v32.534c65.409-72.158 192.43-95.936 264-100.108z" fill="#56b6c2"/></svg>
diff --git a/subprojects/docs/static/icon-any.svg.license b/subprojects/docs/static/icon-any.svg.license
new file mode 100644
index 00000000..a73a3364
--- /dev/null
+++ b/subprojects/docs/static/icon-any.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/logo-dark.svg b/subprojects/docs/static/logo-dark.svg
new file mode 100644
index 00000000..8f9af60d
--- /dev/null
+++ b/subprojects/docs/static/logo-dark.svg
@@ -0,0 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><path d="M447.98 179.335c-139.95-9.583-301.272-50.91-384-147.336v46.117C98.45 129.623 209.442 178.137 294.243 199.1c-84.796 20.963-195.791 69.476-230.265 120.985v46.117c82.73-96.422 244.053-137.752 384.002-147.334z" fill="#ebebff" id="a"/><path d="M447.98 296.729c-113.755 4.192-287.485 40.727-384 136.557v46.716c95.14-103.612 279.898-137.754 384-143.745z" fill="#56b6c2" id="b"/></svg>
diff --git a/subprojects/docs/static/logo-dark.svg.license b/subprojects/docs/static/logo-dark.svg.license
new file mode 100644
index 00000000..a73a3364
--- /dev/null
+++ b/subprojects/docs/static/logo-dark.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/logo.svg b/subprojects/docs/static/logo.svg
new file mode 100644
index 00000000..43d302e3
--- /dev/null
+++ b/subprojects/docs/static/logo.svg
@@ -0,0 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><path d="M447.98 179.335c-139.95-9.583-301.272-50.91-384-147.336v46.117C98.45 129.623 209.442 178.137 294.243 199.1c-84.796 20.963-195.791 69.476-230.265 120.985v46.117c82.73-96.422 244.053-137.752 384.002-147.334z" fill="#35373e" id="a"/><path d="M447.98 296.729c-113.755 4.192-287.485 40.727-384 136.557v46.716c95.14-103.612 279.898-137.754 384-143.745z" fill="#038a99" id="b"/></svg>
diff --git a/subprojects/docs/static/logo.svg.license b/subprojects/docs/static/logo.svg.license
new file mode 100644
index 00000000..a73a3364
--- /dev/null
+++ b/subprojects/docs/static/logo.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/manifest.webmanifest b/subprojects/docs/static/manifest.webmanifest
new file mode 100644
index 00000000..52e608a3
--- /dev/null
+++ b/subprojects/docs/static/manifest.webmanifest
@@ -0,0 +1,35 @@
1{
2 "name": "Refinery Documentation",
3 "short_name": "Refinery Docs",
4 "description": "An efficient graph solver for generating well-formed models",
5 "theme_color": "#f5f5f5",
6 "start_url": ".",
7 "display": "standalone",
8 "background_color": "#282c34",
9 "icons": [
10 {
11 "src": "icon-192x192.png",
12 "sizes": "192x192",
13 "type": "image/png",
14 "purpose": "any maskable"
15 },
16 {
17 "src": "icon-512x512.png",
18 "sizes": "512x512",
19 "type": "image/png",
20 "purpose": "any maskable"
21 },
22 {
23 "src": "icon-any.svg",
24 "sizes": "any",
25 "type": "image/svg+xml",
26 "purpose": "any maskable"
27 },
28 {
29 "src": "mask-icon.svg",
30 "sizes": "any",
31 "type": "image/svg+xml",
32 "purpose": "monochrome"
33 }
34 ]
35}
diff --git a/subprojects/docs/static/manifest.webmanifest.license b/subprojects/docs/static/manifest.webmanifest.license
new file mode 100644
index 00000000..b80566a0
--- /dev/null
+++ b/subprojects/docs/static/manifest.webmanifest.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/mask-icon.svg b/subprojects/docs/static/mask-icon.svg
new file mode 100644
index 00000000..86052c6e
--- /dev/null
+++ b/subprojects/docs/static/mask-icon.svg
@@ -0,0 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><path d="M64 32v46.117c34.47 51.508 145.46 100.02 230.26 120.98C209.463 220.06 98.47 268.573 64 320.077v46.117c82.729-96.428 244.05-137.75 384-147.34v-39.531c-139.95-9.583-301.27-50.908-384-147.34zm384 264.73c-113.75 4.192-287.48 40.728-384 136.56v46.717c95.138-103.61 279.9-137.75 384-143.74v-39.529z"/></svg>
diff --git a/subprojects/docs/static/mask-icon.svg.license b/subprojects/docs/static/mask-icon.svg.license
new file mode 100644
index 00000000..e5db6ccd
--- /dev/null
+++ b/subprojects/docs/static/mask-icon.svg.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/static/papers/icse24-demo.pdf b/subprojects/docs/static/papers/icse24-demo.pdf
new file mode 100644
index 00000000..7b092e85
--- /dev/null
+++ b/subprojects/docs/static/papers/icse24-demo.pdf
Binary files differ
diff --git a/subprojects/docs/static/papers/icse24-demo.pdf.license b/subprojects/docs/static/papers/icse24-demo.pdf.license
new file mode 100644
index 00000000..93208f90
--- /dev/null
+++ b/subprojects/docs/static/papers/icse24-demo.pdf.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2024 Kristóf Marussy, Attila Ficsor, Oszkár Semeráth, Dániel Varró
2
3SPDX-License-Identifier: CC-BY-4.0
diff --git a/subprojects/docs/static/robots.txt b/subprojects/docs/static/robots.txt
new file mode 100644
index 00000000..1cf41da7
--- /dev/null
+++ b/subprojects/docs/static/robots.txt
@@ -0,0 +1,8 @@
1# SPDX-FileCopyrightText: 2024 The Refinery Authors
2#
3# SPDX-License-Identifier: CC0-1.0
4
5Sitemap: https://refinery.tools/sitemap.xml
6
7User-agent: *
8Allow: /
diff --git a/subprojects/docs/static/screenshot.png b/subprojects/docs/static/screenshot.png
new file mode 100644
index 00000000..5dfd1241
--- /dev/null
+++ b/subprojects/docs/static/screenshot.png
Binary files differ
diff --git a/subprojects/docs/static/screenshot.png.license b/subprojects/docs/static/screenshot.png.license
new file mode 100644
index 00000000..ff75bc7c
--- /dev/null
+++ b/subprojects/docs/static/screenshot.png.license
@@ -0,0 +1,3 @@
1SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
2
3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/docs/tsconfig.json b/subprojects/docs/tsconfig.json
new file mode 100644
index 00000000..5b32bb0e
--- /dev/null
+++ b/subprojects/docs/tsconfig.json
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/>
4 *
5 * SPDX-License-Identifier: MIT AND EPL-2.0
6 *
7 * FIle based on
8 * https://github.com/facebook/docusaurus/blob/73016d4936164ba38d4b86ec2aa8c168b5904a21/packages/docusaurus-tsconfig/tsconfig.json
9 * but copied instead of adding an `extends` declaration to let SonarQube see its contents.
10 */
11{
12 "extends": "../../tsconfig.base.json",
13 "compilerOptions": {
14 "jsx": "preserve",
15 "module": "esnext",
16 "moduleResolution": "bundler",
17 "lib": ["DOM", "DOM.Iterable", "ES2022"],
18 "types": [
19 "node",
20 "@docusaurus/module-type-aliases",
21 "@docusaurus/theme-classic"
22 ],
23 "baseUrl": ".",
24 "paths": {
25 "@site/*": ["./*"]
26 }
27 },
28 "include": ["."],
29 "exclude": [
30 ".docusaurus",
31 ".yarn",
32 "build"
33 ]
34}
diff --git a/subprojects/frontend/.gitignore b/subprojects/frontend/.gitignore
new file mode 100644
index 00000000..52a177c5
--- /dev/null
+++ b/subprojects/frontend/.gitignore
@@ -0,0 +1,6 @@
1# SPDX-FileCopyrightText: 2021-2023 The Refinery Authors
2#
3# SPDX-License-Identifier: CC0-1.0
4
5dev-dist/
6*.typegen.ts
diff --git a/subprojects/frontend/build.gradle.kts b/subprojects/frontend/build.gradle.kts
index ac2c1817..10a138b1 100644
--- a/subprojects/frontend/build.gradle.kts
+++ b/subprojects/frontend/build.gradle.kts
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -25,40 +25,34 @@ val productionAssets: Configuration by configurations.creating {
25 isCanBeResolved = false 25 isCanBeResolved = false
26} 26}
27 27
28val sourcesWithoutTypes = fileTree("src") { 28val sourcesWithoutTypes: FileCollection = fileTree("src") {
29 exclude("**/*.typegen.ts") 29 exclude("**/*.typegen.ts")
30} 30}
31 31
32val sourcesWithTypes: FileCollection = fileTree("src") + fileTree("types") 32val sourcesWithTypes: FileCollection = fileTree("src") + fileTree("types")
33 33
34val buildScripts: FileCollection = fileTree("config") + files( 34val installationState: FileCollection = files(
35 rootProject.file(".eslintrc.cjs"), 35 rootProject.file("yarn.lock"),
36 rootProject.file("prettier.config.cjs"), 36 rootProject.file("package.json"),
37 "vite.config.ts", 37 "package.json",
38) 38)
39 39
40val installationState = files( 40val assembleConfigFiles: FileCollection = installationState + files(
41 rootProject.file("yarn.lock"), 41 rootProject.file("tsconfig.base.json"),
42 rootProject.file("package.json"), 42 "tsconfig.json",
43 "package.json", 43 "tsconfig.node.json",
44) 44 "tsconfig.shared.json",
45 45 "vite.config.ts",
46val sharedConfigFiles: FileCollection = installationState + files( 46) + fileTree("config")
47 rootProject.file("tsconfig.base.json"),
48 "tsconfig.json",
49 "tsconfig.node.json",
50 "tsconfig.shared.json",
51)
52 47
53val assembleConfigFiles = sharedConfigFiles + file("vite.config.ts") + fileTree("config") { 48val assembleSources: FileCollection = sourcesWithTypes + fileTree("public") + files("index.html")
54 include("**/*.ts")
55}
56
57val assembleSources = sourcesWithTypes + fileTree("public") + file("index.html")
58 49
59val assembleFiles = assembleSources + assembleConfigFiles 50val assembleFiles: FileCollection = assembleSources + assembleConfigFiles
60 51
61val lintingFiles: FileCollection = sourcesWithTypes + buildScripts + sharedConfigFiles 52val lintingFiles: FileCollection = sourcesWithTypes + assembleConfigFiles + files(
53 rootProject.file(".eslintrc.cjs"),
54 rootProject.file("prettier.config.cjs"),
55)
62 56
63tasks { 57tasks {
64 val generateXStateTypes by registering(RunYarn::class) { 58 val generateXStateTypes by registering(RunYarn::class) {
@@ -112,16 +106,6 @@ tasks {
112 dependsOn(lintFrontend) 106 dependsOn(lintFrontend)
113 } 107 }
114 108
115 register<RunYarn>("serveFrontend") {
116 dependsOn(installFrontend)
117 dependsOn(generateXStateTypes)
118 inputs.files(assembleFiles)
119 outputs.dir(viteOutputDir.map { it.dir("development") })
120 script.set("run serve")
121 group = "run"
122 description = "Start a Vite dev server with hot module replacement."
123 }
124
125 clean { 109 clean {
126 delete("dev-dist") 110 delete("dev-dist")
127 delete(fileTree("src") { 111 delete(fileTree("src") {
diff --git a/subprojects/frontend/index.html b/subprojects/frontend/index.html
index 8992d538..e4271aad 100644
--- a/subprojects/frontend/index.html
+++ b/subprojects/frontend/index.html
@@ -14,7 +14,6 @@
14 <link rel="icon" href="/favicon.png" type="image/png" sizes="32x32"> 14 <link rel="icon" href="/favicon.png" type="image/png" sizes="32x32">
15 <link rel="icon" href="/favicon-96x96.png" type="image/png" sizes="96x96"> 15 <link rel="icon" href="/favicon-96x96.png" type="image/png" sizes="96x96">
16 <link rel="apple-touch-icon" href="/apple-touch-icon.png" type="image/png" sizes="180x180"> 16 <link rel="apple-touch-icon" href="/apple-touch-icon.png" type="image/png" sizes="180x180">
17 <link rel="mask-icon" href="/mask-icon.svg" type="image/svg+xml" color="#038a99">
18 <meta name="theme-color" media="(prefers-color-scheme:light)" content="#f5f5f5"> 17 <meta name="theme-color" media="(prefers-color-scheme:light)" content="#f5f5f5">
19 <meta name="theme-color" media="(prefers-color-scheme:dark)" content="#21252b"> 18 <meta name="theme-color" media="(prefers-color-scheme:dark)" content="#21252b">
20 <style> 19 <style>
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json
index b133e762..a499aeec 100644
--- a/subprojects/frontend/package.json
+++ b/subprojects/frontend/package.json
@@ -11,7 +11,7 @@
11 "private": true, 11 "private": true,
12 "scripts": { 12 "scripts": {
13 "build": "MODE=production vite build", 13 "build": "MODE=production vite build",
14 "serve": "MODE=development vite serve", 14 "dev": "MODE=development vite serve",
15 "typegen": "xstate typegen \"src/**/*.ts?(x)\"", 15 "typegen": "xstate typegen \"src/**/*.ts?(x)\"",
16 "typecheck": "yarn run g:tsc -p subprojects/frontend/tsconfig.shared.json && yarn run g:tsc -p subprojects/frontend/tsconfig.node.json && yarn run g:tsc -p subprojects/frontend/tsconfig.json", 16 "typecheck": "yarn run g:tsc -p subprojects/frontend/tsconfig.shared.json && yarn run g:tsc -p subprojects/frontend/tsconfig.node.json && yarn run g:tsc -p subprojects/frontend/tsconfig.json",
17 "lint": "yarn run g:lint subprojects/frontend", 17 "lint": "yarn run g:lint subprojects/frontend",
@@ -28,21 +28,21 @@
28 }, 28 },
29 "homepage": "https://refinery.tools", 29 "homepage": "https://refinery.tools",
30 "dependencies": { 30 "dependencies": {
31 "@codemirror/autocomplete": "^6.15.0", 31 "@codemirror/autocomplete": "^6.16.0",
32 "@codemirror/commands": "^6.3.3", 32 "@codemirror/commands": "^6.5.0",
33 "@codemirror/language": "^6.10.1", 33 "@codemirror/language": "^6.10.1",
34 "@codemirror/lint": "^6.5.0", 34 "@codemirror/lint": "^6.5.0",
35 "@codemirror/search": "^6.5.6", 35 "@codemirror/search": "^6.5.6",
36 "@codemirror/state": "^6.4.1", 36 "@codemirror/state": "^6.4.1",
37 "@codemirror/view": "^6.26.1", 37 "@codemirror/view": "^6.26.3",
38 "@emotion/cache": "^11.11.0", 38 "@emotion/cache": "^11.11.0",
39 "@emotion/react": "^11.11.4", 39 "@emotion/react": "^11.11.4",
40 "@emotion/serialize": "^1.1.4", 40 "@emotion/serialize": "^1.1.4",
41 "@emotion/styled": "^11.11.5", 41 "@emotion/styled": "^11.11.5",
42 "@emotion/utils": "^1.2.1", 42 "@emotion/utils": "^1.2.1",
43 "@fontsource-variable/jetbrains-mono": "^5.0.20", 43 "@fontsource-variable/jetbrains-mono": "^5.0.21",
44 "@fontsource-variable/open-sans": "^5.0.28", 44 "@fontsource-variable/open-sans": "^5.0.29",
45 "@fontsource/open-sans": "^5.0.27", 45 "@fontsource/open-sans": "^5.0.28",
46 "@hpcc-js/wasm": "^2.16.1", 46 "@hpcc-js/wasm": "^2.16.1",
47 "@lezer/common": "^1.2.1", 47 "@lezer/common": "^1.2.1",
48 "@lezer/highlight": "^1.2.0", 48 "@lezer/highlight": "^1.2.0",
@@ -51,7 +51,7 @@
51 "@mui/icons-material": "^5.15.15", 51 "@mui/icons-material": "^5.15.15",
52 "@mui/material": "^5.15.15", 52 "@mui/material": "^5.15.15",
53 "@mui/system": "^5.15.15", 53 "@mui/system": "^5.15.15",
54 "@mui/x-data-grid": "^7.1.1", 54 "@mui/x-data-grid": "^7.3.1",
55 "ansi-styles": "^6.2.1", 55 "ansi-styles": "^6.2.1",
56 "csstype": "^3.1.3", 56 "csstype": "^3.1.3",
57 "d3": "^7.9.0", 57 "d3": "^7.9.0",
@@ -67,14 +67,14 @@
67 "mobx": "^6.12.3", 67 "mobx": "^6.12.3",
68 "mobx-react-lite": "^4.0.7", 68 "mobx-react-lite": "^4.0.7",
69 "ms": "^2.1.3", 69 "ms": "^2.1.3",
70 "nanoid": "^5.0.6", 70 "nanoid": "^5.0.7",
71 "notistack": "^3.0.1", 71 "notistack": "^3.0.1",
72 "react": "^18.2.0", 72 "react": "^18.3.1",
73 "react-dom": "^18.2.0", 73 "react-dom": "^18.3.1",
74 "react-resize-detector": "^10.0.1", 74 "react-resize-detector": "^10.0.1",
75 "svg2pdf.js": "^2.2.3", 75 "svg2pdf.js": "^2.2.3",
76 "xstate": "^4.38.3", 76 "xstate": "^4.38.3",
77 "zod": "^3.22.4" 77 "zod": "^3.23.4"
78 }, 78 },
79 "devDependencies": { 79 "devDependencies": {
80 "@lezer/generator": "^1.7.0", 80 "@lezer/generator": "^1.7.0",
@@ -86,20 +86,20 @@
86 "@types/html-minifier-terser": "^7.0.2", 86 "@types/html-minifier-terser": "^7.0.2",
87 "@types/jspdf": "^2.0.0", 87 "@types/jspdf": "^2.0.0",
88 "@types/lodash-es": "^4.17.12", 88 "@types/lodash-es": "^4.17.12",
89 "@types/micromatch": "^4.0.6", 89 "@types/micromatch": "^4.0.7",
90 "@types/ms": "^0.7.34", 90 "@types/ms": "^0.7.34",
91 "@types/node": "^20.12.5", 91 "@types/node": "^20.12.7",
92 "@types/pnpapi": "^0.0.5", 92 "@types/pnpapi": "^0.0.5",
93 "@types/react": "^18.2.74", 93 "@types/react": "^18.3.0",
94 "@types/react-dom": "^18.2.24", 94 "@types/react-dom": "^18.3.0",
95 "@vitejs/plugin-react-swc": "^3.6.0", 95 "@vitejs/plugin-react-swc": "^3.6.0",
96 "@xstate/cli": "^0.5.17", 96 "@xstate/cli": "^0.5.17",
97 "html-minifier-terser": "^7.2.0", 97 "html-minifier-terser": "^7.2.0",
98 "micromatch": "^4.0.5", 98 "micromatch": "^4.0.5",
99 "pnpapi": "^0.0.0", 99 "pnpapi": "^0.0.0",
100 "typescript": "5.4.4", 100 "typescript": "5.4.5",
101 "vite": "^5.2.8", 101 "vite": "^5.2.10",
102 "vite-plugin-pwa": "^0.19.8", 102 "vite-plugin-pwa": "^0.19.8",
103 "workbox-window": "^7.0.0" 103 "workbox-window": "^7.1.0"
104 } 104 }
105} 105}
diff --git a/subprojects/frontend/src/ToggleDarkModeButton.tsx b/subprojects/frontend/src/ToggleDarkModeButton.tsx
index 7a835e61..58238cab 100644
--- a/subprojects/frontend/src/ToggleDarkModeButton.tsx
+++ b/subprojects/frontend/src/ToggleDarkModeButton.tsx
@@ -7,6 +7,7 @@
7import DarkModeIcon from '@mui/icons-material/DarkMode'; 7import DarkModeIcon from '@mui/icons-material/DarkMode';
8import LightModeIcon from '@mui/icons-material/LightMode'; 8import LightModeIcon from '@mui/icons-material/LightMode';
9import IconButton from '@mui/material/IconButton'; 9import IconButton from '@mui/material/IconButton';
10import Tooltip from '@mui/material/Tooltip';
10import { observer } from 'mobx-react-lite'; 11import { observer } from 'mobx-react-lite';
11 12
12import { useRootStore } from './RootStoreProvider'; 13import { useRootStore } from './RootStoreProvider';
@@ -16,12 +17,10 @@ export default observer(function ToggleDarkModeButton(): JSX.Element {
16 const { darkMode } = themeStore; 17 const { darkMode } = themeStore;
17 18
18 return ( 19 return (
19 <IconButton 20 <Tooltip title={darkMode ? 'Switch to light mode' : 'Switch to dark mode'}>
20 color="inherit" 21 <IconButton color="inherit" onClick={() => themeStore.toggleDarkMode()}>
21 onClick={() => themeStore.toggleDarkMode()} 22 {darkMode ? <LightModeIcon /> : <DarkModeIcon />}
22 aria-label={darkMode ? 'Switch to light mode' : 'Switch to dark mode'} 23 </IconButton>
23 > 24 </Tooltip>
24 {darkMode ? <LightModeIcon /> : <DarkModeIcon />}
25 </IconButton>
26 ); 25 );
27}); 26});
diff --git a/subprojects/frontend/src/TopBar.tsx b/subprojects/frontend/src/TopBar.tsx
index 6c9c4f7e..5c9bfc0a 100644
--- a/subprojects/frontend/src/TopBar.tsx
+++ b/subprojects/frontend/src/TopBar.tsx
@@ -4,11 +4,14 @@
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6 6
7import BookIcon from '@mui/icons-material/Book';
7import GitHubIcon from '@mui/icons-material/GitHub'; 8import GitHubIcon from '@mui/icons-material/GitHub';
9import HomeIcon from '@mui/icons-material/Home';
8import AppBar from '@mui/material/AppBar'; 10import AppBar from '@mui/material/AppBar';
9import IconButton from '@mui/material/IconButton'; 11import IconButton from '@mui/material/IconButton';
10import Stack from '@mui/material/Stack'; 12import Stack from '@mui/material/Stack';
11import Toolbar from '@mui/material/Toolbar'; 13import Toolbar from '@mui/material/Toolbar';
14import Tooltip from '@mui/material/Tooltip';
12import Typography from '@mui/material/Typography'; 15import Typography from '@mui/material/Typography';
13import { styled, useTheme } from '@mui/material/styles'; 16import { styled, useTheme } from '@mui/material/styles';
14import useMediaQuery from '@mui/material/useMediaQuery'; 17import useMediaQuery from '@mui/material/useMediaQuery';
@@ -134,7 +137,7 @@ export default observer(function TopBar(): JSX.Element {
134 py: 0.5, 137 py: 0.5,
135 }} 138 }}
136 > 139 >
137 <RefineryIcon size={24} /> 140 <RefineryIcon size={32} />
138 <Typography variant="h6" component="h1" pl={1}> 141 <Typography variant="h6" component="h1" pl={1}>
139 Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>} 142 Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>}
140 </Typography> 143 </Typography>
@@ -163,26 +166,41 @@ export default observer(function TopBar(): JSX.Element {
163 <PaneButtons themeStore={themeStore} /> 166 <PaneButtons themeStore={themeStore} />
164 </Stack> 167 </Stack>
165 )} 168 )}
166 <Stack 169 <Stack direction="row" marginLeft={1} gap={1} alignItems="center">
167 direction="row"
168 marginLeft={1}
169 marginRight={1}
170 gap={1}
171 alignItems="center"
172 >
173 <GenerateButton editorStore={editorStore} hideWarnings={!veryLarge} />
174 {large && ( 170 {large && (
175 <IconButton 171 <Stack direction="row" alignItems="center">
176 aria-label="GitHub" 172 <Tooltip title="Refinery home page">
177 href="https://github.com/graphs4value/refinery" 173 <IconButton
178 target="_blank" 174 href="https://refinery.tools/"
179 color="inherit" 175 target="_blank"
180 > 176 color="inherit"
181 <GitHubIcon /> 177 >
182 </IconButton> 178 <HomeIcon />
179 </IconButton>
180 </Tooltip>
181 <Tooltip title="Refinery documentation">
182 <IconButton
183 href="https://refinery.tools/learn/"
184 target="_blank"
185 color="inherit"
186 >
187 <BookIcon />
188 </IconButton>
189 </Tooltip>
190 <Tooltip title="Check us out at GitHub">
191 <IconButton
192 href="https://github.com/graphs4value/refinery"
193 target="_blank"
194 color="inherit"
195 >
196 <GitHubIcon />
197 </IconButton>
198 </Tooltip>
199 </Stack>
183 )} 200 )}
201 <GenerateButton editorStore={editorStore} hideWarnings={!veryLarge} />
202 <ToggleDarkModeButton />
184 </Stack> 203 </Stack>
185 <ToggleDarkModeButton />
186 </Toolbar> 204 </Toolbar>
187 </AppBar> 205 </AppBar>
188 ); 206 );
diff --git a/subprojects/frontend/src/editor/ConnectButton.tsx b/subprojects/frontend/src/editor/ConnectButton.tsx
index eed6fbc7..d08fbb4d 100644
--- a/subprojects/frontend/src/editor/ConnectButton.tsx
+++ b/subprojects/frontend/src/editor/ConnectButton.tsx
@@ -9,6 +9,7 @@ import CloudOffIcon from '@mui/icons-material/CloudOff';
9import SyncIcon from '@mui/icons-material/Sync'; 9import SyncIcon from '@mui/icons-material/Sync';
10import SyncProblemIcon from '@mui/icons-material/SyncProblem'; 10import SyncProblemIcon from '@mui/icons-material/SyncProblem';
11import IconButton from '@mui/material/IconButton'; 11import IconButton from '@mui/material/IconButton';
12import Tooltip from '@mui/material/Tooltip';
12import { keyframes, styled } from '@mui/material/styles'; 13import { keyframes, styled } from '@mui/material/styles';
13import { observer } from 'mobx-react-lite'; 14import { observer } from 'mobx-react-lite';
14 15
@@ -37,37 +38,51 @@ export default observer(function ConnectButton({
37 (editorStore.opening || editorStore.opened) 38 (editorStore.opening || editorStore.opened)
38 ) { 39 ) {
39 return ( 40 return (
40 <IconButton 41 <Tooltip
41 onClick={() => editorStore.disconnect()} 42 title={
42 aria-label="Disconnect" 43 editorStore.opening
43 color="inherit" 44 ? 'Connecting (click to cancel)'
45 : 'Connected (click to disconnect)'
46 }
44 > 47 >
45 {editorStore.opening ? ( 48 <IconButton
46 <AnimatedSyncIcon fontSize="small" /> 49 onClick={() => editorStore.disconnect()}
47 ) : ( 50 aria-label="Disconnect"
48 <CloudIcon fontSize="small" /> 51 color="inherit"
49 )} 52 >
50 </IconButton> 53 {editorStore.opening ? (
54 <AnimatedSyncIcon fontSize="small" />
55 ) : (
56 <CloudIcon fontSize="small" />
57 )}
58 </IconButton>
59 </Tooltip>
51 ); 60 );
52 } 61 }
53 62
63 let title: string;
54 let disconnectedIcon: JSX.Element; 64 let disconnectedIcon: JSX.Element;
55 if (editorStore === undefined) { 65 if (editorStore === undefined) {
66 title = 'Connecting';
56 disconnectedIcon = <SyncIcon fontSize="small" />; 67 disconnectedIcon = <SyncIcon fontSize="small" />;
57 } else if (editorStore.connectionErrors.length > 0) { 68 } else if (editorStore.connectionErrors.length > 0) {
69 title = 'Connection error (click to retry)';
58 disconnectedIcon = <SyncProblemIcon fontSize="small" />; 70 disconnectedIcon = <SyncProblemIcon fontSize="small" />;
59 } else { 71 } else {
72 title = 'Disconnected (click to connect)';
60 disconnectedIcon = <CloudOffIcon fontSize="small" />; 73 disconnectedIcon = <CloudOffIcon fontSize="small" />;
61 } 74 }
62 75
63 return ( 76 return (
64 <IconButton 77 <Tooltip title={title}>
65 disabled={editorStore === undefined} 78 <IconButton
66 onClick={() => editorStore?.connect()} 79 disabled={editorStore === undefined}
67 aria-label="Connect" 80 onClick={() => editorStore?.connect()}
68 color="inherit" 81 aria-label="Connect"
69 > 82 color="inherit"
70 {disconnectedIcon} 83 >
71 </IconButton> 84 {disconnectedIcon}
85 </IconButton>
86 </Tooltip>
72 ); 87 );
73}); 88});
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx
index 4afba607..50cd51dc 100644
--- a/subprojects/frontend/src/editor/EditorButtons.tsx
+++ b/subprojects/frontend/src/editor/EditorButtons.tsx
@@ -22,6 +22,7 @@ import IconButton from '@mui/material/IconButton';
22import Stack from '@mui/material/Stack'; 22import Stack from '@mui/material/Stack';
23import ToggleButton from '@mui/material/ToggleButton'; 23import ToggleButton from '@mui/material/ToggleButton';
24import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; 24import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
25import Tooltip from '@mui/material/Tooltip';
25import { observer } from 'mobx-react-lite'; 26import { observer } from 'mobx-react-lite';
26 27
27import ConnectButton from './ConnectButton'; 28import ConnectButton from './ConnectButton';
@@ -49,103 +50,113 @@ export default observer(function EditorButtons({
49}): JSX.Element { 50}): JSX.Element {
50 return ( 51 return (
51 <Stack direction="row" flexGrow={1}> 52 <Stack direction="row" flexGrow={1}>
52 <IconButton 53 <Tooltip title="Open">
53 disabled={editorStore === undefined}
54 onClick={() => editorStore?.openFile()}
55 aria-label="Open"
56 color="inherit"
57 >
58 <FileOpenIcon fontSize="small" />
59 </IconButton>
60 <IconButton
61 disabled={editorStore === undefined || !editorStore.unsavedChanges}
62 onClick={() => editorStore?.saveFile()}
63 aria-label="Save"
64 color="inherit"
65 >
66 <SaveIcon fontSize="small" />
67 </IconButton>
68 {'showSaveFilePicker' in window && (
69 <IconButton 54 <IconButton
70 disabled={editorStore === undefined} 55 disabled={editorStore === undefined}
71 onClick={() => editorStore?.saveFileAs()} 56 onClick={() => editorStore?.openFile()}
72 aria-label="Save as"
73 color="inherit" 57 color="inherit"
74 > 58 >
75 <SaveAsIcon fontSize="small" /> 59 <FileOpenIcon fontSize="small" />
76 </IconButton> 60 </IconButton>
77 )} 61 </Tooltip>
78 <IconButton 62 <Tooltip title="Save">
79 disabled={editorStore === undefined || !editorStore.canUndo} 63 <IconButton
80 onClick={() => editorStore?.undo()} 64 disabled={editorStore === undefined || !editorStore.unsavedChanges}
81 aria-label="Undo" 65 onClick={() => editorStore?.saveFile()}
82 color="inherit" 66 color="inherit"
83 sx={{ ml: 1 }}
84 >
85 <UndoIcon fontSize="small" />
86 </IconButton>
87 <IconButton
88 disabled={editorStore === undefined || !editorStore.canRedo}
89 onClick={() => editorStore?.redo()}
90 aria-label="Redo"
91 color="inherit"
92 >
93 <RedoIcon fontSize="small" />
94 </IconButton>
95 <ToggleButtonGroup size="small" className="rounded" sx={{ mx: 1 }}>
96 <ToggleButton
97 selected={editorStore?.showLineNumbers ?? false}
98 disabled={editorStore === undefined}
99 onClick={() => editorStore?.toggleLineNumbers()}
100 aria-label="Show line numbers"
101 value="show-line-numbers"
102 >
103 <FormatListNumberedIcon fontSize="small" />
104 </ToggleButton>
105 <ToggleButton
106 selected={editorStore?.colorIdentifiers ?? false}
107 disabled={editorStore === undefined}
108 onClick={() => editorStore?.toggleColorIdentifiers()}
109 aria-label="Color identifiers"
110 value="color-identifiers"
111 > 67 >
112 <LooksIcon fontSize="small" /> 68 <SaveIcon fontSize="small" />
113 </ToggleButton> 69 </IconButton>
114 <ToggleButton 70 </Tooltip>
115 selected={editorStore?.searchPanel?.state ?? false} 71 {'showSaveFilePicker' in window && (
116 disabled={editorStore === undefined} 72 <Tooltip title={`Save as\u2026`}>
117 onClick={() => editorStore?.searchPanel?.toggle()} 73 <IconButton
118 aria-label="Show find/replace" 74 disabled={editorStore === undefined}
119 {...(editorStore !== undefined && 75 onClick={() => editorStore?.saveFileAs()}
120 editorStore.searchPanel.state && { 76 color="inherit"
121 'aria-controls': editorStore.searchPanel.id, 77 >
122 })} 78 <SaveAsIcon fontSize="small" />
123 value="show-search-panel" 79 </IconButton>
80 </Tooltip>
81 )}
82 <Tooltip title="Undo">
83 <IconButton
84 disabled={editorStore === undefined || !editorStore.canUndo}
85 onClick={() => editorStore?.undo()}
86 color="inherit"
87 sx={{ ml: 1 }}
124 > 88 >
125 <SearchIcon fontSize="small" /> 89 <UndoIcon fontSize="small" />
126 </ToggleButton> 90 </IconButton>
127 <ToggleButton 91 </Tooltip>
128 selected={editorStore?.lintPanel?.state ?? false} 92 <Tooltip title="Redo">
129 disabled={editorStore === undefined} 93 <IconButton
130 onClick={() => editorStore?.lintPanel.toggle()} 94 disabled={editorStore === undefined || !editorStore.canRedo}
131 aria-label="Show diagnostics panel" 95 onClick={() => editorStore?.redo()}
132 {...(editorStore !== undefined && 96 color="inherit"
133 editorStore.lintPanel.state && {
134 'aria-controls': editorStore.lintPanel.id,
135 })}
136 value="show-lint-panel"
137 > 97 >
138 {getLintIcon(editorStore?.delayedErrors?.highestDiagnosticLevel)} 98 <RedoIcon fontSize="small" />
139 </ToggleButton> 99 </IconButton>
100 </Tooltip>
101 <ToggleButtonGroup size="small" className="rounded" sx={{ mx: 1 }}>
102 <Tooltip title="Line numbers">
103 <ToggleButton
104 selected={editorStore?.showLineNumbers ?? false}
105 disabled={editorStore === undefined}
106 onClick={() => editorStore?.toggleLineNumbers()}
107 value="show-line-numbers"
108 >
109 <FormatListNumberedIcon fontSize="small" />
110 </ToggleButton>
111 </Tooltip>
112 <Tooltip title="Color identifiers">
113 <ToggleButton
114 selected={editorStore?.colorIdentifiers ?? false}
115 disabled={editorStore === undefined}
116 onClick={() => editorStore?.toggleColorIdentifiers()}
117 value="color-identifiers"
118 >
119 <LooksIcon fontSize="small" />
120 </ToggleButton>
121 </Tooltip>
122 <Tooltip title="Find and replace">
123 <ToggleButton
124 selected={editorStore?.searchPanel?.state ?? false}
125 disabled={editorStore === undefined}
126 onClick={() => editorStore?.searchPanel?.toggle()}
127 {...(editorStore !== undefined &&
128 editorStore.searchPanel.state && {
129 'aria-controls': editorStore.searchPanel.id,
130 })}
131 value="show-search-panel"
132 >
133 <SearchIcon fontSize="small" />
134 </ToggleButton>
135 </Tooltip>
136 <Tooltip title="Diagnostics panel">
137 <ToggleButton
138 selected={editorStore?.lintPanel?.state ?? false}
139 disabled={editorStore === undefined}
140 onClick={() => editorStore?.lintPanel.toggle()}
141 {...(editorStore !== undefined &&
142 editorStore.lintPanel.state && {
143 'aria-controls': editorStore.lintPanel.id,
144 })}
145 value="show-lint-panel"
146 >
147 {getLintIcon(editorStore?.delayedErrors?.highestDiagnosticLevel)}
148 </ToggleButton>
149 </Tooltip>
140 </ToggleButtonGroup> 150 </ToggleButtonGroup>
141 <IconButton 151 <Tooltip title="Automatic format">
142 disabled={editorStore === undefined || !editorStore.opened} 152 <IconButton
143 onClick={() => editorStore?.formatText()} 153 disabled={editorStore === undefined || !editorStore.opened}
144 aria-label="Automatic format" 154 onClick={() => editorStore?.formatText()}
145 color="inherit" 155 color="inherit"
146 > 156 >
147 <FormatPaintIcon fontSize="small" /> 157 <FormatPaintIcon fontSize="small" />
148 </IconButton> 158 </IconButton>
159 </Tooltip>
149 <ConnectButton editorStore={editorStore} /> 160 <ConnectButton editorStore={editorStore} />
150 </Stack> 161 </Stack>
151 ); 162 );
diff --git a/subprojects/frontend/src/editor/SearchToolbar.tsx b/subprojects/frontend/src/editor/SearchToolbar.tsx
index 4ae7e893..bfdff234 100644
--- a/subprojects/frontend/src/editor/SearchToolbar.tsx
+++ b/subprojects/frontend/src/editor/SearchToolbar.tsx
@@ -17,15 +17,14 @@ import Stack from '@mui/material/Stack';
17import TextField from '@mui/material/TextField'; 17import TextField from '@mui/material/TextField';
18import ToggleButton from '@mui/material/ToggleButton'; 18import ToggleButton from '@mui/material/ToggleButton';
19import Toolbar from '@mui/material/Toolbar'; 19import Toolbar from '@mui/material/Toolbar';
20import Tooltip from '@mui/material/Tooltip';
20import { styled } from '@mui/material/styles'; 21import { styled } from '@mui/material/styles';
21import useMediaQuery from '@mui/material/useMediaQuery';
22import { observer } from 'mobx-react-lite'; 22import { observer } from 'mobx-react-lite';
23import { useCallback, useState } from 'react'; 23import { useCallback, useState } from 'react';
24import { useResizeDetector } from 'react-resize-detector';
24 25
25import type SearchPanelStore from './SearchPanelStore'; 26import type SearchPanelStore from './SearchPanelStore';
26 27
27const SPLIT_MEDIA_QUERY = '@media (max-width: 1200px)';
28
29const DimLabel = styled(FormControlLabel)(({ theme }) => ({ 28const DimLabel = styled(FormControlLabel)(({ theme }) => ({
30 '.MuiFormControlLabel-label': { 29 '.MuiFormControlLabel-label': {
31 ...theme.typography.body2, 30 ...theme.typography.body2,
@@ -43,7 +42,8 @@ export default observer(function SearchToolbar({
43 query: { search, valid, caseSensitive, literal, regexp, replace }, 42 query: { search, valid, caseSensitive, literal, regexp, replace },
44 invalidRegexp, 43 invalidRegexp,
45 } = searchPanelStore; 44 } = searchPanelStore;
46 const split = useMediaQuery(SPLIT_MEDIA_QUERY); 45 const { width, ref } = useResizeDetector();
46 const split = width !== undefined && width <= 1200;
47 const [showRepalceState, setShowReplaceState] = useState(false); 47 const [showRepalceState, setShowReplaceState] = useState(false);
48 48
49 const showReplace = !split || showRepalceState || replace !== ''; 49 const showReplace = !split || showRepalceState || replace !== '';
@@ -61,16 +61,19 @@ export default observer(function SearchToolbar({
61 <Toolbar 61 <Toolbar
62 variant="dense" 62 variant="dense"
63 sx={{ py: 0.5, alignItems: 'center', minHeight: 'auto' }} 63 sx={{ py: 0.5, alignItems: 'center', minHeight: 'auto' }}
64 ref={ref}
64 > 65 >
65 <Stack 66 <Stack
66 direction={split ? 'column' : 'row'} 67 direction={split ? 'column' : 'row'}
67 sx={{ 68 sx={{
68 alignItems: 'center', 69 alignItems: 'center',
69 flexGrow: 1, 70 flexGrow: 1,
70 [SPLIT_MEDIA_QUERY]: { 71 ...(split
71 alignItems: 'start', 72 ? {
72 gap: 0.5, 73 alignItems: 'start',
73 }, 74 gap: 0.5,
75 }
76 : {}),
74 }} 77 }}
75 > 78 >
76 <Stack direction="row" flexWrap="wrap" alignItems="center" rowGap={0.5}> 79 <Stack direction="row" flexWrap="wrap" alignItems="center" rowGap={0.5}>
@@ -121,22 +124,24 @@ export default observer(function SearchToolbar({
121 mr={1} 124 mr={1}
122 rowGap={0.5} 125 rowGap={0.5}
123 > 126 >
124 <IconButton 127 <Tooltip title="Previous match">
125 aria-label="Previous" 128 <IconButton
126 disabled={!valid} 129 disabled={!valid}
127 onClick={() => searchPanelStore.findPrevious()} 130 onClick={() => searchPanelStore.findPrevious()}
128 color="inherit" 131 color="inherit"
129 > 132 >
130 <KeyboardArrowUpIcon fontSize="small" /> 133 <KeyboardArrowUpIcon fontSize="small" />
131 </IconButton> 134 </IconButton>
132 <IconButton 135 </Tooltip>
133 aria-label="Next" 136 <Tooltip title="Next match">
134 disabled={!valid} 137 <IconButton
135 onClick={() => searchPanelStore.findNext()} 138 disabled={!valid}
136 color="inherit" 139 onClick={() => searchPanelStore.findNext()}
137 > 140 color="inherit"
138 <KeyboardArrowDownIcon fontSize="small" /> 141 >
139 </IconButton> 142 <KeyboardArrowDownIcon fontSize="small" />
143 </IconButton>
144 </Tooltip>
140 </Stack> 145 </Stack>
141 <Stack 146 <Stack
142 direction="row" 147 direction="row"
@@ -187,24 +192,25 @@ export default observer(function SearchToolbar({
187 label="Regexp" 192 label="Regexp"
188 /> 193 />
189 {split && ( 194 {split && (
190 <ToggleButton 195 <Tooltip title="Replace">
191 value="show-replace" 196 <ToggleButton
192 selected={showReplace} 197 value="show-replace"
193 onClick={() => { 198 selected={showReplace}
194 if (showReplace) { 199 onClick={() => {
195 searchPanelStore.updateQuery({ replace: '' }); 200 if (showReplace) {
196 setShowReplaceState(false); 201 searchPanelStore.updateQuery({ replace: '' });
197 } else { 202 setShowReplaceState(false);
198 setShowReplaceState(true); 203 } else {
199 } 204 setShowReplaceState(true);
200 }} 205 }
201 aria-label="Show replace options" 206 }}
202 aria-controls={replaceId} 207 aria-controls={replaceId}
203 size="small" 208 size="small"
204 className="iconOnly" 209 className="iconOnly"
205 > 210 >
206 <FindReplaceIcon fontSize="small" /> 211 <FindReplaceIcon fontSize="small" />
207 </ToggleButton> 212 </ToggleButton>
213 </Tooltip>
208 )} 214 )}
209 </Stack> 215 </Stack>
210 </Stack> 216 </Stack>
@@ -263,9 +269,7 @@ export default observer(function SearchToolbar({
263 alignSelf="stretch" 269 alignSelf="stretch"
264 alignItems="start" 270 alignItems="start"
265 mt="1px" 271 mt="1px"
266 sx={{ 272 sx={split ? { display: 'none' } : {}}
267 [SPLIT_MEDIA_QUERY]: { display: 'none' },
268 }}
269 > 273 >
270 <IconButton 274 <IconButton
271 aria-label="Close find/replace" 275 aria-label="Close find/replace"
diff --git a/subprojects/frontend/src/graph/SlideInPanel.tsx b/subprojects/frontend/src/graph/SlideInPanel.tsx
index 2c189b5b..47bbe0a6 100644
--- a/subprojects/frontend/src/graph/SlideInPanel.tsx
+++ b/subprojects/frontend/src/graph/SlideInPanel.tsx
@@ -8,6 +8,7 @@ import Dialog from '@mui/material/Dialog';
8import IconButton from '@mui/material/IconButton'; 8import IconButton from '@mui/material/IconButton';
9import Paper from '@mui/material/Paper'; 9import Paper from '@mui/material/Paper';
10import Slide from '@mui/material/Slide'; 10import Slide from '@mui/material/Slide';
11import Tooltip from '@mui/material/Tooltip';
11import { styled } from '@mui/material/styles'; 12import { styled } from '@mui/material/styles';
12import React, { useCallback, useId, useState } from 'react'; 13import React, { useCallback, useId, useState } from 'react';
13 14
@@ -58,15 +59,19 @@ export default function SlideInPanel({
58 59
59 return ( 60 return (
60 <SlideInPanelRoot anchor={anchor}> 61 <SlideInPanelRoot anchor={anchor}>
61 <IconButton 62 <Tooltip
62 role="switch" 63 title={iconLabel}
63 aria-checked={show} 64 placement={anchor === 'left' ? 'right' : 'left'}
64 aria-controls={dialog ? undefined : id}
65 aria-label={iconLabel}
66 onClick={() => setShow(!show)}
67 > 65 >
68 {icon(show)} 66 <IconButton
69 </IconButton> 67 role="switch"
68 aria-checked={show}
69 aria-controls={dialog ? undefined : id}
70 onClick={() => setShow(!show)}
71 >
72 {icon(show)}
73 </IconButton>
74 </Tooltip>
70 {dialog ? ( 75 {dialog ? (
71 <Dialog open={show} onClose={close} maxWidth="xl"> 76 <Dialog open={show} onClose={close} maxWidth="xl">
72 <SlideInDialog close={close} dialog title={title} buttons={buttons}> 77 <SlideInDialog close={close} dialog title={title} buttons={buttons}>
diff --git a/subprojects/frontend/src/graph/VisibilityPanel.tsx b/subprojects/frontend/src/graph/VisibilityPanel.tsx
index 210ff5d5..8474b7be 100644
--- a/subprojects/frontend/src/graph/VisibilityPanel.tsx
+++ b/subprojects/frontend/src/graph/VisibilityPanel.tsx
@@ -199,7 +199,7 @@ function VisibilityPanel({
199 dialog={dialog} 199 dialog={dialog}
200 title="Customize view" 200 title="Customize view"
201 icon={icon} 201 icon={icon}
202 iconLabel="Show filter panel" 202 iconLabel="Filter panel"
203 buttons={ 203 buttons={
204 <> 204 <>
205 <Button 205 <Button
diff --git a/subprojects/frontend/src/graph/ZoomButtons.tsx b/subprojects/frontend/src/graph/ZoomButtons.tsx
index 83938cf4..b292a617 100644
--- a/subprojects/frontend/src/graph/ZoomButtons.tsx
+++ b/subprojects/frontend/src/graph/ZoomButtons.tsx
@@ -10,6 +10,7 @@ import RemoveIcon from '@mui/icons-material/Remove';
10import IconButton from '@mui/material/IconButton'; 10import IconButton from '@mui/material/IconButton';
11import Stack from '@mui/material/Stack'; 11import Stack from '@mui/material/Stack';
12import ToggleButton from '@mui/material/ToggleButton'; 12import ToggleButton from '@mui/material/ToggleButton';
13import Tooltip from '@mui/material/Tooltip';
13 14
14import type { ChangeZoomCallback, SetFitZoomCallback } from './ZoomCanvas'; 15import type { ChangeZoomCallback, SetFitZoomCallback } from './ZoomCanvas';
15 16
@@ -28,22 +29,27 @@ export default function ZoomButtons({
28 p={1} 29 p={1}
29 sx={{ position: 'absolute', bottom: 0, right: 0 }} 30 sx={{ position: 'absolute', bottom: 0, right: 0 }}
30 > 31 >
31 <IconButton aria-label="Zoom in" onClick={() => changeZoom(2)}> 32 <Tooltip title="Zoom in" placement="left">
32 <AddIcon fontSize="small" /> 33 <IconButton onClick={() => changeZoom(2)}>
33 </IconButton> 34 <AddIcon fontSize="small" />
34 <IconButton aria-label="Zoom out" onClick={() => changeZoom(0.5)}> 35 </IconButton>
35 <RemoveIcon fontSize="small" /> 36 </Tooltip>
36 </IconButton> 37 <Tooltip title="Zoom out" placement="left">
37 <ToggleButton 38 <IconButton onClick={() => changeZoom(0.5)}>
38 value="show-replace" 39 <RemoveIcon fontSize="small" />
39 selected={fitZoom} 40 </IconButton>
40 onClick={() => setFitZoom(!fitZoom)} 41 </Tooltip>
41 aria-label="Fit screen" 42 <Tooltip title="Fit screen" placement="left">
42 size="small" 43 <ToggleButton
43 className="iconOnly" 44 value="show-replace"
44 > 45 selected={fitZoom}
45 <CropFreeIcon fontSize="small" /> 46 onClick={() => setFitZoom(!fitZoom)}
46 </ToggleButton> 47 size="small"
48 className="iconOnly"
49 >
50 <CropFreeIcon fontSize="small" />
51 </ToggleButton>
52 </Tooltip>
47 </Stack> 53 </Stack>
48 ); 54 );
49} 55}
diff --git a/subprojects/frontend/src/graph/export/ExportPanel.tsx b/subprojects/frontend/src/graph/export/ExportPanel.tsx
index c93fa837..81bd9081 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}
@@ -127,7 +135,7 @@ function ExportPanel({
127 dialog={dialog} 135 dialog={dialog}
128 title="Export diagram" 136 title="Export diagram"
129 icon={icon} 137 icon={icon}
130 iconLabel="Show export panel" 138 iconLabel={`Export image\u2026`}
131 buttons={buttons} 139 buttons={buttons}
132 > 140 >
133 <SwitchButtonGroup size="small" className="rounded"> 141 <SwitchButtonGroup size="small" className="rounded">
@@ -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..7c691a7b 100644
--- a/subprojects/frontend/src/graph/export/ExportSettingsStore.ts
+++ b/subprojects/frontend/src/graph/export/ExportSettingsStore.ts
@@ -7,18 +7,21 @@
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 StaticTheme = 'light' | 'dark';
11export type ExportTheme = StaticTheme | 'dynamic';
11 12
12export default class ExportSettingsStore { 13export default class ExportSettingsStore {
13 format: ExportFormat = 'svg'; 14 format: ExportFormat = 'svg';
14 15
15 theme: ExportTheme = 'light'; 16 private staticTheme: StaticTheme = 'light';
16 17
17 transparent = true; 18 private _theme: ExportTheme = 'light';
18 19
19 embedSVGFonts = false; 20 private _transparent = true;
20 21
21 embedPDFFonts = true; 22 private embedSVGFonts = false;
23
24 private embedPDFFonts = true;
22 25
23 scale = 100; 26 scale = 100;
24 27
@@ -31,11 +34,14 @@ export default class ExportSettingsStore {
31 } 34 }
32 35
33 setTheme(theme: ExportTheme): void { 36 setTheme(theme: ExportTheme): void {
34 this.theme = theme; 37 this._theme = theme;
38 if (theme !== 'dynamic') {
39 this.staticTheme = theme;
40 }
35 } 41 }
36 42
37 toggleTransparent(): void { 43 toggleTransparent(): void {
38 this.transparent = !this.transparent; 44 this._transparent = !this._transparent;
39 } 45 }
40 46
41 toggleEmbedFonts(): void { 47 toggleEmbedFonts(): void {
@@ -46,7 +52,18 @@ export default class ExportSettingsStore {
46 this.scale = scale; 52 this.scale = scale;
47 } 53 }
48 54
55 get theme(): ExportTheme {
56 return this.format === 'svg' ? this._theme : this.staticTheme;
57 }
58
59 get transparent(): boolean {
60 return this.theme === 'dynamic' ? true : this._transparent;
61 }
62
49 get embedFonts(): boolean { 63 get embedFonts(): boolean {
64 if (this.theme === 'dynamic') {
65 return false;
66 }
50 return this.format === 'pdf' ? this.embedPDFFonts : this.embedSVGFonts; 67 return this.format === 'pdf' ? this.embedPDFFonts : this.embedSVGFonts;
51 } 68 }
52 69
@@ -57,8 +74,19 @@ export default class ExportSettingsStore {
57 this.embedSVGFonts = embedFonts; 74 this.embedSVGFonts = embedFonts;
58 } 75 }
59 76
77 get canSetDynamicTheme(): boolean {
78 return this.format === 'svg';
79 }
80
81 get canChangeTransparency(): boolean {
82 return this.theme !== 'dynamic';
83 }
84
60 get canEmbedFonts(): boolean { 85 get canEmbedFonts(): boolean {
61 return this.format === 'svg' || this.format === 'pdf'; 86 return (
87 (this.format === 'svg' || this.format === 'pdf') &&
88 this.theme !== 'dynamic'
89 );
62 } 90 }
63 91
64 get canScale(): boolean { 92 get canScale(): boolean {
diff --git a/subprojects/frontend/src/graph/export/exportDiagram.tsx b/subprojects/frontend/src/graph/export/exportDiagram.tsx
index 6abbcfdf..52d19aa0 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.removeAttribute('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/subprojects/frontend/src/table/SymbolSelector.tsx b/subprojects/frontend/src/table/SymbolSelector.tsx
index 5272f8ed..10a9ba9d 100644
--- a/subprojects/frontend/src/table/SymbolSelector.tsx
+++ b/subprojects/frontend/src/table/SymbolSelector.tsx
@@ -33,7 +33,7 @@ function SymbolSelector({ graph }: { graph: GraphStore }): JSX.Element {
33 }} 33 }}
34 variant="standard" 34 variant="standard"
35 size="medium" 35 size="medium"
36 placeholder="Symbol" 36 placeholder={`Select symbol to view\u2026`}
37 /> 37 />
38 )} 38 )}
39 options={relations} 39 options={relations}
diff --git a/subprojects/frontend/src/table/TableArea.tsx b/subprojects/frontend/src/table/TableArea.tsx
index 854f3a97..dc886715 100644
--- a/subprojects/frontend/src/table/TableArea.tsx
+++ b/subprojects/frontend/src/table/TableArea.tsx
@@ -5,6 +5,7 @@
5 */ 5 */
6 6
7import Box from '@mui/material/Box'; 7import Box from '@mui/material/Box';
8import Stack from '@mui/material/Stack';
8import { 9import {
9 DataGrid, 10 DataGrid,
10 type GridRenderCellParams, 11 type GridRenderCellParams,
@@ -14,6 +15,7 @@ import { observer } from 'mobx-react-lite';
14import { useMemo } from 'react'; 15import { useMemo } from 'react';
15 16
16import type GraphStore from '../graph/GraphStore'; 17import type GraphStore from '../graph/GraphStore';
18import RelationName from '../graph/RelationName';
17 19
18import TableToolbar from './TableToolbar'; 20import TableToolbar from './TableToolbar';
19import ValueRenderer from './ValueRenderer'; 21import ValueRenderer from './ValueRenderer';
@@ -28,6 +30,54 @@ declare module '@mui/x-data-grid' {
28 interface ToolbarPropsOverrides { 30 interface ToolbarPropsOverrides {
29 graph: GraphStore; 31 graph: GraphStore;
30 } 32 }
33
34 interface NoRowsOverlayPropsOverrides {
35 graph: GraphStore;
36 }
37
38 interface NoResultsOverlayPropsOverrides {
39 graph: GraphStore;
40 }
41}
42
43const noSymbolMessage =
44 'Please select a symbol from the list to view its interpretation';
45
46function NoRowsOverlay({
47 graph: { selectedSymbol },
48}: {
49 graph: GraphStore;
50}): JSX.Element {
51 return (
52 <Stack height="100%" alignItems="center" justifyContent="center">
53 {selectedSymbol === undefined ? (
54 noSymbolMessage
55 ) : (
56 <span>
57 Interpretation of <RelationName metadata={selectedSymbol} /> is empty
58 </span>
59 )}
60 </Stack>
61 );
62}
63
64function NoResultsOverlay({
65 graph: { selectedSymbol },
66}: {
67 graph: GraphStore;
68}): JSX.Element {
69 return (
70 <Stack height="100%" alignItems="center" justifyContent="center">
71 {selectedSymbol === undefined ? (
72 noSymbolMessage
73 ) : (
74 <span>
75 No results in the interpretation of{' '}
76 <RelationName metadata={selectedSymbol} />
77 </span>
78 )}
79 </Stack>
80 );
31} 81}
32 82
33function TableArea({ graph }: { graph: GraphStore }): JSX.Element { 83function TableArea({ graph }: { graph: GraphStore }): JSX.Element {
@@ -97,11 +147,21 @@ function TableArea({ graph }: { graph: GraphStore }): JSX.Element {
97 })} 147 })}
98 > 148 >
99 <DataGrid 149 <DataGrid
100 slots={{ toolbar: TableToolbar }} 150 slots={{
151 toolbar: TableToolbar,
152 noRowsOverlay: NoRowsOverlay,
153 noResultsOverlay: NoResultsOverlay,
154 }}
101 slotProps={{ 155 slotProps={{
102 toolbar: { 156 toolbar: {
103 graph, 157 graph,
104 }, 158 },
159 noRowsOverlay: {
160 graph,
161 },
162 noResultsOverlay: {
163 graph,
164 },
105 }} 165 }}
106 initialState={{ density: 'compact' }} 166 initialState={{ density: 'compact' }}
107 rowSelection={false} 167 rowSelection={false}
diff --git a/subprojects/frontend/src/theme/ThemeProvider.tsx b/subprojects/frontend/src/theme/ThemeProvider.tsx
index 6905fb4b..1d70dbaf 100644
--- a/subprojects/frontend/src/theme/ThemeProvider.tsx
+++ b/subprojects/frontend/src/theme/ThemeProvider.tsx
@@ -206,6 +206,9 @@ function createResponsiveTheme(
206 tooltip: { 206 tooltip: {
207 background: alpha('#212121', 0.93), 207 background: alpha('#212121', 0.93),
208 color: '#fff', 208 color: '#fff',
209 fontSize: '0.875rem',
210 lineHeight: 1.43,
211 letterSpacing: '0.01071em',
209 }, 212 },
210 arrow: { 213 arrow: {
211 color: alpha('#212121', 0.93), 214 color: alpha('#212121', 0.93),
diff --git a/subprojects/language-web/build.gradle.kts b/subprojects/language-web/build.gradle.kts
index c3a0b7e9..02e4ff17 100644
--- a/subprojects/language-web/build.gradle.kts
+++ b/subprojects/language-web/build.gradle.kts
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -55,7 +55,7 @@ tasks {
55 } 55 }
56 } 56 }
57 57
58 register<JavaExec>("serveBackend") { 58 register<JavaExec>("serve") {
59 dependsOn(webapp) 59 dependsOn(webapp)
60 val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath } 60 val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath }
61 dependsOn(mainRuntimeClasspath) 61 dependsOn(mainRuntimeClasspath)
@@ -67,7 +67,7 @@ tasks {
67 description = "Start a Jetty web server serving the Xtext API and assets." 67 description = "Start a Jetty web server serving the Xtext API and assets."
68 } 68 }
69 69
70 register<JavaExec>("serveBackendOnly") { 70 register<JavaExec>("serveBackend") {
71 val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath } 71 val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath }
72 dependsOn(mainRuntimeClasspath) 72 dependsOn(mainRuntimeClasspath)
73 classpath(mainRuntimeClasspath) 73 classpath(mainRuntimeClasspath)
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java
index ff8f4943..77579f8e 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ThreadPoolExecutorServiceProvider.java
@@ -60,9 +60,9 @@ public class ThreadPoolExecutorServiceProvider extends ExecutorServiceProvider {
60 } 60 }
61 61
62 public ThreadPoolExecutorServiceProvider() { 62 public ThreadPoolExecutorServiceProvider() {
63 executorThreadCount = getCount("REFINERY_XTEXT_THREAD_COUNT").orElse(0); 63 executorThreadCount = getCount("REFINERY_XTEXT_THREAD_COUNT").orElse(1);
64 lockExecutorThreadCount = getCount("REFINERY_XTEXT_LOCKING_THREAD_COUNT").orElse(executorThreadCount); 64 lockExecutorThreadCount = getCount("REFINERY_XTEXT_LOCKING_THREAD_COUNT").orElse(executorThreadCount);
65 int semanticsCount = getCount("REFINERY_XTEXT_SEMANTICS_THREAD_COUNT").orElse(0); 65 int semanticsCount = getCount("REFINERY_XTEXT_SEMANTICS_THREAD_COUNT").orElse(executorThreadCount);
66 if (semanticsCount == 0 || executorThreadCount == 0) { 66 if (semanticsCount == 0 || executorThreadCount == 0) {
67 semanticsExecutorThreadCount = 0; 67 semanticsExecutorThreadCount = 0;
68 } else { 68 } else {