flowyaml 0.1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. flowyaml-0.1.0.0/CHANGELOG.md +20 -0
  2. flowyaml-0.1.0.0/LICENSE +21 -0
  3. flowyaml-0.1.0.0/MANIFEST.in +8 -0
  4. flowyaml-0.1.0.0/NOTICE.md +19 -0
  5. flowyaml-0.1.0.0/PKG-INFO +453 -0
  6. flowyaml-0.1.0.0/README.md +416 -0
  7. flowyaml-0.1.0.0/RELEASING.md +95 -0
  8. flowyaml-0.1.0.0/docs/flowyaml_v0_spec.md +209 -0
  9. flowyaml-0.1.0.0/docs/future_fit_ledger.md +28 -0
  10. flowyaml-0.1.0.0/docs/import_reference.md +213 -0
  11. flowyaml-0.1.0.0/docs/live_updates.md +186 -0
  12. flowyaml-0.1.0.0/docs/notebook_and_agent_usage.md +98 -0
  13. flowyaml-0.1.0.0/examples/distribution.yaml +293 -0
  14. flowyaml-0.1.0.0/examples/minimal.yaml +21 -0
  15. flowyaml-0.1.0.0/examples/onboarding.mmd +16 -0
  16. flowyaml-0.1.0.0/examples/order_handling.bpmn +97 -0
  17. flowyaml-0.1.0.0/pyproject.toml +61 -0
  18. flowyaml-0.1.0.0/setup.cfg +4 -0
  19. flowyaml-0.1.0.0/src/flowyaml/__init__.py +120 -0
  20. flowyaml-0.1.0.0/src/flowyaml/__main__.py +10 -0
  21. flowyaml-0.1.0.0/src/flowyaml/_version.py +3 -0
  22. flowyaml-0.1.0.0/src/flowyaml/api.py +229 -0
  23. flowyaml-0.1.0.0/src/flowyaml/assets/__init__.py +5 -0
  24. flowyaml-0.1.0.0/src/flowyaml/assets/elk.LICENSE.md +264 -0
  25. flowyaml-0.1.0.0/src/flowyaml/assets/elk.VERSION.txt +4 -0
  26. flowyaml-0.1.0.0/src/flowyaml/assets/elk.bundled.js +6696 -0
  27. flowyaml-0.1.0.0/src/flowyaml/assets/live.js +173 -0
  28. flowyaml-0.1.0.0/src/flowyaml/assets/runtime.js +1405 -0
  29. flowyaml-0.1.0.0/src/flowyaml/assets/styles.css +569 -0
  30. flowyaml-0.1.0.0/src/flowyaml/cli.py +400 -0
  31. flowyaml-0.1.0.0/src/flowyaml/errors.py +146 -0
  32. flowyaml-0.1.0.0/src/flowyaml/importers/__init__.py +13 -0
  33. flowyaml-0.1.0.0/src/flowyaml/importers/_common.py +204 -0
  34. flowyaml-0.1.0.0/src/flowyaml/importers/bpmn.py +605 -0
  35. flowyaml-0.1.0.0/src/flowyaml/importers/mermaid.py +732 -0
  36. flowyaml-0.1.0.0/src/flowyaml/live.py +349 -0
  37. flowyaml-0.1.0.0/src/flowyaml/loader.py +167 -0
  38. flowyaml-0.1.0.0/src/flowyaml/model.py +203 -0
  39. flowyaml-0.1.0.0/src/flowyaml/py.typed +1 -0
  40. flowyaml-0.1.0.0/src/flowyaml/renderer.py +443 -0
  41. flowyaml-0.1.0.0/src/flowyaml/serialize.py +102 -0
  42. flowyaml-0.1.0.0/src/flowyaml/themes.py +105 -0
  43. flowyaml-0.1.0.0/src/flowyaml/validation.py +546 -0
  44. flowyaml-0.1.0.0/src/flowyaml.egg-info/PKG-INFO +453 -0
  45. flowyaml-0.1.0.0/src/flowyaml.egg-info/SOURCES.txt +70 -0
  46. flowyaml-0.1.0.0/src/flowyaml.egg-info/dependency_links.txt +1 -0
  47. flowyaml-0.1.0.0/src/flowyaml.egg-info/entry_points.txt +2 -0
  48. flowyaml-0.1.0.0/src/flowyaml.egg-info/requires.txt +11 -0
  49. flowyaml-0.1.0.0/src/flowyaml.egg-info/top_level.txt +1 -0
  50. flowyaml-0.1.0.0/tests/conftest.py +70 -0
  51. flowyaml-0.1.0.0/tests/fixtures/accented_labels.yaml +28 -0
  52. flowyaml-0.1.0.0/tests/fixtures/connector_junction.yaml +43 -0
  53. flowyaml-0.1.0.0/tests/fixtures/hostile_labels.yaml +29 -0
  54. flowyaml-0.1.0.0/tests/fixtures/import/kitchen_sink.mmd +26 -0
  55. flowyaml-0.1.0.0/tests/fixtures/import/order.bpmn +74 -0
  56. flowyaml-0.1.0.0/tests/fixtures/import/prefixes.xml +10 -0
  57. flowyaml-0.1.0.0/tests/fixtures/import/two_pools.bpmn +28 -0
  58. flowyaml-0.1.0.0/tests/fixtures/merge_gateway.yaml +26 -0
  59. flowyaml-0.1.0.0/tests/fixtures/no_edges.yaml +11 -0
  60. flowyaml-0.1.0.0/tests/test_browser.py +533 -0
  61. flowyaml-0.1.0.0/tests/test_cli.py +234 -0
  62. flowyaml-0.1.0.0/tests/test_fragment.py +87 -0
  63. flowyaml-0.1.0.0/tests/test_import_bpmn.py +387 -0
  64. flowyaml-0.1.0.0/tests/test_import_mermaid.py +431 -0
  65. flowyaml-0.1.0.0/tests/test_live.py +404 -0
  66. flowyaml-0.1.0.0/tests/test_live_browser.py +135 -0
  67. flowyaml-0.1.0.0/tests/test_loader.py +68 -0
  68. flowyaml-0.1.0.0/tests/test_model.py +191 -0
  69. flowyaml-0.1.0.0/tests/test_offline_assets.py +165 -0
  70. flowyaml-0.1.0.0/tests/test_render_document.py +186 -0
  71. flowyaml-0.1.0.0/tests/test_serialize.py +65 -0
  72. flowyaml-0.1.0.0/tests/test_validation.py +296 -0
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable FlowYAML changes are recorded here.
4
+
5
+ ## 0.1.0.0 - 2026-09-13
6
+
7
+ First public-package candidate and second implementation build.
8
+
9
+ - Render YAML-defined process flows as standalone, self-sufficient HTML.
10
+ - Render instance-scoped fragments for host applications.
11
+ - Keep an HTTP-served page linked to an actively edited YAML source.
12
+ - Import supported Mermaid flowcharts and BPMN 2.0 XML into the FlowYAML model.
13
+ - Navigate embedded subprocess levels without network access.
14
+ - Provide a Python API and `flowyaml` command-line interface.
15
+ - Bundle `elkjs` 0.9.3 with its EPL-2.0 notice for offline layout.
16
+ - Cover rendering, validation, serialization, imports, live updates, browser
17
+ behavior, accessibility, and offline operation with automated tests.
18
+
19
+ The internal `0.0.0.0` build used YAML-linked delivery by default and did not
20
+ yet include standalone HTML output.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dornelles Multitech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ include CHANGELOG.md
2
+ include LICENSE
3
+ include NOTICE.md
4
+ include README.md
5
+ include RELEASING.md
6
+ recursive-include docs *.md
7
+ recursive-include examples *.bpmn *.mmd *.yaml
8
+ recursive-include tests *.bpmn *.mmd *.py *.xml *.yaml
@@ -0,0 +1,19 @@
1
+ # Third-party notices
2
+
3
+ FlowYAML redistributes the following component as package data.
4
+
5
+ ## elkjs 0.9.3
6
+
7
+ - File: `src/flowyaml/assets/elk.bundled.js` (UMD browser bundle)
8
+ - Project: https://github.com/kieler/elkjs
9
+ - Licence: Eclipse Public License 2.0
10
+ - Full licence text: `src/flowyaml/assets/elk.LICENSE.md`
11
+ - Version record: `src/flowyaml/assets/elk.VERSION.txt`
12
+
13
+ The bundle is embedded verbatim and unmodified. FlowYAML wraps it in an
14
+ `if (typeof window.ELK === "undefined") { ... }` guard at render time so that
15
+ mounting several fragments on one page evaluates it once. The wrapper adds no
16
+ code to the bundle itself.
17
+
18
+ The bundle runs its layout in process. FlowYAML never passes `workerUrl`, so no
19
+ web worker is constructed and no script is fetched.
@@ -0,0 +1,453 @@
1
+ Metadata-Version: 2.4
2
+ Name: flowyaml
3
+ Version: 0.1.0.0
4
+ Summary: Callable Python renderer that turns YAML-defined process flows into offline, self-sufficient HTML.
5
+ Author: Dornelles Multitech
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/EngDornelles/flowyaml
8
+ Project-URL: Repository, https://github.com/EngDornelles/flowyaml
9
+ Project-URL: Issues, https://github.com/EngDornelles/flowyaml/issues
10
+ Keywords: yaml,flowchart,diagram,elk,svg,renderer
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: User Interfaces
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ License-File: NOTICE.md
27
+ License-File: src/flowyaml/assets/elk.LICENSE.md
28
+ Requires-Dist: PyYAML>=6.0
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest>=7.4; extra == "test"
31
+ Provides-Extra: browser
32
+ Requires-Dist: playwright>=1.40; extra == "browser"
33
+ Provides-Extra: release
34
+ Requires-Dist: build<2,>=1.2; extra == "release"
35
+ Requires-Dist: twine<8,>=6; extra == "release"
36
+ Dynamic: license-file
37
+
38
+ # FlowYAML
39
+
40
+ A callable Python renderer for YAML-defined process flows.
41
+
42
+ Given valid FlowYAML YAML, the package produces either a complete
43
+ self-sufficient single-page HTML document, or a self-contained embeddable
44
+ HTML/CSS/JS fragment for a host DOM application. The generated artifact renders
45
+ in a browser with **no Python process, no server, no npm build and no network
46
+ access**.
47
+
48
+ It also reads two foreign notations, Mermaid flowcharts and BPMN 2.0 XML, into
49
+ the same validated model.
50
+
51
+ That artifact is a snapshot of the YAML as it stood when it was written. While
52
+ the YAML is still being edited, `flowyaml serve` keeps a page attached to the
53
+ file instead, so a save shows up in the open browser — the arrangement the
54
+ delivered ERP DINFRA flowchart uses. See
55
+ [Keeping the HTML linked to the YAML](docs/live_updates.md).
56
+
57
+ v0 is a renderer, not an editor.
58
+
59
+ The first public-package candidate is `0.1.0.0`. The earlier internal
60
+ `0.0.0.0` build was YAML-linked by default and did not yet provide the
61
+ standalone, self-sufficient HTML delivery mode.
62
+
63
+ ## Install
64
+
65
+ ```bash
66
+ pip install -e .
67
+ ```
68
+
69
+ The only runtime dependency is PyYAML. The browser layout engine is vendored as
70
+ package data, so nothing is fetched at build time or at view time.
71
+
72
+ ## Quick start
73
+
74
+ ```python
75
+ from flowyaml import render, render_file, validate, write_html
76
+
77
+ html = render(
78
+ yaml_text,
79
+ model_id="distribution",
80
+ output="document",
81
+ assets="inline",
82
+ theme="dornelles_multitech",
83
+ )
84
+
85
+ write_html(yaml_text, "distribution.html")
86
+ errors = validate(yaml_text)
87
+ ```
88
+
89
+ Import an existing diagram instead of writing YAML by hand:
90
+
91
+ ```python
92
+ import flowyaml as fyml
93
+
94
+ graph = fyml.read_mermaid("workflow.mmd")
95
+ other = fyml.read_bpmn("process.bpmn")
96
+
97
+ fyml.write_html(graph, "workflow.html")
98
+ ```
99
+
100
+ ```bash
101
+ flowyaml render examples/distribution.yaml -o distribution.html
102
+ flowyaml render examples/distribution.yaml --output fragment > panel.html
103
+ flowyaml serve examples/distribution.yaml
104
+ flowyaml data examples/distribution.yaml -o distribution.data.json
105
+ flowyaml validate examples/distribution.yaml
106
+ flowyaml models examples/distribution.yaml
107
+ flowyaml import examples/onboarding.mmd -o onboarding.yaml
108
+ flowyaml import examples/order_handling.bpmn -o order.yaml
109
+ flowyaml themes
110
+ ```
111
+
112
+ Open the written file directly from disk. Nothing else is needed.
113
+
114
+ While you are still editing the YAML, serve it instead:
115
+
116
+ ```bash
117
+ flowyaml serve examples/distribution.yaml --open
118
+ ```
119
+
120
+ Edit the file, save, and the open page follows it. Nothing is rebuilt.
121
+
122
+ For notebook-kernel installation and deterministic LLM/agent invocation, see
123
+ [Notebook and agent usage](docs/notebook_and_agent_usage.md).
124
+
125
+ ## Public API
126
+
127
+ | Callable | Purpose |
128
+ | --- | --- |
129
+ | `render(source, *, model_id=None, output="document", assets="inline", theme="dornelles_multitech", instance_id=None) -> str` | Render YAML text or a path to HTML. |
130
+ | `render_file(path, **options) -> str` | Render the YAML file at `path`. |
131
+ | `write_html(source_or_path, destination, **options) -> Path` | Render and write; returns the written path. |
132
+ | `validate(source) -> tuple[ValidationIssue, ...]` | Structured issues; empty means renderable. |
133
+ | `load(source) -> Diagram` | The immutable normalized model. |
134
+ | `models(source) -> tuple[str, ...]` | Every `meta.id`, in document order. |
135
+ | `read_mermaid(source, *, model_id=None, name=None) -> Diagram` | Import a Mermaid flowchart. |
136
+ | `read_bpmn(source, *, model_id=None, name=None) -> Diagram` | Import a BPMN 2.0 XML process. |
137
+ | `to_yaml(source) -> str` | Canonical FlowYAML YAML for any source or diagram. |
138
+ | `payload(source, *, model_id=None, revision=None) -> dict` | The JSON body a host serves to a `data="url"` page. |
139
+ | `payload_json(source, ...) -> str` | The same body, serialized. |
140
+ | `serve(source, *, host, port, model_id, theme, poll_ms, open_browser) -> None` | Local host that re-reads the YAML per request. |
141
+
142
+ Everywhere the table says `source`, the argument may be YAML text, a path to a
143
+ YAML file, or a `Diagram` — which is what the two importers return, so an
144
+ imported graph goes straight to `render` and `write_html` with no YAML round
145
+ trip.
146
+
147
+ Rendering raises `FlowYAMLValidationError` when any issue exists,
148
+ `FlowYAMLModelError` for an unknown `model_id`, and `FlowYAMLOptionError` for an
149
+ unsupported option value. An importer raises `FlowYAMLImportError`, which
150
+ carries the format, the source name and the offending line where the position
151
+ is recoverable.
152
+
153
+ When `model_id` is omitted the first YAML document opens first. **Every** model
154
+ stays embedded either way, which is what makes subprocess navigation work
155
+ without a server.
156
+
157
+ ### Options
158
+
159
+ - `output="document"` emits `<!doctype html>`, metadata, style, data, runtime
160
+ and the diagram mount point.
161
+ - `output="fragment"` emits an instance-scoped mount point plus scoped CSS and
162
+ JS. It assumes no global id, no document-level body style and no server route,
163
+ and it can be mounted more than once on one page.
164
+ - `assets="inline"` is the only implemented v0 asset mode. The boundary stays
165
+ explicit so a future `assets="url"` mode can reference the same vendored asset
166
+ from an approved URL.
167
+ - `data="inline"` embeds the graph, which is what makes the artifact offline
168
+ and permanent. `data="url"` leaves the graph out and has the page fetch it
169
+ from `data_url`, so an edited YAML file reaches a page that is already open.
170
+ `revision_url` and `poll_ms` tune how the page notices a change; `poll_ms=0`
171
+ means it notices on reload only. A `data="url"` page needs an http host:
172
+ browsers refuse the fetch when the page itself was opened over `file://`,
173
+ and the page says so rather than sitting empty. See
174
+ [docs/live_updates.md](docs/live_updates.md).
175
+ - `instance_id` fixes the DOM prefix. Omit it and every render gets a fresh
176
+ random prefix, which is what keeps two fragments from colliding. Pass it when
177
+ you want byte-reproducible output.
178
+
179
+ ## YAML contract
180
+
181
+ A source is UTF-8 YAML with one or more documents separated by `---`. One
182
+ document is one flow level.
183
+
184
+ ```yaml
185
+ meta:
186
+ id: procurement
187
+ name: Procurement decision flow
188
+ version: "1.0.0"
189
+ date: "2026-08-30"
190
+
191
+ nodes:
192
+ - id: start
193
+ type: startEvent
194
+ label: Request received
195
+ - id: review
196
+ type: task
197
+ label: Review documentation
198
+ - id: decision
199
+ type: gateway
200
+ label: Documentation complete?
201
+ - id: archive
202
+ type: subprocess
203
+ label: Archive and publish
204
+ ref: archive_flow
205
+ - id: end
206
+ type: endEvent
207
+ label: Process closed
208
+
209
+ edges:
210
+ - from: start
211
+ to: review
212
+ - from: review
213
+ to: decision
214
+ - from: decision
215
+ to: archive
216
+ tag: "Yes"
217
+ label: Documentation is complete and can be archived.
218
+ - from: decision
219
+ to: end
220
+ tag: "No"
221
+ ```
222
+
223
+ Node types: `startEvent`, `endEvent`, `intermediateEvent`, `gateway`,
224
+ `subprocess`, `state`, `task`.
225
+
226
+ Optional node key `detail` (alias `description`) becomes a hover and focus
227
+ tooltip. Optional edge keys are `tag` (the compact chip), `label` (the full
228
+ text, shown as a tooltip when it differs from the chip), `kind`
229
+ (`sequence` or `association`) and `style` (`solid` or `dotted`).
230
+
231
+ `actors` and other non-rendering metadata are accepted and preserved on the
232
+ model for forward compatibility. v0 does not render swimlanes or actor columns.
233
+
234
+ > Bare `Yes`, `No`, `On` and `Off` are YAML booleans, not strings. Quote them.
235
+ > FlowYAML rejects a boolean tag with a message that says so.
236
+
237
+ ### Validation rules
238
+
239
+ - Each nonempty document needs `meta.id`, a nonempty `nodes` list, and an
240
+ `edges` list (which may be empty).
241
+ - Model ids and node ids are unique in their scopes.
242
+ - Every node needs a supported `type` and a string `label`. A label may be empty
243
+ only on the two connector shapes: a `gateway` used purely as a merge, and an
244
+ `intermediateEvent` used purely as a junction. Every other type carries
245
+ process meaning and needs text.
246
+ - Every edge has `from` and `to`, and both resolve inside the same model.
247
+ - Self-loops are invalid in v0.
248
+ - `subprocess.ref`, when present, must name an embedded model id. Only
249
+ `subprocess` nodes may carry `ref`. A subprocess without a `ref` renders as a
250
+ destination card, not a control.
251
+ - Only `kind: association` and `style: dotted` are accepted beyond the defaults.
252
+ Anything else is rejected until it is explicitly introduced.
253
+
254
+ Each issue carries a stable `code`, a `message`, the `model_id`, the
255
+ `document_index`, the offending `field`, and the YAML `line`/`column` where the
256
+ position is recoverable.
257
+
258
+ ## Importing Mermaid and BPMN
259
+
260
+ ```python
261
+ import flowyaml as fyml
262
+
263
+ graph = fyml.read_mermaid("workflow.mmd") # text or a path
264
+ other = fyml.read_bpmn("process.bpmn") # text or a path
265
+
266
+ fyml.render(graph) # straight to HTML
267
+ fyml.to_yaml(other) # or to editable FlowYAML YAML
268
+ ```
269
+
270
+ ```bash
271
+ flowyaml import workflow.mmd -o workflow.yaml
272
+ flowyaml import process.bpmn --format bpmn > process.yaml
273
+ ```
274
+
275
+ Two rules hold for both importers.
276
+
277
+ - **The output is always valid.** An importer builds plain FlowYAML documents
278
+ and runs them through the same loader and validator as a hand written
279
+ source. There is no second code path.
280
+ - **No geometry, ever.** Nothing an importer writes describes a position, a
281
+ size or any other `ui` metadata. FlowYAML measures wrapped labels in the
282
+ browser and runs ELK there, so a stored coordinate would only fight the
283
+ layout engine. BPMN's whole `bpmndi:BPMNDiagram` section is read past and
284
+ discarded.
285
+
286
+ Identifiers survive when they are safe: a Mermaid `check_stock` and a BPMN
287
+ `Activity_0x9f2c` stay exactly themselves. Only characters that would need
288
+ escaping downstream are folded to `_`, and a collision gains a deterministic
289
+ `_2` suffix.
290
+
291
+ **Mermaid.** `graph` and `flowchart` declarations with any direction; the
292
+ twelve node shapes; solid, thick, dotted and invisible links; edge text in all
293
+ four spellings; chains and `&` groups; frontmatter `title`. Subgraphs flatten
294
+ and keep their membership in a non-rendering `group` key. Styling, class and
295
+ click statements are read and discarded, because FlowYAML owns its theme.
296
+ `A([...])` and `A((...))` become a start, intermediate or end event according
297
+ to node degree, since Mermaid spells the whole event family one way.
298
+
299
+ **BPMN 2.0.** Events, the eight task kinds, the five gateways, sub-processes,
300
+ call activities, data references, text annotations, sequence flows,
301
+ associations, data associations, boundary attachments and message flows.
302
+ Elements are matched by local name, so `bpmn:`, `bpmn2:`, `semantic:` and a
303
+ default namespace all read the same. An expanded `subProcess` becomes a model
304
+ of its own and the node that held it gets the `ref` that opens it, so BPMN
305
+ nesting arrives as FlowYAML's own in-document navigation. Lanes become
306
+ `actors` and a per-node `lane` key.
307
+
308
+ Anything outside those boundaries stops with a `FlowYAMLImportError` naming
309
+ the format, the source and the line: a `sequenceDiagram`, an unclosed shape, a
310
+ self-loop, a subgraph used as an edge endpoint, an unmapped BPMN flow element,
311
+ an unresolved sequence flow, a DTD. Nothing is silently dropped that would
312
+ change the graph.
313
+
314
+ `docs/import_reference.md` carries the full mapping tables and the exact
315
+ parsing boundaries. `examples/onboarding.mmd` and
316
+ `examples/order_handling.bpmn` exercise both surfaces.
317
+
318
+ ## Keeping the page linked to the YAML
319
+
320
+ A rendered artifact is a snapshot. `flowyaml serve` is the other half: a
321
+ stdlib host that answers three fixed paths and re-reads the YAML file on every
322
+ data request, so editing and saving updates the page in place.
323
+
324
+ ```
325
+ / the page, rendered with data="url"
326
+ /flowyaml/data.json the graph, re-read and re-validated per request
327
+ /flowyaml/revision.json a content digest, so the page can poll cheaply
328
+ ```
329
+
330
+ This is the mechanism the delivered ERP DINFRA flowchart uses, where a Django
331
+ view re-reads `dinfra_workflows.yaml` per request and the static page fetches
332
+ it. To put a linked diagram inside your own application, render the shell with
333
+ `data="url"` and serve `flowyaml.payload(...)` from your own route.
334
+
335
+ Two limits stated plainly: a browser refuses to fetch a sibling file from a
336
+ `file://` page, so a linked page opened from disk reports that instead of
337
+ pretending, and nothing in a browser watches a file, so the update is bounded
338
+ by the poll interval. `docs/live_updates.md` has the whole picture.
339
+
340
+ ## Rendering and navigation
341
+
342
+ The engine normalizes every document into an immutable graph model, embeds that
343
+ model as escaped JSON, and hands it to the browser renderer.
344
+
345
+ - Layout is [elkjs](https://github.com/kieler/elkjs) with `elk.algorithm=layered`,
346
+ `elk.direction=RIGHT`, orthogonal routing and `NETWORK_SIMPLEX` placement.
347
+ Node sizes are measured in the browser from the wrapped label, so the geometry
348
+ follows the real font metrics and Python carries no JS layout dependency.
349
+ - Nodes, edges, arrow markers, labels and subprocess controls are SVG.
350
+ - Selecting a `subprocess` does not load a route. It switches the active
351
+ embedded model in the same document and writes the choice to the URL hash, so
352
+ browser Back and Forward walk the levels with no network access.
353
+ A standalone document uses the hash key `model`; a fragment uses its own
354
+ instance prefix as the key, so several instances never read each other's state.
355
+ - Pointer drag pans, wheel zoom is cursor-centred, two-finger pinch zooms on
356
+ touch, and scale is clamped to 0.2x-2.6x. The view fits the viewport on load
357
+ and refits on resize until the reader adjusts it.
358
+
359
+ ### Keyboard and accessibility
360
+
361
+ | Key | Action |
362
+ | --- | --- |
363
+ | `Tab` | Move between subprocess controls and detailed labels |
364
+ | `Enter` / `Space` | Open the focused subprocess |
365
+ | `Backspace` | Go up one level |
366
+ | Arrow keys | Pan |
367
+ | `+` / `-` | Zoom |
368
+ | `0` | Fit |
369
+ | `Escape` | Dismiss the tooltip |
370
+
371
+ Subprocess controls are `role="button"` with a visible focus ring. A node or
372
+ chip that carries `detail` folds that text into its own accessible name, so a
373
+ screen reader never has to read it from the shared tooltip element. Meaning
374
+ never depends on colour alone: the seven node types differ by shape and stroke, a
375
+ navigable subprocess carries a BPMN collapsed-marker box, a chevron underline
376
+ and a pointer cursor, and a detailed edge chip carries a `≡` glyph.
377
+
378
+ Every label reaches the DOM through `textContent`, never `innerHTML`, and the
379
+ embedded payload escapes `<`, `>` and `&` as JSON unicode escapes so no label
380
+ can close the script element.
381
+
382
+ Every output path writes UTF-8: `--out`, `write_html`, and rendering to stdout
383
+ alike. The document declares `<meta charset="utf-8">`, so the console codepage
384
+ never decides how an accented label is encoded.
385
+
386
+ ## Theme
387
+
388
+ `dornelles_multitech` is the v0 default and, in v0, the only registered theme.
389
+ It is a Work Utilities expression of the parent B+D baseline: canvas `#F7F5F1`,
390
+ surface `#FFFFFF`, ink `#151B24`, shell `#202732` / `#2A313C`, slate `#4B5563`,
391
+ muted `#8A94A3`, structure line `#D8D2C8`, amber `#B46D3A` / `#D99A57` reserved
392
+ for selected and evidence hierarchy, and semantic start `#2F7D4E`, warning
393
+ `#A86716`, danger `#B33A2E`, info `#346A8A`.
394
+
395
+ It keeps the parent system's compact geometry, technical system-sans typography,
396
+ visible focus state, low-radius surfaces (8 px maximum) and print legibility. It
397
+ deliberately does not inherit ERP DINFRA's institutional navy and gold identity.
398
+
399
+ Tokens are written as `--fy-*` custom properties scoped to the instance root, so
400
+ every rule in the stylesheet reads a variable and no colour is hard coded.
401
+
402
+ ## Vendored assets
403
+
404
+ `src/flowyaml/assets/elk.bundled.js` is the elkjs 0.9.3 UMD browser bundle
405
+ (~1.6 MB), redistributed under the Eclipse Public License 2.0. See
406
+ `src/flowyaml/assets/elk.LICENSE.md` and `NOTICE.md`. It runs its layout in
407
+ process; no web worker is constructed and no script is fetched. Repeated
408
+ inclusion on one page is guarded by `if (typeof window.ELK === "undefined")`, so
409
+ the bundle evaluates once however many fragments are mounted.
410
+
411
+ ## Tests
412
+
413
+ ```bash
414
+ python -m pytest tests -q # everything
415
+ python -m pytest tests -q -m "not browser" # no browser needed
416
+ python -m pytest tests/test_browser.py -q # headless offline checks
417
+ python -m pytest tests/test_live.py -q # the YAML-linked update path
418
+ python -m pytest tests/test_live_browser.py -q # an open page follows an edit
419
+ ```
420
+
421
+ The browser tests need `pip install playwright && playwright install chromium`
422
+ and skip cleanly when it is unavailable. They load the generated `file://`
423
+ document with an interception rule that aborts and records every request that
424
+ leaves the file scheme, so a passing run is itself the offline proof. The
425
+ non-browser suite makes the same claim statically, by asserting that the only
426
+ absolute URL FlowYAML writes is the SVG namespace.
427
+
428
+ Release preparation and the account-side Trusted Publishing gates are recorded
429
+ in [RELEASING.md](RELEASING.md).
430
+
431
+ ## Not in v0
432
+
433
+ No graph editor, drag-to-reposition, notebook widget, database persistence,
434
+ collaboration, authentication, swimlane, Gantt, map, export button or remote
435
+ asset loading. See `docs/future_fit_ledger.md`.
436
+
437
+ `flowyaml serve` is a local authoring host with no authentication, three fixed
438
+ routes and no filesystem mapping; it is not a production server, and it does
439
+ not make v0 an editor. The YAML is edited in your editor.
440
+
441
+ Importing BPMN is not BPMN compliance. v0 renders seven node types, two edge
442
+ kinds and two edge styles; it has no pool boundary, no swimlane and no
443
+ execution semantics. The importer translates a BPMN file into that model and
444
+ refuses what it cannot, rather than widening the contract.
445
+
446
+ There is no export back to Mermaid or BPMN. `to_yaml` writes FlowYAML.
447
+
448
+ ## License
449
+
450
+ FlowYAML is released under the [MIT License](LICENSE). The bundled, unmodified
451
+ `elkjs` layout engine remains under the Eclipse Public License 2.0; its license
452
+ and version records ship with every distribution and are summarized in
453
+ [NOTICE.md](NOTICE.md).