Skip to content

feat: Highlight elements when hovering selector in debugger view#1129

Merged
allansson merged 5 commits intomainfrom
cursor/record-clicks-menu-b1e0
Mar 30, 2026
Merged

feat: Highlight elements when hovering selector in debugger view#1129
allansson merged 5 commits intomainfrom
cursor/record-clicks-menu-b1e0

Conversation

@allansson
Copy link
Copy Markdown
Collaborator

@allansson allansson commented Mar 18, 2026

Description

Implements element highlighting in the debugger view when hovering over selectors, with full support for all selector types (role, testId, alt, label, placeholder, text, title, and css).

Key Changes:

  • Created shared findElementsBySelector utility using testing-library's query functions
  • Updated debugger to use NodeSelector instead of CSS-only selectors
  • Both recorder and debugger now use the same highlighting logic
  • All selector types work consistently across recorder and debugger views

When you hover over any locator in the browser actions list, the corresponding element(s) in the replay iframe are highlighted using the appropriate testing-library query function (e.g., queryByRole, queryByTestId, etc.).

How to Test

  1. Start k6 Studio and create a browser test recording with various selector types
  2. Navigate to the Validator view and debug the script
  3. Once the replay is loaded, hover over different locators in the browser actions list (role, testId, alt, label, etc.)
  4. The corresponding element(s) should be highlighted in the replay view with a blue overlay

You also need to test that hover work in the recorder:

  1. Start a recording
  2. Generate some events
  3. Open the event sidepanel
  4. Hover over a selector
    • The correct element should be highlighted
  5. Go to the event list in k6 Studio
  6. Hover over a selector
    • The correct element should be highlighted

Checklist

  • I have performed a self-review of my code.
  • I have added tests for my changes.
  • I have commented on my code, particularly in hard-to-understand areas.

Related PR(s)/Issue(s)

Resolves #1063

Open in Web Open in Cursor 

cursoragent and others added 5 commits March 18, 2026 20:37
- Add onHighlight callback through BrowserAction component tree
- Create DebuggerHighlightContext to manage highlighted selector state
- Expose Replayer instance from usePlayer hook
- Create ReplayerHighlights component to query and display overlays
- Implements issue #1063

Co-authored-by: Johan Suleiko Allansson <allansson@users.noreply.github.com>
…menu-b1e0

Co-authored-by: Johan Suleiko Allansson <allansson@users.noreply.github.com>
- Create shared findElementsBySelector utility using testing-library
- Update debugger to use NodeSelector instead of CSS-only selectors
- Support role, testId, alt, label, placeholder, text, title, and css selectors
- Share logic between recorder and debugger highlighting
- Update RemoteHighlights to use shared utility

This ensures all selector types work consistently in both recorder
and debugger views, using the same testing-library query functions.

Co-authored-by: Johan Suleiko Allansson <allansson@users.noreply.github.com>
@allansson allansson marked this pull request as ready for review March 26, 2026 16:31
@allansson allansson requested a review from a team as a code owner March 26, 2026 16:31
@allansson allansson requested review from Llandy3d and e-fisher March 26, 2026 16:31
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: ResizeObserver observes wrong element in cross-frame context
    • Changed ResizeObserver to observe the element parameter instead of hardcoded document.body, ensuring it watches the correct document context (iframe or main page).
  • ✅ Fixed: Scroll offset uses wrong window for iframe elements
    • Modified toBounds and getElementBounds to use the element's ownerDocument.defaultView instead of global window, ensuring correct scroll offsets for iframe elements.

Create PR

Or push these changes by commenting:

