Skip to content

fix(web): keep file path tooltips open for selection - #24603

Open
juliusmarminge wants to merge 7 commits into
mainfrom
fix/11558-file-tooltip-hover
Open

juliusmarminge wants to merge 7 commits into
mainfrom
fix/11558-file-tooltip-hover

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

File path tooltips stay open when the pointer enters the popup, allowing native selection and copying. Scrolling a long path inside the popup keeps it open; scrolling the timeline still dismisses an unfocused tooltip.

Original description by @soupslurpr

Moving the pointer from a file chip into its path tooltip closes the popup, preventing users from reading, selecting, or scrolling a long path. Allow the popup to receive pointer events. The surrounding positioner stays transparent to pointer events, and Base UI continues to handle hover retention and dismissal.

Verified against upstream commit 0a91b9a116 in Chromium: the tooltip disappears before the fix and stays open afterward. Real-client checks covered full-path selection and native copying, horizontal scrolling, Escape, leaving the popup, keyboard focus, and the file context menu. All 60 focused Markdown and clipboard tests, web typecheck, targeted lint, and formatting pass.

Before:

Entering the tooltip closes it

After:

The complete path can be selected and copied

Before/after video (AV1, recorded at 200% zoom; the cyan circle marks the pointer):

tooltip-before-after-av1.mp4

Model and harness: GPT-6-Astra, Codex in T3 Code.

Takeover

Credits @soupslurpr and #11558. Retained the original popup pointer-events fix on current main.
Added the portal-scroll containment guard after reproducing dismissal while scrolling a long path. Added two focused browser regressions; no dependencies or capture fixtures are committed.
The shared popup now receives pointer events; controlled callers retain their own open-state rules.

Evidence

Real app route supplement

Full-checkout comparison: 76da264 → a429aa3, using isolated synthetic state. Updated fixture matrix · Recordings, timestamps, stills, both diffs, seed and driver scripts · Independent proof audit.

The audit finds SUFFICIENT evidence for the limited new observations below, including the explicitly recorded failures. It does not certify the whole original caller matrix. All 23 new pairs use real app routes and actual service queries, with no component/query stubs or response interception. The auditor decoded every recording, recomputed all 210 diffs for 105 still pairs, and independently reproduced native glass-popup selection, exact clipboard and paste at the head.

Real-app row Observed behavior / limit
G1: real glass sidebar Search-result row: exact native title selection and clipboard at desktop and narrow widths; desktop paste exact. Normal sortable row: native title drag activates sorting and clears selection in all four width/theme takes, despite improved popup entry retention. The actual footer has no activation callback and is informational.
F2: fallback button and context menu Actual standalone PR-description route with no thread reference and a restricted read-only session. Relative and outside-workspace absolute paths expose real fallback buttons; head popup selection/clipboard and both versions' full/relative menu copies are exact where offered, with cancellation/reopening recorded.
C1: copy controls Real expanded PR header branch and checkout controls at both widths, plus a real long branch wrapping at 390px. Clipboard values exact. Narrow baseline checkout entry transfers to the wrong sibling branch tooltip; head retains the checkout tooltip.
C2: diagnostics and changed-file paths Real native-monitor Diagnostics command and commit-review file-path controls. Head native selection and clipboard exact. Changed-file selection also attempts editor activation and shows an “Unable to open file” toast; this is not selection-only behavior. No commit is confirmed.
P1: PR previews Real GitCafe query failure and success. The controlled error/no-fallback URL tooltip closes on entry on both versions; the success PreviewCard remains open on both.
E1: disabled wrapper and nested menu Real Connections Network access wrapper retains its explanation at head; the switch stays disabled/unchecked. Real unavailable-project nested menu explanation closes on entry on both versions; clicking leaves the option disabled/unchecked and the menu open.
N2: attachment clipboard Actual unsent UTF-8 text attachment preview, desktop: entire clipboard equals the fixture on both versions, including Unicode, tab and terminal newline. Head tooltip entry retains the popup. This supersedes the earlier containment-only clipboard gap for this text-preview case.

Key G1 pair: hover the real search-result row → enter its glass popup → native title drag → copy/paste → clear scratch and leave. Exact per-side timestamps and selection-node identity are in the linked capture index. Before is left; after is right.

Before After RGB difference ×4 Threshold >16
Before: real sidebar search glass popup closes before selection After: real sidebar search glass popup title selected Diff0 Diff16

