From 7bd5d5ad4a99c07107265d84355bfc7982c7d46f Mon Sep 17 00:00:00 2001 From: zz_y Date: Wed, 26 Aug 2026 07:52:47 -0600 Subject: [PATCH] fix(devtools): clean up Pre/Post-ASAP DAG viewer --- tools/dag-viewer/README.md | 4 +- tools/dag-viewer/index.html | 29 +- tools/dag-viewer/node-style.js | 129 +-------- tools/dag-viewer/render.py | 20 -- tools/dag-viewer/test_render.py | 56 +--- tools/dag-viewer/viewer.js | 497 ++++---------------------------- 6 files changed, 92 insertions(+), 643 deletions(-) diff --git a/tools/dag-viewer/README.md b/tools/dag-viewer/README.md index 8cd22c1a..1c3fd694 100644 --- a/tools/dag-viewer/README.md +++ b/tools/dag-viewer/README.md @@ -4,8 +4,8 @@ The viewer has one visualization mode: **Pre/Post-ASAP**. - Select one query to see that query's complete pre-ASAP and post-ASAP DAGs. - Select multiple queries to see two workload-union DAGs: one pre-ASAP union - and one post-ASAP union. Structurally identical nodes and edges are - collapsed while query roots and ownership are retained. + and one post-ASAP union. Nodes with the same exporter-assigned workload + identity are collapsed while query roots and ownership are retained. - Pre-ASAP nodes show only their original IR content. - Post-ASAP nodes show their translated IR content and the explicit planner decision carried by that node. diff --git a/tools/dag-viewer/index.html b/tools/dag-viewer/index.html index 4379cc58..cb1a1d01 100644 --- a/tools/dag-viewer/index.html +++ b/tools/dag-viewer/index.html @@ -140,7 +140,7 @@ .schemaHint { color:var(--muted); font-size:.7rem; } .plannerActions { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.55rem; } #plannerStatus { color: var(--muted); font-size: 0.75rem; } - #baPicker { + #scopePicker { display: none; flex-wrap: wrap; gap: 0.6rem 1.2rem; @@ -148,15 +148,15 @@ border-bottom: 1px solid var(--border); background: var(--panel); } - #baPicker.visible { display: flex; } - .baGroup { display: flex; flex-direction: column; gap: 0.3rem; min-width: 220px; } - .baGroupLabel { + #scopePicker.visible { display: flex; } + .scopeGroup { display: flex; flex-direction: column; gap: 0.3rem; min-width: 220px; } + .scopeGroupLabel { font-size: 0.72rem; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); } - .baRow { + .scopeRow { display: flex; align-items: center; gap: 0.5rem; @@ -165,29 +165,16 @@ border-radius: 8px; background: var(--panel2); color: var(--fg); - cursor: pointer; font: inherit; font-size: 0.78rem; text-align: left; } - .baRow:hover { border-color: var(--accent); } - .baRow:disabled { opacity: 0.55; cursor: not-allowed; } - .baRow:disabled:hover { border-color: var(--border); } - .baRow.active { + .scopeRow.active { border-color: var(--accent); background: var(--bg); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 15%, transparent); } - .baRow .baWinner { - font-size: 0.66rem; - font-weight: 700; - color: var(--accent); - border: 1px solid var(--accent); - border-radius: 999px; - padding: 0.05rem 0.4rem; - flex-shrink: 0; - } - .baRow .baMeta { color: var(--muted); font-size: 0.72rem; margin-left: auto; white-space: nowrap; } + .scopeRow .scopeMeta { color: var(--muted); font-size: 0.72rem; margin-left: auto; white-space: nowrap; } .proxyNote { font-size: 0.72rem; color: var(--muted); @@ -424,7 +411,7 @@

Input table schemas

