Skip to content

iOS browser stream: focus editables under replayed taps, suppress stray backspace, verbose diagnostics - #9729

Merged
azooz2003-bit merged 3 commits into
mainfrom
feat-browser-stream-field-focus
Aug 7, 2026
Merged

azooz2003-bit merged 3 commits into
mainfrom
feat-browser-stream-field-focus

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes the streamed-browser input bugs Aziz hit dogfooding #9577, and adds the verbose browser-stream diagnostics he asked for.

Symptoms (all one root cause): tapping a page text field does nothing (no caret, no keyboard); typed text lands nowhere on most pages; backspace with "highlighted" text navigates the page back in history instead of deleting. Diagnosis: replayed phone clicks reach the page as DOM events (verified: mousedown/click with correct coordinates and target), but WebKit refuses to move field focus for clicks in a window that is never key, and the streamed webview lives in the offscreen render host whose window is deliberately non-key. With document.activeElement stuck on BODY, editable_focused never fires (no phone keyboard), text insertion has no target, and backspace falls through as WebKit's page-level history back-navigation.

Fix

  • BrowserPanel.replayMobileBrowserPointer replays the click and then runs a focus assist: programmatic JS focus is exempt from the key-window rule, so it hit-tests the tap point (elementFromPoint, descending one shadow-root level for widget-wrapped inputs, then walking ancestors) and calls focus({preventScroll: true}) on the editable it finds. The existing editable_focused beacon then raises the phone keyboard, and both text-insertion paths (key events and JS insertion) have a real target.
  • BrowserPanel.replayMobileBrowserKey suppresses a bare backspace when no editable has focus: the phone keyboard's backspace is a text-editing key, and letting it through navigated history and lost page state. Modified combinations pass through unchanged.
  • Red/green commit pair: testReplayedClickFocusesEditableUnderTap (red on the bare replay seam) then the assist; testBareBackspaceOutsideEditableIsSuppressed covers the suppression plus delivery-while-editing.

Verbose browser-stream diagnostics

Mac host ring (Sentry-attached via the existing diagnostics tap): new DiagnosticEventCodes record stream lifecycle (start / replace / stop / first committed frame), input replay outcomes (kind, click count, suppressed backspace, text length), focus-assist results, beacon editable-focus transitions, and mobile.browser.create resolutions, all correlated by a stable panel ID. Phone debug log (Copy Debug Logs, feedback bundles): browser.create outcomes and browser.stream start / start-deferred / start-failed / stop / force-restart / closed-by-mac lines.

Localization audit: no user-facing UI strings changed; the four new diagnostics titles follow the package's existing localized(_:defaultValue:) pattern used by all 55 prior event codes.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Fixes iOS browser stream input so tapped fields focus, typing goes into the page, and bare backspace no longer navigates history. Adds shadow‑root‑aware focus detection and richer, semantic diagnostics (including first delivered frame) to debug lifecycle, input, and focus.

  • Bug Fixes

    • After a replayed click, hit-test the tap (elementFromPoint, descend one shadow root) and focus the editable via JS to bypass WebKit’s key-window rule.
    • Suppress bare backspace when no editable is focused; focus checks descend shadow roots. Modified combos still pass.
    • Tests added: testReplayedClickFocusesEditableUnderTap, testBareBackspaceOutsideEditableIsSuppressed, testBackspaceDeliversToShadowRootInput.
  • New Features

    • New diagnostics with semantic fields: browserStreamLifecycle, browserInputReplayed (now includes a distinct “suppressed backspace” kind), browserEditableFocus, browserPanelCreateResolved — with stage/input/count/focus-outcome and a stable panel correlation ID.
    • Record “first delivered frame” once per session after a successfully delivered frame.
    • Mac host ring logs lifecycle, input outcomes, focus-assist results, and beacon transitions; phone debug logs emit browser.create outcomes and browser.stream start/start-deferred/start-failed/stop/force-restart/closed-by-mac.

Written for commit b584cc9. Summary will update on new commits.

Review in cubic


Note

Medium Risk
Changes Mac-side WebKit input replay and RPC key handling (async, delivered flag); user-visible browser streaming behavior but confined to the mobile browser stream path with new test coverage.

