Skip to content

Tooltip cursor#338

Open
MatthewKhouzam wants to merge 7 commits into
eclipse-cdt-cloud:masterfrom
MatthewKhouzam:tooltip-cursor
Open

Tooltip cursor#338
MatthewKhouzam wants to merge 7 commits into
eclipse-cdt-cloud:masterfrom
MatthewKhouzam:tooltip-cursor

Conversation

@MatthewKhouzam

@MatthewKhouzam MatthewKhouzam commented May 13, 2026

Copy link
Copy Markdown
Contributor

What it does

How to test

Follow-ups

Review checklist

  • As an author, I have thoroughly tested my changes and carefully followed the instructions in this template

Summary by CodeRabbit

  • New Features

    • Added richer timeline chart cursor behavior with on-chart markers and tooltips.
    • Added support for displaying XY/line-series data within chart rows.
    • Improved chart interaction responsiveness for selection, hover, and pointer actions.
  • Bug Fixes

    • Better handling of cursor and selection visuals so they clear and redraw correctly.
    • Improved chart sizing and background rendering to match the canvas more reliably.
  • Tests

    • Added coverage for cursor behavior, chart interactions, and XY-series rendering.

- Replace InteractionEvent with FederatedPointerEvent
- Update event API: event.data.X -> event.X, event.data.originalEvent -> event.nativeEvent
- Replace deprecated 'transparent' with 'backgroundAlpha'
- Cast application.view as HTMLCanvasElement (ICanvas change)
- Replace PIXI.AbstractRenderer with PIXI.IRenderer
- Fix fontWeight type (string -> 'bold' as const / '900' as const)
- Replace buttonMode with cursor='pointer'
- Use globalmousemove for reliable stage event delivery in v7
- Add stage.hitArea for full-canvas pointer event coverage
- Add pixi-v7-augment.d.ts for DisplayObject event mixin types
- Update test snapshots for v7 internals
- Cursor now follows mouse position continuously

Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
- New TimeGraphChartRichCursor layer that follows mouse position
- Draws vertical line, dots on intersecting states, and label tooltips
- Operates in screen coordinates (unaffected by viewport scroll/scale)
- Added to example application

Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Prevents warnings from pixi.js packages missing source maps.

Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5202d5ad-3adc-4ccf-a28b-c93dc1253f3c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b23952 and 53d8e40.

📒 Files selected for processing (1)
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts

📝 Walkthrough

Walkthrough

The PR updates Pixi v7 integration across the timeline chart, adds XY-series rows and a rich cursor overlay, rewires chart interactions to federated pointer events, and expands the example and tests around the new behavior.

Changes

Pixi v7 Migration and Rich Cursor

Layer / File(s) Summary
Pixi metadata and display typings
package.json, timeline-chart/package.json, timeline-chart/src/pixi-v7-augment.d.ts, timeline-chart/src/layer/time-graph-viewport-layer.ts
package.json metadata and Pixi dependency versions are updated, Pixi display-object event typings are added, and the viewport layer imports Pixi at module scope.
Base Pixi runtime and migration tests
timeline-chart/src/components/time-graph-component.ts, timeline-chart/src/time-graph-container.ts, timeline-chart/src/time-graph-font-controller.ts, timeline-chart/src/__tests__/pixi-v7-migration.test.ts
Base components and the container use Pixi v7 event and application APIs, font-weight literals are narrowed, and the migration test suite covers component, container, font, and display-object behavior.
Chart interactions and cursor state access
timeline-chart/src/layer/time-graph-chart-cursors.ts, timeline-chart/src/layer/time-graph-chart.ts
Chart stage and selection handlers move to federated pointer events, cursor cleanup and destroy wiring change, and row/state lookup methods are added for cursor consumers.
XY-series model and row rendering
timeline-chart/src/time-graph-model.ts, timeline-chart/src/components/time-graph-xy.ts, timeline-chart/src/components/time-graph-row.ts, timeline-chart/src/layer/time-graph-chart.ts, example/src/test-data-provider.ts
XY-series types are added, rows manage XY child components, the chart renders XY-only rows, and the example data provider generates XY series rows.
Rich cursor rendering and example wiring
timeline-chart/src/layer/time-graph-chart-rich-cursor.ts, example/src/index.ts
The rich cursor layer draws the screen-space cursor, state and XY markers, and pooled tooltips, and the example adds it to the chart layer list.
Rich cursor tests
timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts
The rich-cursor test suite covers initialization, drawing, tooltip placement, leave/update/destroy handling, and style options.

