drawio-digest
Your .drawio diagrams, readable by LLMs — and by humans in code review.
draw.io saves a canvas — shapes and coordinates, not meaning. That makes
diagrams opaque to reviewers, scripts and coding agents. drawio-digest
reads the geometry and recovers the structure: nodes, edges, labels, lanes.
What you drew:
What you get — one command, and GitHub renders the result:
drawio-digest examples/order-review.drawio
flowchart TD
subgraph lane0["Customer"]
n0(["Start"])
n1["Browse catalogue"]
n2["Place order"]
n3["Pay"]
n4(["End"])
end
subgraph lane1["Store"]
n5{"In stock?"}
n6["Reserve items"]
n7["Create backorder"]
n8["Ship parcel"]
end
n0 --> n1
n1 --> n2
n2 -->|"submit"| n5
n5 -->|"yes"| n6
n5 -->|"no"| n7
n6 -->|"invoice"| n3
n3 -->|"paid"| n8
n8 --> n4
n7 -->|"restocked"| n5
That diagram is not an image — it is the actual output, a Markdown file with
a fenced Mermaid block per page, rendered by GitHub. Lanes become subgraph
blocks, rhombus and ellipse shapes are preserved, and edge labels survive
whether they are stored on the edge or as separate edgeLabel cells. Node
numbering is stable, so regenerated output stays diffable.
Use it with a coding agent
drawio-digest is a plain CLI, so Claude Code, Codex and friends can run it
without a plugin or MCP server. Two lines in your project instructions
(CLAUDE.md, AGENTS.md) are enough:
Diagrams under docs/ are .drawio files. Read one with
`drawio-digest <file> --stdout`; scan them all with `drawio-digest docs/*.drawio --summary`.
An agent that previously saw only coordinate XML now reads your diagrams like
any other document. --summary keeps the cheap case cheap — it answers
"is this diagram worth opening?" in a few lines:
$ drawio-digest examples/*.drawio --summary
order-review
Page 1: 9 nodes, 9 edges, 2 lanes
lanes: Customer(5), Store(4)
entry: Start
exit: End
Why not just read the XML?
Because the file describes pixels, not meaning. The diagram above is stored like this:
<mxCell id="start" value="Start" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="170" y="100" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="browse" value="Browse catalogue" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="140" y="220" width="160" height="50" as="geometry" />
</mxCell>
<!-- ...and a few hundred more lines of geometry -->
Worse, because draw.io is a free-form canvas, several things that look structural on screen are not structural in the file. This tool handles the cases that bite:
| In the file | What it means | What naive parsing does |
|---|---|---|
| A large titled rectangle holding other shapes | A swimlane | Emits it as a giant node |
A vertex with edgeLabel style |
A label on an edge | Emits a floating node, edge loses its label |
An edge with no source |
Endpoint dropped on a connection point, not inside the shape — looks attached on screen | Silently loses the edge |
endArrow=none |
A divider or annotation | Emits a phantom connection |
The third one is the nasty one. draw.io renders it identically to a real
connection, so it is invisible until something tries to read the file.
drawio-digest reattaches such an endpoint when the stored coordinate lies
on or within 20px of a shape, and flags it for review rather than
fixing it silently:
> ℹ️ These connections were not bound to a shape in the source file and were
> reattached by coordinate. Please verify:
> - Place order -> In stock? (submit)
If an endpoint is too far from anything to be certain, the edge is dropped and reported — never guessed:
> ⚠️ These connections have an unattached endpoint that could not be resolved
> and were skipped. Please check them:
> - Ship parcel -> ?
The right fix is in the source diagram: drag the endpoint until the whole shape highlights, not just a connection point. This tool tells you where.
Install
Not on PyPI yet. Install from a checkout:
pip install /path/to/drawio-digest
Requires Python 3.8+. No dependencies.
Usage
drawio-digest FILE... [options] # FILE may be - for stdin
-f, --format {markdown,mermaid,json} output format (default: markdown)
-o, --outdir DIR output directory (default: alongside source)
--stdout print instead of writing files
--summary short overview instead of converting
--direction {TD,LR,BT,RL} mermaid flow direction (default: TD)
--no-notes omit notes about recovered/dropped edges
--strict exit non-zero if any edge was dropped
drawio-digest flow.drawio # -> flow.md Markdown + Mermaid
drawio-digest flow.drawio -f mermaid # -> flow.mmd bare diagram source
drawio-digest flow.drawio -f json # -> flow.json structured data
drawio-digest *.drawio --summary # one short block per diagram
cat flow.drawio | drawio-digest - # stdin
Formats. markdown is a ready-to-read document — a # title, a fenced
mermaid block per page, and any review notes. mermaid is the bare diagram
source, for pasting into a document you already have. json is the full
model, for scripts.
--summary reports nodes that touch no edge as unconnected rather than
counting them as entry and exit points — legends and date markers are common
in real diagrams and would otherwise misdescribe the flow.
--strict is for CI: fail the build when a diagram contains connections
that cannot be resolved.
As a library
from drawio_digest import parse, parse_string, to_markdown, to_summary
diagram = parse("flow.drawio")
for page in diagram.pages:
print(page.name, len(page.nodes), len(page.edges))
for edge in page.recovered:
print("check this one:", edge.source, "->", edge.target)
print(to_summary(diagram))
print(to_markdown(diagram, direction="LR"))
diagram = parse_string(xml_text) # already in memory
Features
Output
- Markdown document — a title, a fenced mermaid block per page, review notes
- Bare Mermaid source, for embedding in a document you already have
- JSON, for scripts and further processing
-
--summary— shape, lanes, entry/exit points in a few lines -
--direction TD|LR|BT|RLfor Mermaid flow direction - Multi-page diagrams — one section and one block per page
Structure recovery
- Lanes detected by containment, including plain rectangles used as lanes
- Explicit
swimlaneshapes - Flat diagrams with no lanes at all
- Node shapes — box, diamond, ellipse
- Edge labels stored inline or as separate
edgeLabelcells - Endpoints that look attached but are not, reattached by coordinate and flagged
- Unresolvable endpoints reported, never guessed
-
endArrow=nonedividers and unconnected annotations excluded from the flow - Compressed (deflated)
.drawiofiles
Interface
- Batch conversion, with
-oto redirect output -
-reads from stdin,--stdoutprints instead of writing -
--strictexits non-zero when an edge could not be resolved, for CI - Stable node numbering, so regenerated output stays diffable
- Python API —
parse,parse_string,to_markdown,to_mermaid,to_json,to_summary - Zero dependencies, Python 3.8+
Not done
- Sequence, class and ER diagrams — flowcharts only
- Layout, colours and styling beyond node shape
- Images and custom shape libraries
- Nested lanes — an inner lane is dropped and its nodes fall to the outer one
- Writing
.drawioback out — this tool only reads
Limitations
Mermaid is a constrained, auto-laid-out language and draw.io is not, so some loss is unavoidable and intentional:
- Layout is not preserved. Mermaid lays out its own graph.
- Lanes are optional. Flat diagrams convert fine and simply produce no
subgraphblocks. - Lanes are inferred from geometry, since real
swimlaneshapes are rare in hand-drawn diagrams. A shape counts as a lane when it has a title and encloses at least three other shapes — size alone misclassifies both ways, because a narrow lane in one diagram can be smaller than a plain box in another. Explicitswimlaneshapes are always honoured. - Images, custom shapes, and styling beyond node shape are dropped.
- Compressed diagrams are supported, but if a page fails to decompress, save it with File → Properties → Compressed unchecked.
For anything beyond a flowchart, use -f json and build what you need.
Development
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file drawio_digest-0.1.0.tar.gz.
File metadata
- Download URL: drawio_digest-0.1.0.tar.gz
- Upload date:
- Size: 57.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4662adc26ed832a8e0aebbc034df473f0d84f13579ce4a257c86c07b1e72e715
|
|
| MD5 |
4015b4e457eb7014c7bd7aeb3d24ae7d
|
|
| BLAKE2b-256 |
fd9d7d74b36929f1d0ff13deb52b80af111403f28a484726e2346ae126d62401
|
File details
Details for the file drawio_digest-0.1.0-py3-none-any.whl.
File metadata
- Download URL: drawio_digest-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d73e12b3243445eb78cec9329a00f52ee923b94737f38b6abc5e795773b3ad1
|
|
| MD5 |
43c57cb7af7a84f93f3860fda8c2b774
|
|
| BLAKE2b-256 |
2f9175c3737281657de4781bf4c5a1cecdb5b1cfa75a9259ea9671b0198c45d6
|