Overview
Fixes iOS streamed-browser input when the Mac web view lives in a non-key offscreen host: replayed taps did not focus fields (no keyboard, typing went nowhere), and bare backspace could trigger history navigation.

Input behavior: After pointer replay, click focus assist hit-tests the tap (elementFromPoint, shadow-root descent) and calls programmatic focus() so editables can receive keys. Bare backspace is dropped when no editable is focused; shadow-root–aware checks align the dirty beacon and suppression logic with widget-wrapped inputs.

Diagnostics: Four new DiagnosticEventCode values (56–59) with presentation/decoding record stream lifecycle, input replay outcomes, editable focus, and mobile.browser.create results on the Mac host ring (panel correlation IDs). The phone shell adds MobileDebugLog.anchormux lines for browser create and stream start/stop/restart paths.

Tests: MobileBrowserStreamInputFocusTests covers click-to-focus, backspace suppression, and shadow-root backspace delivery.

Reviewed by Cursor Bugbot for commit b584cc9. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added detailed diagnostics for browser streaming, panel creation, input replay, editable focus, and stream lifecycle events.
    • Added support for focusing editable controls inside shadow roots during mobile browser interaction.
    • Added clearer reporting for browser stream and input delivery outcomes.
  • Bug Fixes

    • Prevented unmodified Backspace/Delete inputs from being sent when no editable field is focused.
    • Improved mobile click and keyboard replay reliability for editable controls.

Replayed clicks reach the streamed page as DOM events, but WebKit
refuses to move field focus for clicks in a window that is never key
(the offscreen render host). A tapped text field never focuses, the
phone keyboard never rises, and backspace falls through as page-level
history back-navigation instead of deleting. Red test plus the bare
panel replay seam the fix will hang off.
…iagnostics

Programmatic JS focus is exempt from WebKit's key-window rule, so a
replayed click now hit-tests the tap point (descending one shadow-root
level) and focuses the editable it finds. The phone keyboard rises via
the existing editable_focused beacon, and typing lands in the field. A
bare backspace with no focused editable is suppressed instead of
falling through as WebKit history back-navigation, which lost page
state when users tried to delete text.

Verbose browser-stream diagnostics for debugging user reports: the Mac
host ring (Sentry-attached) records stream lifecycle (start, replace,
stop, first frame), input replay outcomes (kind, click count,
suppressed backspace, text length), focus-assist results, beacon
editable transitions, and create resolutions; the phone debug log
gains browser.create and browser.stream lifecycle lines.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds four browser-stream diagnostic events and records lifecycle, creation, capture, focus, and input outcomes. Browser pointer and key replay now use asynchronous panel APIs. Clicks can focus editable elements, and unmodified backspace or delete is suppressed without editable focus.

Changes

Browser streaming observability and input

Layer / File(s) Summary
Diagnostic event contracts
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventCode.swift, Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventPresentation.swift, Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/DiagnosticEventPresentationTests.swift
Adds four browser diagnostic event codes, localized titles, payload decoding, field labels, and presentation tests.
Stream lifecycle diagnostics
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+BrowserStream.swift, Sources/Mobile/MobileBrowserStreamCoordinator.swift, Sources/Mobile/MobileBrowserStreamSession.swift, Sources/TerminalController+MobileBrowser.swift
Records panel creation, stream transitions, session replacement, first-frame delivery, focus changes, text input outcomes, and panel correlation data.
Asynchronous input replay and focus handling
Sources/Panels/BrowserPanel+MobileBrowserStreaming.swift, Sources/TerminalController+MobileBrowser.swift, cmuxTests/BrowserPanelTests.swift
Adds asynchronous pointer and key replay, editable-focus assistance through shadow roots, backspace/delete suppression, delivery status responses, and input-focus tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TerminalController
  participant BrowserPanel
  participant WebView
  participant DiagnosticLog
  TerminalController->>BrowserPanel: Replay pointer or key input asynchronously
  BrowserPanel->>WebView: Detect or assist editable focus
  BrowserPanel->>WebView: Deliver allowed input
  BrowserPanel-->>TerminalController: Return focus outcome or delivery status
  TerminalController->>DiagnosticLog: Record input and focus diagnostics