Sequence Diagram(s)

Federated pointer interaction path

sequenceDiagram
  participant Stage
  participant TimeGraphChart
  participant TimeGraphChartCursors
  Stage->>TimeGraphChart: federated mouseover / mouseout / mousedown / globalmousemove / mouseup
  Stage->>TimeGraphChartCursors: federated mousedown / globalmousemove / mouseup
  TimeGraphChart->>TimeGraphChart: row clicks and state interactions use static event mode
  TimeGraphChartCursors->>TimeGraphChartCursors: update selection range and cursor cleanup
Loading

Rich cursor render flow

sequenceDiagram
  participant Canvas
  participant TimeGraphChartRichCursor
  participant TimeGraphChart
  Canvas->>TimeGraphChartRichCursor: pointermove / pointerleave
  TimeGraphChartRichCursor->>TimeGraphChart: getStatesAtTimestamp(timestamp)
  TimeGraphChart-->>TimeGraphChartRichCursor: row/state hits and row components
  TimeGraphChartRichCursor->>TimeGraphChartRichCursor: interpolate XY series values
  TimeGraphChartRichCursor->>TimeGraphChartRichCursor: draw cursor line and pooled tooltips
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

(_/)
( •_•) I hopped through Pixi’s v7 light,
/ >🥕 with XY trails and cursors bright.
Tooltips bobbed and selection rails ran free,
a timeline chart that bounced with bunny glee.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the change, but it is too vague to identify the main implementation. Use a more specific title, such as "Add rich tooltip cursor for timeline charts".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🧹 Nitpick comments (2)
timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts (1)

165-177: ⚡ Quick win

Consider asserting the tooltip position for edge-aware behavior.

The test verifies that a tooltip is drawn near the right edge, but doesn't assert that it's actually positioned to the left of the cursor as the test name suggests. Consider adding an assertion on bg.x to verify the edge-aware positioning logic.

📍 Suggested assertion to verify left-side positioning
             // Draw near right edge (canvas is 800px wide)
             (cursor as any).drawCursor(790);
             const bg = (cursor as any).tooltipContainer.children[0] as PIXI.Graphics;
