Skip to content

Tooltip cursor#338

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

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

Conversation

@MatthewKhouzam
Copy link
Copy Markdown
Contributor

@MatthewKhouzam MatthewKhouzam commented May 13, 2026

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

    • Rich cursor overlay enhanced with per-row XY-series dots and numeric tooltips for timeline values.
  • Improvements

    • Pointer interaction overhaul for more reliable click/drag/selection, panning, and cursor behavior; selection edge cases handled more accurately.
  • Tests

    • Extensive tests added for Pixi v7 migration, rich cursor behavior, interactions, and font/display handling.
  • Chores

    • Updated Pixi-related dependency footprints and bumped TypeScript/dev metadata; example updated to include the rich cursor.

Review Change Stack

- 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
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Warning

Rate limit exceeded

@MatthewKhouzam has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 35 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f720e59e-2016-4694-afcf-78c54daaac8d

📥 Commits

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

📒 Files selected for processing (6)
  • 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/time-graph-font-controller.ts
📝 Walkthrough

Walkthrough

This PR migrates timeline-chart to Pixi v7: adds TypeScript augmentations, replaces legacy InteractionEvent handlers with FederatedPointerEvent across components, refactors chart mouse wiring, adds XY-series types/components and XY rendering, implements TimeGraphChartRichCursor with interpolated XY indicators and tooltips, updates container/font/build metadata, and adds tests and example wiring.

Changes

Pixi v7 Migration and Rich Cursor

Layer / File(s) Summary
Pixi v7 Type System and Augmentations
timeline-chart/src/pixi-v7-augment.d.ts, timeline-chart/src/layer/time-graph-viewport-layer.ts
Add DisplayObject event/property augmentations for federated events and ensure PIXI types import for compile-time checks.
Build & Dependency Updates
package.json, timeline-chart/package.json, example/webpack.config.js
Bump pixi.js-legacy to v7, update root TypeScript devDependency and packageManager metadata, remove root pixi.js dependency, and exclude @pixi from source-map-loader.
Migration Tests and DisplayObject Augmentation Tests
timeline-chart/src/__tests__/pixi-v7-migration.test.ts
Add tests validating FederatedPointerEvent behavior, TimeGraphContainer options, FontController behavior, and DisplayObject event augmentation.
Rich Cursor Tests
timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts
Add comprehensive tests for TimeGraphChartRichCursor initialization, drawing, tooltip behavior, event lifecycle, and styles.
Event Signature Updates in Components
timeline-chart/src/components/time-graph-component.ts
Change interaction handler parameter types to PIXI.FederatedPointerEvent, set eventMode = 'static' and register on(...) handlers.
Chart Mouse Interaction Refactor
timeline-chart/src/layer/time-graph-chart.ts
Refactor chart mouse/drag handling to use federated events (globalmousemove, mouseover, mouseout, mousedown, mouseup), replace e.data.global with e.global, and add getStatesAtTimestamp(timestamp) to fetch visible state hits.
Cursor Selection & Cleanup
timeline-chart/src/layer/time-graph-chart-cursors.ts
Migrate cursors to FederatedPointerEvent with left-button/modifier-aware selection, refine cursor create/cleanup rules, and update destroy cleanup.
Container & Font Adjustments
timeline-chart/src/time-graph-container.ts, timeline-chart/src/time-graph-font-controller.ts
Change renderer typing to PIXI.IRenderer, map transparent→backgroundAlpha, set/update stage.hitArea, and use numeric '900' fontWeight for generated styles.
Rich Cursor Layer Implementation
timeline-chart/src/layer/time-graph-chart-rich-cursor.ts
Add TimeGraphChartRichCursor that tracks pointer X, computes timestamp, queries getStatesAtTimestamp, renders vertical line, per-row state dots and interpolated XY-series indicator dots (binary-search + bigint interpolation), and edge-aware tooltips; manages pointer handlers and lifecycle.
XY-series model, component, and row wiring
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
Add TimeGraphXYPoint/TimeGraphXYSeries and optional xySeries on rows, implement TimeGraphXYComponent, integrate per-row XY components, extend example data with XY rows, and render XY components when present.
Example Integration
example/src/index.ts
Import and add TimeGraphChartRichCursor to the example chart container's layers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🐰 A cursor that hops through the timeline with whimsical dots,
FederatedPointerEvents dance where interactions are caught,
Tooltips bloom like clover in the v7 spring—
Rich cursors guide you through each temporal thing! 🌱✨

🚥 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 PR title 'Tooltip cursor' is vague and generic. It does not clearly describe the main changes, which involve adding XY plot swimlanes with EKG data, introducing TimeGraphXYComponent, implementing a rich cursor with interpolated XY values, and migrating from deprecated Pixi v5 patterns to v7 (eventMode). Consider a more specific title such as 'Add XY plot visualization with rich cursor tooltip' or 'Add TimeGraphXYComponent and XY series support' to better reflect the primary changes.
✅ 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@MatthewKhouzam
Copy link
Copy Markdown
Contributor Author

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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

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.

2 participants