Skip to content

Track metrics view explicitly so the segment indicator stays in sync#6463

Open
Karakatiza666 wants to merge 3 commits into
mainfrom
issue-segment-indicator
Open

Track metrics view explicitly so the segment indicator stays in sync#6463
Karakatiza666 wants to merge 3 commits into
mainfrom
issue-segment-indicator

Conversation

@Karakatiza666

Copy link
Copy Markdown
Contributor

Added onNodeClick to porfiler-lib to directly track when the node is clicked to properly disambiguate overview - node stats - top nodes tab states. This fixes indicator not showing that you are viewing the "overview" metrics.

Testing: manual, added unit tests


/** User-initiated single-click on a node (not fired by `showGlobalMetrics` / `showTopNodes`).
* Fires before the matching `displayNodeAttributes` so consumers can switch view state. */
onNodeClick?: (nodeId: string) => void;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

normally the profiler should not know about user actions, it should provide some APIs to manipulate the profile. It is up to the UI to decide what happens on single click, not to the profiler.
But maybe it's too late, since the callbacks already have this kind of information.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is up to the UI to decide what happens on single click

This is pretty much what we're doing here, though? Profiler-lib only communicates to the UI (profiler-layout) that the single or double click happened, and then the UI desscribes what happens as the result.
profiler-lib does not care what happens on the click, it only reports it (as well as manages the internal state)

return typeof view === 'string' ? view : 'node'
}

/** Accepts `null` so "is there a node to restore?" is a single call. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this commend makes no sense in this context. What is "restore?"

import type { NodeAttributes } from 'profiler-lib'
import type { MetricsMode } from '../components/MetricsView.svelte'

/** The analysis panel's current selection. `{ node }` carries the operator id so "Node" can be

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see any state machine, maybe there is one in your head

})

// Regression for the title-heuristic bug: previously the mode was derived from
// `tooltipData.title === 'n region'`, so any profile whose root id wasn't `n` got stuck on

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what does it mean "got stuck on?"


// Regression for the title-heuristic bug: previously the mode was derived from
// `tooltipData.title === 'n region'`, so any profile whose root id wasn't `n` got stuck on
// "Node". With the explicit discriminator the projection is heuristic-free.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is a projection?

@mythical-fred mythical-fred left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good direction. Tracking analysisView explicitly instead of inferring view state from tooltip data removes a feedback-loop hazard and makes the segment indicator deterministic. The AnalysisView discriminated union ('overview' | 'top-nodes' | { node: string }) is a clean model.

Bonus points for adding a vitest config + unit tests for metricsMode.ts — exactly the kind of test infrastructure the profiler-layout package needed.

@Karakatiza666 Karakatiza666 force-pushed the issue-segment-indicator branch from c9eefcc to c3b01ef Compare June 14, 2026 11:32
@Karakatiza666 Karakatiza666 marked this pull request as ready for review June 14, 2026 11:32

@mythical-fred mythical-fred left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good direction — explicit AnalysisView state is much cleaner than the title-string heuristic, and pushing a dedicated onNodeClick callback into ProfilerCallbacks so cytograph can distinguish a user click from a programmatic refresh is the right shape. The unit tests for metricsModeOf / isNodeView plus the new vitest infrastructure for profiler-layout are a real win.

Two concerns, one of them real:

  1. Profile-reload effect no longer resets analysisView (blocker-shaped, but soft because hard to hit). The old code did metricsMode = 'overview' inside the queueMicrotask after showGlobalMetrics(true). The new code only calls diagram.showGlobalMetrics(true), leaving analysisView at whatever it was for the previous profile. Two knock-on effects:

    • SegmentedControl indicator stays on "Node" / "Top nodes" while the diagram is now showing the overview — exactly the desync this PR is fixing for clicks, recreated for the profile-load path.
    • showGlobalMetrics(true) triggers displayNodeAttributes with isSticky=true. In the sticky branch the new code does if (isNodeView(analysisView)) lastNodeData = attrs — so if the previous view was { node }, the overview payload silently overwrites lastNodeData, and the "Node" segment then has nothing useful to restore.
    • Fix: write analysisView = 'overview' in the queueMicrotask callback alongside the showGlobalMetrics call.
  2. nodeIdOf couples profiler-layout to cytograph's title-string format (non-blocking, but worth a comment). It does attrs.title.split(' ')[0], which assumes the title is <id> <operation> — set in cytograph.ts via the kv-loop that prepends id and appends " " + operation. That coupling is exactly the kind of brittle thing the rest of the PR is removing. Cheaper alternatives: cache the clicked nodeId from onNodeClick in SupportBundleViewerLayout (you already have it), or thread the id through NodeAttributes as a first-class field instead of splitting the display title. The latter is cleaner since NodeAttributes already lives in profiler-lib.

Nit: minor diff noise in index.ts (reordered createLookupCoordinator / createLoadGuard) and IssuesTab.svelte (line wrapping the destructure) — fine, just don't squash them into the substantive commit message.

Once #1 is fixed, happy to re-approve.

@mythical-fred mythical-fred left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rebased tip reviewed (c3b01efd73). The blocker from my prior review is still present:

The profile-reload $effect (around line 200 of SupportBundleViewerLayout.svelte) still only calls diagram.showGlobalMetrics(true) without resetting analysisView = 'overview' inside the queueMicrotask. Switching profiles while viewing a node will leave the SegmentedControl on "Node" while the diagram shows the overview — exactly the desync this PR fixes for clicks.

One-line fix: analysisView = 'overview' before diagram.showGlobalMetrics(true) in the microtask callback.

Keeping REQUEST_CHANGES from the prior review until this is addressed.

…stays in sync

The analysis panel's view is an explicit discriminated union — `AnalysisView =
'overview' | 'top-nodes' | { node: string }` — projected to the SegmentedControl
mode via `metricsModeOf`. Every transition (segment click, diagram click, initial
mount) writes `analysisView` directly with the value the action chose, and a new
`onNodeClick` callback on `ProfilerCallbacks` lets cytograph distinguish a user
diagram click from a programmatic `showGlobalMetrics` / `showTopNodes` refresh.
`displayNodeAttributes` only refreshes `tooltipData`. The "Node" segment restores
the previous operator by building `{ node: nodeIdOf(lastNodeData) }` from the
cached attributes; `lastNodeData` is narrowed to `NodeAttributes`.
…me as a constant

Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
… it's matches the hard-coded constant

Signed-off-by: Karakatiza666 <bulakh.96@gmail.com>
@Karakatiza666 Karakatiza666 force-pushed the issue-segment-indicator branch from c3b01ef to 251499f Compare June 15, 2026 11:10

@mythical-fred mythical-fred left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Blocker resolved — the profile-reload $effect now sets analysisView = 'overview' inside the queueMicrotask right before showGlobalMetrics(true), so the SegmentedControl indicator follows the reload and the sticky displayNodeAttributes triggered by showGlobalMetrics finds a non-node view and leaves lastNodeData alone. My earlier non-blocking nit about nodeIdOf parsing the cytograph title is also gone — nodeId is a first-class field on NodeAttributes now, isOverviewAttributes compares against the loaded profile's actual rootNodeId, and CircuitProfile.isTop no longer hardcodes "n" (nice regression test for that).

LGTM.

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.

3 participants