You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three interrelated fixes for per-layer-accuracy usability in Observatory:
**Build-time label cap (exporter.py)**
- Add `_MAX_LABEL_EXTENSIONS = 2` (cap on label rows reserved in the layout
slot) and `_layout_constants_payload()` which emits line_height, y_padding,
font sizes, and `max_label_extensions` into `payload.layout` so the JS
runtime reads the same values Python used for layout.
- `_select_top_label_extensions()`: for each node, picks the 2 extensions
with the largest label text to reserve bbox space, keeping the reserved slot
bounded to 2 extra rows regardless of how many label-bearing layers exist.
- `_compute_layout` and `relayout_payload_base` both use the new selector.
- `GraphExtensionPayload.has_label_formatter` flag (models.py + extension.py)
lets JS identify which extensions are label-bearing without inspecting nodes.
- `GraphPayload.layout` field carries the constants block.
**JS LRU queue (graph_data_store.js + view_controller.js + ui_manager.js)**
- `GraphDataStore` gets `labelLru` (Set, insertion-order LRU, cap from
`layoutConstants.max_label_extensions`) and `recordLabelActivation /
recordLabelDeactivation / extensionHasLabels` methods.
- `upsertExtension` preserves `has_label_formatter` and `sync_keys`.
- `computeActiveGraph` gates `label_append` on LRU membership; non-LRU
labeled extensions still contribute color/info/tooltip.
- `ViewerController.setState` diffs prev/next active set, calling
`recordLabelActivation` for newly-activated label-bearing exts (LRU evicts
oldest on overflow) and `recordLabelDeactivation` for newly-removed.
- Seeded at construction time so default layers render labels on first paint.
- Layer panel shows a small "L" badge on the ≤2 extensions whose labels are
currently visible; badge synced in `syncControlsFromState`.
**Canvas visual-rect shrink + edge re-anchor (canvas_renderer.js)**
- `_visualHeight(node)`: `Math.max(floor, Math.min(desired, node.height))`.
Hard-clamped to Python-reserved `node.height` so no rendering can exceed
layout. Floor ensures a rect is always drawn.
- `_visualBottom(node)`: `node.y - node.height/2 + _visualHeight(node)`.
Top of rect is fixed at the layout-slot top; bottom floats.
- `_effectiveSourceStart(edge)`: substitutes the polyline's first point y
with source's `_visualBottom`, re-anchoring outgoing edges to the visible
bottom of the drawn rect. Target-side (incoming) endpoints unchanged.
- All node draw calls (fillRect/strokeRect), label positioning, highlight
overlays, and hit-testing updated to use `_visualHeight`/`_visualBottom`/
`_effectiveSourceStart`.
- `edge.bounds.minY` widened at `_initTopology` time by the worst-case
source-endpoint substitution delta so AABB hover-test covers the new range.
- JS magic literals (16, 14px, 12px) replaced with `layoutConstants.*`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments