-
Notifications
You must be signed in to change notification settings - Fork 431
feat(web): add stargazers avatar grid background to opensource hero section #2007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(web): add stargazers avatar grid background to opensource hero section #2007
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Rate limit exceeded@ComputelessComputer has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 59 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a React Query hook to fetch GitHub stargazers, an animated StargazersGrid overlay on the opensource hero, platform-aware DownloadButton rendering, new CSS keyframe animations, and two new download routes that redirect to external installers. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser UI (React)
participant Hook as useGitHubStargazers
participant Cache as React Query Cache
participant GitHub as GitHub API
Browser->>Hook: mount / call useGitHubStargazers(500)
Hook->>Cache: check cache (staleTime = 1h)
alt cache miss or stale
Hook->>GitHub: GET /repos/:owner/:repo (fetch stargazers_count)
GitHub-->>Hook: repo metadata
Hook->>GitHub: GET /repos/:owner/:repo/stargazers?per_page=...&page=...
GitHub-->>Hook: stargazer list
Hook->>Hook: map -> [{username, avatar}] and reverse
Hook->>Cache: store result
Hook-->>Browser: stargazer array
else cache hit
Cache-->>Hook: cached stargazers
Hook-->>Browser: stargazer array
end
Browser->>Browser: render StargazersGrid (CSS animations rows)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Comment |
There was a problem hiding this 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 (2)
apps/web/src/routes/_view/opensource.tsx (2)
72-88: Avatar anchor accessibility & tab orderNice small, lazy-loaded avatar component. One concern: every avatar is an
<a>and thus focusable; with ~100 stargazers you’ll add a large number of tab stops in what is essentially a decorative background. That can make keyboard navigation through the hero section pretty painful.If you don’t explicitly need these in the keyboard tab order, consider either:
- Marking the entire grid container as
aria-hidden="true"and/or- Adding
tabIndex={-1}to the avatar anchors so they remain mouse/touch clickable but are skipped by keyboard focus.
90-127: cn usage and pointer-events behavior in scrolling rowsThe grid layout and duplication pattern for infinite scroll look good and should scale fine for ~100 avatars. Two small points:
- cn usage: Per the project guideline to “always pass an array” to
cn, it’d be more consistent to change:className={cn( "flex gap-1 pointer-events-auto", isEvenRow ? "animate-scroll-left" : "animate-scroll-right", )}to:
className={cn([ "flex gap-1 pointer-events-auto", isEvenRow ? "animate-scroll-left" : "animate-scroll-right", ])}
- Pointer-events layering: You’re using
pointer-events-noneon the outer grid andpointer-events-autoon the row. Combined with the hero content beingrelative z-10, most avatars will sit visually behind the text/buttons. This is fine if they’re mainly decorative, but it’s worth double‑checking in the browser that:
- Hero CTAs remain fully clickable.
- Avatars are only clickable where you actually want interaction (e.g., edges of the hero).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/src/queries.ts(1 hunks)apps/web/src/routes/_view/opensource.tsx(2 hunks)apps/web/src/styles.css(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/web/src/routes/_view/opensource.tsxapps/web/src/queries.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/web/src/queries.ts
🧬 Code graph analysis (1)
apps/web/src/routes/_view/opensource.tsx (2)
apps/web/src/queries.ts (2)
Stargazer(21-24)useGitHubStargazers(26-78)packages/utils/src/cn.ts (1)
cn(20-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: fmt
- GitHub Check: ci (macos, macos-14)
🔇 Additional comments (2)
apps/web/src/routes/_view/opensource.tsx (1)
129-135: Hook usage and conditional render look goodUsing
useGitHubStargazers(100)with a default[]and guarding the grid onstargazers.length > 0is a clean pattern: no loading spinner needed and failures degrade to the previous static hero.No issues from a React/query standpoint here.
apps/web/src/styles.css (1)
40-66: Scroll animations correctly support infinite horizontal gridThe
scroll-left/scroll-rightkeyframes plus.animate-scroll-*utilities line up well with the duplicated-row pattern in the grid and, combined with long durations, should be light on performance. No changes needed here.
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
…ection - Add useGitHubStargazers hook to fetch recent stargazers from GitHub API - Create StargazersGrid component with animated scrolling rows - Display stargazer avatars as a subtle background in the hero section - Add CSS keyframe animations for horizontal scrolling effect - Include proper error handling for API failures Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
- First fetch repo info to get total star count - Calculate last page to get most recent stargazers - Reverse the results so newest stargazers appear first Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Change /product/opensource to /opensource to match actual route Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Replace the carousel-style scrolling hero with a fixed grid of profile pictures that fade in and out. The grid is now 6 rows by 20 columns and each avatar is placed deterministically (wrapping the stargazers array) with a staggered animation delay so avatars appear and disappear. Added a fade-in-out keyframes animation and utility class in styles.css. This change simplifies the hero UI (no carousel behavior), ensures avatars are visible in a consistent layout, and provides a subtle animated presence without continuous scrolling.
Fit more people into the Stargazers grid by reducing individual avatar size and expanding the grid dimensions. The change lowers the avatar block from size-10 to size-8 and increases rows from 6 to 12 and cols from 20 to 32 so the 1150x530 area can display more contributors.
Increase the number of rows in the StargazersGrid from 12 to 16 to fill more vertical space inside the hero section. The change was needed because the current vertical padding in the hero left too much empty space; adding more rows better utilizes that space and provides a denser display of stargazers.
Increase the number of GitHub stargazers fetched from 100 to 500 to display a larger, less repetitive sample of contributors. This reduces repetition in the hero section and makes the people shown more representative. - Changed useGitHubStargazers default argument from 100 to 500 in HeroSection of opensource route.
Change the background overlay to use a radial gradient centered in the view so the central area (where text is shown) is solid white while the outer regions remain visible/transparent. This replaces the previous two orthogonal linear gradients with a single radial gradient to better focus a white center and clear outer edges, simplifying the markup and achieving the requested visual effect.
Adjust the radial gradient to create an oval-shaped whitened area centered in the element. The gradient parameters were changed from a default ellipse with tight white stop to a wider, offset oval (ellipse_60%_40%_at_center) and extended white/transparent stops to achieve a softer oval vignette effect rather than the previous small centered white spot.
Adjust gradient background so it is anchored to the header area rather than spanning the entire container. Remove an extra full-container radial gradient and add a positioned radial gradient inside the header wrapper (using pointer-events-none) and make the header positioned relative so the gradient aligns with the headline, description, and CTA buttons. This keeps the visual focus localized to the headline and description and prevents the gradient from affecting layout or interactions.
Make the download call-to-action use the same platform-aware icon and text as the view route. Introduce usePlatform in the DownloadButton, add getIcon/getLabel helpers, and replace hardcoded Apple icon/label with dynamic variants. Replace duplicated download buttons in the opensource view with the new DownloadButton and adjust surrounding buttons to preserve styling and functionality.
Switch the GitHub CTA to a dark background style and move the Download button to the right of the GitHub link. This improves visual contrast for the GitHub action and adjusts the icon/CTA order to the requested left-to-right arrangement. Changes: - Update GitHub link classes to use dark gradient, white text, and transform-based hover/active states. - Move <DownloadButton /> to appear after the GitHub link in the button group.
e997a68 to
12121ab
Compare
There was a problem hiding this 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
♻️ Duplicate comments (1)
apps/web/src/queries.ts (1)
26-77: Harden stargazer pagination: clamp per_page and handle zero-star edge casesThe overall flow (read repo → compute last page → fetch stargazers → reverse) is solid, but a couple of edge cases can bite here—this mirrors earlier feedback on this hook.
per_page > 100
GitHub capsper_pageat 100. Usingcountdirectly (e.g.,useGitHubStargazers(500)) to compute bothlastPageandper_pagecan produce an invalid combination and break the “most recent” guarantee. Clamp it and use the clamped value consistently:Zero (or missing) stargazer counts
WhentotalStarsis0,lastPagebecomes0and you’ll requestpage=0, which is not valid for GitHub’s pagination.A small refactor addresses both:
- const repoData = await repoResponse.json(); - const totalStars = repoData.stargazers_count ?? LAST_SEEN_STARS; - const lastPage = Math.ceil(totalStars / count); + const repoData = await repoResponse.json(); + const totalStars = repoData.stargazers_count ?? LAST_SEEN_STARS; + if (!totalStars) { + return []; + } + + const perPage = Math.min(Math.max(count, 1), 100); + const lastPage = Math.ceil(totalStars / perPage); @@ - const response = await fetch( - `https://api.github.com/repos/${ORG_REPO}/stargazers?per_page=${count}&page=${lastPage}`, + const response = await fetch( + `https://api.github.com/repos/${ORG_REPO}/stargazers?per_page=${perPage}&page=${lastPage}`,This keeps the behavior as “up to
perPagemost recent stargazers from the last page” while staying within GitHub’s documented limits and avoiding invalidpage=0requests.
🧹 Nitpick comments (1)
apps/web/src/routes/_view/opensource.tsx (1)
73-89: StargazersGrid density and defensive guard for empty dataNice job on the decorative grid and conditional render in
HeroSection; the opacity and radial overlay should keep text readable.A couple of points to tighten this up:
Grid density / perf:
rows = 16andcols = 32means 512 animated tiles. Combined with per-tileanimationDelayandanimationDuration, this can be heavy on low‑end or mobile devices and is notably higher than the “six rows” described in the PR summary. Consider:
- Reducing
rows(and/orcols) overall, or- Using fewer rows on small screens (e.g., via a prop or
useMediaQuery) to keep DOM size and animation cost closer to the ~100-avatar target.Empty array safety: Right now
StargazersGridassumesstargazers.length > 0(because of the guard inHeroSection) and doesindex % stargazers.length. It’s fine today, but fragile if the component is ever reused without that guard. Adding an early return inside the grid keeps it self-contained:function StargazersGrid({ stargazers }: { stargazers: Stargazer[] }) { - const rows = 16; - const cols = 32; + if (stargazers.length === 0) { + return null; + } + + const rows = 16; + const cols = 32;These tweaks should improve robustness and help with performance, especially on mobile.
Also applies to: 91-123, 125-132
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/src/components/download-button.tsx(2 hunks)apps/web/src/queries.ts(1 hunks)apps/web/src/routes/_view/opensource.tsx(4 hunks)apps/web/src/styles.css(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/src/styles.css
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/web/src/routes/_view/opensource.tsxapps/web/src/components/download-button.tsxapps/web/src/queries.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/web/src/queries.ts
🧬 Code graph analysis (2)
apps/web/src/routes/_view/opensource.tsx (2)
apps/web/src/queries.ts (2)
Stargazer(21-24)useGitHubStargazers(26-78)apps/web/src/components/download-button.tsx (1)
DownloadButton(7-51)
apps/web/src/components/download-button.tsx (1)
apps/web/src/hooks/use-platform.ts (1)
usePlatform(5-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: fmt
- GitHub Check: ci
🔇 Additional comments (1)
apps/web/src/routes/_view/opensource.tsx (1)
158-159: Consistent reuse of DownloadButton across sections looks goodSwapping the hard-coded “Download for free” CTAs for the shared
DownloadButtonin bothHeroSectionandCTASectionkeeps the download call-to-action consistent and reduces duplication. Once the underlying href/label mismatch inDownloadButtonis addressed, this wiring should not need further changes here.Also applies to: 516-516
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/web/src/routes/_view/download/linux.tsx (1)
3-10: Same issues as windows.tsx.This file has the same concerns already flagged in
apps/web/src/routes/_view/download/windows.tsx:
- Hardcoded
channel=nightlyin production- Vague TODO comment
- Unnecessary
asynckeyword- Code duplication between download routes
Please refer to the review comments on the Windows route file for detailed recommendations.
🧹 Nitpick comments (2)
apps/web/src/routes/_view/download/windows.tsx (2)
4-4: Remove unnecessaryasynckeyword.The
beforeLoadfunction is markedasyncbut doesn't useawait, making the keyword unnecessary.- beforeLoad: async () => { + beforeLoad: () => { throw redirect({
3-10: Consider abstracting the common redirect pattern.Both
windows.tsxandlinux.tsxfollow an identical pattern with only the download URL differing. Consider creating a shared helper function to reduce code duplication.Example approach—create a shared utility:
// apps/web/src/utils/download-routes.ts import { redirect } from "@tanstack/react-router"; export const createDownloadRedirect = (url: string) => ({ beforeLoad: () => { throw redirect({ href: url }); }, });Then simplify both route files:
import { createFileRoute } from "@tanstack/react-router"; import { createDownloadRedirect } from "@/utils/download-routes"; export const Route = createFileRoute("/_view/download/windows")({ ...createDownloadRedirect("https://desktop2.hyprnote.com/download/latest/msi?channel=nightly"), });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/web/src/routeTree.gen.tsis excluded by!**/*.gen.ts
📒 Files selected for processing (2)
apps/web/src/routes/_view/download/linux.tsx(1 hunks)apps/web/src/routes/_view/download/windows.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/web/src/routes/_view/download/windows.tsxapps/web/src/routes/_view/download/linux.tsx
🧬 Code graph analysis (2)
apps/web/src/routes/_view/download/windows.tsx (1)
apps/web/src/routes/_view/download/linux.tsx (1)
Route(3-10)
apps/web/src/routes/_view/download/linux.tsx (1)
apps/web/src/routes/_view/download/windows.tsx (1)
Route(3-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci
- GitHub Check: fmt
🔇 Additional comments (2)
apps/web/src/routes/_view/download/windows.tsx (2)
6-6: Clarify the TODO comment.The TODO comment "needs to be fixed" is vague. Please specify what needs to be fixed—whether it's the download URL, the channel parameter, the redirect approach, or something else.
3-10: This route is incomplete and serves a placeholder redirect.The download routes for Windows and Linux (
windows.tsxandlinux.tsx) are marked as unavailable in the UI (available={false}inindex.tsxlines 75-76, 81-82) and redirect to nightly builds. However, thechannel=nightlyparameter is not unique to production concern — all platform routes (including the available macOS variants) consistently usechannel=nightly.The TODO comment and the unavailable status indicate these routes are not ready for release. Before enabling these platforms, ensure:
- The TODO is addressed and the route implementation is complete
- Clarify the intended channel strategy: should this redirect to stable, or should the channel be configurable per deployment environment?
feat(web): add stargazers avatar grid background to opensource hero
Summary
Adds an animated grid of GitHub stargazer avatars as a background to the hero section on the opensource page. The avatars scroll horizontally in alternating directions, creating a subtle visual effect that showcases community engagement.
Changes:
useGitHubStargazershook to fetch the most recent stargazers from GitHub API with error handlingStargazersGridcomponent displaying avatars in 6 animated rows/product/opensource→/opensource)Updates since last revision
Review & Testing Checklist for Human
Recommended test plan:
Notes
Link to Devin run: https://app.devin.ai/sessions/e453ca73e4dc474d82ff0dcb76d8bd52
Requested by: john@hyprnote.com (@ComputelessComputer)