-            // The tooltip box should be positioned to the left of cursor
-            expect(bg).toBeTruthy();
+            // The tooltip box should be positioned to the left of cursor
+            expect(bg).toBeTruthy();
+            expect(bg.x).toBeLessThan(790);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts`
around lines 165 - 177, The test 'flips tooltip to left when near right edge'
currently only checks that the tooltip background exists; update it to assert
the tooltip is positioned left of the cursor by comparing the bg.x (from (cursor
as any).tooltipContainer.children[0]) to the cursor draw x (use the same 790
value or read a public/accessible cursor property like (cursor as any).lastX if
available) and expect bg.x to be less than the cursor x (or less than 790 minus
the tooltip width if you can read it) to verify edge-aware flipping logic in
drawCursor.
timeline-chart/src/layer/time-graph-chart-rich-cursor.ts (1)

93-118: ⚖️ Poor tradeoff

Consider object pooling for tooltip elements.

New PIXI.Text and PIXI.Graphics objects are created on every mousemove event. With many visible rows, this could cause performance issues or GC pressure. Consider reusing objects or implementing an object pool, especially if users frequently move the mouse across the chart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts` around lines 93 -
118, This code creates new PIXI.Text (textObj) and PIXI.Graphics (bg) on every
mousemove and should instead reuse pooled/displayed tooltip objects to avoid GC
and allocation spikes: add persistent members (e.g., this.tooltipText and
this.tooltipBg) or an object pool used by the mousemove handler to update
text/geometry and reposition the existing PIXI.Text and PIXI.Graphics rather
than creating new ones, ensure tooltipContainer.addChild is called only once (or
check contains) and update textObj.text, textObj.x/y and
bg.clear()/bg.drawRoundedRect(...) as needed when showing/hiding the tooltip.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts`:
- Around line 64-65: The code is reaching into a private field by casting
chartLayer to any to access rowComponents; add a public accessor on
TimeGraphChart (e.g., getRowComponent(rowId: string) or a method that returns
the needed position data) that wraps access to the internal rowComponents map,
then replace the cast in time-graph-chart-rich-cursor.ts where it does
(this.chartLayer as any).rowComponents.get(hit.row.id) with a call to the new
public method (e.g., this.chartLayer.getRowComponent(hit.row.id)) and handle
undefined the same way as before.

In `@timeline-chart/src/layer/time-graph-chart.ts`:
- Around line 222-231: The inline mouseover/mouseout handlers on this.stage
(which set triggerKeyEvent and update this.stage.cursor and check
this.mousePanning) must be stored as named functions on the instance (e.g.,
this._onStageMouseOver / this._onStageMouseOut) so they can be removed later,
and then unregistered in the component's destroy/cleanup method using
this.stage.off('mouseover', this._onStageMouseOver) and
this.stage.off('mouseout', this._onStageMouseOut); apply the same change for the
other occurrence of these handlers in the file so re-init won't retain stale
listeners or duplicate behavior.
- Around line 1125-1126: The lookup for a matching state uses an inclusive end
check which can match adjacent states at boundaries; in the row.states.find used
in time-graph-chart (the expression referencing row.states, state.range.start,
state.range.end and timestamp) change the condition from using an inclusive end
(state.range.end >= timestamp) to an end-exclusive check (state.range.end >
timestamp) so it matches the rest of the file's end-exclusive semantics and
prevents boundary collisions; update the predicate accordingly and run unit/edge
tests around timestamp boundaries to confirm behavior.

In `@timeline-chart/src/time-graph-font-controller.ts`:
- Line 9: Two fontWeight values are inconsistent—change the dynamic creation
that currently uses '900' to match the default whose fontWeight is "bold";
update the fontWeight property in the default font object and the fontWeight
used when dynamically creating fonts so both use "bold" (search for occurrences
of fontWeight: '900' and the default fontWeight: "bold" and make them
identical).

---

Nitpick comments:
In `@timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts`:
- Around line 165-177: The test 'flips tooltip to left when near right edge'
currently only checks that the tooltip background exists; update it to assert
the tooltip is positioned left of the cursor by comparing the bg.x (from (cursor
as any).tooltipContainer.children[0]) to the cursor draw x (use the same 790
value or read a public/accessible cursor property like (cursor as any).lastX if
available) and expect bg.x to be less than the cursor x (or less than 790 minus
the tooltip width if you can read it) to verify edge-aware flipping logic in
drawCursor.

In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts`:
- Around line 93-118: This code creates new PIXI.Text (textObj) and
PIXI.Graphics (bg) on every mousemove and should instead reuse pooled/displayed
tooltip objects to avoid GC and allocation spikes: add persistent members (e.g.,
this.tooltipText and this.tooltipBg) or an object pool used by the mousemove
handler to update text/geometry and reposition the existing PIXI.Text and
PIXI.Graphics rather than creating new ones, ensure tooltipContainer.addChild is
called only once (or check contains) and update textObj.text, textObj.x/y and
bg.clear()/bg.drawRoundedRect(...) as needed when showing/hiding the tooltip.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f79501a-23d4-4ccb-a501-f3ec6b3abd5f

📥 Commits

Reviewing files that changed from the base of the PR and between ec39e6b and 335a144.

⛔ Files ignored due to path filters (3)
  • timeline-chart/src/components/__tests__/__snapshots__/time-graph-arrow.test.ts.snap is excluded by !**/*.snap
  • timeline-chart/src/components/__tests__/__snapshots__/time-graph-axis-cursor.test.ts.snap is excluded by !**/*.snap
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (14)
  • example/src/index.ts
  • example/webpack.config.js
  • package.json
  • timeline-chart/package.json
  • timeline-chart/src/__tests__/pixi-v7-migration.test.ts
  • timeline-chart/src/components/time-graph-component.ts
  • timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts
  • timeline-chart/src/layer/time-graph-chart-cursors.ts
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts
  • timeline-chart/src/layer/time-graph-chart.ts
  • timeline-chart/src/layer/time-graph-viewport-layer.ts
  • timeline-chart/src/pixi-v7-augment.d.ts
  • timeline-chart/src/time-graph-container.ts
  • timeline-chart/src/time-graph-font-controller.ts