-
+
diff --git a/tools/dag-viewer/node-style.js b/tools/dag-viewer/node-style.js index ca6c31d1..e688a80b 100644 --- a/tools/dag-viewer/node-style.js +++ b/tools/dag-viewer/node-style.js @@ -1,13 +1,12 @@ // Category table for QueryExpr node kinds — the single source of truth for -// how the viewer colors/labels/icons each `DagNode.kind`. Edit this file to +// how the viewer colors and labels each `DagNode.kind`. Edit this file to // reclassify a kind or retune a palette; nothing else in index.html needs to -// change. Icon SVGs are adapted from ProjectASAP/bgp-query-dag-explorer's -// hand-drawn icon set (see tools/dag-viewer/README.md). +// change. // // Also covers the 7 post-ASAP-only SummaryDagNode kinds (KeepPreAsap, // SummaryAgg, SummaryJoin, SummarySubtract, SummaryDelete, SummaryEstimate, -// SummaryMerge) that only ever appear in Before/After mode's After lane when -// an entry's `after.kind === "Summary"` — see the `summary` category below. +// SummaryMerge) that appear in the post-ASAP lane — see the `summary` +// category below. // kind (DagNode.kind from crates/ir/src/dag_export.rs) -> category name. const KIND_CATEGORY = { @@ -36,7 +35,7 @@ const KIND_CATEGORY = { Sort: 'sort', Limit: 'sort', LetBinding: 'bind', - // Post-ASAP SummaryDagNode kinds (Before/After mode's After lane only). + // Post-ASAP SummaryDagNode kinds (post-ASAP lane only). KeepPreAsap: 'summary', SummaryAgg: 'summary', SummaryJoin: 'summary', @@ -46,47 +45,6 @@ const KIND_CATEGORY = { SummaryMerge: 'summary', }; -const ICON_STROKE = '2.2'; -const ICON_FILL = 'rgba(255,255,255,.06)'; - -// Each icon fn takes a stroke/fill color and returns a standalone SVG string -// (viewBox 0 0 48 48, consistent stroke weight) for use as a node background -// image. Cylinder/funnel/transform/grid/venn/dashed-circle/sort-arrow are -// adapted directly from the reference repo's `iconFor()`; `bind` and the -// root-node badge are new, drawn in the same style. -const ICONS = { - data: (c) => ``, - filter: (c) => ``, - derive: (c) => ``, - aggregate: (c) => `Σ`, - window: (c) => ``, - join: (c) => ``, - set: (c) => ``, - sort: (c) => ``, - // New: a name tag, for LetBinding naming a sub-expression for reuse. - bind: (c) => ``, - // New: stacked layers, for the post-ASAP `summary` category — a materialized - // structure (a sketch, a rollup, a merged/estimated join) rather than a - // pre-ASAP relational operator. - summary: (c) => ``, -}; - -// KeepPreAsap-only: a dashed box with a through-arrow, standing in for -// "unchanged pre-ASAP content carried through as-is" — see the `summary` -// CATEGORIES entry and buildCyStyle's `node[kind = "KeepPreAsap"]` override -// in viewer.js for why this doesn't just reuse ICONS.summary's stroke color. -const KEEP_PRE_ASAP_ICON = (c) => ``; - -function keepPreAsapIconDataUri() { - const muted = getComputedStyle(document.documentElement).getPropertyValue('--muted').trim() || '#6b7280'; - return svgDataUri(KEEP_PRE_ASAP_ICON(muted)); -} - -// New: a small flag/marker badge layered onto whichever node is a query's -// root, since QueryExpr has no dedicated terminal "output" node kind the way -// the reference's BGP `out_*` steps do. -const ROOT_BADGE_ICON = (c) => ``; - // name -> { label, description, light: {bg, border}, dark: {bg, border} } const CATEGORIES = { data: { @@ -143,7 +101,7 @@ const CATEGORIES = { light: { bg: '#fff5ea', border: '#b45309' }, dark: { bg: '#3a2408', border: '#fb923c' }, }, - // Post-ASAP only (Before/After mode's After lane): every other category + // Post-ASAP only (post-ASAP lane): every other category // above is a saturated, hand-picked hue for a pre-ASAP QueryExpr operator. // `summary` is deliberately plain neutral gray instead of another hue — // partly because a 10th saturated color starts getting hard to @@ -157,7 +115,7 @@ const CATEGORIES = { // through) rather than a different concept. summary: { label: 'Summary', - description: 'KeepPreAsap, SummaryAgg, SummaryJoin, SummarySubtract, SummaryDelete, SummaryEstimate, SummaryMerge — post-ASAP materialized structures (Before/After mode only)', + description: 'KeepPreAsap, SummaryAgg, SummaryJoin, SummarySubtract, SummaryDelete, SummaryEstimate, SummaryMerge — post-ASAP materialized structures', light: { bg: '#f1f2f4', border: '#4b5563' }, dark: { bg: '#20242b', border: '#9ca3af' }, }, @@ -170,35 +128,10 @@ const ROOT_BADGE = { dark: { border: '#f87171' }, }; -// `DagNote.kind` -> badge color, for a node whose `notes` array (issue #257 — -// asap-aware-mapping's ReplacementExplanation, matched onto this node by -// dag_export's own hash) is non-empty. Only the two kinds -// asap_aware_mapping::ExplanationKind currently ships get their own entry; -// NOTE_KIND_FALLBACK covers anything else (a future kind, or a node whose -// notes mix more than one kind) without needing a new color per addition. -const NOTE_KIND_COLOR = { - SketchApproximation: { light: '#a16207', dark: '#facc15' }, // reuses the 'aggregate' category hue - CommonSubexpressionReuse: { light: '#4f46e5', dark: '#a5b4fc' }, // reuses the 'set' category hue -}; -const NOTE_KIND_FALLBACK = { light: '#334155', dark: '#cbd5e1' }; -const NOTE_BADGE_LABEL = { - SketchApproximation: 'Sketch alternative available', - CommonSubexpressionReuse: 'Shareable across consumers', -}; - -// Small "why" marker (a filled circle + exclamation mark) for a node whose -// `notes` is non-empty — layered in the bottom-right corner, so it never -// collides with the root badge (top-right) or the category icon (top-center). -const NOTE_BADGE_ICON = (c) => ``; - function isDarkMode() { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } -function svgDataUri(svg) { - return `data:image/svg+xml,${encodeURIComponent(svg)}`; -} - function categoryOf(kind) { return KIND_CATEGORY[kind] || 'derive'; } @@ -208,52 +141,12 @@ function categoryColors(name) { return isDarkMode() ? cat.dark : cat.light; } -function categoryIconDataUri(name) { - const { border } = categoryColors(name); - const icon = ICONS[name] || ICONS.derive; - return svgDataUri(icon(border)); -} - -function rootBadgeIconDataUri() { - const { border } = isDarkMode() ? ROOT_BADGE.dark : ROOT_BADGE.light; - return svgDataUri(ROOT_BADGE_ICON(border)); -} - -// The color a `notes` badge/chip should use for `kind` — one of -// ExplanationKind's two known variants, or NOTE_KIND_FALLBACK for anything -// else (future kind, or `noteBadgeColor`'s own "mixed kinds" case below). -function noteKindColor(kind) { - const c = NOTE_KIND_COLOR[kind] || NOTE_KIND_FALLBACK; - return isDarkMode() ? c.dark : c.light; -} - -// The color for a node's *badge* (as opposed to one note's own chip): the -// shared color if every note on the node is the same kind, otherwise the -// fallback — a node need not itself distinguish "two sketch findings" from -// "a sketch finding and a reuse finding" the way the side-panel detail -// (which lists each note individually) does. -function noteBadgeColor(notes) { - const kinds = new Set((notes || []).map((n) => n.kind)); - if (kinds.size === 1) return noteKindColor(notes[0].kind); - const c = NOTE_KIND_FALLBACK; - return isDarkMode() ? c.dark : c.light; -} - -function noteBadgeIconDataUri(notes) { - return svgDataUri(NOTE_BADGE_ICON(noteBadgeColor(notes))); -} - -// Mirror the palette into CSS custom properties so plain-DOM chrome (legend, -// side panel chips) can use var(--cat--bg/border) instead of -// duplicating these hex values in index.html's