ofplang export
Readable views of ofplang documents: a
workflow as its dataflow graph, and the execution plans
ofp-schedule produces from it, shown
side by side and linked — as a web site, and as one self-contained HTML file.
The viewer is OFP View (OFP: Object-Flow Programming).
It reads the documents the ofplang specifications define and nothing more, so it does not depend on any dialect built on top of them.
pip install ofplang-export
ofp-export view plan.yaml -o plan.html # a plan; its workflow and environment come from its meta
ofp-export view workflow.yaml -o wf.html # a workflow on its own
ofp-export view writes the viewer as one HTML file with the documents in
it, to open from disk or send to someone. It is also ofp export view
(pip install "ofplang[export]", from ofplang 0.7) and, under labcode,
lc export view (labcode[export], from labcode 0.9).
→ ofplang.github.io/export
— eleven plans are bundled (one of them, data_flow, curated here: Pure Data, and an entry with inputs and outputs); ?doc=plate_batch opens one directly. Drop your own
YAML on the window to read that instead: a plan, a workflow, an environment, or
all three at once. A workflow on its own is fine — the graph does not need a
plan to be read. Either pane can take the whole window: Both / Workflow / Plan
in the top bar, or &layout=workflow / &layout=plan in the address.
The point is to be able to hand someone a URL. They open it and see the dataflow graph and the Gantt chart of a plan side by side, linked: pick a bar and the workflow node it came from lights up, pick a node and every bar under it lights up. No install, no server, no Python.
The Gantt chart slices the plan four ways: by machine, by top-level step, by
activity, and by Object — one lane per plate or sample, from where it
appears to where it is used up, with every step, move and wait on the way.
Which Object is which comes from the workflow's objects declarations, since
a plan names none; where they cannot be followed, that view is not offered.
Status: early but usable. Both panes work and are linked: pick a bar and the workflow box it came from lights up, pick a box and everything under it lights up in the plan, pick an arc and it names the type it carries and the moves that carry it. No arc is drawn through a box it does not join. Plans can be exported as SVG or put in a link.
prototype/holds the single-file look-and-feel study the visual decisions were made against, under the name the viewer had then.
The command
ofp-export view <file>... [-o OUT] [--layout split|workflow|plan] [--gantt device|flow|activity|object]
[--name NAME] [--no-follow] [--json]
- Files are a plan, a workflow and/or an environment, in any order; which is
which is read from the file. A plan's
metasupplies the workflow and the environment unless they are given (--no-followturns that off). - Without
-othe HTML goes to standard output, asofp-scheduledoes with a plan. With-othe path written is printed;--jsonprints a summary instead. - Exit codes: 0 written (warnings, if any, on stderr — parts of a workflow the viewer shows only as source structure), 2 bad input, 3 refused and nothing written — a joint plan (several workflows scheduled together as jobs), which this viewer does not draw.
skills/ofp-export/SKILL.md says the same for an
agent calling the command, as a skill to install where the agent looks for them.
A single file
The same viewer also builds as one self-contained HTML file that carries
its documents inside it: open it from disk, no server, nothing beside it.
npm run build:single writes the empty template, web/dist-single/viewer.html
(also kept as the viewer-template artifact of every CI run), and
web/scripts/embed.mjs puts documents into it:
node scripts/embed.mjs dist-single/viewer.html out.html plan.yaml --workflow w.yaml --env e.yaml
The documents go in as the YAML text as written, in one element —
<script type="application/json" id="ofp-documents" data-contract="1"> — and
the page reads them with the same reader it uses for a dropped file. An empty
template (null in that element) is an offline viewer to drop files on. The
web fonts stay a link, so offline the page falls back to system fonts; Copy
link is hidden, since a link made from a file on disk would point at the disk.
Layout
| Path | What it is |
|---|---|
web/ |
the application — Vite + TypeScript, no runtime dependency beyond yaml |
web/src/model/ |
types for the workflow, the environment and the execution document |
web/src/read/ |
YAML → those types; the only part that tracks the specifications |
web/src/model/scene.ts |
the indices every view is built on — by node, by arc, by machine |
web/src/layout/ |
lanes, bars and the time scale; pure functions, no DOM |
web/src/view/ |
SVG rendering, the inspector, and the SVG export |
web/scripts/collect-datasets.mjs |
turns the submodule's examples into the bundled datasets |
web/scripts/build-single.mjs |
folds the build into the one-file viewer template |
web/scripts/embed.mjs |
puts documents into that template — the contract, in one place |
ofplang/export/ |
the ofp-export Python package; template.py is the same contract in Python |
tests/ |
its tests (pytest), against the pinned submodule's examples |
skills/ofp-export/SKILL.md |
how an agent calls ofp-export; not in the package |
datasets/curated/ |
plans bundled here that the submodule does not ship (data_flow), with their provenance |
web/tests/golden/ |
every example the pinned submodule ships must read |
web/tests/routing.test.ts |
no arc of any bundled workflow runs through a box it does not join |
external/ofplang-schedule |
submodule, pinned by tag — specifications and examples |
prototype/ |
a single-file look-and-feel study; not the codebase |
web/public/favicon.png |
the ofplang mark, from ofplang/spec logos/symbol-circle.png; the SVG version is inline in web/index.html |
Why a TypeScript reader instead of reusing the Python one
The sibling repositories own the specifications and the semantics, and this one
does not modify them. It also has to run with nothing installed on the viewer's
machine, which rules out a Python pre-processing step. So the document readers
here are a deliberate, bounded re-implementation of two stable schemas —
SPECIFICATIONS.md §5 (environment) and §6 (execution document) — kept honest
by the golden test against the pinned submodule's own examples.
Anything outside that subset — $import, generics, structured nodes, and
joint plans that schedule several workflows together as jobs — is refused
rather than guessed at.
Working on it
git clone --recurse-submodules git@github.com:ofplang/export.git
cd web
npm install
npm run dev # development server (collects the datasets first)
npm run datasets # rebuild public/datasets/ from external/
npm run typecheck # tsc --noEmit
npm test # golden tests against external/ofplang-schedule
npm run build # typecheck + production build into web/dist
npm run build:single # …and the one-file template into web/dist-single
npm run test:e2e # the browser tests (builds the template first)
The Python package, from the repository root (after npm run build:single,
which also puts the template into ofplang/export/_template/):
pip install -e ".[dev]"
pytest
A release is a v* tag: publish.yml builds the template and the wheel from
the same commit and publishes through PyPI trusted publishing (an rc tag goes
to TestPyPI). Installing straight from git gives no template — it is built, not
committed — and the command says so.
Already cloned without --recurse-submodules? git submodule update --init.
License
MIT — see LICENSE.
Metadata
Release files for ofplang-export 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ofplang_export-0.1.3.tar.gz | 88.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ofplang_export-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 163.9 kB
Release files / ofplang_export-0.1.3.tar.gz
| Download URL | ofplang_export-0.1.3.tar.gz |
|---|---|
| Size | 88.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c4461c423a6c08f75f7381ef3a04012167fa7ee84250432f47c0967f2114c2b
|
|
BLAKE2b-256 checksum How to use checksums |
5a61f7b705a22c84baec19f0c4c692ce0e69e440287603cff10bb380499806b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / ofplang_export-0.1.3-py3-none-any.whl
| Download URL | ofplang_export-0.1.3-py3-none-any.whl |
|---|---|
| Size | 75.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5b4ea383b8e11bbaeda5a994b5b939501b0bc4d7d343e03dca93d67cc75ef935
|
|
BLAKE2b-256 checksum How to use checksums |
34feb52b43488e5a380f8f97655b584461920405710288ecc8c4d22117f33b9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log