Sitelet https://github.com/feldera/feldera/pull/6949
Skip to content

[profiler] Mark the node the metrics are about, and trace its edges - #6949

Open
Karakatiza666 wants to merge 1 commit into
redesign-profiler-diagram-8from
redesign-profiler-diagram-9
Open

[profiler] Mark the node the metrics are about, and trace its edges#6949
Karakatiza666 wants to merge 1 commit into
redesign-profiler-diagram-8from
redesign-profiler-diagram-9

Conversation

@Karakatiza666

@Karakatiza666 Karakatiza666 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Part 9 of 15 of #6895, split one commit per PR. Based on redesign-profiler-diagram-8; merge in order.

The stack (this is 9 of 15)
  1. [profiler] Remove the unused hierarchical table #6941 [profiler] Remove the unused hierarchical table
  2. [profiler] Select the whole SQL range a node came from #6942 [profiler] Select the whole SQL range a node came from
  3. [profiler] Count the primitive operators inside every region #6943 [profiler] Count the primitive operators inside every region
  4. [profiler] Add the diagram palettes, stylesheet and corner chips #6944 [profiler] Add the diagram palettes, stylesheet and corner chips
  5. [profiler] Size an expanded region for its name and its counter #6945 [profiler] Size an expanded region for its name and its counter
  6. [profiler] Draw the diagram from the palette-driven stylesheet #6946 [profiler] Draw the diagram from the palette-driven stylesheet
  7. [profiler] Draw a collapsed region nested inside an expanded one #6947 [profiler] Draw a collapsed region nested inside an expanded one
  8. [profiler] Paint a node's id and its operator as two text runs #6948 [profiler] Paint a node's id and its operator as two text runs
  9. [profiler] Mark the node the metrics are about, and trace its edges #6949 [profiler] Mark the node the metrics are about, and trace its edges <-- this PR
  10. [profiler] Show a picture of the circuit on the minimap, and steer from it #6950 [profiler] Show a picture of the circuit on the minimap, and steer from it
  11. [profiler] Move the diagram's lifecycle reactions into observers #6951 [profiler] Move the diagram's lifecycle reactions into observers
  12. [profiler] Make the corner chips pressable #6952 [profiler] Make the corner chips pressable
  13. [profiler] Follow the application theme #6953 [profiler] Follow the application theme
  14. [profiler] Add a browser harness for the diagram, and pin what it paints #6954 [profiler] Add a browser harness for the diagram, and pin what it paints
  15. [profiler] Pin what a pointer on the diagram does #6955 [profiler] Pin what a pointer on the diagram does

Which node the metrics panel is describing was readable only from the
panel itself. The node now carries an accent glow, painted by
nodeShadow.ts in place of the ambient shadow every node has, and one
node holds the mark at a time - reached by click, by hover or by search.

Clicks and hovers went through one displayEventTargetAttributes with an
isSticky flag, which read as one behaviour and was two. They are now
reportOn and hoverNode:

click reports, and the report stays; an expanded region reports as
readily as an operator
hover reports only while nothing is pinned; a pinned report keeps its
mark, and a hover over anything else moves the trace alone

The mark is asked of cytoscape rather than tracked beside it, so the
report and the glow cannot disagree about which node holds it.

Describe Manual Test Plan

Click a node: it takes an accent glow and its edges are traced. Hovering elsewhere moves the trace but leaves the glow on the pinned node.

Verified at this commit, not just at the tip of the stack: checked out detached with js-packages/profiler-lib/dist deleted and rebuilt from this commit's source, then profiler-lib bun run check and bun run test, and profiler-layout bun run check and bun run test (all three vitest projects, browser suites included). All four green.

Checklist

  • Unit tests added/updated
  • Integration tests added/updated
  • Documentation updated
  • Changelog updated

Breaking Changes?

Mark if you think the answer is yes for any of these components:

  • OpenAPI / REST HTTP API / feldera-types / manager
  • Feldera SQL (Syntax, Semantics)
  • feldera-sqllib (incl. dependencies fxp, etc.)
  • Python SDK
  • fda (CLI arguments)
  • Adapters (including configuration)
  • Storage Format / Checkpoints
  • Others (specify)

Describe Incompatible Changes

None. The change is confined to js-packages/.

Which node the metrics panel is describing was readable only from the
panel itself. The node now carries an accent glow, painted by
`nodeShadow.ts` in place of the ambient shadow every node has, and one
node holds the mark at a time - reached by click, by hover or by search.

Clicks and hovers went through one `displayEventTargetAttributes` with an
`isSticky` flag, which read as one behaviour and was two. They are now
`reportOn` and `hoverNode`:

  click  reports, and the report stays; an expanded region reports as
         readily as an operator
  hover  reports only while nothing is pinned; a pinned report keeps its
         mark, and a hover over anything else moves the trace alone