Environment: headed Chromium 156 on isolated Xvfb; ffmpeg x11grab; native scripted pointer/button paths; DPR 1, 1440×900 and fine-pointer responsive web 390×844; G1 also light/dark. Only synthetic T3 homes were used. The independent reverse-direction reproduction used a separate display, browser, ports and synthetic state copy. No source changes were made for evidence capture.

What else changed: the normal sortable-row drag can regroup the synthetic thread; narrow search-row paste is empty despite exact popup selection/clipboard; C2 file selection attempts editor activation and fails. E1 Escape/reopening and actual PR media availability differ between takes. Process metrics, timestamps and live forge/media changes remain visible and unmasked. The capture index describes each exception; neither static parity nor universal tooltip retention is claimed.

Earlier evidence is retained below; the real-route supplement above supersedes its stated route-coverage gaps only where explicitly proved.

Fixture matrix · Recordings, source hashes and public reproduction scripts

Matrix row Coverage / limit
F1 Native file-path selection, copy/paste and leave; both widths × both themes
F2 Fallback button and context menu; labeled component harness, both widths
F3 Real long-path horizontal scroll, timeline dismissal and keyboard focus; both widths
F4 Unchanged action dispatch; F2 menu representative
N1 Real ordinary URL tooltip; both widths × both themes
N2 Real attachment preview action, copied label, sibling transfer and Escape
G1 Real glass sidebar hover/sibling transfer; callback-only footer combination in labeled harness
G2–G3 Existing interactive badge unchanged; dynamic/overlay shape represented by N2/M1
C1–C2 Long wrapping copy control in labeled production-component harness; diagnostics source-equivalent
M1 Real image MediaActions; explicit outside-click menu cancellation, suppression and reopen
P1 Labeled query-stub harness: existing error-entry closure unchanged; success card exclusion
H1 Real snooze suppression and sortable drag; both widths
E1 Labeled disabled-wrapper/nested-menu production-component harness
L1 Collision code unchanged; narrow wrapping represented; existing long-path clipping
A1, Z1 Equivalent default-label callers; no blanket runtime claim

Baseline provenance: the initial core comparison swaps the exact tooltip source; supplemental comparisons reverse the two production hunks by response interception on the same final surrounding app. These are component A/B comparisons in the real app, not full-checkout baseline builds. Public source hashes and drivers are linked in the indexes.

Headed Chromium, Xvfb and ffmpeg x11grab; DPR 1; fine pointer; reduced motion; system fonts loaded; frozen browser Date. Narrow captures are responsive web. Synthetic state only; no live user data or provider turns. See the audit for fixture and comparison limits. Diff0 is RGB difference amplified 4×; DiffT masks channel differences >16/255.

Each clip captions the actual steps and times: approach trigger → enter popup → select/copy/paste (file) or observe (non-file) → leave. F3 additionally compares popup scrolling and timeline scrolling. Timestamp logs are linked from the evidence index.

Fixture / viewport / theme Before After Diff0 DiffT
F1 / 1440×900 / light before: F1-1440x900-light
Recording
eea9895
after: F1-1440x900-light
Recording
3490923
F1-1440x900-light zero difference F1-1440x900-light threshold 16
F1 / 1440×900 / dark before: F1-1440x900-dark
Recording
eea9895
after: F1-1440x900-dark
Recording
3490923
F1-1440x900-dark zero difference F1-1440x900-dark threshold 16
F1 / 390×844 / light before: F1-390x844-light
Recording
eea9895
after: F1-390x844-light
Recording
3490923
F1-390x844-light zero difference F1-390x844-light threshold 16
F1 / 390×844 / dark before: F1-390x844-dark
Recording
eea9895
after: F1-390x844-dark
Recording
3490923
F1-390x844-dark zero difference F1-390x844-dark threshold 16
N1 / 1440×900 / light before: N1-1440x900-light
Recording
eea9895
after: N1-1440x900-light
Recording
3490923
N1-1440x900-light zero difference N1-1440x900-light threshold 16
N1 / 1440×900 / dark before: N1-1440x900-dark
Recording
eea9895
after: N1-1440x900-dark
Recording
3490923
N1-1440x900-dark zero difference N1-1440x900-dark threshold 16
N1 / 390×844 / light before: N1-390x844-light
Recording
eea9895
after: N1-390x844-light
Recording
3490923
N1-390x844-light zero difference N1-390x844-light threshold 16
N1 / 390×844 / dark before: N1-390x844-dark
Recording
eea9895
after: N1-390x844-dark
Recording
3490923
N1-390x844-dark zero difference N1-390x844-dark threshold 16
G1 / 1440×900 / light before: G1-1440x900-light
Recording
eea9895
after: G1-1440x900-light
Recording
3490923
G1-1440x900-light zero difference G1-1440x900-light threshold 16
G1 / 1440×900 / dark before: G1-1440x900-dark
Recording
eea9895
after: G1-1440x900-dark
Recording
3490923
G1-1440x900-dark zero difference G1-1440x900-dark threshold 16
G1 / 390×844 / light before: G1-390x844-light
Recording
eea9895
after: G1-390x844-light
Recording
3490923
G1-390x844-light zero difference G1-390x844-light threshold 16
G1 / 390×844 / dark before: G1-390x844-dark
Recording
eea9895
after: G1-390x844-dark
Recording
3490923
G1-390x844-dark zero difference G1-390x844-dark threshold 16

Supplemental interactions, labeled production-component harnesses and public scripts · Pass-3 audit. Corrected F3 recordings supersede the first capture index's long-path takes. Normalized static controls and explicit media-menu cancellation supersede the earlier contaminated still comparisons. Six hover-trigger controls have zero changed pixels at both thresholds; entry behavior is proved by the recordings, not static parity.

What else changed: the intended popup hit testing applies to ordinary and glass tooltips too. The positioner remains click-through. Existing controlled PR-preview error tooltips still close on entry; the earlier glass selection gap is resolved for real sidebar search-result rows in the supplement above, with the sortable-row limitation explicitly retained. Long path popups already exceed the 390px viewport on both versions. Some earlier supplemental combinations use visibly labeled production-component harnesses, including a query stub for PR-preview error/success; those earlier captures do not prove route/network integration. The new supplement uses the real app routes and actual GitCafe queries.

Normalized hover-trigger controls (static parity):

Fixture / viewport / theme Before After Diff0 DiffT
F3-1440x900 / light Before: F3-1440x900
eea9895
After: F3-1440x900
3490923
Diff 0 ×4 Diff 16
F3-390x844 / light Before: F3-390x844
eea9895
After: F3-390x844
3490923
Diff 0 ×4 Diff 16
G1H1-1440x900 / light Before: G1H1-1440x900
eea9895
After: G1H1-1440x900
3490923
Diff 0 ×4 Diff 16
G1H1-390x844 / light Before: G1H1-390x844
eea9895
After: G1H1-390x844
3490923
Diff 0 ×4 Diff 16
M1-1440x900 / light Before: M1-1440x900
eea9895
After: M1-1440x900
3490923
Diff 0 ×4 Diff 16
N2-1440x900 / light Before: N2-1440x900
eea9895
After: N2-1440x900
3490923
Diff 0 ×4 Diff 16

M1 explicit menu cancellation — before left, after right. Outside-click cancellation leaves the media dialog open; it also zooms the image on both versions.

Final merged-head reproduction at a429aa3: correct path retained on entry and horizontal scroll 0→240; actual timeline scroll dismisses it. Recording · Executed source proof · Public reproduction bundle. Normalized frame differs by 0 / 329,160 pixels at both thresholds.

Final merged head: narrow long-path popup retained

Verification log
  • Browser tests: ChatMarkdown 6 → 8; both new regressions fail without the respective fix. Final scoped run: 8 Markdown + 14 timeline tests pass.
  • Targeted lint, formatting and typecheck pass. No repository-wide checks run.
  • Two independent review rounds: round 1 found popup-scroll dismissal; round 2 both reviewers returned MERGE after the guard and regression.
  • Core file selection/copy independently reproduced at 390×844 light with zero changed pixels; final independent audit: SUFFICIENT, with normalized final-head repeat at zero changed pixels.
  • Final head a429aa3 merges main by merge commit. Web source is byte-identical to captured 3490923; scoped tests, lint and formatting pass again after the merge. CI: all 28 reported checks green on this head (8 required checks passed). CodeRabbit found no actionable comments; Macroscope correctness and UI checks passed. Its approvability note flags the shared interaction default for human judgment; this PR makes that default change explicitly.
  • Unverified: separate glass branch/model text selection and PR-error-with-fallback combinations were not exercised in the real-route supplement. Exact attachment clipboard equality is proved for the desktop unsent UTF-8 text preview; other attachment formats/surfaces were not exercised. Known caller failures are recorded above. All changed paths are web; no native mobile or OS-specific code changed.
  • Model and harness: Claude Opus 5.5 and GPT-6.1-Sol through T3 Code; lead GPT-6.1-Sol, Codex.

juliusmarminge and others added 4 commits October 24, 2026 21:46
…er leaves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Popup scrolls bubble through the React portal into
TooltipScrollDismissArea, so only scrolls from inside the area's own
DOM dismiss hovered tooltips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the vouch:trusted Author is trusted by repo permissions or the VOUCHED list. label Oct 24, 2026
@github-actions github-actions Bot added the size:XS 0-9 changed lines (additions + deletions). label Oct 24, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 0e06830

Macroscope's review found this PR approvable — File-path tooltips become hoverable so users can select their text. The change is limited to three tooltip popups and leaves file actions and shared tooltip defaults unchanged.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Three tooltip popups now explicitly accept pointer events. A browser test checks that a file-path tooltip remains visible while the displayed path is selected and closes after the pointer resets.

Changes: Tooltip pointer interaction

Layer / File(s) Summary
Tooltip behavior and browser coverage
apps/web/src/components/chat/MarkdownFileLink.tsx, apps/web/src/components/ComposerPromptEditorTiptap.tsx, apps/web/src/components/chat/UserMessageContextChips.tsx, apps/web/src/components/ChatMarkdown.browser.test.tsx
The tooltip popups add pointer-events-auto. The browser test verifies that the file-path tooltip stays visible during selection and closes after the pointer resets.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: flamboh, yash-singh1

Merge Risk: Minimal · up to 0e068

This change lets users move into file-path and mention tooltips to select and copy text. It is a small UI change with no known blocking issues, so merge risk is minimal.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title "fix(web): keep file path tooltips open for selection" names the main change. The diff sets pointer-events-auto on tooltip popups so users can enter a file-path tooltip and select or copy th…
Description check Passed The description covers the problem, the change, and verification. It explains that tooltips close when the pointer moves into the popup, and that the fix allows native selection and copying. It links …
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.
Full details: Title check

Explanation

The title "fix(web): keep file path tooltips open for selection" names the main change. The diff sets pointer-events-auto on tooltip popups so users can enter a file-path tooltip and select or copy the path. The title follows conventional commit format and is specific. It does not mention the scroll guard or the glass-popup scope, which is acceptable because the title need not cover every detail.

Full details: Description check

Explanation

The description covers the problem, the change, and verification. It explains that tooltips close when the pointer moves into the popup, and that the fix allows native selection and copying. It links a related issue-style upstream commit and an earlier PR (#11558) as the origin, and it states the scope as a bug fix. The Verification section is extensive and lists focused tests, browser regressions, and observed results with limits. Several template items are weaker: the Scope section does not link a tracked issue that shows the bug. It cites an upstream commit and a prior PR instead, and it does not explain why no issue is needed. The description also includes a large amount of evidence tables and external links that are hard to review. The template asks for evidence to be uploaded and embedded, which the author did. It also asks for the model and harness, which the author gave at the end. Overall the description is mostly complete, so it passes. The Scope gap and the length are worth noting for the author.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between efbb71a and 0e06830.

Files selected for processing: 4

  • apps/web/src/components/ChatMarkdown.browser.test.tsx
  • apps/web/src/components/ComposerPromptEditorTiptap.tsx
  • apps/web/src/components/chat/MarkdownFileLink.tsx
  • apps/web/src/components/chat/UserMessageContextChips.tsx

Limit details: You’ve used all 10 included reviews currently available.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed pr:in-progress A lead is working on this PR pr:needs-julius Needs Julius: route gate, unverifiable surface, or blocked and removed pr:gate-failed A land gate failed pr:in-progress A lead is working on this PR labels Oct 25, 2026
@juliusmarminge juliusmarminge added the takeover:in-progress A takeover lead is working on this PR label Nov 14, 2026
juliusmarminge and others added 2 commits November 14, 2026 07:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ip layer

File paths in tooltips now wrap on main, so the regressions move from
ChatMarkdown to the shared tooltip they exercise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Nov 14, 2026
Making every tooltip popup take the pointer let open tooltips block the
controls under them. Restore the shared pass-through default and opt the
file path tooltips in, as ThreadStatusIndicators already does. Drop the
popup scroll guard: wrapped file paths no longer scroll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review November 14, 2026 07:20

Dismissing prior approval to re-evaluate 0e06830

@juliusmarminge juliusmarminge added takeover:needs-julius Takeover done; route gate says Julius decides and removed takeover:in-progress A takeover lead is working on this PR labels Nov 14, 2026

This branch has not been deployed

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

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:needs-julius Needs Julius: route gate, unverifiable surface, or blocked size:XS 0-9 changed lines (additions + deletions). takeover:needs-julius Takeover done; route gate says Julius decides vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant