Skip to content

fix(voyager): clear node border highlight residue on switch#101

Merged
allmonday merged 1 commit into
masterfrom
fix/008-node-border-residue
Jul 3, 2026
Merged

fix(voyager): clear node border highlight residue on switch#101
allmonday merged 1 commit into
masterfrom
fix/008-node-border-residue

Conversation

@allmonday

Copy link
Copy Markdown
Collaborator

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::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:

  1. Its jQuery data store lacks stroke-width (it hardcodes 1 on restore — see graphviz.svg.js:479)
  2. The two stores are blind to each other's writes — setAttribute changes don't propagate to jQuery .data(), and data-original-* is private to graph-ui.js

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.

   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. highlightSchemaBanner and _saveOriginalAttributes are intentionally untouched (the write path is correct; the bug was in the clear path).

Scope boundaries (what this fix does NOT do)

  • ❌ Doesn't touch graphviz.svg.js (vendored library; larger blast radius)
  • ❌ Doesn't change HIGHLIGHT_COLOR / HIGHLIGHT_STROKE_WIDTH constants (no color tweak)
  • ❌ Doesn't change which node gets highlighted or when (no trigger logic change)
  • ❌ Doesn't touch spec 007's renderErDiagram subgraph refetch logic (independent visual fix)

Test plan

  • pytest tests/ -k voyager41 passed, 0 regressions (this change is front-end only)
  • Manual browser verification still pending (T003/T004 in tasks.md):
    • uv run uvicorn demo.enterprise_voyager.voyager_demo:app --port 8010
    • Follow specs/008-voyager-node-border-residue/quickstart.md §2.2–2.10
    • Key check (§2.2): double-click Employee → click Department → DevTools compare Employee outer-frame polygon stroke / stroke-width / fill with never-clicked Role node — must be identical
    • §2.8: 20+ rapid switches, no accumulating residue
    • §3: regression sweep (existing toggles, sidebar tabs, schema/mode switches all unchanged)

Spec 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

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 allmonday merged commit ffa01af into master Jul 3, 2026
6 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant