Skip to content

fix: 悬停始终弹出描述tooltip,趋势页新增tooltip#170

Merged
AmintaCCCP merged 4 commits into
mainfrom
fix/165-hover-tooltip-always-show
May 29, 2026
Merged

fix: 悬停始终弹出描述tooltip,趋势页新增tooltip#170
AmintaCCCP merged 4 commits into
mainfrom
fix/165-hover-tooltip-always-show

Conversation

@AmintaCCCP

@AmintaCCCP AmintaCCCP commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • 移除 RepositoryCard 的截断检测门槛(scrollHeight > clientHeight),悬停时始终弹出 tooltip,解决因字体渲染差异导致检测不准的问题
  • 为 SubscriptionRepoCard(趋势页)新增描述和 AI 总结的悬停 tooltip,之前趋势页完全没有查看完整文本的方式

Closes #165

Test plan

  • 仓库列表页悬停任意描述(包括短文本),tooltip 始终弹出
  • 趋势页悬停描述和 AI 总结,tooltip 弹出显示完整内容
  • tooltip 在 light/dark mode 下样式正常
  • 长文本 tooltip 内可滚动
  • 离开悬停区域 tooltip 消失

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a reusable floating tooltip with smarter positioning and automatic flip when space is limited.
    • Tooltips now have independent controls for description and AI summary, and support touch interactions.
  • Bug Fixes

    • Tooltips reliably show full text via hover, focus, and touch, with mouse-leave hide delay.
    • Simplified timeout cleanup to avoid lingering timers.

Review Change Stack

…s page

Remove truncation detection threshold in RepositoryCard — the scrollHeight > clientHeight check was unreliable across different fonts/browsers, causing tooltips to not show when text appeared visually truncated. Now tooltips always show on hover.

Add hover tooltip to SubscriptionRepoCard (trends page) for both description and AI summary sections, which previously had no way to view truncated text.

Closes #165

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b91d0f85-693b-4ead-855b-29f811984c6b

📥 Commits

Reviewing files that changed from the base of the PR and between 24d5375 and 6d9ad1f.

📒 Files selected for processing (3)
  • src/components/FloatingTooltip.tsx
  • src/components/RepositoryCard.tsx
  • src/components/SubscriptionRepoCard.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/SubscriptionRepoCard.tsx
  • src/components/RepositoryCard.tsx
  • src/components/FloatingTooltip.tsx

📝 Walkthrough

Walkthrough

Adds a reusable FloatingTooltip (portal, placement/flip, resize/scroll updates) and rewires RepositoryCard and SubscriptionRepoCard to show full-text tooltips via explicit boolean state and trigger refs on hover/focus/touch; RepositoryCard simplifies timeout cleanup to unmount-only clearing.

Changes

Tooltip Hover Enhancement

Layer / File(s) Summary
FloatingTooltip implementation
src/components/FloatingTooltip.tsx
Adds FloatingTooltip with props, placement/flip math, resize/scroll listeners using rAF, and portal rendering when visible is true.
RepositoryCard: state and render changes
src/components/RepositoryCard.tsx
Removes truncation-measurement refs/state, introduces descTriggerRef and tooltipHideTimerRef, simplifies effect cleanup, and renders FloatingTooltip anchored to the trigger with hover/focus/touch handlers controlling showTooltip.
SubscriptionRepoCard: independent tooltips
src/components/SubscriptionRepoCard.tsx
Adds descTooltip/aiTooltip state and corresponding trigger refs; wraps description and AI summary in hover/focus/touchable wrappers and renders independent FloatingTooltip instances for each.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant RepositoryCard
  participant SubscriptionRepoCard
  participant FloatingTooltip
  participant Window

  User->>RepositoryCard: mouseenter / focus / touchstart (description)
  RepositoryCard->>FloatingTooltip: render visible tooltip anchored to descTriggerRef
  FloatingTooltip->>Window: measure trigger bounds and viewport
  FloatingTooltip->>FloatingTooltip: compute placement / flip if needed
  FloatingTooltip->>User: show positioned tooltip

  User->>SubscriptionRepoCard: mouseenter / focus / touchstart (description or ai_summary)
  SubscriptionRepoCard->>FloatingTooltip: render visible tooltip anchored to corresponding triggerRef
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AmintaCCCP/GithubStarsManager#128: Modifies src/components/RepositoryCard.tsx tooltip behavior and interaction logic; overlaps with tooltip control changes in this PR.

Poem

🐰 I nibble at tooltips in the night,

hover wakes the hidden lines of light,
focus and touch, a gentle reveal,
full text unfurls, scrollable and real,
a rabbit cheers — no trims, just sight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title in Chinese mentions hover tooltip fixes and trends page additions, which directly matches the main changes in the changeset.
Linked Issues check ✅ Passed The PR successfully implements the core requirements from issue #165: tooltips now display descriptions/AI summaries on hover in both RepositoryCard and SubscriptionRepoCard components, and removes truncation detection for consistent behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing hover tooltips for descriptions and AI summaries across both components and the new FloatingTooltip component, with no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/165-hover-tooltip-always-show

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 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 `@src/components/RepositoryCard.tsx`:
- Around line 866-879: The tooltip is currently only toggled by mouse events;
update the container (the element using setShowTooltip and showTooltip in
RepositoryCard, and the paragraph rendering highlightSearchTerm) to support
keyboard and touch by adding onFocus and onBlur handlers that
setShowTooltip(true/false), a tabIndex={0} so it is focusable, and touch/click
handlers (onClick and/or onTouchStart) that toggle or open the tooltip for touch
users; also add appropriate ARIA attributes (e.g., aria-expanded={showTooltip}
and aria-describedby or role="button"/role="group" as appropriate) so screen
readers can detect the tooltip state.

In `@src/components/SubscriptionRepoCard.tsx`:
- Around line 408-424: The tooltip currently controlled only by
onMouseEnter/onMouseLeave (descTooltip and setDescTooltip) is inaccessible to
keyboard and touch users; update the container(s) that show repo.description
(the div wrapping the <p> and the similar block at lines 429-445) to be
focusable (add tabIndex={0}), handle onFocus/onBlur to setDescTooltip and add an
onClick or onKeyDown handler (handling Enter/Space) to toggle the tooltip for
touch/keyboard activation, and ensure appropriate ARIA attributes (e.g.,
role="button" and aria-expanded={descTooltip}) are set so screen readers and
keyboard users can open the full-text panels as well.
🪄 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: 0618bf99-2c22-4707-8f76-1d84fe4a55f8

📥 Commits

Reviewing files that changed from the base of the PR and between 1f047d1 and 64f9c78.

📒 Files selected for processing (2)
  • src/components/RepositoryCard.tsx
  • src/components/SubscriptionRepoCard.tsx

Comment thread src/components/RepositoryCard.tsx Outdated
Comment thread src/components/SubscriptionRepoCard.tsx
AmintaCCCP and others added 2 commits May 29, 2026 15:29
Add onFocus/onBlur/onTouchStart handlers and tabIndex={0} to all tooltip
containers so keyboard and touch users can also access full descriptions.

Addresses CodeRabbitAI review findings on PR #170.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace absolute-positioned tooltips with FloatingTooltip component that
uses createPortal to render outside overflow containers. The tooltip uses
fixed positioning calculated from getBoundingClientRect(), so it's never
clipped by parent overflow-y-auto scroll containers.

This fixes the issue where trend page tooltips were being cut off by the
DiscoveryView scroll container boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/FloatingTooltip.tsx (2)

53-58: 💤 Low value

Optional: throttle scroll/resize repositioning. handleResize runs getBoundingClientRect() + style writes synchronously on every capture-phase scroll/resize event, which can be a hot path in the DiscoveryView scroll container. Consider coalescing with requestAnimationFrame.

🤖 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 `@src/components/FloatingTooltip.tsx` around lines 53 - 58, The scroll/resize
handler (handleResize -> updatePosition) runs expensive layout reads/writes on
every event; wrap/coalesce calls via requestAnimationFrame to avoid synchronous
work on every capture-phase scroll/resize. Modify where
window.addEventListener('resize', handleResize) and
window.addEventListener('scroll', handleResize, true) are registered so
handleResize schedules a single rAF-driven runner (cancel previous rAF if
pending) that calls updatePosition once per animation frame, and ensure the
cleanup removes both the event listeners and cancels any pending rAF handle;
reference the handleResize function, updatePosition call, and the
addEventListener/removeEventListener calls when making this change.

19-48: ⚡ Quick win

Reposition runs in useEffect, causing a first-frame flash. useEffect fires after paint, so on first show the fixed tooltip is briefly painted at its default location (top-left of the viewport) before updatePosition moves it — visible because of animate-fade-in. Use useLayoutEffect so positioning happens before the browser paints.

♻️ Proposed change
-import React, { useEffect, useRef, useCallback } from 'react';
+import React, { useLayoutEffect, useRef, useCallback } from 'react';
-  useEffect(() => {
+  useLayoutEffect(() => {
     if (visible) {
       updatePosition();
🤖 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 `@src/components/FloatingTooltip.tsx` around lines 19 - 48, The repositioning
is running in useEffect which causes a first-frame flash; change the effect that
calls updatePosition to useLayoutEffect so positioning runs before paint. Locate
the effect that invokes updatePosition (references updatePosition, triggerRef,
tooltipRef, visible) and replace useEffect with useLayoutEffect; ensure
updatePosition's dependency array includes tooltipRef.current (or tooltipRef)
and any window listeners (resize/scroll) are also registered/cleaned up inside
that useLayoutEffect so the tooltip (and its animate-fade-in) is positioned
before the first paint.
🤖 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 `@src/components/FloatingTooltip.tsx`:
- Around line 50-61: The tooltip immediately unmounts when the trigger's
onMouseLeave fires, preventing hovering/scrolling the tooltip; change
FloatingTooltip to implement a hover-intent hide delay: keep the existing
visible/updatePosition logic but add a hideTimeoutRef (or similar) that starts a
short timeout on the trigger's onMouseLeave instead of immediately setting
visible=false, expose an onMouseEnter handler on the tooltip DOM that clears
that timeout (and also clear the timeout on tooltip onMouseLeave to restart it),
ensure cleanup of the timeout in useEffect cleanup and when visible changes, and
update consumers (SubscriptionRepoCard/RepositoryCard) to stop toggling
visibility synchronously and rely on this delayed hide behavior.

---

Nitpick comments:
In `@src/components/FloatingTooltip.tsx`:
- Around line 53-58: The scroll/resize handler (handleResize -> updatePosition)
runs expensive layout reads/writes on every event; wrap/coalesce calls via
requestAnimationFrame to avoid synchronous work on every capture-phase
scroll/resize. Modify where window.addEventListener('resize', handleResize) and
window.addEventListener('scroll', handleResize, true) are registered so
handleResize schedules a single rAF-driven runner (cancel previous rAF if
pending) that calls updatePosition once per animation frame, and ensure the
cleanup removes both the event listeners and cancels any pending rAF handle;
reference the handleResize function, updatePosition call, and the
addEventListener/removeEventListener calls when making this change.
- Around line 19-48: The repositioning is running in useEffect which causes a
first-frame flash; change the effect that calls updatePosition to
useLayoutEffect so positioning runs before paint. Locate the effect that invokes
updatePosition (references updatePosition, triggerRef, tooltipRef, visible) and
replace useEffect with useLayoutEffect; ensure updatePosition's dependency array
includes tooltipRef.current (or tooltipRef) and any window listeners
(resize/scroll) are also registered/cleaned up inside that useLayoutEffect so
the tooltip (and its animate-fade-in) is positioned before the first paint.
🪄 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: de63050c-27eb-429c-b511-7773f407fc93

📥 Commits

Reviewing files that changed from the base of the PR and between a1bacb8 and 24d5375.

📒 Files selected for processing (3)
  • src/components/FloatingTooltip.tsx
  • src/components/RepositoryCard.tsx
  • src/components/SubscriptionRepoCard.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/RepositoryCard.tsx

Comment thread src/components/FloatingTooltip.tsx Outdated
Comment on lines +50 to +61
useEffect(() => {
if (visible) {
updatePosition();
const handleResize = () => updatePosition();
window.addEventListener('resize', handleResize);
window.addEventListener('scroll', handleResize, true);
return () => {
window.removeEventListener('resize', handleResize);
window.removeEventListener('scroll', handleResize, true);
};
}
}, [visible, updatePosition]);

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 | 🏗️ Heavy lift

Tooltip closes before the user can reach it, breaking scrollable long content. The tooltip is rendered 8px above the trigger and visibility is driven entirely by the trigger wrapper's onMouseLeave (see SubscriptionRepoCard lines 415/439 and RepositoryCard). When the cursor leaves the trigger to move onto the tooltip, onMouseLeave fires immediately and unmounts it — so a user can never hover the panel to scroll, which the PR test plan ("长文本 tooltip 内可滚动") requires. Adding onMouseEnter here alone won't fix it because the trigger has already toggled visible to false.

Recommend a hover-intent pattern: delay the hide, expose onMouseEnter on the tooltip to cancel the pending hide, and have consumers clear the timer on re-entry.

🛠️ Suggested pattern
 interface FloatingTooltipProps {
   content: React.ReactNode;
   visible: boolean;
   triggerRef: React.RefObject<HTMLElement | null>;
+  onMouseEnter?: () => void;
   onMouseLeave: () => void;
 }
     <div
       ref={tooltipRef}
+      onMouseEnter={onMouseEnter}
       onMouseLeave={onMouseLeave}

Consumers should hide on a short timeout (cleared by the tooltip's onMouseEnter) instead of synchronously on the wrapper's onMouseLeave.

🤖 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 `@src/components/FloatingTooltip.tsx` around lines 50 - 61, The tooltip
immediately unmounts when the trigger's onMouseLeave fires, preventing
hovering/scrolling the tooltip; change FloatingTooltip to implement a
hover-intent hide delay: keep the existing visible/updatePosition logic but add
a hideTimeoutRef (or similar) that starts a short timeout on the trigger's
onMouseLeave instead of immediately setting visible=false, expose an
onMouseEnter handler on the tooltip DOM that clears that timeout (and also clear
the timeout on tooltip onMouseLeave to restart it), ensure cleanup of the
timeout in useEffect cleanup and when visible changes, and update consumers
(SubscriptionRepoCard/RepositoryCard) to stop toggling visibility synchronously
and rely on this delayed hide behavior.

Three fixes addressing CodeRabbitAI audit findings:

1. Hover-intent pattern: tooltip hide is delayed 150ms, cancelled on
   re-entry (both trigger and tooltip onMouseEnter). Users can now move
   cursor from trigger to tooltip and scroll long content.

2. useLayoutEffect instead of useEffect for positioning: prevents
   first-frame flash where tooltip briefly appears at default position.

3. requestAnimationFrame throttling for scroll/resize: coalesces
   rapid reposition events into one per animation frame.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@AmintaCCCP AmintaCCCP merged commit 67b69a9 into main May 29, 2026
5 checks passed
@AmintaCCCP AmintaCCCP deleted the fix/165-hover-tooltip-always-show branch May 29, 2026 08:00
lostiv pushed a commit to lostiv/GithubStarsManager that referenced this pull request May 30, 2026
Add onFocus/onBlur/onTouchStart handlers and tabIndex={0} to all tooltip
containers so keyboard and touch users can also access full descriptions.

Addresses CodeRabbitAI review findings on PR AmintaCCCP#170.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit a1bacb8)
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.

AI分析能否手动展开

1 participant