Loading

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (3 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Actor Isolation ❌ Error The PR adds pure mobileBrowserPanelCorrelation inside @MainActor TerminalController; it only transforms UUID bytes and should not inherit MainActor isolation. Mark mobileBrowserPanelCorrelation as nonisolated static, or move it to a nonisolated value-only utility.
Cmux Swift Package Boundaries ❌ Error The PR keeps mobile-browser focus, editable detection, and backspace suppression in BrowserPanel app code, although CmuxBrowser already uses injectable WebKit seams for testable browser services. Extract the focus/editable policy and JavaScript into the CmuxBrowser target. Expose BrowserMobileInputFocusService and BrowserMobileInputScriptEvaluating; leave only the WKWebView adapter and RPC wiring in Sources/.
Cmux Full Internationalization ❌ Error The PR adds 26 localized diagnostic keys, but none exist in CMUXMobileCore/Resources/Localizable.xcstrings, which supports en and ja. Add translated en and ja catalog entries for all new browser event, stage, input, focus, unknown-value, and field keys.
Docstring Coverage ⚠️ Warning Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description gives a detailed summary but omits the required Testing, Demo Video, Review Trigger, and Checklist sections. Add the missing template sections, document test commands and manual verification, include a demo video or explain why none applies, and complete the checklist.
✅ Passed checks (20 passed)
Check name Status Explanation
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.
Cmux Swift Blocking Runtime ✅ Passed The full PR diff adds no semaphores, blocking waits, sleeps, delayed dispatch, timers, polling, main-queue sync, or manual locks; new awaits wait on WebKit/RPC completion signals.
Cmux Browser Automation Off-Main ✅ Passed The PR changes no targeted control-socket policy/router files and adds no browser.* routing; its mobile.browser.* handlers are outside this rule's scope.
Cmux Expensive Synchronous Load ✅ Passed The PR adds no agent-history loader, workspace scan, transcript/JSONL read, or per-record syscall. The only JSON decoder is the existing mobileBrowserDecode helper; new diagnostics use a bounded ri...
Cmux Cache Substitution Correctness ✅ Passed The full PR diff adds diagnostics, input/focus logic, and a per-session first-frame marker; it does not replace a fresh persistence, history, undo, or snapshot read with a cache.
Cmux No Hacky Sleeps ✅ Passed The PR changes only Swift files; it adds no TypeScript, JavaScript, shell, or build/runtime script delay. This non-Swift check is not applicable.
Cmux Algorithmic Complexity ✅ Passed The PR adds only linear ancestor/shadow-root walks and fixed-size type checks; no new batch rescans or sorting/filtering. The active-paint DOM scans predate the PR.
Cmux Swift Concurrency ✅ Passed The full PR diff adds no legacy Dispatch, Combine, completion-handler, or fire-and-forget Task pattern; existing Task counts are unchanged, and input replay now uses async/await.
Cmux Swift @Concurrent ✅ Passed The diff adds no @concurrent or nonisolated async code. New async browser methods inherit @MainActor and only perform UI/WebKit work; existing network/capture helpers keep their prior isolation.
Cmux Swiftpm Lockfiles ✅ Passed The full PR diff contains only Swift source and test files; it changes no Package.swift, Package.resolved, Xcode project, .gitignore, workflow, or dependency references, so the lockfile policy is n...
Cmux Swift Logging ✅ Passed Full PR diff adds no direct print/debugPrint/dump/NSLog; it uses the existing DiagnosticLog ring and MobileDebugLog.anchormux, whose NSLog and file sink are DEBUG-only.
Cmux User-Facing Error Privacy ✅ Passed Added browser errors and diagnostics use generic product terms, bounded enums/counts, and short correlation identifiers; no vendor names, secrets, raw upstream messages, session tokens, or payload...
Cmux Swiftui State Layout ✅ Passed The PR changes AppKit/browser coordinators, diagnostics, and tests; no added SwiftUI view, ObservableObject state, GeometryReader, lazy-row store reference, or render-time state mutation appears in...
Cmux Architecture Rethink ✅ Passed The diff adds no timing, blocking, polling, lock, or observer repair path; input stays owned by @MainActor BrowserPanel, lifecycle by the session, with direct WebKit bridging and explicit delivery...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Full PR diff adds no standalone NSWindow/NSPanel/WindowGroup or window identifier; only test fixtures call panel.close(). The existing lint is scripts/lint_auxiliary_window_close_shortcuts.py.
Cmux Source Artifacts ✅ Passed The PR changes only nine existing Swift source/test files; all are text modifications with no binary entries, scratch directories, generated outputs, caches, or artifact-like paths.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Full PR diff adds no test-build guards or test/debug-named members; browser helpers and diagnostics have production callers in TerminalController, coordinator, and stream session.
Cmux No Ambient Global State ✅ Passed The production additions are instance methods or a per-session private var; no new file-scope funcs/vars, static-only namespace, or singleton declarations were added. Existing diagnostic singletons...
Title check ✅ Passed The title clearly summarizes the main input-focus, backspace, and diagnostics changes in the pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-browser-stream-field-focus

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.

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

🤖 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
`@Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventPresentation.swift`:
- Around line 356-363: The new diagnostic codes need semantic payload decoding
instead of generic detail fields. Update decodeA, decodeB, and decodeC to map
browserStreamLifecycle, browserInputReplayed, browserEditableFocus, and
browserPanelCreateResolved payload positions to their documented lifecycle,
input-kind, focus-assist, and panel-correlation field names and decoded values,
then include all four codes in decodesEveryStructuredPayloadIntoSemanticFields.

In `@Sources/Mobile/MobileBrowserStreamSession.swift`:
- Around line 264-268: Move the stage-4 DiagnosticEvent recording from before
frame encoding and delivery to immediately after the first successful
connection.sendEvent(topic: "browser.frame", ...) in the browser stream
lifecycle flow. Ensure it is emitted only once per stream, and do not record it
for later synchronized PNG captures or failed sends.

In `@Sources/Panels/BrowserPanel`+MobileBrowserStreaming.swift:
- Around line 159-175: The editable-focus predicate in
mobileBrowserEditableHasFocus must traverse the active element through shadow
roots instead of relying only on document.activeElement. Reuse the same
shadow-aware predicate in mobileBrowserDirtyBeaconScript so both focus checks
behave consistently, and add a regression test that focuses an input inside a
shadow root and verifies a bare backspace is delivered.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c9e6430d-db50-4f86-8806-b0f6cedc0e17

📥 Commits

Reviewing files that changed from the base of the PR and between d7a5b5a and b7d6904.

📒 Files selected for processing (9)
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventCode.swift
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventPresentation.swift
  • Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/DiagnosticEventPresentationTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+BrowserStream.swift
  • Sources/Mobile/MobileBrowserStreamCoordinator.swift
  • Sources/Mobile/MobileBrowserStreamSession.swift
  • Sources/Panels/BrowserPanel+MobileBrowserStreaming.swift
  • Sources/TerminalController+MobileBrowser.swift
  • cmuxTests/BrowserPanelTests.swift

Comment thread Sources/Mobile/MobileBrowserStreamSession.swift Outdated
Comment thread Sources/Panels/BrowserPanel+MobileBrowserStreaming.swift
…ry, shadow-aware focus checks

The four browser diagnostic codes now decode into named fields (stage,
input kind, count, focus outcome, panel correlation) instead of falling
through to detail_1/2/3, with a suppressed backspace becoming its own
input kind so counts stay unambiguous. The first-frame lifecycle stage
records once per session after the first successful browser.frame
delivery, never for a capture whose send failed. Editable-focus
detection descends shadow roots in the suppression check, the focus
assist's already-focused check, and the beacon, so widget-wrapped
inputs receive backspace and raise the keyboard; regression test
covers backspace delivery to a shadow-root input.

@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)
Sources/TerminalController+MobileBrowser.swift (1)

