Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughWhitespace normalization across many files, updated site branding text/metadata, refactored Features icon handling to use string-to-component mapping, added React cache for DB collection retrieval, several UI/behavior tweaks (Navbar debounced scroll + CSS hover, PageTransition mode, LazySection memo removal, intersection observer stabilization), and narrowed lint-staged globs. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
2 issues found across 36 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/api/contact/route.ts">
<violation number="1" location="app/api/contact/route.ts:101">
P1: Rate-limit timestamp is persisted even when the Discord webhook fails, which can block legitimate retries after unsuccessful submissions.</violation>
</file>
<file name="components/Navbar.tsx">
<violation number="1" location="components/Navbar.tsx:162">
P2: The scroll listener callback is recreated every render, causing unnecessary listener re-subscription churn.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
components/Navbar.tsx (1)
15-32: Keep the debounced scroll handler stable and dispose pending timers.Because Line 162 passes a fresh inline callback into
useDebouncedCallback(),handleScrollchanges every render and Line 166 tears down/re-adds the window listener each time. The hook also leaves the last timeout alive after unmount. Storing the latest callback in a ref makes this optimization actually reduce work.♻️ Proposed fix
function useDebouncedCallback<T extends (...args: unknown[]) => unknown>( callback: T, delay: number, ): (...args: Parameters<T>) => void { - const timeoutRef = useRef<NodeJS.Timeout | null>(null); + const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); + const callbackRef = useRef(callback); + + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); return useCallback( (...args: Parameters<T>) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } timeoutRef.current = setTimeout(() => { - callback(...args); + callbackRef.current(...args); + timeoutRef.current = null; }, delay); }, - [callback, delay], + [delay], ); }Also applies to: 162-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Navbar.tsx` around lines 15 - 32, The debounced handler should be stable and cancel pending timers on unmount: inside useDebouncedCallback keep the latest callback in a ref (e.g., latestCallbackRef.current = callback inside a useEffect) and call that ref from the setTimeout instead of closing over the changing callback so the returned function (from useCallback) can be memoized without depending on callback; also add a cleanup effect that clears timeoutRef.current on unmount to dispose any pending timer; adjust the useCallback dependency list to only include delay so handleScroll (the caller) stays stable across renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/contact/route.ts`:
- Around line 92-106: The code currently runs fetch(webhookUrl, ...) and
collection.updateOne(...) in parallel which can mark the sender as rate-limited
even if delivery to Discord fails or return a 500 if the DB update fails after a
successful send; instead, call fetch(webhookUrl, { ...discordMessage }) first
and await it, verify response.ok (or throw/return an error if not), and only
after a successful delivery perform collection.updateOne({ ip }, { $set: {
timestamp: now } }, { upsert: true }); if the DB update fails after a successful
fetch, log the error (do not return a failure to the client) or retry
asynchronously so the client sees success but the rate slot is only consumed on
confirmed delivery; refer to webhookUrl, discordMessage, fetch(...) and
collection.updateOne(...) to find and change the logic.
In `@lib/db.ts`:
- Around line 23-28: The comment and use of React.cache around
getCollectionCached is misleading for Route Handlers; remove the React.cache
wrapper from getCollectionCached (or replace it with a plain async function) and
update the comment to state that memoization via React.cache only applies to
Server Components, not Route Handlers, since __dbConnectionPromise already
coalesces global concurrent connections; locate getCollectionCached and
getCollection in this file and remove the cache(...) wrapper and/or adjust the
comment to reference React.cache and __dbConnectionPromise accordingly.
---
Nitpick comments:
In `@components/Navbar.tsx`:
- Around line 15-32: The debounced handler should be stable and cancel pending
timers on unmount: inside useDebouncedCallback keep the latest callback in a ref
(e.g., latestCallbackRef.current = callback inside a useEffect) and call that
ref from the setTimeout instead of closing over the changing callback so the
returned function (from useCallback) can be memoized without depending on
callback; also add a cleanup effect that clears timeoutRef.current on unmount to
dispose any pending timer; adjust the useCallback dependency list to only
include delay so handleScroll (the caller) stays stable across renders.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: adee6458-20e8-4d13-8e42-87cb8013a94d
📒 Files selected for processing (36)
app/about/layout.tsxapp/about/page.tsxapp/api/contact/route.tsapp/apple-icon.tsxapp/contact/layout.tsxapp/contact/page.tsxapp/icon.tsxapp/layout.tsxapp/open-source/layout.tsxapp/open-source/page.tsxapp/opengraph-image.tsxapp/page.tsxapp/privacy/page.tsxapp/robots.tsapp/services/hosting/page.tsxapp/services/layout.tsxapp/services/page.tsxapp/services/seo/page.tsxapp/services/seo/seo-content.tsxapp/services/web-design/page.tsxapp/services/web-development/page.tsxapp/services/web-development/web-development-content.tsxapp/sitemap.tsapp/terms/page.tsxapp/twitter-image.tsxcomponents/About.tsxcomponents/Contact.tsxcomponents/Features.tsxcomponents/Footer.tsxcomponents/Hero.tsxcomponents/LazySection.tsxcomponents/Navbar.tsxcomponents/PageTransition.tsxhooks/use-intersection-observer.tsxlib/db.tspackage.json
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
components/Navbar.tsx (1)
72-86: Respect reduced-motion for the new hover effects.The glitch animation now runs on every hover, including for users who request reduced motion. Please gate the animation and transition classes with
motion-safe/motion-reduce.💡 Suggested fix
- className="relative inline-block group-hover:animate-[glitch_0.4s_ease_forwards]" + className="relative inline-block motion-safe:group-hover:animate-[glitch_0.4s_ease_forwards]" ... - className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100" + className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm motion-safe:transition-opacity motion-safe:duration-300 group-hover:opacity-100"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Navbar.tsx` around lines 72 - 86, The hover/glitch animation and transitions in Navbar (the span with className "relative inline-block group-hover:animate-[glitch_0.4s_ease_forwards]" and the spans using "transition-[width] duration-300 ease-out" and "transition-opacity duration-300 group-hover:opacity-100") need to respect reduced-motion: wrap hover/animation/transition classes with motion-safe (e.g. change group-hover:animate-... to motion-safe:group-hover:animate-...) and add motion-reduce fallbacks to disable motion (e.g. motion-reduce:animate-none and motion-reduce:transition-none or motion-reduce:opacity-100 as appropriate) so users who request reduced motion get no animations/transitions. Ensure you update all related spans (the per-char span, the underline span, and the glow span) to include these prefixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/Navbar.tsx`:
- Around line 15-32: The debounced hook useDebouncedCallback currently leaves a
pending timer after unmount; add a cleanup effect inside useDebouncedCallback
that on unmount clears timeoutRef.current (clearTimeout) and sets it to null so
no delayed callback runs after the component unmounts; reference timeoutRef in
the cleanup (useEffect(() => () => { if (timeoutRef.current) {
clearTimeout(timeoutRef.current); timeoutRef.current = null } }, [])) so the
timer is cancelled when useDebouncedCallback's consumer unmounts.
- Around line 170-174: The navbar's scroll state (isScrolled) must be set
synchronously because handleScroll is debounced; compute and set the initial
isScrolled value immediately (e.g., from window.scrollY or
document.documentElement.scrollTop compared to your threshold) before
registering the debounced listener in useEffect, then attach the existing
debounced handleScroll for subsequent events and remove it on cleanup; update
references to useEffect, handleScroll, isScrolled, and setIsScrolled to reflect
this two-step init + subscribe approach.
---
Nitpick comments:
In `@components/Navbar.tsx`:
- Around line 72-86: The hover/glitch animation and transitions in Navbar (the
span with className "relative inline-block
group-hover:animate-[glitch_0.4s_ease_forwards]" and the spans using
"transition-[width] duration-300 ease-out" and "transition-opacity duration-300
group-hover:opacity-100") need to respect reduced-motion: wrap
hover/animation/transition classes with motion-safe (e.g. change
group-hover:animate-... to motion-safe:group-hover:animate-...) and add
motion-reduce fallbacks to disable motion (e.g. motion-reduce:animate-none and
motion-reduce:transition-none or motion-reduce:opacity-100 as appropriate) so
users who request reduced motion get no animations/transitions. Ensure you
update all related spans (the per-char span, the underline span, and the glow
span) to include these prefixes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1de11a48-eb4d-4a15-98be-c50852a5418b
📒 Files selected for processing (2)
app/api/contact/route.tscomponents/Navbar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- app/api/contact/route.ts
There was a problem hiding this comment.
3 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/Navbar.tsx">
<violation number="1" location="components/Navbar.tsx:91">
P2: `motion-reduce:w-full` makes inactive links always look active for reduced-motion users. Remove the forced full width and only disable the transition.</violation>
<violation number="2" location="components/Navbar.tsx:96">
P3: `motion-reduce:opacity-100` makes the hover glow permanently visible in reduced-motion mode. Keep hover behavior and only remove transition/animation.</violation>
<violation number="3" location="components/Navbar.tsx:160">
P2: Reading `window.scrollY` in the state initializer can desync initial client/server render state for `data-nav-state`. Initialize to a stable value and sync after mount.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| className={`pointer-events-none absolute -bottom-1 left-0 h-0.5 bg-coral transition-[width] duration-300 ease-out ${ | ||
| isActive ? "w-full" : "w-0 group-hover:w-full" | ||
| className={`pointer-events-none absolute -bottom-1 left-0 h-0.5 bg-coral transition-[width] duration-300 ease-out motion-reduce:transition-none ${ | ||
| isActive ? "w-full" : "w-0 group-hover:w-full motion-reduce:w-full" |
There was a problem hiding this comment.
P2: motion-reduce:w-full makes inactive links always look active for reduced-motion users. Remove the forced full width and only disable the transition.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Navbar.tsx, line 91:
<comment>`motion-reduce:w-full` makes inactive links always look active for reduced-motion users. Remove the forced full width and only disable the transition.</comment>
<file context>
@@ -69,21 +79,21 @@ const NavLink = memo(function NavLink({
- className={`pointer-events-none absolute -bottom-1 left-0 h-0.5 bg-coral transition-[width] duration-300 ease-out ${
- isActive ? "w-full" : "w-0 group-hover:w-full"
+ className={`pointer-events-none absolute -bottom-1 left-0 h-0.5 bg-coral transition-[width] duration-300 ease-out motion-reduce:transition-none ${
+ isActive ? "w-full" : "w-0 group-hover:w-full motion-reduce:w-full"
}`}
/>
</file context>
| isActive ? "w-full" : "w-0 group-hover:w-full motion-reduce:w-full" | |
| isActive ? "w-full" : "w-0 group-hover:w-full" |
| const pathname = usePathname(); | ||
| const [isScrolled, setIsScrolled] = useState(false); | ||
| // Initialize scroll state from window to avoid setState in effect | ||
| const [isScrolled, setIsScrolled] = useState(() => { |
There was a problem hiding this comment.
P2: Reading window.scrollY in the state initializer can desync initial client/server render state for data-nav-state. Initialize to a stable value and sync after mount.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Navbar.tsx, line 160:
<comment>Reading `window.scrollY` in the state initializer can desync initial client/server render state for `data-nav-state`. Initialize to a stable value and sync after mount.</comment>
<file context>
@@ -146,7 +156,11 @@ GlitchLogo.displayName = "GlitchLogo";
const pathname = usePathname();
- const [isScrolled, setIsScrolled] = useState(false);
+ // Initialize scroll state from window to avoid setState in effect
+ const [isScrolled, setIsScrolled] = useState(() => {
+ if (typeof window === "undefined") return false;
+ return window.scrollY > 10;
</file context>
| className={`pointer-events-none absolute inset-0 -z-10 blur-sm transition-opacity duration-300 ${ | ||
| isHovered ? "opacity-100" : "opacity-0" | ||
| }`} | ||
| className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100 motion-reduce:opacity-100 motion-reduce:transition-none" |
There was a problem hiding this comment.
P3: motion-reduce:opacity-100 makes the hover glow permanently visible in reduced-motion mode. Keep hover behavior and only remove transition/animation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Navbar.tsx, line 96:
<comment>`motion-reduce:opacity-100` makes the hover glow permanently visible in reduced-motion mode. Keep hover behavior and only remove transition/animation.</comment>
<file context>
@@ -69,21 +79,21 @@ const NavLink = memo(function NavLink({
{/* Glow - CSS-only hover */}
<span
- className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100"
+ className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100 motion-reduce:opacity-100 motion-reduce:transition-none"
style={{
background:
</file context>
| className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100 motion-reduce:opacity-100 motion-reduce:transition-none" | |
| className="pointer-events-none absolute inset-0 -z-10 opacity-0 blur-sm transition-opacity duration-300 group-hover:opacity-100 motion-reduce:transition-none" |
Summary by cubic
Improved UI performance and scroll behavior by reducing re-renders, initializing navbar scroll state correctly, and debouncing the scroll handler. Also fixed contact form rate limiting to only apply after successful webhook delivery, and refreshed site copy and metadata.
Performance & Fixes
Copy Updates
Written for commit 95564d5. Summary will update on new commits.
Summary by CodeRabbit
Updates
Chores