feat: Landing Page V2 - Complete Redesign#215
Conversation
- accordion: FAQ section support - dialog: modal/waitlist popup - navigation-menu: navbar dropdowns - separator: visual dividers - sheet: mobile menu drawer - switch: theme toggle - tabs: tabbed content - tooltip: hover hints - update landing barrel exports
- ThemeProvider using next-themes - wrap App with ThemeProvider + TooltipProvider - light mode CSS variables for backgrounds, text, accents - accordion animations in tailwind config
- switch to bun for faster installs (0.6s vs 23s) - downgrade vite 7.3 -> 6.4.1 (fixes config bug) - update .gitignore for bun and pnpm artifacts - remove package-lock.json, add bun.lock
- add GitHub stars badge with live API count + caching - add ThemeToggle component (sun/moon icon) - add MobileMenu with slide-out drawer (Sheet) - add nav links: Features, Pricing, Docs - sticky navbar with blur on scroll - responsive desktop/mobile layouts - useGitHubStars hook with 5min cache
- new headline: 'Stop feeling lost in unfamiliar codebases' - add 'Now in beta' badge with sparkle icon - auto-typing animation cycles through demo queries - auto-triggers search to show live results - dual CTA: primary 'Index your first repo' + secondary 'View on GitHub' - improved subheadline copy - light/dark mode color fixes
- semantic search, context understanding, instant results - dependency graph, MCP integration, self-host option - animated grid with staggered reveal on scroll - hover effects with subtle glow - fix landing page to use theme bg/text colors
- Free: 3 repos, public only, community support - Pro $19/mo: unlimited repos, private, API, MCP (waitlist) - Enterprise: self-host, SSO, dedicated support - highlighted Pro tier with badge - animated cards with stagger reveal
- FAQ: 5 common questions with accordion - Footer: product/company/legal links, social icons - wired into landing page flow - minor Pricing cleanup
- install Lenis for butter-smooth momentum scrolling - hero: scroll-linked parallax on gradient orbs - hero: staggered text reveal with blur effect - hero: grid pattern background - hero: search box glow pulse - features: spring-based hover lift + colored shadows - features: icon scale+rotate on hover - pricing: enhanced card lift animations - all: better spring physics throughout - reduced whitespace for tighter flow
- removed @studio-freight/lenis (caused janky scroll) - removed scroll-linked parallax (too complex) - keep floating orbs with simple CSS animations - add CSS scroll-behavior: smooth - simplify headline stagger animation (remove blur) - native scroll feels better
- replace hardcoded white/[0.06] with border-border/50 - feature cards, FAQ items, pricing cards now visible in light mode - footer borders use theme tokens
|
@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds a set of new landing UI components and Radix-based UI primitives, introduces a ThemeProvider and TooltipProvider, adds a GitHub-stars hook, expands theming and CSS (light mode, smooth scrolling), updates frontend dependencies and .gitignore, and switches frontend CI steps to Bun. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
- removed em-dash from hero subtitle - completely rewrote all 5 FAQ answers - answers now explain concepts in storytelling format - mentions open source nature and MIT license - no more AI-generated vibes
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @DevanshuNEU. * #215 (comment) The following files were modified: * `frontend/src/App.tsx` * `frontend/src/components/landing/FAQ.tsx` * `frontend/src/components/landing/Features.tsx` * `frontend/src/components/landing/Footer.tsx` * `frontend/src/components/landing/GitHubStars.tsx` * `frontend/src/components/landing/Hero.tsx` * `frontend/src/components/landing/MobileMenu.tsx` * `frontend/src/components/landing/Navbar.tsx` * `frontend/src/components/landing/Pricing.tsx` * `frontend/src/components/landing/ThemeToggle.tsx` * `frontend/src/components/providers/ThemeProvider.tsx` * `frontend/src/hooks/useGitHubStars.ts` * `frontend/src/pages/LandingPage.tsx`
- replace setup-node with oven-sh/setup-bun - replace npm ci with bun install - replace npm run with bun run - fixes CI failure due to missing package-lock.json
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@frontend/src/components/landing/Footer.tsx`:
- Around line 105-107: The external author credit anchor in the Footer component
currently lacks target and rel attributes; update the <a> element (the author
link in Footer.tsx) to open in a new tab by adding target="_blank" and to be
secure by adding rel="noopener noreferrer" so it matches other external links in
the footer.
- Around line 38-54: The social icon anchor elements wrapping the Github and
Twitter components lack accessible labels; add aria-label attributes to each
anchor (the <a> elements that contain the Github and Twitter components) such as
aria-label="OpenCodeIntel on GitHub" and aria-label="OpenCodeIntel on Twitter"
so screen readers can announce the link destinations; update the anchor elements
near the Github and Twitter components in Footer.tsx to include these
aria-labels while keeping existing target/rel/className attributes.
In `@frontend/src/components/landing/Hero.tsx`:
- Around line 45-74: The typing effect currently includes the search function in
its dependency array causing the effect to restart on every query change; fix it
by creating a stable ref for the search handler (e.g., searchRef) and update
that ref whenever the real search changes, then remove search from the useEffect
deps for the typing animation; inside the typing effect call
searchRef.current(query) (or pass the final currentQuery to the ref call)
instead of calling the captured search closure, and keep the dependency array to
[isTyping, typingIndex, setQuery] so the animation no longer retriggers per
character.
- Around line 49-51: The useEffect in Hero.tsx currently calls onResultsReady
whenever results populate, which lets the auto-typing demo trigger navigation;
add a search source tracker (e.g., const lastSearchSource =
useRef<'demo'|'user'|null>(null)) and set lastSearchSource.current = 'demo'
immediately before the demo calls search() and set lastSearchSource.current =
'user' in the user submit handler (or anywhere user-initiated search is
invoked), then guard the effect that calls onResultsReady (the useEffect
referencing results, query, repo.id, searchTime, onResultsReady) to only invoke
onResultsReady when lastSearchSource.current !== 'demo' (and optionally reset
lastSearchSource.current after handling).
In `@frontend/src/components/landing/MobileMenu.tsx`:
- Line 24: The SheetContent in MobileMenu (component MobileMenu / JSX element
SheetContent) uses hardcoded white-based classes like "border-white/10" and
hover/bg classes that don't adapt to light mode; update these className usages
(on the SheetContent element and the internal divider and hoverable items in
MobileMenu) to use theme-aware variants similar to the Navbar pattern (e.g.,
replace fixed "border-white/10" and "hover:bg-white/5" with dual classes such as
"dark:border-white/10 light:border-black/10" and "dark:hover:bg-white/5
light:hover:bg-black/5" or equivalent Tailwind theme-aware utilities) so borders
and hover backgrounds adapt to dark and light themes.
In `@frontend/src/components/landing/Pricing.tsx`:
- Line 33: The ctaHref in Pricing.tsx currently uses a personal email address
(devanshurajesh@gmail.com); update the value of ctaHref to use a
product-specific or company email such as enterprise@codeintel.dev or
sales@codeintel.dev so production contact goes to an official inbox (locate the
ctaHref assignment in the Pricing component and replace the mailto address
accordingly).
In `@frontend/src/components/landing/ThemeToggle.tsx`:
- Around line 16-22: The button's className in ThemeToggle uses a non-existent
Tailwind variant "light:hover:bg-black/5"; remove the "light:" prefix and apply
the default hover class (hover:bg-black/5) for the light mode while keeping the
dark-mode override (dark:hover:bg-white/5). Locate the button in ThemeToggle.tsx
(the element using setTheme and isDark) and replace the "light:hover:bg-black/5"
token with "hover:bg-black/5" so the hover styles work correctly in the light
theme while preserving dark:hover:bg-white/5.
In `@frontend/src/components/ui/sheet.tsx`:
- Around line 67-70: The SheetPrimitive.Close button may render as a plain
<button> and inadvertently submit surrounding forms; update the
SheetPrimitive.Close usage in sheet.tsx (the Close element) to explicitly set
type="button" (or pass the appropriate prop to ensure a non-submit button) so
clicking it won’t trigger form submission, and verify the Close component still
renders the X icon and sr-only span unchanged.
In `@frontend/src/hooks/useGitHubStars.ts`:
- Around line 19-27: The component assumes localStorage JSON is valid but
JSON.parse can throw on corrupted data in useGitHubStars; wrap the parse in a
try-catch around the retrieval of CACHE_KEY and handle parse failures by
removing the bad cache (localStorage.removeItem(CACHE_KEY)), avoid setting stars
from invalid data, and continue with the normal fetch path (ensure setLoading
remains true until a successful fetch or cached data is used); update the code
surrounding CACHE_KEY/CacheData/JSON.parse to perform defensive parsing and
graceful fallback.
🧹 Nitpick comments (10)
frontend/src/components/ui/navigation-menu.tsx (1)
45-65: Trim redundantgroupclass on the trigger.
navigationMenuTriggerStylealready includesgroup, so adding it again is redundant.♻️ Proposed cleanup
- className={cn(navigationMenuTriggerStyle(), "group", className)} + className={cn(navigationMenuTriggerStyle(), className)}frontend/src/components/providers/ThemeProvider.tsx (1)
1-8:"use client"directive is unnecessary in a Vite-only React app.The
"use client"directive is a Next.js-specific feature for the App Router. In a pure Vite/React application, this directive has no effect. It's harmless but adds noise.Consider removing it unless there are plans to migrate to Next.js, or keep it for potential future compatibility.
frontend/src/hooks/useGitHubStars.ts (1)
29-49: Consider adding an AbortController for cleanup on unmount.If the component unmounts while the fetch is in-flight,
setStarsandsetLoadingwill be called on an unmounted component. This can cause React warnings and potential memory leaks.Proposed fix with abort signal
useEffect(() => { + const controller = new AbortController() + const fetchStars = async () => { // ... cache check ... try { - const res = await fetch(`https://api.github.com/repos/${REPO}`) + const res = await fetch(`https://api.github.com/repos/${REPO}`, { + signal: controller.signal + }) // ... rest of logic ... } catch { + if (controller.signal.aborted) return // fallback to cached even if expired // ... } finally { setLoading(false) } } fetchStars() + return () => controller.abort() }, [])frontend/src/index.css (1)
13-16: Duplicatescroll-behavior: smoothdeclaration.This property is declared here (line 15) and again inside
@layer base html(line 198). Consider removing one to avoid redundancy.Proposed fix
Remove lines 13-16 since the same rule exists in
@layer base:-/* Smooth scroll */ -html { - scroll-behavior: smooth; -} - `@layer` base {frontend/src/components/landing/MobileMenu.tsx (3)
1-1: Remove unusedXimport.The
Xicon is imported but never used in this component.🔧 Proposed fix
-import { Menu, X } from 'lucide-react' +import { Menu } from 'lucide-react'
10-14: DuplicateNAV_LINKSdefinition—consider extracting to a shared constant.This same
NAV_LINKSarray is defined inNavbar.tsx(lines 11-15). Extracting it to a shared location (e.g.,constants.ts) would improve maintainability and ensure consistency between desktop and mobile navigation.
20-22: Addaria-labelto the mobile menu trigger button for accessibility.Screen readers need context for icon-only buttons.
♿ Proposed fix
- <button className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-white/5 transition-colors md:hidden"> + <button + className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-white/5 transition-colors md:hidden" + aria-label="Open navigation menu" + >frontend/src/components/landing/Features.tsx (1)
104-104: Remove unusedindexvariable.The
indexparameter in the map callback is declared but never used.🔧 Proposed fix
- {FEATURES.map((feature, index) => ( + {FEATURES.map((feature) => (frontend/src/components/landing/Navbar.tsx (1)
11-15: NAV_LINKS duplication with MobileMenu.tsx.As noted in the MobileMenu review, this constant is duplicated. Consider extracting to a shared module.
frontend/src/components/landing/Pricing.tsx (1)
109-120: Feature list item animations may re-trigger on scroll.Each feature
<motion.li>has its ownwhileInViewanimation. Since these are inside a card that's already animating, andviewport={{ once: true }}is set, they should only animate once. However, the staggered delay based on index (delay: 0.1 * i) may not sync well with the parent card's entrance animation.Consider whether these per-item animations add value or if they could be simplified by inheriting the parent's staggered animation pattern.
| <a | ||
| href="https://github.com/OpenCodeIntel/opencodeintel" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="text-muted-foreground hover:text-foreground transition-colors" | ||
| > | ||
| <Github className="w-5 h-5" /> | ||
| </a> | ||
| <a | ||
| href="https://twitter.com/codeintel" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="text-muted-foreground hover:text-foreground transition-colors" | ||
| > | ||
| <Twitter className="w-5 h-5" /> | ||
| </a> | ||
| </div> |
There was a problem hiding this comment.
Add accessible labels to social icon links.
The social links contain only icons without accessible text. Screen reader users won't know what these links are for. Add aria-label attributes to describe the link destinations.
Suggested fix
<div className="flex gap-4">
<a
href="https://github.com/OpenCodeIntel/opencodeintel"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground transition-colors"
+ aria-label="GitHub repository"
>
<Github className="w-5 h-5" />
</a>
<a
href="https://twitter.com/codeintel"
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground transition-colors"
+ aria-label="Twitter profile"
>
<Twitter className="w-5 h-5" />
</a>
</div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a | |
| href="https://github.com/OpenCodeIntel/opencodeintel" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-muted-foreground hover:text-foreground transition-colors" | |
| > | |
| <Github className="w-5 h-5" /> | |
| </a> | |
| <a | |
| href="https://twitter.com/codeintel" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-muted-foreground hover:text-foreground transition-colors" | |
| > | |
| <Twitter className="w-5 h-5" /> | |
| </a> | |
| </div> | |
| <a | |
| href="https://github.com/OpenCodeIntel/opencodeintel" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-muted-foreground hover:text-foreground transition-colors" | |
| aria-label="GitHub repository" | |
| > | |
| <Github className="w-5 h-5" /> | |
| </a> | |
| <a | |
| href="https://twitter.com/codeintel" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-muted-foreground hover:text-foreground transition-colors" | |
| aria-label="Twitter profile" | |
| > | |
| <Twitter className="w-5 h-5" /> | |
| </a> | |
| </div> |
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/Footer.tsx` around lines 38 - 54, The social
icon anchor elements wrapping the Github and Twitter components lack accessible
labels; add aria-label attributes to each anchor (the <a> elements that contain
the Github and Twitter components) such as aria-label="OpenCodeIntel on GitHub"
and aria-label="OpenCodeIntel on Twitter" so screen readers can announce the
link destinations; update the anchor elements near the Github and Twitter
components in Footer.tsx to include these aria-labels while keeping existing
target/rel/className attributes.
| <p className="text-sm text-muted-foreground/60"> | ||
| Built with ❤️ by <a href="https://github.com/DevanshuNEU" className="hover:text-foreground transition-colors">Devanshu</a> | ||
| </p> |
There was a problem hiding this comment.
External link missing target and rel attributes.
The author credit link opens to GitHub but lacks target="_blank" and rel="noopener noreferrer" for consistency with other external links in the footer.
Suggested fix
<p className="text-sm text-muted-foreground/60">
- Built with ❤️ by <a href="https://github.com/DevanshuNEU" className="hover:text-foreground transition-colors">Devanshu</a>
+ Built with ❤️ by <a href="https://github.com/DevanshuNEU" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">Devanshu</a>
</p>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <p className="text-sm text-muted-foreground/60"> | |
| Built with ❤️ by <a href="https://github.com/DevanshuNEU" className="hover:text-foreground transition-colors">Devanshu</a> | |
| </p> | |
| <p className="text-sm text-muted-foreground/60"> | |
| Built with ❤️ by <a href="https://github.com/DevanshuNEU" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">Devanshu</a> | |
| </p> |
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/Footer.tsx` around lines 105 - 107, The
external author credit anchor in the Footer component currently lacks target and
rel attributes; update the <a> element (the author link in Footer.tsx) to open
in a new tab by adding target="_blank" and to be secure by adding rel="noopener
noreferrer" so it matches other external links in the footer.
| const [isTyping, setIsTyping] = useState(true) | ||
| const [typingIndex, setTypingIndex] = useState(0) | ||
| const typingTimeoutRef = useRef<ReturnType<typeof setTimeout>>() | ||
|
|
||
| useEffect(() => { | ||
| if (results.length) onResultsReady?.(results, query, repo.id, searchTime) | ||
| }, [results, query, repo.id, searchTime, onResultsReady]) | ||
|
|
||
| // Typing animation | ||
| useEffect(() => { | ||
| if (!isTyping) return | ||
| const currentQuery = TYPING_QUERIES[typingIndex] | ||
| let charIndex = 0 | ||
| const typeChar = () => { | ||
| if (charIndex <= currentQuery.length) { | ||
| setQuery(currentQuery.slice(0, charIndex)) | ||
| charIndex++ | ||
| typingTimeoutRef.current = setTimeout(typeChar, 60 + Math.random() * 40) | ||
| } else { | ||
| typingTimeoutRef.current = setTimeout(() => { | ||
| search() | ||
| typingTimeoutRef.current = setTimeout(() => { | ||
| setTypingIndex((i) => (i + 1) % TYPING_QUERIES.length) | ||
| }, 4000) | ||
| }, 500) | ||
| } | ||
| } | ||
| typingTimeoutRef.current = setTimeout(typeChar, 1500) | ||
| return () => { if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current) } | ||
| }, [isTyping, typingIndex, setQuery, search]) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find frontend -type f -name "Hero.tsx" | head -5Repository: OpenCodeIntel/opencodeintel
Length of output: 111
🏁 Script executed:
wc -l frontend/src/components/landing/Hero.tsxRepository: OpenCodeIntel/opencodeintel
Length of output: 115
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | sed -n '1,100p'Repository: OpenCodeIntel/opencodeintel
Length of output: 4141
🏁 Script executed:
find frontend -type f -name "*useDemoSearch*" | head -5Repository: OpenCodeIntel/opencodeintel
Length of output: 106
🏁 Script executed:
cat -n frontend/src/hooks/useDemoSearch.tsRepository: OpenCodeIntel/opencodeintel
Length of output: 3489
Remove search from the typing effect dependency array to prevent re-triggering on each character.
The typing effect resets every character because search is recreated whenever query changes in the hook's state, and since search is in the dependency array, it triggers a re-run. Use a stable reference via searchRef and pass the query explicitly to search() instead of relying on the closure.
Suggested fix
const [isTyping, setIsTyping] = useState(true)
const [typingIndex, setTypingIndex] = useState(0)
const typingTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
+ const searchRef = useRef(search)
+
+ useEffect(() => {
+ searchRef.current = search
+ }, [search])
// ... existing code ...
// Typing animation
useEffect(() => {
if (!isTyping) return
const currentQuery = TYPING_QUERIES[typingIndex]
let charIndex = 0
const typeChar = () => {
if (charIndex <= currentQuery.length) {
setQuery(currentQuery.slice(0, charIndex))
charIndex++
typingTimeoutRef.current = setTimeout(typeChar, 60 + Math.random() * 40)
} else {
typingTimeoutRef.current = setTimeout(() => {
- search()
+ searchRef.current(currentQuery, repo.id)
typingTimeoutRef.current = setTimeout(() => {
setTypingIndex((i) => (i + 1) % TYPING_QUERIES.length)
}, 4000)
}, 500)
}
}
typingTimeoutRef.current = setTimeout(typeChar, 1500)
return () => { if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current) }
- }, [isTyping, typingIndex, setQuery, search])
+ }, [isTyping, typingIndex, setQuery, repo.id])🤖 Prompt for AI Agents
In `@frontend/src/components/landing/Hero.tsx` around lines 45 - 74, The typing
effect currently includes the search function in its dependency array causing
the effect to restart on every query change; fix it by creating a stable ref for
the search handler (e.g., searchRef) and update that ref whenever the real
search changes, then remove search from the useEffect deps for the typing
animation; inside the typing effect call searchRef.current(query) (or pass the
final currentQuery to the ref call) instead of calling the captured search
closure, and keep the dependency array to [isTyping, typingIndex, setQuery] so
the animation no longer retriggers per character.
| useEffect(() => { | ||
| if (results.length) onResultsReady?.(results, query, repo.id, searchTime) | ||
| }, [results, query, repo.id, searchTime, onResultsReady]) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "*.tsx" -o -name "*.ts" | xargs grep -l "Hero" | grep -E "(landing|hero)" | head -10Repository: OpenCodeIntel/opencodeintel
Length of output: 205
🏁 Script executed:
git ls-files | grep "landing"Repository: OpenCodeIntel/opencodeintel
Length of output: 803
🏁 Script executed:
find . -path "*/frontend/src/components/landing/*" -name "*.tsx" -o -name "*.ts" | head -20Repository: OpenCodeIntel/opencodeintel
Length of output: 1008
🏁 Script executed:
ls -la frontend/src/components/landing/ 2>/dev/null || echo "Directory not found"Repository: OpenCodeIntel/opencodeintel
Length of output: 1223
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | head -80Repository: OpenCodeIntel/opencodeintel
Length of output: 3378
🏁 Script executed:
wc -l frontend/src/components/landing/Hero.tsxRepository: OpenCodeIntel/opencodeintel
Length of output: 115
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | sed -n '40,150p'Repository: OpenCodeIntel/opencodeintel
Length of output: 5815
🏁 Script executed:
rg -l "onResultsReady" frontend/src/components/landing/Repository: OpenCodeIntel/opencodeintel
Length of output: 111
🏁 Script executed:
rg "onResultsReady" frontend/src/ -A 3 -B 3Repository: OpenCodeIntel/opencodeintel
Length of output: 2157
🏁 Script executed:
rg "Hero" frontend/src/components/landing/ --include="*.tsx" | grep -v "HeroSearch"Repository: OpenCodeIntel/opencodeintel
Length of output: 512
🏁 Script executed:
find frontend/src -name "*Landing*" -o -name "*landing*" | grep -v node_modulesRepository: OpenCodeIntel/opencodeintel
Length of output: 137
🏁 Script executed:
cat frontend/src/pages/LandingPage.tsxRepository: OpenCodeIntel/opencodeintel
Length of output: 4129
🏁 Script executed:
rg "useDemoSearch" frontend/src/ -A 20 -B 2Repository: OpenCodeIntel/opencodeintel
Length of output: 7143
🏁 Script executed:
grep -n "function useDemoSearch\|export.*useDemoSearch" frontend/src/hooks/*.ts frontend/src/hooks/*.tsxRepository: OpenCodeIntel/opencodeintel
Length of output: 234
🏁 Script executed:
cat frontend/src/hooks/useDemoSearch.tsRepository: OpenCodeIntel/opencodeintel
Length of output: 2845
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | sed -n '75,150p'Repository: OpenCodeIntel/opencodeintel
Length of output: 4105
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | sed -n '200,337p'Repository: OpenCodeIntel/opencodeintel
Length of output: 9303
🏁 Script executed:
cat -n frontend/src/components/landing/HeroSearch.tsxRepository: OpenCodeIntel/opencodeintel
Length of output: 7572
🏁 Script executed:
cat -n frontend/src/components/landing/Hero.tsx | sed -n '53,75p'Repository: OpenCodeIntel/opencodeintel
Length of output: 1058
Gate demo results from triggering ResultsView navigation.
The auto-typing demo calls search() (line 65), which populates results and fires the useEffect at lines 49-51. This invokes onResultsReady, causing LandingPage.handleHeroResults() to set hasSearched=true and flip to ResultsView. The demo should loop through results inline without navigating away. Add a source tracker to distinguish demo from user-initiated searches.
🛠️ Suggested guard
+ const lastSearchSource = useRef<'demo' | 'user'>('demo')
+
useEffect(() => {
- if (results.length) onResultsReady?.(results, query, repo.id, searchTime)
+ if (!results.length || lastSearchSource.current !== 'user') return
+ onResultsReady?.(results, query, repo.id, searchTime)
}, [results, query, repo.id, searchTime, onResultsReady])Wiring (example):
- Set
lastSearchSource.current = 'demo'beforesearch()at line 65 - Set
lastSearchSource.current = 'user'in the form submit handler or before callingsearch()from user interaction
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| if (results.length) onResultsReady?.(results, query, repo.id, searchTime) | |
| }, [results, query, repo.id, searchTime, onResultsReady]) | |
| const lastSearchSource = useRef<'demo' | 'user'>('demo') | |
| useEffect(() => { | |
| if (!results.length || lastSearchSource.current !== 'user') return | |
| onResultsReady?.(results, query, repo.id, searchTime) | |
| }, [results, query, repo.id, searchTime, onResultsReady]) |
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/Hero.tsx` around lines 49 - 51, The useEffect
in Hero.tsx currently calls onResultsReady whenever results populate, which lets
the auto-typing demo trigger navigation; add a search source tracker (e.g.,
const lastSearchSource = useRef<'demo'|'user'|null>(null)) and set
lastSearchSource.current = 'demo' immediately before the demo calls search() and
set lastSearchSource.current = 'user' in the user submit handler (or anywhere
user-initiated search is invoked), then guard the effect that calls
onResultsReady (the useEffect referencing results, query, repo.id, searchTime,
onResultsReady) to only invoke onResultsReady when lastSearchSource.current !==
'demo' (and optionally reset lastSearchSource.current after handling).
| <Menu className="w-5 h-5" /> | ||
| </button> | ||
| </SheetTrigger> | ||
| <SheetContent side="right" className="w-[300px] bg-background border-l border-white/10"> |
There was a problem hiding this comment.
Hardcoded border-white/10 and hover:bg-white/5 won't adapt to light mode.
Unlike the Navbar which uses dark:border-white/10 light:border-black/10 patterns, this component uses fixed white-based borders and backgrounds. This will cause poor contrast in light mode.
🎨 Proposed fix for theme-aware styling
- <SheetContent side="right" className="w-[300px] bg-background border-l border-white/10">
+ <SheetContent side="right" className="w-[300px] bg-background border-l border-white/10 dark:border-white/10 light:border-black/10">Apply similar pattern to divider on line 39 and hover states throughout.
Also applies to: 31-31, 39-39
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/MobileMenu.tsx` at line 24, The SheetContent
in MobileMenu (component MobileMenu / JSX element SheetContent) uses hardcoded
white-based classes like "border-white/10" and hover/bg classes that don't adapt
to light mode; update these className usages (on the SheetContent element and
the internal divider and hoverable items in MobileMenu) to use theme-aware
variants similar to the Navbar pattern (e.g., replace fixed "border-white/10"
and "hover:bg-white/5" with dual classes such as "dark:border-white/10
light:border-black/10" and "dark:hover:bg-white/5 light:hover:bg-black/5" or
equivalent Tailwind theme-aware utilities) so borders and hover backgrounds
adapt to dark and light themes.
| description: 'For teams that need control', | ||
| features: ['Self-hosted deployment', 'SSO / SAML authentication', 'Dedicated support', 'Custom integrations', 'SLA guarantee', 'On-premise option'], | ||
| cta: 'Contact Sales', | ||
| ctaHref: 'mailto:devanshurajesh@gmail.com?subject=CodeIntel%20Enterprise', |
There was a problem hiding this comment.
Personal email address in production code.
Using a personal email (devanshurajesh@gmail.com) for enterprise sales contact is not ideal for production. Consider using a product-specific email like enterprise@codeintel.dev or sales@codeintel.dev.
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/Pricing.tsx` at line 33, The ctaHref in
Pricing.tsx currently uses a personal email address (devanshurajesh@gmail.com);
update the value of ctaHref to use a product-specific or company email such as
enterprise@codeintel.dev or sales@codeintel.dev so production contact goes to an
official inbox (locate the ctaHref assignment in the Pricing component and
replace the mailto address accordingly).
| <button | ||
| onClick={() => setTheme(isDark ? 'light' : 'dark')} | ||
| className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-white/5 dark:hover:bg-white/5 light:hover:bg-black/5 transition-colors" | ||
| aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`} | ||
| > | ||
| {isDark ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />} | ||
| </button> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find Tailwind config files
fd -e js -e ts -e mjs -e mts | grep -E "(tailwind|theme)" | head -20Repository: OpenCodeIntel/opencodeintel
Length of output: 131
🏁 Script executed:
#!/bin/bash
# Search for the Tailwind config file and check for light: variant definition
find . -maxdepth 2 -type f \( -name "tailwind.config.*" -o -name "next.config.*" \) 2>/dev/nullRepository: OpenCodeIntel/opencodeintel
Length of output: 100
🏁 Script executed:
#!/bin/bash
# Read the full ThemeToggle.tsx file to see all imports and context
cat -n frontend/src/components/landing/ThemeToggle.tsx 2>/dev/null || echo "File not found"Repository: OpenCodeIntel/opencodeintel
Length of output: 1039
🏁 Script executed:
#!/bin/bash
# Search for "light:" variant anywhere in config/source files
rg "light:" -t js -t ts --max-count 10Repository: OpenCodeIntel/opencodeintel
Length of output: 1444
🏁 Script executed:
#!/bin/bash
cat -n frontend/tailwind.config.jsRepository: OpenCodeIntel/opencodeintel
Length of output: 7678
Remove non-functional light: Tailwind variant.
The light:hover:bg-black/5 class uses a light: prefix that isn't defined in Tailwind (neither standard nor custom in tailwind.config.js). With darkMode: ["class"], the pattern is to style the default (light) state without a prefix and use dark: for dark mode overrides.
Suggested fix
<button
onClick={() => setTheme(isDark ? 'light' : 'dark')}
- className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-white/5 dark:hover:bg-white/5 light:hover:bg-black/5 transition-colors"
+ className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| onClick={() => setTheme(isDark ? 'light' : 'dark')} | |
| className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-white/5 dark:hover:bg-white/5 light:hover:bg-black/5 transition-colors" | |
| aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`} | |
| > | |
| {isDark ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />} | |
| </button> | |
| <button | |
| onClick={() => setTheme(isDark ? 'light' : 'dark')} | |
| className="p-2 rounded-lg text-zinc-400 hover:text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-colors" | |
| aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`} | |
| > | |
| {isDark ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />} | |
| </button> |
🤖 Prompt for AI Agents
In `@frontend/src/components/landing/ThemeToggle.tsx` around lines 16 - 22, The
button's className in ThemeToggle uses a non-existent Tailwind variant
"light:hover:bg-black/5"; remove the "light:" prefix and apply the default hover
class (hover:bg-black/5) for the light mode while keeping the dark-mode override
(dark:hover:bg-white/5). Locate the button in ThemeToggle.tsx (the element using
setTheme and isDark) and replace the "light:hover:bg-black/5" token with
"hover:bg-black/5" so the hover styles work correctly in the light theme while
preserving dark:hover:bg-white/5.
| <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> | ||
| <X className="h-4 w-4" /> | ||
| <span className="sr-only">Close</span> | ||
| </SheetPrimitive.Close> |
There was a problem hiding this comment.
Ensure the Close button doesn’t submit surrounding forms.
Same concern as Dialog: if SheetPrimitive.Close renders a plain <button>, default submit behavior can leak into forms. Consider setting type="button" if Radix doesn’t already do so.
🛠️ Suggested fix
- <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
+ <SheetPrimitive.Close type="button" className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> | |
| <X className="h-4 w-4" /> | |
| <span className="sr-only">Close</span> | |
| </SheetPrimitive.Close> | |
| <SheetPrimitive.Close type="button" className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> | |
| <X className="h-4 w-4" /> | |
| <span className="sr-only">Close</span> | |
| </SheetPrimitive.Close> |
🤖 Prompt for AI Agents
In `@frontend/src/components/ui/sheet.tsx` around lines 67 - 70, The
SheetPrimitive.Close button may render as a plain <button> and inadvertently
submit surrounding forms; update the SheetPrimitive.Close usage in sheet.tsx
(the Close element) to explicitly set type="button" (or pass the appropriate
prop to ensure a non-submit button) so clicking it won’t trigger form
submission, and verify the Close component still renders the X icon and sr-only
span unchanged.
| const cached = localStorage.getItem(CACHE_KEY) | ||
| if (cached) { | ||
| const data: CacheData = JSON.parse(cached) | ||
| if (Date.now() - data.timestamp < CACHE_DURATION) { | ||
| setStars(data.stars) | ||
| setLoading(false) | ||
| return | ||
| } | ||
| } |
There was a problem hiding this comment.
Wrap JSON.parse in try-catch to handle corrupted cache.
If localStorage contains malformed JSON (e.g., manually edited or corrupted), JSON.parse will throw and crash the component. Consider defensive parsing:
Proposed fix
// check cache first
const cached = localStorage.getItem(CACHE_KEY)
if (cached) {
- const data: CacheData = JSON.parse(cached)
- if (Date.now() - data.timestamp < CACHE_DURATION) {
- setStars(data.stars)
- setLoading(false)
- return
+ try {
+ const data: CacheData = JSON.parse(cached)
+ if (Date.now() - data.timestamp < CACHE_DURATION) {
+ setStars(data.stars)
+ setLoading(false)
+ return
+ }
+ } catch {
+ localStorage.removeItem(CACHE_KEY)
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const cached = localStorage.getItem(CACHE_KEY) | |
| if (cached) { | |
| const data: CacheData = JSON.parse(cached) | |
| if (Date.now() - data.timestamp < CACHE_DURATION) { | |
| setStars(data.stars) | |
| setLoading(false) | |
| return | |
| } | |
| } | |
| const cached = localStorage.getItem(CACHE_KEY) | |
| if (cached) { | |
| try { | |
| const data: CacheData = JSON.parse(cached) | |
| if (Date.now() - data.timestamp < CACHE_DURATION) { | |
| setStars(data.stars) | |
| setLoading(false) | |
| return | |
| } | |
| } catch { | |
| localStorage.removeItem(CACHE_KEY) | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@frontend/src/hooks/useGitHubStars.ts` around lines 19 - 27, The component
assumes localStorage JSON is valid but JSON.parse can throw on corrupted data in
useGitHubStars; wrap the parse in a try-catch around the retrieval of CACHE_KEY
and handle parse failures by removing the bad cache
(localStorage.removeItem(CACHE_KEY)), avoid setting stars from invalid data, and
continue with the normal fetch path (ensure setLoading remains true until a
successful fetch or cached data is used); update the code surrounding
CACHE_KEY/CacheData/JSON.parse to perform defensive parsing and graceful
fallback.
Landing Page V2 - Complete Redesign
Summary
Complete landing page overhaul with modern design, improved UX, and professional polish.
Changes
Infrastructure
Sections Implemented
Design System
Screenshots
Hero section with auto-typing demo and gradient background
Feature cards with hover lift animations
Pricing section with highlighted Pro tier
FAQ and Footer
Commits (11 total)
Testing
bun run build)Bundle Size
Summary by CodeRabbit
New Features
Chores
Updates
✏️ Tip: You can customize this high-level summary in your review settings.