Skip to content

Commit 95fc2e4

Browse files
zzylolclaude
andauthored
feat(dag-viewer): standalone Python render + animated transitions (#255)
* feat(dag-viewer): standalone Python render + animated transitions Adds a headless/no-browser path for tools/dag-viewer, on top of #249's Compare/Union modes: - `render.py` bakes a dag_export WorkloadGraph JSON (or several, merged the same way index.html's multi-file drop does) into one self-contained HTML file — every vendored script and the query data inlined, so it opens directly with no server and no drag-and-drop. index.html's own fetch('dag.json') convenience is blocked as cross-origin by most browsers over file://; embedding sidesteps that too, not just the no-browser case. `--mode compare|union` opens the page straight into that view with every loaded query pre-selected. - Extracted index.html's inline <script> into `viewer.js`, unchanged apart from the additions below, so index.html and render.py's output share one copy of the interaction logic instead of forking it. - Mode switches, the shared-subtree ring, and initial layout now animate (dagre's `animate: true`, a `transition-property` on node/edge style so class toggles ease instead of snap, and a fade-in on newly built elements) rather than hard-cutting to a new static layout on every render() call. - `test_render.py` unit-tests render.py's merge/inline/escape logic (plain unittest.TestCase, matching tools/clickhouse's convention; not wired into CI, same as that tool). Query cost isn't rendered — dag_export doesn't emit one yet (no cost estimator wired into pre-ASAP IR). The side panel dumps a clicked node's `detail` verbatim, so a future `cost` field there needs no viewer change. Stacked on #249 (targets that branch, not main, until it merges) — see issue #186 and PR #249's description for the Compare/Union groundwork this builds on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(dag-viewer): correct test_render.py's run instructions python3 -m unittest tools/dag-viewer/test_render.py doesn't actually work run from the repo root -- unittest resolves the file-path form to a bare 'test_render' module but never adds tools/dag-viewer/ to sys.path, so the file's own 'from render import ...' fails with ModuleNotFoundError. Same latent bug exists in tools/clickhouse/test_extract_functions.py's docs (copied from there), left alone since it's pre-existing and out of scope here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(dag-viewer): clean error messages for render.py's bad-input paths A nonexistent input file or malformed JSON crashed with a raw Python traceback (FileNotFoundError / json.JSONDecodeError propagating straight out of main()) instead of a one-line stderr message + exit 1, matching the existing 'no queries in input' / '--mode needs 2+ queries' style. Found by actually running every command tools/dag-viewer's docs mention, end to end, including through a real cargo-built dag_export. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(dag-viewer): reverse edge arrow direction to data-flow order Structural edges pointed from a node to its children (parent -> child, i.e. output -> its input), which is backwards for a data-flow diagram: an arrowhead should point at the consumer, not the input. Swap source/target for the edges built in all three view modes (single/compare/union) so arrows now run input -> consumer, matching the top-to-bottom dagre layout (scans at top, query root at bottom). Addresses review feedback on #255. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 7e5afa8 commit 95fc2e4

6 files changed

Lines changed: 1225 additions & 764 deletions

File tree

‎tools/dag-viewer/README.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,41 @@ picker.
3838
mode (buttons next to the drop zone) to see them together instead of one
3939
at a time — see below.
4040

41+
## Render a standalone page from Python
42+
43+
`index.html` needs a browser attached — dropping a file onto it, or a
44+
sibling `dag.json` it can `fetch()`. Neither works if you're generating a
45+
graph from an environment with no browser to look at it in (piped over SSH,
46+
a CI job, an agent session): `render.py` bakes a `dag_export` JSON straight
47+
into a single portable HTML file instead, with the query data and every
48+
vendored script inlined — open the output directly, nothing else to fetch:
49+
50+
```sh
51+
cargo run -p asap-devtools --bin dag_export -- --sql "..." --name q1 \
52+
| python3 tools/dag-viewer/render.py -o rendered.html
53+
54+
# or from files already on disk, opening straight into Union mode:
55+
python3 tools/dag-viewer/render.py dag1.json dag2.json -o rendered.html --mode union
56+
```
57+
58+
It's the exact same UI as `index.html` — `--mode` just pre-selects every
59+
loaded query and switches the page's *initial* view; Single/Compare/Union,
60+
click-to-inspect, and the highlight toggle all still work after it opens.
61+
`index.html` and `render.py`'s output share one copy of the interaction
62+
logic (`viewer.js`) and the category table (`node-style.js`), so anything
63+
below in this doc — the mode descriptions, the shared-hash caveats — applies
64+
to both equally.
65+
66+
Node/edge transitions (mode switches, the shared-subtree ring, layout on
67+
load) are animated in both — see `viewer.js`'s `LAYOUT_ANIMATION` and the
68+
`transition-property` entries in `buildCyStyle()` if you want to retune or
69+
disable that.
70+
71+
Per-node cost isn't rendered anywhere in either page — `dag_export` doesn't
72+
emit one today (no cost estimator is wired into pre-ASAP IR yet). The side
73+
panel already dumps a clicked node's full `detail` verbatim, so a future
74+
`cost` field added there would show up with no viewer change needed.
75+
4176
## Visual style and node categories
4277

4378
Node colors, icons, and shapes are adapted from

‎tools/dag-viewer/RUNNING.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,17 @@ python3 -m http.server 8420
5656

5757
Tunnel port 8420 to your machine, then open `http://localhost:8420/` locally. `dag.json` loads on page load, same as before.
5858

59-
## 4. Using the page
59+
## 4. No browser attached at all?
60+
61+
If there's nowhere to even point a tunnel — an agent session, a CI job, anywhere you can only produce a file, not view one live — skip the server and bake a standalone page instead:
62+
63+
```sh
64+
python3 tools/dag-viewer/render.py tools/dag-viewer/dag.json -o rendered.html
65+
```
66+
67+
`rendered.html` is fully self-contained (data and every script inlined) — hand it off however you'd hand off any file; whoever opens it gets the same page as steps 1–3, no setup on their end. See "Render a standalone page from Python" in README.md.
68+
69+
## 5. Using the page
6070

6171
Click a node to see its detail in the side panel. That includes its category and whether it's a query's root.
6272

0 commit comments

Comments
 (0)