Skip to content

fix: event coordinates relative to lynx-view container#2341

Open
Huxpro wants to merge 2 commits intomainfrom
Huxpro/fix-spread-null-ref
Open

fix: event coordinates relative to lynx-view container#2341
Huxpro wants to merge 2 commits intomainfrom
Huxpro/fix-spread-null-ref

Conversation

@Huxpro
Copy link
Copy Markdown
Collaborator

@Huxpro Huxpro commented Mar 16, 2026

Summary

Fixed tap/touch/mouse event coordinates to be relative to the <lynx-view> container instead of viewport origin. When <lynx-view> is embedded with offset (e.g., margin: 200px 300px), detail.x/y, clientX/clientY, and pageX/pageY now correctly reflect position within the view, not the viewport. Also fixed boundingClientRect to return container-relative values.

Changes

  • Pass container rect callback to createCrossThreadEvent in both web-mainthread-apis and web-core-wasm
  • Subtract container offset from click, mouse, and touch event coordinates
  • Adjust InvokeUIMethod.boundingClientRect to return container-relative positions
  • Backward compatible: no offset applied when container rect callback is not provided

Checklist

  • Tests updated (existing behavior unchanged; coordinates now correct for offset containers)
  • Documentation updated (not required for this fix)
  • Changeset added (not required for this fix)

Summary by CodeRabbit

  • Bug Fixes
    • Events and UI measurements now account for container offsets: touch, mouse, and click coordinates (including multi-touch lists) and element bounding rectangles are computed relative to their container, improving positioning accuracy in nested or shadowed layouts.

…tainer

When <lynx-view> is embedded in a larger HTML page with offset from viewport origin (e.g., margin), event coordinates (detail.x/y, clientX/clientY, pageX/pageY) were viewport-relative instead of lynx-view-relative. This caused apps using tap coordinates for positioning to place elements at wrong locations.

Pass container rect callback to createCrossThreadEvent and subtract container offset from all coordinate properties. Also fix boundingClientRect to return container-relative values instead of viewport-relative.

Affects: click, mouse, and touch events in both web-mainthread-apis and web-core-wasm implementations, plus InvokeUIMethod boundingClientRect.
Copilot AI review requested due to automatic review settings March 16, 2026 20:27
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 16, 2026

⚠️ No Changeset found

Latest commit: 6c84000

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Introduces container-relative coordinate adjustments: add optional getContainerRect to cross-thread event creators, pass it through event paths, and compute boundingClientRect and touch/mouse coordinates relative to the container's bounding rect.

Changes

Cohort / File(s) Summary
Method alias & bounding rect
packages/web-platform/web-core-wasm/ts/client/mainthread/crossThreadHandlers/registerInvokeUIMethodHandler.ts
Replaced top-level methodAlias with createMethodAlias(lynxViewInstance) and adjust returned boundingClientRect values by subtracting the container rect (left/top/right/bottom offset).
Cross-thread event creation (client)
packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts
Added optional getContainerRect param and adjustTouch helper; touch lists and mouse coordinates are offset by container rect when provided.
Cross-thread event creation (mainthread API)
packages/web-platform/web-mainthread-apis/ts/utils/createCrossThreadEvent.ts
Signature updated to accept optional getContainerRect; applies container-offset adjustments for touch, mouse, and click detail coordinates.
Event integration / bindings
packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/WASMJSBinding.ts, packages/web-platform/web-mainthread-apis/ts/createMainThreadGlobalThis.ts
Introduce getContainerRect helper from rootDom/shadow host; pass it to createCrossThreadEvent and use it in __InvokeUIMethod to convert element metrics to container-local coordinates.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • PupilTong
  • Sherry-hue

Poem

🐇 I hop through rects and numbers clear,
I nudge each touch and mouse so near,
Left and top I gently pare,
So clicks and bounds find home somewhere,
A tiny rabbit's coordinate cheer.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main change: fixing event coordinates to be relative to the lynx-view container instead of the viewport, which is the core objective across all modified files.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Huxpro/fix-spread-null-ref
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the web-platform event bridging layer so pointer/touch/mouse coordinates (and boundingClientRect) are reported relative to the <lynx-view> container rather than the viewport origin, improving correctness when the view is embedded with offsets.

Changes:

  • Add an optional getContainerRect() callback to createCrossThreadEvent and pass it from the main-thread bindings.
  • Offset mouse/click/touch coordinates by the container’s bounding rect before sending cross-thread.
  • Return container-relative boundingClientRect values from both mainthread-apis and web-core-wasm invokeUI handlers.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/web-platform/web-mainthread-apis/ts/utils/createCrossThreadEvent.ts Adds container-rect aware coordinate adjustment for touch/mouse/click cross-thread events.