@cursor push cb21d467f3
Preview (cb21d467f3)
diff --git a/extension/src/frontend/view/TextSelectionPopover.hooks.ts b/extension/src/frontend/view/TextSelectionPopover.hooks.ts
--- a/extension/src/frontend/view/TextSelectionPopover.hooks.ts
+++ b/extension/src/frontend/view/TextSelectionPopover.hooks.ts
@@ -7,8 +7,11 @@
 import { TextSelection } from './TextSelectionPopover.types'
 
 function measureRange(range: Range) {
+  const ownerWindow = range.startContainer.ownerDocument?.defaultView ?? window
   return {
-    highlights: Array.from(range.getClientRects()).map(toBounds),
+    highlights: Array.from(range.getClientRects()).map((rect) =>
+      toBounds(rect, ownerWindow)
+    ),
     bounds: getElementBounds(range),
   }
 }

diff --git a/src/components/Browser/ElementHighlights.hooks.ts b/src/components/Browser/ElementHighlights.hooks.ts
--- a/src/components/Browser/ElementHighlights.hooks.ts
+++ b/src/components/Browser/ElementHighlights.hooks.ts
@@ -65,7 +65,7 @@
       })
     })
 
-    observer.observe(document.body)
+    observer.observe(element)
 
     return () => {
       observer.disconnect()

diff --git a/src/components/Browser/utils.ts b/src/components/Browser/utils.ts
--- a/src/components/Browser/utils.ts
+++ b/src/components/Browser/utils.ts
@@ -1,18 +1,22 @@
 import { Bounds } from './types'
 
-export function toBounds(rect: DOMRect): Bounds {
+export function toBounds(rect: DOMRect, ownerWindow: Window): Bounds {
   // `getBoundingClientRect` returns the coordinates relative to the viewport
   // and not the document, so we add the scroll position so that the element
   // is relative to the page instead. This means that content will stay in place
   // when scrolling.
   return {
-    top: rect.top + window.scrollY,
-    left: rect.left + window.scrollX,
+    top: rect.top + ownerWindow.scrollY,
+    left: rect.left + ownerWindow.scrollX,
     width: rect.width,
     height: rect.height,
   }
 }
 
 export function getElementBounds(element: Element | Range): Bounds {
-  return toBounds(element.getBoundingClientRect())
+  const ownerWindow =
+    element instanceof Range
+      ? (element.startContainer.ownerDocument?.defaultView ?? window)
+      : (element.ownerDocument?.defaultView ?? window)
+  return toBounds(element.getBoundingClientRect(), ownerWindow)
 }

You can send follow-ups to this agent here.

})
})

observer.observe(document.body)
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.

ResizeObserver observes wrong element in cross-frame context

Medium Severity

The ResizeObserver is hardcoded to observe document.body, but useHighlightedElements is now a shared hook that can receive an element from a different document context. When used from the debugger via SelectorHighlights, the element is the rrweb iframe's documentElement, but the observer watches the main app's document.body instead. Layout changes inside the replay iframe won't trigger highlight repositioning. The element parameter (already available and used in the first useEffect) needs to be used here as well.

Additional Locations (1)
Fix in Cursor Fix in Web

// when scrolling.
return {
top: rect.top + window.scrollY,
left: rect.left + window.scrollX,
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.

Scroll offset uses wrong window for iframe elements

Low Severity

toBounds adds window.scrollY and window.scrollX to convert viewport-relative coordinates to document-relative ones. This is correct in the extension context (where window is the page's window), but when getElementBounds is called on elements inside the rrweb iframe from the main app (via SelectorHighlightsuseHighlightedElements), window refers to the main app's window, not the iframe's. Any non-zero main-window scroll would offset highlight overlays from the correct position.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown
Collaborator

@e-fisher e-fisher left a comment

Choose a reason for hiding this comment

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

LGTM
Would be nice if replay would automatically rewind to the time when selector existed when clicked, but perhaps that's already planned for the future

@allansson allansson merged commit 7715df4 into main Mar 30, 2026
14 checks passed
@allansson allansson deleted the cursor/record-clicks-menu-b1e0 branch March 30, 2026 12:58
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.

Highlight elements when hovering selector in debugger view

3 participants