fix(voyager): clear node border highlight residue on switch#101
Merged
Conversation
When a user double-clicked an entity in the ER diagram (banner highlight
kicks in: outerFrame + titleBg painted orange) and then clicked another
entity, the previous node's title background cleared correctly but a
faint orange stroke lingered on the outer frame — a visible "didn't
clean up fully" smudge that compounded with every switch.
Root cause: two independent original-color stores that don't talk to
each other.
- `graph-ui.js::highlightSchemaBanner` writes stroke / stroke-width /
fill via `setAttribute` and saves the originals to DOM attributes
`data-original-stroke` / `-stroke-width` / `-fill` (via
`_saveOriginalAttributes`).
- `graphviz.svg.js::setupNodesEdges` (init-time) snapshots fill+stroke
only (no stroke-width) to jQuery `.data("graphviz.svg.color")`.
- `clearSchemaBanners` was calling `gv.highlight()` (which routes
through `restoreElement` → `_restoreElementColors`) and then just
`removeAttribute`-ing the `data-original-*` — without ever writing
the saved originals back to the SVG attributes. `restoreElement`
couldn't reliably restore because (a) its jQuery data store lacks
stroke-width (hardcodes 1 on restore) and (b) the two stores are
blind to each other's writes.
Fix: in `clearSchemaBanners`, between `gv.highlight()` and
`removeAttribute`, write the saved `data-original-*` values back to
their corresponding SVG attributes. graph-ui.js becomes the last
writer and overrides any inconsistency left by graphviz.svg.js.
Highlights and the `data-original-*` lifecycle are otherwise
unchanged — `highlightSchemaBanner` and `_saveOriginalAttributes`
are not touched.
Spec kit artifacts under `specs/008-voyager-node-border-residue/`
(spec, plan, research, data-model, contract, quickstart, tasks).
Tests: no front-end test baseline (consistent with spec 005/006/007);
verification is the manual quickstart checklist. Backend regression
`pytest tests/ -k voyager` → 41 passed, 0 regressions (this change
is front-end only).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
allmonday
added a commit
that referenced
this pull request
Jul 3, 2026
Version bump for PR #101 (Voyager ER diagram node border highlight residue fix in clearSchemaBanners). Updates CHANGELOG, pyproject.toml, uv.lock. Co-Authored-By: Claude Opus 4.7 <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.
Problem
When a user double-clicked an entity in the ER diagram (banner highlight kicks in: outer frame + title background painted orange) and then clicked another entity, the previous node's title background cleared correctly but a faint orange stroke lingered on the outer frame — a visible "didn't clean up fully" smudge that compounded with every switch.
Root cause
Two independent original-color stores that don't talk to each other:
graph-ui.js::highlightSchemaBannerwritesstroke/stroke-width/fillviasetAttributeand saves the originals to DOM attributesdata-original-stroke/-stroke-width/-fill(via_saveOriginalAttributes).graphviz.svg.js::setupNodesEdges(init-time) snapshotsfill+strokeonly (nostroke-width) to jQuery.data("graphviz.svg.color").clearSchemaBannerswas callinggv.highlight()(which routes throughrestoreElement→_restoreElementColors) and then justremoveAttribute-ing thedata-original-*— without ever writing the saved originals back to the SVG attributes.restoreElementcouldn't reliably restore because:stroke-width(it hardcodes1on restore — seegraphviz.svg.js:479)setAttributechanges don't propagate to jQuery.data(), anddata-original-*is private tograph-ui.jsFix
In
clearSchemaBanners, betweengv.highlight()andremoveAttribute, write the saveddata-original-*values back to their corresponding SVG attributes.graph-ui.jsbecomes the last writer and overrides any inconsistency left bygraphviz.svg.js.clearSchemaBanners() { if (this.gv) { this.gv.highlight() } this._lastHighlight = null const allPolygons = document.querySelectorAll("polygon[data-original-stroke]") allPolygons.forEach((polygon) => { + const origStroke = polygon.getAttribute("data-original-stroke") + const origStrokeWidth = polygon.getAttribute("data-original-stroke-width") + const origFill = polygon.getAttribute("data-original-fill") + if (origStroke !== null) polygon.setAttribute("stroke", origStroke) + if (origStrokeWidth !== null) polygon.setAttribute("stroke-width", origStrokeWidth) + if (origFill !== null) polygon.setAttribute("fill", origFill) polygon.removeAttribute("data-original-stroke") polygon.removeAttribute("data-original-stroke-width") polygon.removeAttribute("data-original-fill") }) }~12 lines added, single file, no API change.
highlightSchemaBannerand_saveOriginalAttributesare intentionally untouched (the write path is correct; the bug was in the clear path).Scope boundaries (what this fix does NOT do)
graphviz.svg.js(vendored library; larger blast radius)HIGHLIGHT_COLOR/HIGHLIGHT_STROKE_WIDTHconstants (no color tweak)renderErDiagramsubgraph refetch logic (independent visual fix)Test plan
pytest tests/ -k voyager→ 41 passed, 0 regressions (this change is front-end only)tasks.md):uv run uvicorn demo.enterprise_voyager.voyager_demo:app --port 8010specs/008-voyager-node-border-residue/quickstart.md§2.2–2.10Employee→ clickDepartment→ DevTools compareEmployeeouter-frame polygonstroke/stroke-width/fillwith never-clickedRolenode — must be identicalSpec kit artifacts
Full spec-kit trail under
specs/008-voyager-node-border-residue/:spec.md(3 clarify Q&As)plan.md+research.md(4 implementation decisions, root cause analysis)data-model.md(DOM attribute data flow + 8 edge cases)contracts/clear-banners-restore.md(before/after code, invariants, scope boundaries)quickstart.md(10 manual verification procedures + DevTools snippets + failure triage)tasks.md(6 tasks, 3 auto-completed, 3 pending manual)🤖 Generated with Claude Code