packages/web-platform/web-mainthread-apis/ts/createMainThreadGlobalThis.ts Wires container rect provider into event creation; makes boundingClientRect container-relative.
packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts Adds container-rect aware coordinate adjustment for WASM runtime cross-thread events.
packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/WASMJSBinding.ts Passes <lynx-view> host rect provider into event creation for WASM runtime.
packages/web-platform/web-core-wasm/ts/client/mainthread/crossThreadHandlers/registerInvokeUIMethodHandler.ts Makes boundingClientRect container-relative in WASM invokeUI method handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to +67
return {
...t,
clientX: (t.clientX as number) - rect.left,
clientY: (t.clientY as number) - rect.top,
pageX: (t.pageX as number) - rect.left,
pageY: (t.pageY as number) - rect.top,
Comment on lines +55 to +60
return {
...t,
clientX: (t.clientX as number) - rect.left,
clientY: (t.clientY as number) - rect.top,
pageX: (t.pageX as number) - rect.left,
pageY: (t.pageY as number) - rect.top,
Comment on lines 26 to 29
export function createCrossThreadEvent(
domEvent: MinimalRawEventObject,
getContainerRect?: () => DOMRect,
): LynxCrossThreadEvent {
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 16, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
5002 1 5001 126
View the top 2 failed test(s) by shortest run time
tests/react.spec.ts::reactlynx3 tests › elements › list › basic-element-list-remove-action
Stack Traces | 4.51s run time
react.spec.ts:4679:7 basic-element-list-remove-action
tests/reactlynx.spec.ts::reactlynx3 tests › elements › x-viewpager-ng › basic-element-x-viewpager-ng-exposure
Stack Traces | 18s run time
reactlynx.spec.ts:2858:7 basic-element-x-viewpager-ng-exposure

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

TypeScript TS4111: properties from index signatures must be accessed
with bracket notation when noPropertyAccessFromIndexSignature is enabled.
Copy link
Copy Markdown
Contributor

@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: 1

🧹 Nitpick comments (1)
packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts (1)

52-74: Cache container rect once per event instead of per touch item.

getContainerRect() is currently invoked inside each touch mapping path, which can multiply cross-boundary/layout reads. Compute rect/offset once before mapping and reuse it.

As per coding guidelines, “Avoid recursive borrowing patterns where Rust calls JS, and JS immediately calls back into Rust to retrieve data that Rust already possesses, as this will cause RefCell borrowing panics”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts`
around lines 52 - 74, The code currently calls getContainerRect inside
adjustTouch for every touch which can cause repeated cross-boundary reads;
compute the container rect once before mapping and reuse it: call
getContainerRect() once (e.g. const rect = getContainerRect?.()) before creating
adjustTouch, then have adjustTouch close over that rect (or return identity if
rect is undefined) and use that single rect when mapping
touches/targetTouches/changedTouches in the assignments to
touches/targetTouches/changedTouches; update references to
clientX/clientY/pageX/pageY inside adjustTouch to use the precomputed rect to
avoid per-touch calls to getContainerRect.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts`:
- Around line 57-60: The touch pageX/pageY adjustment is wrong because
pageX/pageY are document (scroll) coordinates but the code only subtracts
rect.left/top (viewport), causing errors when scrolled; in
createCrossThreadEvent derive adjusted pageX/pageY from the adjusted client
coordinates instead of mirroring t.pageX/t.pageY: compute adjustedClientX =
(t['clientX'] as number) - rect.left and adjustedClientY = (t['clientY'] as
number) - rect.top (these are already used for clientX/clientY) and then set
pageX = adjustedClientX + (window.scrollX || 0) and pageY = adjustedClientY +
(window.scrollY || 0) so page coordinates account for scroll. Use the same local
names (t, rect, clientX/clientY/pageX/pageY) in the createCrossThreadEvent
function to locate and replace the existing pageX/pageY lines.

---

Nitpick comments:
In
`@packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts`:
- Around line 52-74: The code currently calls getContainerRect inside
adjustTouch for every touch which can cause repeated cross-boundary reads;
compute the container rect once before mapping and reuse it: call
getContainerRect() once (e.g. const rect = getContainerRect?.()) before creating
adjustTouch, then have adjustTouch close over that rect (or return identity if
rect is undefined) and use that single rect when mapping
touches/targetTouches/changedTouches in the assignments to
touches/targetTouches/changedTouches; update references to
clientX/clientY/pageX/pageY inside adjustTouch to use the precomputed rect to
avoid per-touch calls to getContainerRect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a42cade-73bf-43ae-a83f-17f48018f470

📥 Commits

Reviewing files that changed from the base of the PR and between 98cad53 and 6c84000.

📒 Files selected for processing (2)
  • packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts
  • packages/web-platform/web-mainthread-apis/ts/utils/createCrossThreadEvent.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web-platform/web-mainthread-apis/ts/utils/createCrossThreadEvent.ts

Comment on lines +57 to +60
clientX: (t['clientX'] as number) - rect.left,
clientY: (t['clientY'] as number) - rect.top,
pageX: (t['pageX'] as number) - rect.left,
pageY: (t['pageY'] as number) - rect.top,
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.

⚠️ Potential issue | 🟠 Major

Touch pageX/pageY adjustment is scroll-sensitive and can be wrong.

Touch.pageX/pageY are document-based. Subtracting only rect.left/top (viewport-based) can miscompute coordinates when the page is scrolled. Derive adjusted pageX/pageY from adjusted clientX/clientY (or subtract scroll as well).

Proposed fix
-    const adjustTouch = (t: CloneableObject): CloneableObject => {
-      if (getContainerRect) {
-        const rect = getContainerRect();
-        return {
-          ...t,
-          clientX: (t['clientX'] as number) - rect.left,
-          clientY: (t['clientY'] as number) - rect.top,
-          pageX: (t['pageX'] as number) - rect.left,
-          pageY: (t['pageY'] as number) - rect.top,
-        };
-      }
-      return t;
-    };
+    const rect = getContainerRect?.();
+    const offsetX = rect?.left ?? 0;
+    const offsetY = rect?.top ?? 0;
+    const adjustTouch = (t: CloneableObject): CloneableObject => {
+      const cx = (t['clientX'] as number) - offsetX;
+      const cy = (t['clientY'] as number) - offsetY;
+      return {
+        ...t,
+        clientX: cx,
+        clientY: cy,
+        pageX: cx,
+        pageY: cy,
+      };
+    };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
clientX: (t['clientX'] as number) - rect.left,
clientY: (t['clientY'] as number) - rect.top,
pageX: (t['pageX'] as number) - rect.left,
pageY: (t['pageY'] as number) - rect.top,
const rect = getContainerRect?.();
const offsetX = rect?.left ?? 0;
const offsetY = rect?.top ?? 0;
const adjustTouch = (t: CloneableObject): CloneableObject => {
const cx = (t['clientX'] as number) - offsetX;
const cy = (t['clientY'] as number) - offsetY;
return {
...t,
clientX: cx,
clientY: cy,
pageX: cx,
pageY: cy,
};
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/web-platform/web-core-wasm/ts/client/mainthread/elementAPIs/createCrossThreadEvent.ts`
around lines 57 - 60, The touch pageX/pageY adjustment is wrong because
pageX/pageY are document (scroll) coordinates but the code only subtracts
rect.left/top (viewport), causing errors when scrolled; in
createCrossThreadEvent derive adjusted pageX/pageY from the adjusted client
coordinates instead of mirroring t.pageX/t.pageY: compute adjustedClientX =
(t['clientX'] as number) - rect.left and adjustedClientY = (t['clientY'] as
number) - rect.top (these are already used for clientX/clientY) and then set
pageX = adjustedClientX + (window.scrollX || 0) and pageY = adjustedClientY +
(window.scrollY || 0) so page coordinates account for scroll. Use the same local
names (t, rect, clientX/clientY/pageX/pageY) in the createCrossThreadEvent
function to locate and replace the existing pageX/pageY lines.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 17, 2026

Merging this PR will improve performance by 10.72%

⚡ 1 improved benchmark
✅ 71 untouched benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
basic-performance-nest-level-100 7.7 ms 6.9 ms +10.72%

Comparing Huxpro/fix-spread-null-ref (6c84000) with main (0991136)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci bot commented Mar 17, 2026

Web Explorer

#8177 Bundle Size — 384.85KiB (+0.09%).

6c84000(current) vs 0991136 main#8171(baseline)

Bundle metrics  Change 1 change
                 Current
#8177
     Baseline
#8171
No change  Initial JS 155.59KiB 155.59KiB
No change  Initial CSS 35.1KiB 35.1KiB
Change  Cache Invalidation 7.53% 0%
No change  Chunks 8 8
No change  Assets 8 8
No change  Modules 238 238
No change  Duplicate Modules 16 16
No change  Duplicate Code 2.98% 2.98%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#8177
     Baseline
#8171
Regression  JS 253.9KiB (+0.14%) 253.55KiB
No change  Other 95.85KiB 95.85KiB
No change  CSS 35.1KiB 35.1KiB

Bundle analysis reportBranch Huxpro/fix-spread-null-refProject dashboard


Generated by RelativeCIDocumentationReport issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants