feat(dag-viewer): standalone Python render + animated transitions - #255
Merged
Merged
Conversation
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>
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>
zzylol
force-pushed
the
feat/dag-viewer-python-render
branch
from
August 23, 2026 04:27
0f30584 to
6ef8197
Compare
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>
milindsrivastava1997
left a comment
Collaborator
There was a problem hiding this comment.
I think the order of arrows should be reversed. Otherwise, lgtm
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.
Contributor
Author
|
@milindsrivastava1997 done — reversed the edge direction in all three view modes (single/compare/union), arrows now point from input to consumer instead of parent to child. Pushed in 16310d4. Screenshot added to the PR description showing the union-mode render with the fix. |
zzylol
added a commit
that referenced
this pull request
Aug 24, 2026
…viewer.js split PR #255 landed on main while this branch was in flight, moving nearly all of tools/dag-viewer/index.html's inline JS out into viewer.js and adding render.py/test_render.py for a standalone-page renderer. This branch's fedf0ed commit had added a replacement-explanation "notes" badge/panel feature by editing index.html's old inline-script structure, which no longer exists on main — causing PR #259 to conflict. - Bring tools/dag-viewer/ up to date with main: pull in viewer.js, render.py, test_render.py, and the new (much shorter) index.html shell verbatim from origin/main. node-style.js and dag.example.json are untouched by main, so both are left exactly as this branch already had them (dag.example.json still demonstrates the notes feature). - Re-port the notes UI onto viewer.js's actual structure: the root-badge block in finalizeGraphInteractions() gets a sibling block for the notes badge, notesHtml() is spliced into both showDetail() and showUnionDetail(), and renderLegend() gets one row per ExplanationKind. All of it calls node-style.js's already-committed NOTE_* helpers exactly as the root-badge code already does. The .notesBlock/.noteItem/etc CSS moves into index.html's <style> block, which main kept in place. - Bring README.md/RUNNING.md's main-side documentation of render.py into this branch too (previously undocumented here since render.py didn't exist on this branch before), reapplying the notes-specific bullets and the "Replacement explanations (notes)" section on top. Verified: cargo build/test/fmt/clippy all clean; python3 -m unittest test_render passes (12/12); render.py runs against dag.example.json without error. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #249 (targets that branch, not
main, until it merges). Adds aheadless/no-browser rendering path for
tools/dag-viewer, plus animatedtransitions in the existing page.
What's here
render.py— bakes adag_exportWorkloadGraphJSON (or several,merged the same way
index.html's multi-file drop does — colliding querynames get the source filename appended) into one self-contained HTML
file: every vendored script and the query data inlined, so the output
opens directly with no server and no drag-and-drop needed.
index.html's ownfetch('dag.json')convenience is blocked ascross-origin by most browsers when opened over
file://— embedding thedata directly sidesteps that too, not just the no-browser case.
--mode compare|unionopens the page straight into that view with everyloaded query pre-selected.
viewer.js—index.html's inline<script>extracted verbatiminto its own file, so
index.htmlandrender.py's output share onecopy of the interaction logic instead of forking it. Pure extraction plus
the animation/embedded-data additions below — no behavior change to the
existing single/compare/union element-building logic.
Animated transitions — mode switches, the shared-subtree ring
toggling, and initial layout now animate (dagre's
animate: true, atransition-propertyon node/edge style so class/style changes easeinstead of snapping, and a fade-in on newly built elements) instead of
hard-cutting to a new static layout on every
render()call.test_render.py— unit tests forrender.py's merge/inline/escapelogic (plain
unittest.TestCase, matchingtools/clickhouse'sconvention; not wired into CI, same as that tool — this repo doesn't run
Python in CI today).
README/RUNNING doc updates for both of the above.
Deliberately out of scope
Query cost.
dag_exportdoesn't emit one today — no cost estimator iswired into pre-ASAP IR. Nothing renders it because there's nothing to
render; the side panel already dumps a clicked node's
detailverbatim, soa future
costfield added there shows up automatically, no viewer changeneeded.
Validation
python3 -m unittest tools/dag-viewer/test_render.py— 10/10 passing.render.py's output checked againstdag.example.json: JSONround-trips byte-for-byte through the embedded
<script>tag,esprimaparses every inlined
<script>block, no<script src=...>tags remain.viewer.jslogic (embedded-data loading,--modeoverride,animated-layout config) exercised in a stubbed V8 context via
py_mini_racer— same validation approach feat(dag-viewer): lane-based Compare/Union DAG-merge view #249's own description used —with a capturing
cytoscape()stub asserting on the elements/layoutobjects the code hands it (real vendored cytoscape/dagre are unmodified
and already covered by feat(dag-viewer): lane-based Compare/Union DAG-merge view #249's own validation).
dag.example.jsonthroughrender.py --mode unionandopened the output.
Screenshot
Per review feedback, edge arrows now point from input to consumer
(data-flow direction) instead of parent to child —
render.py --mode unionoutput ondag.example.json, top-to-bottom: scans at top, eachquery's root at bottom.
Single-query view, same fix:
🤖 Generated with Claude Code