Comment thread timeline-chart/src/layer/time-graph-chart-rich-cursor.ts Outdated
Comment thread timeline-chart/src/layer/time-graph-chart.ts Outdated
Comment thread timeline-chart/src/layer/time-graph-chart.ts Outdated
Comment thread timeline-chart/src/time-graph-font-controller.ts
- Add TimeGraphChartRichCursor: grey hover cursor with dots and labels
- Hide blue cursor lines when not actively selecting a range (start===end)
- Remove hover-tracking selectionRange updates from cursors layer
- Rich cursor hides during mouse-button drag for clean selection UX
- Set eventMode='none' on rich cursor graphics to not block stage events

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
timeline-chart/src/layer/time-graph-chart-cursors.ts (1)

110-111: 💤 Low value

Prefer reading modifier keys from the federated event directly.

The mousedown handler at lines 80–84 reads event.shiftKey, event.ctrlKey, and event.altKey directly from the FederatedPointerEvent. However, the globalmousemove and mouseup handlers cast to event.nativeEvent for the same reads—an unnecessary inconsistency. PixiJS v7's FederatedPointerEvent API exposes these modifier properties directly, eliminating the need for the cast.

♻️ Proposed simplification
                 if ((this.mouseButtons & 1) === 0) {
                     // handle missed button mouseup event
                     this.mouseSelecting = false;
-                    const orig = event.nativeEvent as PointerEvent;
-                    if (!orig.shiftKey || orig.ctrlKey || orig.altKey) {
+                    if (!event.shiftKey || event.ctrlKey || event.altKey) {
                         this.stage.cursor = 'default';
                     }
                     return;
                 }
             if (this.mouseSelecting && event.button === 0) {
                 this.mouseSelecting = false;
-                const orig = event.nativeEvent as PointerEvent;
-                if (!orig.shiftKey || orig.ctrlKey || orig.altKey) {
+                if (!event.shiftKey || event.ctrlKey || event.altKey) {
                     this.stage.cursor = 'default';
                 }
             }

Also applies to: 132-133

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/time-graph-chart-cursors.ts` around lines 110 - 111,
The globalmousemove and mouseup handlers currently cast event.nativeEvent to
PointerEvent to read modifier keys; change them to read shiftKey/ctrlKey/altKey
directly from the FederatedPointerEvent (the event parameter) — i.e., remove the
`const orig = event.nativeEvent as PointerEvent` pattern and use
`event.shiftKey`, `event.ctrlKey`, `event.altKey` in the globalmousemove and
mouseup handlers so they match the mousedown handler and PixiJS v7's
FederatedPointerEvent API.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@timeline-chart/src/layer/time-graph-chart-cursors.ts`:
- Around line 110-111: The globalmousemove and mouseup handlers currently cast
event.nativeEvent to PointerEvent to read modifier keys; change them to read
shiftKey/ctrlKey/altKey directly from the FederatedPointerEvent (the event
parameter) — i.e., remove the `const orig = event.nativeEvent as PointerEvent`
pattern and use `event.shiftKey`, `event.ctrlKey`, `event.altKey` in the
globalmousemove and mouseup handlers so they match the mousedown handler and
PixiJS v7's FederatedPointerEvent API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12551653-7d0e-440d-ad92-a088c425cb86

📥 Commits

Reviewing files that changed from the base of the PR and between 335a144 and ff73cc0.

📒 Files selected for processing (2)
  • timeline-chart/src/layer/time-graph-chart-cursors.ts
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts

@JonatanAntoni

Copy link
Copy Markdown

Without having understood the entire code change, I feel like we are mixing two aspects into one here: Cursors and markers. In the view there is this grey mouse marker with the new overlay label and one or two cursors which are sync across all time-based views.

With a mouse click I'd expect a blue cursor is set which does not move along with a mouse move. A second click sets the second blue cursor. Additionally, the grey cursor always moves along with the mouse pointer. Combined with either the shift-modifier or while holding the mouse button down, the range gets selected between the first cursor and the current mouse pointer position.

- Add TimeGraphXYComponent for rendering line plots within rows
- Add XY series model (TimeGraphXYPoint, TimeGraphXYSeries) to row model
- Render XY plots only on empty rows (no states)
- Rich cursor shows interpolated XY value on hover with tooltip
- Example: two EKG-style swimlanes (Heart Rate, Respiration)

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
timeline-chart/src/layer/time-graph-chart-rich-cursor.ts (1)

47-51: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing cleanup of pointerleave handler in destroy().

The pointerleave handler registered at line 47 is an anonymous function and is not tracked or removed in destroy(). This creates a potential memory leak if the cursor layer is destroyed while the canvas remains.

🔧 Proposed fix
 export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
     private graphics: PIXI.Graphics;
     private tooltipContainer: PIXI.Container;
     private _mouseMoveHandler: (event: Event) => void;
+    private _pointerLeaveHandler: (event: Event) => void;
     private lastMouseX: number = -1;
     private isMouseInCanvas: boolean = false;
-        this.onCanvasEvent('pointerleave', () => {
+        this._pointerLeaveHandler = () => {
             this.graphics.clear();
             this.removeTooltips();
             this.isMouseInCanvas = false;
-        });
+        };
+        this.onCanvasEvent('pointerleave', this._pointerLeaveHandler);
     destroy() {
         if (this._mouseMoveHandler) {
             this.removeOnCanvasEvent('pointermove', this._mouseMoveHandler);
         }
+        if (this._pointerLeaveHandler) {
+            this.removeOnCanvasEvent('pointerleave', this._pointerLeaveHandler);
+        }
         super.destroy();
     }

Also applies to: 191-196

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts` around lines 47 -
51, The pointerleave handler passed as an anonymous function to
this.onCanvasEvent('pointerleave', ...) is never removed in destroy(), causing a
leak; capture that handler in a variable (e.g. this._onPointerLeave) when
registering and unregister it in destroy() by calling the corresponding remover
(e.g. this.offCanvasEvent('pointerleave', this._onPointerLeave) or
this.canvas.removeEventListener if onCanvasEvent wraps DOM listeners); do the
same for the other pointerleave registration around the other block (lines
~191-196) so both handlers are tracked and cleaned up in destroy().
timeline-chart/src/layer/time-graph-chart-cursors.ts (1)

298-300: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incorrect event type in handler cleanup.

_cursorKeyUpHandler was registered on 'keyup' (line 75) but is being removed from 'mousedown'. This means the keyup handler will never be properly removed, causing it to remain attached after destroy().

🐛 Proposed fix
         if (this._cursorKeyUpHandler) {
-            this.removeOnCanvasEvent('mousedown', this._cursorKeyUpHandler);
+            this.removeOnCanvasEvent('keyup', this._cursorKeyUpHandler);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/time-graph-chart-cursors.ts` around lines 298 - 300,
The cleanup is removing the wrong event type: the handler stored in
_cursorKeyUpHandler was registered for 'keyup' but the code calls
removeOnCanvasEvent('mousedown', this._cursorKeyUpHandler); update the call to
removeOnCanvasEvent to use the 'keyup' event so _cursorKeyUpHandler is properly
removed during destroy() (locate usages in the same file alongside the
registration of _cursorKeyUpHandler).
🧹 Nitpick comments (1)
timeline-chart/src/layer/time-graph-chart-rich-cursor.ts (1)

164-170: 💤 Low value

Tooltip Y position may overflow canvas bounds.

boxY is computed as y - boxHeight / 2 without clamping, which could position tooltips partially or fully outside the visible canvas area when the cursor is near the top or bottom edges.

🔧 Suggested clamping
         let boxY = y - boxHeight / 2;
+        const canvasHeight = this.stateController.canvasDisplayHeight;
+        if (boxY < 0) {
+            boxY = 0;
+        } else if (boxY + boxHeight > canvasHeight) {
+            boxY = canvasHeight - boxHeight;
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts` around lines 164 -
170, Tooltip vertical position (boxY) can overflow the canvas when the cursor is
near the top or bottom; clamp the computed boxY (currently set as y - boxHeight
/ 2) to the visible area by ensuring boxY is no less than 0 and no greater than
canvasHeight - boxHeight (use y, boxHeight, canvasHeight and the existing boxY
calculation), so the tooltip is always fully inside the canvas while preserving
the horizontal flip logic that uses boxX and boxWidth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@timeline-chart/src/layer/time-graph-chart-cursors.ts`:
- Around line 298-300: The cleanup is removing the wrong event type: the handler
stored in _cursorKeyUpHandler was registered for 'keyup' but the code calls
removeOnCanvasEvent('mousedown', this._cursorKeyUpHandler); update the call to
removeOnCanvasEvent to use the 'keyup' event so _cursorKeyUpHandler is properly
removed during destroy() (locate usages in the same file alongside the
registration of _cursorKeyUpHandler).

In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts`:
- Around line 47-51: The pointerleave handler passed as an anonymous function to
this.onCanvasEvent('pointerleave', ...) is never removed in destroy(), causing a
leak; capture that handler in a variable (e.g. this._onPointerLeave) when
registering and unregister it in destroy() by calling the corresponding remover
(e.g. this.offCanvasEvent('pointerleave', this._onPointerLeave) or
this.canvas.removeEventListener if onCanvasEvent wraps DOM listeners); do the
same for the other pointerleave registration around the other block (lines
~191-196) so both handlers are tracked and cleaned up in destroy().

---

Nitpick comments:
In `@timeline-chart/src/layer/time-graph-chart-rich-cursor.ts`:
- Around line 164-170: Tooltip vertical position (boxY) can overflow the canvas
when the cursor is near the top or bottom; clamp the computed boxY (currently
set as y - boxHeight / 2) to the visible area by ensuring boxY is no less than 0
and no greater than canvasHeight - boxHeight (use y, boxHeight, canvasHeight and
the existing boxY calculation), so the tooltip is always fully inside the canvas
while preserving the horizontal flip logic that uses boxX and boxWidth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c980156d-57be-4afc-92aa-98b834625de6

📥 Commits

Reviewing files that changed from the base of the PR and between ff73cc0 and 1dd7c6b.

📒 Files selected for processing (8)
  • example/src/test-data-provider.ts
  • timeline-chart/src/components/time-graph-component.ts
  • timeline-chart/src/components/time-graph-row.ts
  • timeline-chart/src/components/time-graph-xy.ts
  • timeline-chart/src/layer/time-graph-chart-cursors.ts
  • timeline-chart/src/layer/time-graph-chart-rich-cursor.ts
  • timeline-chart/src/layer/time-graph-chart.ts
  • timeline-chart/src/time-graph-model.ts

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

With a mouse click I'd expect a blue cursor is set which does not move along with a mouse move. A second click sets the second blue cursor. Additionally, the grey cursor always moves along with the mouse pointer. Combined with either the shift-modifier or while holding the mouse button down, the range gets selected between the first cursor and the current mouse pointer position.

The original way of doing it is mouse down == start select, mouse up == end select. The grey follows the mouse as it is a "tooltip cursor"

@JonatanAntoni

Copy link
Copy Markdown

The original way of doing it is mouse down == start select, mouse up == end select. The grey follows the mouse as it is a "tooltip cursor"

Agree, I think we are aligned on the requirements. In my local tests on Mac it just didn't work exactly like that. And I got confused why mouse move events seem to operate on the cursor positions rather then having this entirely separated.

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

The original way of doing it is mouse down == start select, mouse up == end select. The grey follows the mouse as it is a "tooltip cursor"

Agree, I think we are aligned on the requirements. In my local tests on Mac it just didn't work exactly like that. And I got confused why mouse move events seem to operate on the cursor positions rather then having this entirely separated.

please understand I'm in goofing around mode and trying to align with what I understood was a common req too. :)

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

@JonatanAntoni I think this is what you're looking for, try it out.

@JonatanAntoni

JonatanAntoni commented Jun 18, 2026

Copy link
Copy Markdown

@MatthewKhouzam, there is a check failing, not sure about it.
Are you able to reproduce the mouse navigation lags I described above?

What do we need to do to get this merged and wired up into the trace view extension?

cc @thorstendb-ARM

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

Before merging, I want to make sure you like it. ;)

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

Re the CI, it's dash licenses looking like a false positive.

@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

About the lags, I feel this was fixed, but please confirm on your side.

@arfio arfio left a comment

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.

Great addition! On my setup I have overlapping tooltip information, making it hard to read, especially in views where we have lots of rows. Would it be possible to either reduce the tooltip height and also making the tooltip position not depend on the xy value ?

Image

Comment thread example/webpack.config.js Outdated
test: /\.js$/,
loader: "source-map-loader"
loader: "source-map-loader",
exclude: /node_modules\/@pixi/

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.

Why do you exclude pixijs here?

Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
@MatthewKhouzam

Copy link
Copy Markdown
Contributor Author

@arfio try out the latest one?

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