Skip to content

Commit ece6c4b

Browse files
feat: ship landing page with mobile and desktop variants
1 parent 4ddd2d2 commit ece6c4b

8 files changed

Lines changed: 294 additions & 323 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use client'
2+
3+
import { useState, useEffect } from 'react'
4+
import AppStoreButton from '@/components/AppStoreButton'
5+
import GooglePlayButton from '@/components/GooglePlayButton'
6+
import StarOnGithubButton from '@/components/GithubButton'
7+
import LaunchWebAppButton from '@/components/WebAppButton'
8+
import PhoneBundle from '@/components/phones/PhoneBundle'
9+
import Terminal from '@/components/Terminal'
10+
11+
// Download links constants
12+
const GOOGLE_PLAY_LINK = 'https://play.google.com/store/apps/details?id=com.ex3ndr.happy'
13+
const APP_STORE_LINK = 'https://apps.apple.com/us/app/happy-claude-code-client/id6748571505'
14+
15+
export default function DesktopHeroSection() {
16+
// Device detection state
17+
const [deviceType, setDeviceType] = useState<'ios' | 'android' | 'desktop'>('desktop')
18+
19+
useEffect(() => {
20+
// Client-side device detection
21+
const userAgent = navigator.userAgent.toLowerCase()
22+
23+
if (/iphone|ipad|ipod/.test(userAgent)) {
24+
setDeviceType('ios')
25+
} else if (/android/.test(userAgent)) {
26+
setDeviceType('android')
27+
} else {
28+
setDeviceType('desktop')
29+
}
30+
}, [])
31+
32+
// Render store buttons based on device type
33+
const renderStoreButtons = () => {
34+
switch (deviceType) {
35+
case 'ios':
36+
return (
37+
<>
38+
<AppStoreButton href={APP_STORE_LINK} />
39+
</>
40+
)
41+
case 'android':
42+
return (
43+
<>
44+
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
45+
</>
46+
)
47+
case 'desktop':
48+
default:
49+
return (
50+
<>
51+
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
52+
<AppStoreButton href={APP_STORE_LINK} />
53+
</>
54+
)
55+
}
56+
}
57+
58+
return (
59+
<section className="py-8 md:py-12 xl:py-24 pb-16 hidden sm:block">
60+
<div className="max-w-6xl mx-auto px-5">
61+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20 items-center">
62+
<div>
63+
<h1 className="text-3xl sm:text-5xl font-bold mb-5 leading-tight">
64+
Native Mobile App for Claude Code CLI
65+
</h1>
66+
67+
<div className="mb-6 text-lg sm:text-xl">
68+
<div className="flex items-start mb-4 text-gray-700">
69+
<span className="text-green-500 mr-2.5 font-bold"></span>
70+
<span>Real-time sync between CLI and mobile - same session, everywhere</span>
71+
</div>
72+
<div className="flex items-start mb-4 text-gray-700">
73+
<span className="text-green-500 mr-2.5 font-bold"></span>
74+
<span>100% Free & Open Source (MIT Licensed)</span>
75+
</div>
76+
<div className="flex items-start mb-4 text-gray-700">
77+
<span className="text-green-500 mr-2.5 font-bold"></span>
78+
<span>Works with your existing Claude Code workflow</span>
79+
</div>
80+
<div className="flex items-start mb-4 text-gray-700">
81+
<span className="text-green-500 mr-2.5 font-bold"></span>
82+
<span>Multiple concurrent sessions support</span>
83+
</div>
84+
<Terminal command="npm i -g happy-coder && happy" variant="light" />
85+
</div>
86+
87+
<div className="flex flex-col gap-5">
88+
<div className="flex gap-4 flex-wrap">
89+
{renderStoreButtons()}
90+
<LaunchWebAppButton href="https://app.happy.engineering" />
91+
<StarOnGithubButton href="https://github.com/slopus/happy" />
92+
</div>
93+
<div className="flex gap-4 items-center">
94+
</div>
95+
</div>
96+
97+
</div>
98+
99+
<div className="relative h-[500px]">
100+
<PhoneBundle size="medium" />
101+
</div>
102+
</div>
103+
</div>
104+
</section>
105+
)
106+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
'use client'
2+
3+
import { useState, useEffect } from 'react'
4+
import AppStoreButton from '@/components/AppStoreButton'
5+
import GooglePlayButton from '@/components/GooglePlayButton'
6+
import Terminal from '@/components/Terminal'
7+
import PhoneBundle from '@/components/phones/PhoneBundle'
8+
9+
// Download links constants
10+
const GOOGLE_PLAY_LINK = 'https://play.google.com/store/apps/details?id=com.ex3ndr.happy'
11+
const APP_STORE_LINK = 'https://apps.apple.com/us/app/happy-claude-code-client/id6748571505'
12+
13+
export default function MobileHeroSection() {
14+
// Background video source (using existing video)
15+
const videoSrc = '/riding-bg-2.mp4'
16+
17+
// Device detection state
18+
const [deviceType, setDeviceType] = useState<'ios' | 'android' | 'desktop'>('desktop')
19+
20+
useEffect(() => {
21+
// Client-side device detection
22+
const userAgent = navigator.userAgent.toLowerCase()
23+
24+
if (/iphone|ipad|ipod/.test(userAgent)) {
25+
setDeviceType('ios')
26+
} else if (/android/.test(userAgent)) {
27+
setDeviceType('android')
28+
} else {
29+
setDeviceType('desktop')
30+
}
31+
}, [])
32+
33+
// Render store buttons based on device type
34+
const renderStoreButtons = () => {
35+
switch (deviceType) {
36+
case 'ios':
37+
return (
38+
<>
39+
<AppStoreButton href={APP_STORE_LINK} />
40+
</>
41+
)
42+
case 'android':
43+
return (
44+
<>
45+
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
46+
</>
47+
)
48+
case 'desktop':
49+
default:
50+
return (
51+
<>
52+
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
53+
<AppStoreButton href={APP_STORE_LINK} />
54+
</>
55+
)
56+
}
57+
}
58+
59+
return (
60+
<section className="min-h-screen bg-background relative sm:hidden"
61+
style={{
62+
marginTop: "calc(-1 * var(--nextra-navbar-height))",
63+
paddingTop: "calc(var(--spacing) * 8 + var(--nextra-navbar-height))",
64+
}}
65+
>
66+
<div className="absolute inset-0 w-full h-full overflow-hidden bg-black">
67+
<video
68+
src={videoSrc}
69+
preload="metadata"
70+
autoPlay
71+
loop
72+
playsInline
73+
className="absolute inset-0 w-full h-full object-cover scale-110"
74+
style={{
75+
minWidth: '110%',
76+
minHeight: '110%',
77+
width: 'auto',
78+
height: 'auto',
79+
left: '50%',
80+
top: '50%',
81+
transform: 'translate(-50%, -50%) scale(1.1)'
82+
}}
83+
>
84+
Your browser does not support the video tag.
85+
</video>
86+
87+
{/* Gradient overlays for better text readability */}
88+
<div className="absolute inset-0 bg-gradient-to-b from-white/45 via-white/25 to-white/15" />
89+
<div className="absolute inset-0 bg-radial-gradient from-transparent via-transparent to-white/25" />
90+
<div
91+
className="absolute inset-0 pointer-events-none"
92+
style={{
93+
background: 'linear-gradient(transparent 0%, transparent 75%, rgba(255, 255, 255, 0.2) 85%, rgba(255, 255, 255, 0.5) 92%, rgba(255, 255, 255, 0.8) 98%)'
94+
}}
95+
/>
96+
97+
<div className="absolute inset-0 flex items-start justify-center pt-8 md:pt-12">
98+
<div className="relative p-6 sm:p-10 md:p-14 pt-12 sm:pt-16 md:pt-20 animate-in fade-in slide-in-from-top-4 duration-600">
99+
100+
<div className="text-center">
101+
<div className="space-y-4 mb-8">
102+
<h2
103+
className="text-2xl sm:text-2xl md:text-3xl font-semibold text-[#1a1a1a] mb-3 drop-shadow-[0_2px_12px_rgba(255,255,255,0.9)]"
104+
>
105+
Native Mobile App for Claude Code
106+
</h2>
107+
</div>
108+
<PhoneBundle size="small" />
109+
110+
<div className="flex gap-2 justify-center items-center flex-wrap w-screen mt-12">
111+
{renderStoreButtons()}
112+
</div>
113+
114+
<div className="mt-6 flex flex-col items-center space-y-2">
115+
<span
116+
className="text-md font-semibold drop-shadow-[0_2px_8px_rgba(255,255,255,0.8)]"
117+
>
118+
Then get started with:
119+
</span>
120+
<Terminal command="npm install happy-coder && happy" variant="light" />
121+
</div>
122+
</div>
123+
</div>
124+
</div>
125+
</div>
126+
</section>
127+
)
128+
}
Lines changed: 5 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,13 @@
11
'use client'
22

3-
import { useState, useEffect } from 'react'
4-
import Link from 'next/link'
5-
import AppStoreButton from '@/components/AppStoreButton'
6-
import GooglePlayButton from '@/components/GooglePlayButton'
7-
import NpmButton from '@/components/NpmButton'
8-
import Terminal from '@/components/Terminal'
9-
import PhoneBundle from '@/components/phones/PhoneBundle'
10-
11-
// Download links constants
12-
const NPM_LINK = 'https://www.npmjs.com/package/happy-coder'
13-
const GOOGLE_PLAY_LINK = 'https://play.google.com/store/apps/details?id=com.ex3ndr.happy'
14-
const APP_STORE_LINK = 'https://apps.apple.com/us/app/happy-claude-code-client/id6748571505'
15-
16-
export default function HeroSection() {
17-
// Background video source (using existing video)
18-
const videoSrc = '/riding-bg-2.mp4'
19-
20-
// Device detection state
21-
const [deviceType, setDeviceType] = useState<'ios' | 'android' | 'desktop'>('desktop')
22-
23-
useEffect(() => {
24-
// Client-side device detection
25-
const userAgent = navigator.userAgent.toLowerCase()
26-
27-
if (/iphone|ipad|ipod/.test(userAgent)) {
28-
setDeviceType('ios')
29-
} else if (/android/.test(userAgent)) {
30-
setDeviceType('android')
31-
} else {
32-
setDeviceType('desktop')
33-
}
34-
}, [])
35-
36-
// Render store buttons based on device type
37-
const renderStoreButtons = () => {
38-
switch (deviceType) {
39-
case 'ios':
40-
return (
41-
<>
42-
<AppStoreButton href={APP_STORE_LINK} />
43-
</>
44-
)
45-
case 'android':
46-
return (
47-
<>
48-
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
49-
</>
50-
)
51-
case 'desktop':
52-
default:
53-
return (
54-
<>
55-
<GooglePlayButton href={GOOGLE_PLAY_LINK} />
56-
<AppStoreButton href={APP_STORE_LINK} />
57-
</>
58-
)
59-
}
60-
}
3+
import MobileHeroSection from './MobileHeroSection'
4+
import DesktopHeroSection from './DesktopHeroSection'
615

6+
export default function TestLightPage() {
627
return (
638
<>
64-
<section className="min-h-screen bg-background relative"
65-
style={{
66-
marginTop: "calc(-1 * var(--nextra-navbar-height))",
67-
paddingTop: "calc(var(--spacing) * 8 + var(--nextra-navbar-height))",
68-
}}
69-
>
70-
<div className="absolute inset-0 w-full h-full overflow-hidden bg-black">
71-
<video
72-
src={videoSrc}
73-
preload="metadata"
74-
autoPlay
75-
loop
76-
playsInline
77-
className="absolute inset-0 w-full h-full object-cover scale-110"
78-
style={{
79-
minWidth: '110%',
80-
minHeight: '110%',
81-
width: 'auto',
82-
height: 'auto',
83-
left: '50%',
84-
top: '50%',
85-
transform: 'translate(-50%, -50%) scale(1.1)'
86-
}}
87-
>
88-
Your browser does not support the video tag.
89-
</video>
90-
91-
{/* Gradient overlays for better text readability */}
92-
<div className="absolute inset-0 bg-gradient-to-b from-white/45 via-white/25 to-white/15" />
93-
<div className="absolute inset-0 bg-radial-gradient from-transparent via-transparent to-white/25" />
94-
<div
95-
className="absolute inset-0 pointer-events-none"
96-
style={{
97-
background: 'linear-gradient(transparent 0%, transparent 75%, rgba(255, 255, 255, 0.2) 85%, rgba(255, 255, 255, 0.5) 92%, rgba(255, 255, 255, 0.8) 98%)'
98-
}}
99-
/>
100-
101-
<div className="absolute inset-0 flex items-start justify-center pt-8 md:pt-12">
102-
<div className="relative p-6 sm:p-10 md:p-14 pt-12 sm:pt-16 md:pt-20 animate-in fade-in slide-in-from-top-4 duration-600">
103-
104-
<div className="text-center">
105-
<div className="space-y-4 mb-8">
106-
<h2
107-
className="text-2xl sm:text-2xl md:text-3xl font-semibold text-[#1a1a1a] mb-3 drop-shadow-[0_2px_12px_rgba(255,255,255,0.9)]"
108-
>
109-
Your Claude Code CLI, Now Seamlessly Mobile
110-
</h2>
111-
</div>
112-
<PhoneBundle size="small" />
113-
114-
<div className="flex gap-2 justify-center items-center flex-wrap w-screen mt-12">
115-
{renderStoreButtons()}
116-
</div>
117-
118-
<div className="mt-6 flex flex-col items-center space-y-2">
119-
<span
120-
className="text-md font-semibold drop-shadow-[0_2px_8px_rgba(255,255,255,0.8)]"
121-
>
122-
Then get started with:
123-
</span>
124-
<Terminal command="npm install happy-coder && happy" variant="light" />
125-
126-
</div>
127-
</div>
128-
</div>
129-
</div>
130-
131-
</div>
132-
</section>
133-
<section>
134-
<span
135-
className="text-[#1a1a1a]/60 text-sm drop-shadow-[0_2px_8px_rgba(255,255,255,0.8)]"
136-
>
137-
Then get started with:
138-
</span>
139-
<Terminal command="npm install happy-coder && happy" variant="light" />
140-
141-
142-
</section>
9+
<MobileHeroSection />
10+
<DesktopHeroSection />
14311
</>
14412
)
14513
}

0 commit comments

Comments
 (0)