215-224: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Record the actual editable-focus state.

At Line 222, focusAssist == 0 means that no editable exists at the tap point. It does not mean that no editable remains focused. A click on a non-focusable element can retain focus in an existing input, but this records a: 0.

After replay, call await panel.mobileBrowserEditableHasFocus() and use that result for a. Keep focusAssist in b.

Proposed fix
             if input.kind == .click {
                 mobileBrowserRecordDiagnostic(
                     .browserEditableFocus,
                     panel: panel,
-                    a: focusAssist == 0 ? 0 : 1,
+                    a: await panel.mobileBrowserEditableHasFocus() ? 1 : 0,
                     b: focusAssist
                 )
             }
🤖 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 `@Sources/TerminalController`+MobileBrowser.swift around lines 215 - 224,
Update the .browserEditableFocus diagnostic in the input.click branch to await
panel.mobileBrowserEditableHasFocus() and use its result for field a, preserving
focusAssist as field b; do not infer the actual focused-editable state from
focusAssist.
Sources/Panels/BrowserPanel+MobileBrowserStreaming.swift (1)

12-19: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use one editable classification in the dirty beacon.

The dirty-beacon predicate excludes select, but mobileBrowserEditableHasFocus() and assistMobileBrowserEditableFocus(...) include it. After focus assist selects a <select>, the beacon can publish editable_focused: false and overwrite the stream state.