The mark is asked of cytoscape rather than tracked beside it, so the
report and the glow cannot disagree about which node holds it.

Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
Comment on lines +933 to 938
if (this.stickyInformation) {
if (!this.reportIsMarked()) {
this.traceSelection(node);
}
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message's central invariant — "the report and the glow cannot disagree about which node holds it" — doesn't hold, and the manual test plan describes behaviour this code can't produce.

traceSelection always calls markSelected, so the trace and the glow are inseparable; "hovering elsewhere moves the trace but leaves the glow on the pinned node" is not reachable.

Two concrete paths:

steps report panel glow
click node A, hover node B A A (hover is a no-op — the trace does not move, contrary to the description)
click an expanded region R, hover operator B R B

The second case is the disagreement: reportOn deliberately lets an expanded region report, traceSelection refuses to mark it, so reportIsMarked() is false and the next hover paints the glow on a node whose metrics are not on display. Either the sticky-hover branch should not mark (only colour edges, as the description says), or a region should not be reportable.

return false;
}
this.center(Option.some(value));
this.markSelected(el.nodes());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search() marks but never reports, so the glow lands on the searched node while the panel still shows the previously clicked one — the same report/glow disagreement as in hoverNode, and it makes markSelected's doc ("the one the diagram reports on ... whether reached by click, hover or search") untrue. It also skips the edge trace, so a searched node glows without its edges coloured, unlike a clicked one.

Three edge cases this path gets wrong, none covered by a test:

  • getElementById(value) can resolve an edge: nonempty() is true, el.nodes() is empty, so markSelected clears the existing mark and adds none, yet search returns true. Verified against a real headless cytoscape instance.
  • searching an expanded region or the root: markSelected adds the class, nodeShadow() refuses to paint a parent, so the previous glow is silently dropped and nothing replaces it. traceSelection's guards are bypassed here.
  • searching a node inside a collapsed region: search() returns false, profiler.ts reveal() expands ancestors and centres after the layout, and the node is never marked at all.

Routing this through traceSelection (or displayNodeAttributes) rather than raw markSelected would give all four entry points one set of rules.

Comment on lines +112 to +121
// The body box cytoscape draws, which is the node's own size plus its padding. The
// border straddles the edge of that box and so adds nothing to it.
const padding = Number(node.numericStyle('padding')) || 0;
paintShadow(
context,
node,
shadow,
pos ?? node.position(),
w ?? node.width() + 2 * padding,
h ?? node.height() + 2 * padding

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth knowing that this fallback is the only path that runs in a browser, not a defensive branch: cytoscape's cached-element pass calls drawElementUnderlay(context, ele) with two arguments (cytoscape.cjs.js, CRp$a.drawElementUnderlay), and the five-argument call from drawNode is guarded by shouldDrawOverlay, which the texture-cache draw passes as false. So pos/w/h are always undefined on screen, and the test that pins the five-argument shape exercises a call site the renderer never takes.

Two consequences:

  • node.padding() is cytoscape's own resolver for this and is what drawNodeOverlayUnderlay uses for the identical fallback; numericStyle('padding') returns the bare number and would be wrong for a percentage padding. Both current paddings are px, so no bug today — but reusing node.padding() costs nothing and can't drift.
  • drawCachedElement skips a node whose bounding box misses the viewport extent, and the box doesn't include the 20-unit glow, so a marked node just off-screen loses its glow entirely instead of bleeding in.

A test driving a real (non-headless) cytoscape renderer, or the browser harness later in the stack, would have caught the argument-shape assumption.

@mythical-fred-oss

Copy link
Copy Markdown

Ran in js-packages/profiler-lib: bun install, bun run check (clean), bun run test (9 files / 201 tests green), and pre-commit run --files on the three changed files (JavaScript Check passed); also a scratch vitest against a real headless cytoscape and a read of cytoscape.cjs.js to check the drawNodeUnderlay call shape. nodeShadow.ts is genuinely well covered, but this fails hard rule #2 (unit tests for changed behaviour): every line changed in cytograph.tsreportOn, hoverNode, reportIsMarked, traceSelection, clearTrace, mouseOut, search — is the actual feature and has no test at all, and that gap is why two of the three findings below survived to review. Concretely, the commit message says "the report and the glow cannot disagree about which node holds it" and the manual test plan says "hovering elsewhere moves the trace but leaves the glow on the pinned node"; neither is what the code does (details inline) — since traceSelection always calls markSelected, trace and glow are inseparable, and clicking an expanded region then hovering an operator puts the glow on a node whose metrics are not on display. Uncovered cases I would want pinned, all cheap to write headlessly by asserting on the selected-node / highlight-* classes:

case expected
click A, hover B, mouse out of B glow + trace stay on A
click expanded region, hover operator region reports; glow does not move to the operator
search(id) of a drawn node glow and trace on it, report follows
search of an edge id / a region / the root mark unchanged, and search should not return true for an edge
search of a node inside a collapsed region marked once the layout that reveals it completes
Escape, then double-click to collapse the marked node mark and trace cleared, currentTooltipNode reset
metadata refresh while pinned updateMetadata re-enters displayNodeAttributes, so the mark must survive

No flakiness, no dependency, licensing, unsafe or workflow concerns; the change is confined to js-packages/, so no docs or breaking-change gate applies.

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