Skip to content

Commit bc9aa2b

Browse files
Improve dot rendering (#26)
* Rendering Source Hierarchy - Rendering packages as subgraphs in the class graph - Rendering packages as subgraphs and rectangles in the package graph * - Reverted nested package graph rendering - All DOT graph images now hyperlink to git provider location * Apply suggested CodeRabbit fixes * Apply suggested CodeRabbit fixes * Apply suggested CodeRabbit fixes and override white fill for package boxes in dark mode * Now using deep rose for package clusters in dark mode for the clusters that need to be broken up * Highlighting nodes and edges and what they connect when they are moused over * Highlighting nodes and edges and what they connect when they are moused over in the 2D and 3D popups * fixing e2e tests * Adding back globals.css
1 parent c13c388 commit bc9aa2b

15 files changed

Lines changed: 3125 additions & 339 deletions

‎AGENTS.md‎

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,36 @@ in `out/` is served by any static host (GitHub Pages, GitLab Pages, Bitbucket).
3333
branch (`rf:branch-resolved` event) so the label matches the loaded report
3434
(`main` 404 → `master` fallback)
3535
- Reports rendered with Mustache.js (bundled template is authoritative)
36+
- Graphs synthesized client-side from the nested hierarchy in
37+
refactor-first.json (`lib/hierarchy-dot.js`, byte-identical to the report
38+
module's Java `NestedDotBuilder`; packages nest as clusters, classes as
39+
ellipses, break-up packages fill pink). The package map renders the flat
40+
relationship graph — one ellipse per package with only the weighted
41+
edges, break-up packages in red — through vizdom like the class and
42+
cycle graphs; every graph's nodes are hyperlinked to their source
43+
files/directories via `project.repoUrl` (http(s)-gated, new tab
44+
restored post-render since vizdom drops the target); hovering a vizdom
45+
edge highlights it plus both endpoints, hovering (or keyboard-focusing)
46+
a node highlights its incoming edges + source nodes and outgoing edges +
47+
sink nodes in two directional colors (`bindGraphHoverHighlights` in
48+
`lib/report-view.js` derives the neighbor model from the vizdom
49+
`<title>` texts and toggles `rf-hover-*` classes, styled per palette via
50+
`--graph-hover-*`); highlighted nodes fill with their accent and their
51+
label glyphs re-ink to `--graph-hover-on` (white on the dark light-mode
52+
accents, black on the bright dark-mode accents) to keep AA contrast; the
53+
2D/3D popups carry the same directional model (`bindSigmaHoverHighlight`
54+
for the sigma 2D popup — node hover only, sigma 2.4 has no edge events —
55+
and the hover block in `createForceGraph` for the 3D force graph, which
56+
also highlights hovered links), resolving accents from the JS mirror
57+
`GRAPH_HOVER_ACCENTS` in `lib/report-view.js`: the canvas-drawn 2D popup
58+
follows the active palette like the SVGs, while the 3D popup's WebGL
59+
canvas is always dark and therefore always uses the dark values. The 3D
60+
popup's hover highlighting is always on (per-hover sprite recoloring
61+
touches only nodes whose state changed, keeping it O(changed)), and
62+
while nothing is highlighted the DOT graph's own colors show — each
63+
link's DOT color (red removals, white fallback) and each node's DOT
64+
color on its sprite label; the full
65+
hierarchy stays in refactor-first.json for LLM consumption
3666
- Enhanced report tables: sticky headers, pagination (>20 rows), sortable
3767
columns, in-table search, CSV export, click/keyboard cell copy with toasts
3868
- Repository submission via pre-filled platform issues — no login, apps or tokens
@@ -91,7 +121,10 @@ lib/ # shared logic (client + RSC): routes, fetcher,
91121
# renderer, search, utils, host, rate-limiter,
92122
# cache-manager, error-handler, repo-submission,
93123
# report-view, static-params, widget-loader,
94-
# breadcrumbs,
124+
# breadcrumbs, hierarchy-dot (hierarchy-to-DOT
125+
# synthesis mirroring the Java NestedDotBuilder in
126+
# the report module; byte-identical goldens in
127+
# tests/unit/hierarchy-dot.test.js),
95128
# table-operations (filter/sort/paginate/CSV/copy +
96129
# TABLE_CONFIG + REPORT_TABLES descriptors),
97130
# table-enhancer (binds toolbar/sort/pagination/
@@ -158,12 +191,31 @@ coverage there when introducing new markup patterns.
158191
SVGs' embedded black strokes/fills are likewise re-themed to
159192
`--graph-line` (`#9fb0c0` dark = `CHART_LEGEND_TEXT.dark`) via
160193
`path[stroke]`/`polygon` selectors on `.fullscreen-svg` — red cycle
161-
edges stay red. Edge labels are glyph paths baked by vizdom, so CSS
162-
can't reach them: `withEdgeFontColor` in `lib/report-view.js` rewrites
163-
the DOT with `fontcolor = "#9fb0c0"` per edge when the dark palette
164-
is active (vizdom uppercases the hex in the SVG), and theme switches
165-
re-parse the graphs via `redrawGraphsForTheme` alongside the chart
166-
legend redraw in `bindThemeChartRedraw`.
194+
edges stay red — and the package clusters (`g.cluster rect[fill="white"]`,
195+
the packages that do not need to be broken up) re-drive their fill to
196+
`--bg-color` with their label glyph paths via a rect-sibling selector, so
197+
the boxes match the page in dark mode. Break-up clusters
198+
(`g.cluster rect[fill="pink"]`) re-drive through their own
199+
`--graph-breakup-fill` (light: vizdom's baked `#ffc0cb`; dark: a deep rose
200+
`#7f3350` that keeps the `--graph-line` edges crossing the box visible)
201+
and `--graph-breakup-line` ink (light: baked black; dark: light pink
202+
`#f2b8cd` for the boundary + label) — no fill can pass 3:1 against both
203+
the near-black page and the light edges, so the fill prioritizes edge
204+
visibility and the pink ink carries the perceivable boundary. Graph DOT
205+
is synthesized
206+
from the `.hierarchy` payloads
207+
(`classMap`/`packageMap`/`largestCycle`, see `lib/hierarchy-dot.js`);
208+
edge labels are fill-only glyph paths baked by vizdom, so CSS can't
209+
reach them: the dark palette bakes `fontcolor = "#9fb0c0"` into every
210+
edge at synthesis time via `graphDotOptions()` in `lib/report-view.js`
211+
(vizdom uppercases the hex in the SVG), and theme switches
212+
re-synthesize + re-render the graphs via `redrawGraphsForTheme` alongside
213+
the chart legend redraw in `bindThemeChartRedraw`. Node labels sit on
214+
vizdom's white shapes and keep the baked black ink in the non-hover
215+
state (hovered or keyboard-focused node labels re-ink to
216+
`--graph-hover-on`), while cluster labels are recolored by CSS along
217+
with their rects (regular clusters' label glyphs follow `--graph-line`,
218+
break-up clusters' labels take `--graph-breakup-line`).
167219

168220
## Report Tables (Enhanced)
169221

@@ -220,7 +272,7 @@ coverage there when introducing new markup patterns.
220272

221273
## Current Test Count
222274

223-
~584 unit/integration + ~229 E2E (~202 across three browsers + 4 basePath leg + 27 dark mode).
275+
~787 unit/integration + ~232 E2E (~202 across three browsers + 4 basePath leg + 30 dark mode).
224276

225277
WCAG 2.2 AA / HTML5 guards live in tests/unit/html5-attributes.test.js,
226278
tests/unit/report-template-wcag.test.js, tests/unit/css-a11y.test.js and

‎app/globals.css‎

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@
3535
/* vizdom WASM graph edges bake black stroke/fill attributes into the SVG;
3636
palette-driven so the dark theme can swap them to a readable tint. */
3737
--graph-line: #000000;
38+
/* Break-up package clusters: vizdom bakes fillcolor=pink onto the cluster
39+
rect with the default black stroke/label glyphs. The light values equal
40+
those baked colors, so the re-driving rules below never change the
41+
light theme by a pixel; the dark block swaps them for a tuned surface. */
42+
--graph-breakup-fill: #ffc0cb;
43+
--graph-breakup-line: #000000;
44+
/* DOT-graph hover highlights (vizdom SVGs): the hovered edge/node and its
45+
partners take the amber attention accent; a hovered node colors its
46+
incoming (source) and outgoing (sink) neighborhoods green/blue. All
47+
three pass >= 3:1 on the white page and on the pink breakup clusters;
48+
highlighted nodes fill with their accent, and the label ink flips to
49+
--graph-hover-on to stay readable on it (white on these dark accents). */
50+
--graph-hover-edge: #b45309;
51+
--graph-hover-in: #1e8449;
52+
--graph-hover-out: #2a6f97;
53+
--graph-hover-on: #ffffff;
3854
--error-color: #c0392b;
3955
--success-color: #1e8449;
4056
--error-bg: #fdf6f5;
@@ -102,6 +118,23 @@ html:root {
102118
/* Hamburger glyph + 1px border on the dark menu surface (6.8:1). */
103119
--menu-toggle-color: #6fb1d9;
104120
--graph-line: #9fb0c0;
121+
/* Break-up package clusters: the baked pink is a glaring pastel slab on
122+
the dark page (11.8:1) and swallows the #9fb0c0 edges crossing it
123+
(1.45:1), so the box drops to a deep rose — flagged against the
124+
near-black regular clusters (2.2:1 plus a warm hue shift) while the
125+
edges stay visible on it (3.8:1) — and a light pink ink carries the
126+
boundary and label (5.0:1 on the fill, 10.8:1 on the page). */
127+
--graph-breakup-fill: #7f3350;
128+
--graph-breakup-line: #f2b8cd;
129+
/* DOT-graph hover highlights: bright accents that stay distinct from the
130+
slate #9fb0c0 edges (hue + width) and pass >= 3:1 on both cluster
131+
fills (#10161d regular, #7f3350 rose). */
132+
--graph-hover-edge: #f0b95a;
133+
--graph-hover-in: #7ee2a8;
134+
--graph-hover-out: #8ed0fa;
135+
/* Label ink on the accent-filled highlighted nodes: the bright fills take
136+
black glyphs (the labels bake a black fill in both palettes). */
137+
--graph-hover-on: #000000;
105138
--error-color: #f0928c;
106139
--success-color: #5bbd8b;
107140
--error-bg: #2b1d1f;
@@ -146,6 +179,23 @@ html:root {
146179
/* Hamburger glyph + 1px border on the dark menu surface (6.8:1). */
147180
--menu-toggle-color: #6fb1d9;
148181
--graph-line: #9fb0c0;
182+
/* Break-up package clusters: the baked pink is a glaring pastel slab on
183+
the dark page (11.8:1) and swallows the #9fb0c0 edges crossing it
184+
(1.45:1), so the box drops to a deep rose — flagged against the
185+
near-black regular clusters (2.2:1 plus a warm hue shift) while the
186+
edges stay visible on it (3.8:1) — and a light pink ink carries the
187+
boundary and label (5.0:1 on the fill, 10.8:1 on the page). */
188+
--graph-breakup-fill: #7f3350;
189+
--graph-breakup-line: #f2b8cd;
190+
/* DOT-graph hover highlights: bright accents that stay distinct from the
191+
slate #9fb0c0 edges (hue + width) and pass >= 3:1 on both cluster
192+
fills (#10161d regular, #7f3350 rose). */
193+
--graph-hover-edge: #f0b95a;
194+
--graph-hover-in: #7ee2a8;
195+
--graph-hover-out: #8ed0fa;
196+
/* Label ink on the accent-filled highlighted nodes: the bright fills take
197+
black glyphs (the labels bake a black fill in both palettes). */
198+
--graph-hover-on: #000000;
149199
--error-color: #f0928c;
150200
--success-color: #5bbd8b;
151201
--error-bg: #2b1d1f;
@@ -964,3 +1014,120 @@ main#app .fullscreen-svg polygon:not([fill="red"]) {
9641014
stroke: var(--graph-line);
9651015
fill: var(--graph-line);
9661016
}
1017+
1018+
/* Package clusters: vizdom bakes the DOT's fillcolor onto each cluster rect
1019+
(probe against the vendored 0.1.19: g.cluster > a > rect fill="white"
1020+
stroke="black", with the cluster label as a black glyph path following the
1021+
rect as a sibling). appendPackage only ever emits fillcolor=white (regular
1022+
packages) or fillcolor=pink (break-up candidates), so the white boxes are
1023+
the packages that do not need to be broken up: their fill re-drives from
1024+
the palette so it matches the page — vizdom's default white on the light
1025+
page, the dark --bg-color in dark mode — and the baked black stroke follows
1026+
--graph-line like the rest of the graph. The cluster label glyphs ride on
1027+
the re-driven fill, so they follow --graph-line too; anchored to the white
1028+
rect sibling so the rule never touches the break-up clusters, which the
1029+
rules below theme through their own palette. Pure CSS: the DOT (and its
1030+
byte-identical lockstep with the Java NestedDotBuilder) keeps
1031+
fillcolor=white/fillcolor=pink. */
1032+
main#app .fullscreen-svg g.cluster rect[fill="white"] {
1033+
fill: var(--bg-color);
1034+
stroke: var(--graph-line);
1035+
}
1036+
1037+
main#app .fullscreen-svg g.cluster rect[fill="white"] ~ path[fill="black"] {
1038+
fill: var(--graph-line);
1039+
}
1040+
1041+
/* Break-up packages (the pink rects) invert that treatment: their problem
1042+
signal survives the dark page best as a deep rose box with a light pink
1043+
boundary and label, because no single fill can pass 3:1 against both the
1044+
near-black page and the light --graph-line edges crossing the box — the
1045+
rose fill keeps the edges visible (SC 1.4.11) and the pink ink keeps the
1046+
box boundary and its label readable (SC 1.4.3). The light palette values
1047+
equal vizdom's baked pink/black, so this never changes light mode. */
1048+
main#app .fullscreen-svg g.cluster rect[fill="pink"] {
1049+
fill: var(--graph-breakup-fill);
1050+
stroke: var(--graph-breakup-line);
1051+
}
1052+
1053+
main#app .fullscreen-svg g.cluster rect[fill="pink"] ~ path[fill="black"] {
1054+
fill: var(--graph-breakup-line);
1055+
}
1056+
1057+
/* Hover highlights on the DOT graphs: lib/report-view.js derives the
1058+
node/edge neighborhood from the vizdom titles and toggles rf-hover-*
1059+
state classes. Hovering an edge flags the edge plus both endpoint nodes
1060+
with the amber attention accent; hovering a node flags it with the same
1061+
accent while its incoming edges + source nodes go green and its outgoing
1062+
edges + sink nodes go blue, so dependency direction reads at a glance.
1063+
Highlighted nodes FILL with their accent (their baked white/red/pink fill
1064+
yields to the highlight like red removal edges do), and the node labels —
1065+
glyph paths with a baked black fill riding on the shape — re-ink to
1066+
--graph-hover-on to keep AA contrast on the fill. A node flagged both in
1067+
and out is a bidirectional partner of the hovered node and takes the
1068+
attention accent over either directional state. */
1069+
main#app .fullscreen-svg g.edge.rf-hover-edge path[stroke] {
1070+
stroke: var(--graph-hover-edge);
1071+
stroke-width: 2.5px;
1072+
}
1073+
1074+
main#app .fullscreen-svg g.edge.rf-hover-edge polygon {
1075+
stroke: var(--graph-hover-edge);
1076+
fill: var(--graph-hover-edge);
1077+
}
1078+
1079+
main#app .fullscreen-svg g.node.rf-hover-edge ellipse,
1080+
main#app .fullscreen-svg g.node.rf-hover-edge rect,
1081+
main#app .fullscreen-svg g.node.rf-hover-focus ellipse,
1082+
main#app .fullscreen-svg g.node.rf-hover-focus rect {
1083+
stroke: var(--graph-hover-edge);
1084+
fill: var(--graph-hover-edge);
1085+
stroke-width: 2px;
1086+
}
1087+
1088+
main#app .fullscreen-svg g.edge.rf-hover-in path[stroke] {
1089+
stroke: var(--graph-hover-in);
1090+
stroke-width: 2.5px;
1091+
}
1092+
1093+
main#app .fullscreen-svg g.edge.rf-hover-in polygon {
1094+
stroke: var(--graph-hover-in);
1095+
fill: var(--graph-hover-in);
1096+
}
1097+
1098+
main#app .fullscreen-svg g.node.rf-hover-in ellipse,
1099+
main#app .fullscreen-svg g.node.rf-hover-in rect {
1100+
stroke: var(--graph-hover-in);
1101+
fill: var(--graph-hover-in);
1102+
stroke-width: 2px;
1103+
}
1104+
1105+
main#app .fullscreen-svg g.edge.rf-hover-out path[stroke] {
1106+
stroke: var(--graph-hover-out);
1107+
stroke-width: 2.5px;
1108+
}
1109+
1110+
main#app .fullscreen-svg g.edge.rf-hover-out polygon {
1111+
stroke: var(--graph-hover-out);
1112+
fill: var(--graph-hover-out);
1113+
}
1114+
1115+
main#app .fullscreen-svg g.node.rf-hover-out ellipse,
1116+
main#app .fullscreen-svg g.node.rf-hover-out rect {
1117+
stroke: var(--graph-hover-out);
1118+
fill: var(--graph-hover-out);
1119+
stroke-width: 2px;
1120+
}
1121+
1122+
main#app .fullscreen-svg g.node.rf-hover-in.rf-hover-out ellipse,
1123+
main#app .fullscreen-svg g.node.rf-hover-in.rf-hover-out rect {
1124+
stroke: var(--graph-hover-edge);
1125+
fill: var(--graph-hover-edge);
1126+
}
1127+
1128+
main#app .fullscreen-svg g.node.rf-hover-edge path[fill="black"],
1129+
main#app .fullscreen-svg g.node.rf-hover-focus path[fill="black"],
1130+
main#app .fullscreen-svg g.node.rf-hover-in path[fill="black"],
1131+
main#app .fullscreen-svg g.node.rf-hover-out path[fill="black"] {
1132+
fill: var(--graph-hover-on);
1133+
}

0 commit comments

Comments
 (0)