Include tag === 'select' in this predicate, or remove SELECT from the other two predicates. Keep all three paths consistent.

🤖 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 `@Sources/Panels/BrowserPanel`+MobileBrowserStreaming.swift around lines 12 -
19, Update the dirty-beacon editable predicate in the JavaScript around
mobileBrowserEditableHasFocus to classify select elements consistently with
mobileBrowserEditableHasFocus() and assistMobileBrowserEditableFocus(...).
Prefer adding the select tag to this predicate while preserving the existing
input, textarea, and contenteditable checks.
🤖 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 `@Sources/Panels/BrowserPanel`+MobileBrowserStreaming.swift:
- Around line 12-19: Update the dirty-beacon editable predicate in the
JavaScript around mobileBrowserEditableHasFocus to classify select elements
consistently with mobileBrowserEditableHasFocus() and
assistMobileBrowserEditableFocus(...). Prefer adding the select tag to this
predicate while preserving the existing input, textarea, and contenteditable
checks.

In `@Sources/TerminalController`+MobileBrowser.swift:
- Around line 215-224: Update the .browserEditableFocus diagnostic in the
input.click branch to await panel.mobileBrowserEditableHasFocus() and use its
result for field a, preserving focusAssist as field b; do not infer the actual
focused-editable state from focusAssist.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a8fdad91-5925-4212-b63d-4848da5c72b1

📥 Commits

Reviewing files that changed from the base of the PR and between b7d6904 and b584cc9.

📒 Files selected for processing (7)
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventCode.swift
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/DiagnosticEventPresentation.swift
  • Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/DiagnosticEventPresentationTests.swift
  • Sources/Mobile/MobileBrowserStreamSession.swift
  • Sources/Panels/BrowserPanel+MobileBrowserStreaming.swift
  • Sources/TerminalController+MobileBrowser.swift
  • cmuxTests/BrowserPanelTests.swift

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b584cc9. Configure here.

if (tag !== 'INPUT') return false;
const type = String(el.type || 'text').toLowerCase();
return !['button','checkbox','color','file','hidden','image','radio','range','reset','submit'].includes(type);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Select treated as text editable

Medium Severity

isEditable in the new focus and backspace helpers treats SELECT as editable, but the dirty-beacon check does not. With a focused select, bare backspace is delivered and WebKit can navigate history—the failure this PR aims to stop—especially while the phone keyboard is still up after leaving a text field or via manual keyboard request.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b584cc9. Configure here.

if (inner) el = inner;
}
while (el && !isEditable(el)) el = el.parentElement || (el.getRootNode && el.getRootNode().host) || null;
if (!el) return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Label taps skip focus assist

Medium Severity

Focus assist only walks from the hit element through parents and shadow hosts for an editable. Taps on a label (via for or wrapping text) never resolve the associated control, so those common form targets still leave document.activeElement on BODY and the phone keyboard does not rise.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b584cc9. Configure here.

if (el && el.shadowRoot) {
const inner = el.shadowRoot.elementFromPoint(\(x), \(y));
if (inner) el = inner;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shadow text insert still shallow

Medium Severity

Focus assist can focus an input inside a shadow root, and the beacon/backspace paths now pierce shadow trees, but insertTextWithJavaScript still uses shallow document.activeElement (the host). Non-ASCII phone text to widget-wrapped fields then fails insertion even though the field is focused and the keyboard is up.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b584cc9. Configure here.

@azooz2003-bit
azooz2003-bit merged commit 3347fd9 into main Aug 7, 2026
24 checks passed
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.

1 participant