Skip to content

Commit 67d3462

Browse files
committed
minor ui updates
1 parent cae6bf7 commit 67d3462

File tree

4 files changed

+230
-36
lines changed

4 files changed

+230
-36
lines changed

landing/src/components/Features.tsx

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { motion, useInView } from 'framer-motion';
2-
import { useRef } from 'react';
2+
import { useRef, useState } from 'react';
33

44
const features = [
55
{
@@ -65,21 +65,13 @@ export function Features() {
6565
{/* Feature grid */}
6666
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-10 mb-20">
6767
{features.map((feature, index) => (
68-
<motion.div
68+
<FeatureCard
6969
key={feature.title}
70-
initial={{ opacity: 0, y: 20 }}
71-
animate={isInView ? { opacity: 1, y: 0 } : {}}
72-
transition={{ duration: 0.5, delay: index * 0.05 }}
73-
whileHover={{ scale: 1.02 }}
74-
className="p-6 rounded-lg border border-zinc-800 bg-zinc-900/20 hover:border-emerald-500/50 hover:shadow-[0_0_20px_rgba(16,185,129,0.15)] transition-all duration-300"
75-
>
76-
<h3 className="text-white font-medium mb-2">
77-
{feature.title}
78-
</h3>
79-
<p className="text-zinc-500 text-sm leading-relaxed">
80-
{feature.description}
81-
</p>
82-
</motion.div>
70+
title={feature.title}
71+
description={feature.description}
72+
index={index}
73+
isInView={isInView}
74+
/>
8375
))}
8476
</div>
8577

@@ -95,7 +87,7 @@ export function Features() {
9587
{capabilities.map((cap) => (
9688
<span
9789
key={cap}
98-
className="px-3 py-1.5 rounded-full bg-zinc-900 border border-zinc-800 text-zinc-400 text-sm"
90+
className="px-3 py-1.5 rounded-full bg-zinc-900 border border-zinc-800 text-zinc-400 text-sm hover:border-emerald-500/50 hover:shadow-[0_0_15px_rgba(16,185,129,0.15)] transition-all duration-300 cursor-default"
9991
>
10092
{cap}
10193
</span>
@@ -106,3 +98,74 @@ export function Features() {
10698
</section>
10799
);
108100
}
101+
102+
function FeatureCard({
103+
title,
104+
description,
105+
index,
106+
isInView,
107+
}: {
108+
title: string;
109+
description: string;
110+
index: number;
111+
isInView: boolean;
112+
}) {
113+
const cardRef = useRef<HTMLDivElement>(null);
114+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
115+
const [isHovering, setIsHovering] = useState(false);
116+
117+
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
118+
if (!cardRef.current) return;
119+
const rect = cardRef.current.getBoundingClientRect();
120+
setMousePosition({
121+
x: e.clientX - rect.left,
122+
y: e.clientY - rect.top,
123+
});
124+
};
125+
126+
return (
127+
<motion.div
128+
ref={cardRef}
129+
initial={{ opacity: 0, y: 20 }}
130+
animate={isInView ? { opacity: 1, y: 0 } : {}}
131+
transition={{ duration: 0.5, delay: index * 0.05 }}
132+
whileHover={{ scale: 1.02 }}
133+
onMouseMove={handleMouseMove}
134+
onMouseEnter={() => setIsHovering(true)}
135+
onMouseLeave={() => setIsHovering(false)}
136+
className="relative p-6 rounded-lg border border-zinc-800 bg-zinc-900/20 overflow-hidden transition-all duration-300 group"
137+
style={{
138+
boxShadow: isHovering
139+
? '0 0 20px rgba(16, 185, 129, 0.15)'
140+
: 'none',
141+
}}
142+
>
143+
{/* Cursor-following glow effect on border */}
144+
{isHovering && (
145+
<div
146+
className="absolute inset-0 rounded-lg pointer-events-none"
147+
style={{
148+
background: `radial-gradient(600px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.15), transparent 40%)`,
149+
}}
150+
/>
151+
)}
152+
{/* Border gradient that follows cursor */}
153+
<div
154+
className="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"
155+
style={{
156+
background: `radial-gradient(250px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.4), transparent 100%)`,
157+
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
158+
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
159+
maskComposite: 'exclude',
160+
WebkitMaskComposite: 'xor',
161+
padding: '1px',
162+
}}
163+
/>
164+
165+
<div className="relative z-10">
166+
<h3 className="text-white font-medium mb-2">{title}</h3>
167+
<p className="text-zinc-500 text-sm leading-relaxed">{description}</p>
168+
</div>
169+
</motion.div>
170+
);
171+
}

landing/src/components/Problem.tsx

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { motion, useInView } from 'framer-motion';
2-
import { useRef } from 'react';
2+
import { useRef, useState } from 'react';
33

44
const problems = [
55
{
@@ -52,19 +52,84 @@ export function Problem() {
5252

5353
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4 max-w-5xl mx-auto">
5454
{problems.map((problem, index) => (
55-
<motion.div
55+
<ProblemCard
5656
key={problem.title}
57-
initial={{ opacity: 0, y: 20 }}
58-
animate={isInView ? { opacity: 1, y: 0 } : {}}
59-
transition={{ duration: 0.5, delay: 0.1 * index }}
60-
className="p-5 rounded-lg border border-zinc-800 bg-zinc-900/30 hover:border-emerald-500/50 hover:shadow-[0_0_20px_rgba(16,185,129,0.15)] transition-all duration-300"
61-
>
62-
<h3 className="text-base font-medium text-white mb-2">{problem.title}</h3>
63-
<p className="text-zinc-500 text-sm leading-relaxed">{problem.description}</p>
64-
</motion.div>
57+
title={problem.title}
58+
description={problem.description}
59+
index={index}
60+
isInView={isInView}
61+
/>
6562
))}
6663
</div>
6764
</div>
6865
</section>
6966
);
7067
}
68+
69+
function ProblemCard({
70+
title,
71+
description,
72+
index,
73+
isInView,
74+
}: {
75+
title: string;
76+
description: string;
77+
index: number;
78+
isInView: boolean;
79+
}) {
80+
const cardRef = useRef<HTMLDivElement>(null);
81+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
82+
const [isHovering, setIsHovering] = useState(false);
83+
84+
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
85+
if (!cardRef.current) return;
86+
const rect = cardRef.current.getBoundingClientRect();
87+
setMousePosition({
88+
x: e.clientX - rect.left,
89+
y: e.clientY - rect.top,
90+
});
91+
};
92+
93+
return (
94+
<motion.div
95+
ref={cardRef}
96+
initial={{ opacity: 0, y: 20 }}
97+
animate={isInView ? { opacity: 1, y: 0 } : {}}
98+
transition={{ duration: 0.5, delay: 0.1 * index }}
99+
onMouseMove={handleMouseMove}
100+
onMouseEnter={() => setIsHovering(true)}
101+
onMouseLeave={() => setIsHovering(false)}
102+
className="relative p-5 rounded-lg border border-zinc-800 bg-zinc-900/30 overflow-hidden transition-all duration-300 group"
103+
style={{
104+
boxShadow: isHovering
105+
? '0 0 20px rgba(16, 185, 129, 0.15)'
106+
: 'none',
107+
}}
108+
>
109+
{isHovering && (
110+
<div
111+
className="absolute inset-0 rounded-lg pointer-events-none"
112+
style={{
113+
background: `radial-gradient(600px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.15), transparent 40%)`,
114+
}}
115+
/>
116+
)}
117+
<div
118+
className="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"
119+
style={{
120+
background: `radial-gradient(250px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.4), transparent 100%)`,
121+
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
122+
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
123+
maskComposite: 'exclude',
124+
WebkitMaskComposite: 'xor',
125+
padding: '1px',
126+
}}
127+
/>
128+
129+
<div className="relative z-10">
130+
<h3 className="text-base font-medium text-white mb-2">{title}</h3>
131+
<p className="text-zinc-500 text-sm leading-relaxed">{description}</p>
132+
</div>
133+
</motion.div>
134+
);
135+
}

landing/src/components/QuickCommands.tsx

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { motion, useInView } from 'framer-motion';
2-
import { useRef } from 'react';
2+
import { useRef, useState } from 'react';
33

44
const commands = [
55
{
@@ -69,19 +69,84 @@ export function QuickCommands() {
6969

7070
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-3">
7171
{commands.map((cmd, index) => (
72-
<motion.div
72+
<CommandCard
7373
key={cmd.command}
74-
initial={{ opacity: 0, y: 20 }}
75-
animate={isInView ? { opacity: 1, y: 0 } : {}}
76-
transition={{ duration: 0.5, delay: 0.05 * index }}
77-
className="p-4 rounded-lg border border-zinc-800 bg-zinc-900/30 hover:border-emerald-500/50 hover:shadow-[0_0_20px_rgba(16,185,129,0.15)] transition-all duration-300"
78-
>
79-
<code className="text-emerald-400 text-sm font-mono">{cmd.command}</code>
80-
<p className="text-zinc-500 text-xs mt-2">{cmd.description}</p>
81-
</motion.div>
74+
command={cmd.command}
75+
description={cmd.description}
76+
index={index}
77+
isInView={isInView}
78+
/>
8279
))}
8380
</div>
8481
</div>
8582
</section>
8683
);
8784
}
85+
86+
function CommandCard({
87+
command,
88+
description,
89+
index,
90+
isInView,
91+
}: {
92+
command: string;
93+
description: string;
94+
index: number;
95+
isInView: boolean;
96+
}) {
97+
const cardRef = useRef<HTMLDivElement>(null);
98+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
99+
const [isHovering, setIsHovering] = useState(false);
100+
101+
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
102+
if (!cardRef.current) return;
103+
const rect = cardRef.current.getBoundingClientRect();
104+
setMousePosition({
105+
x: e.clientX - rect.left,
106+
y: e.clientY - rect.top,
107+
});
108+
};
109+
110+
return (
111+
<motion.div
112+
ref={cardRef}
113+
initial={{ opacity: 0, y: 20 }}
114+
animate={isInView ? { opacity: 1, y: 0 } : {}}
115+
transition={{ duration: 0.5, delay: 0.05 * index }}
116+
onMouseMove={handleMouseMove}
117+
onMouseEnter={() => setIsHovering(true)}
118+
onMouseLeave={() => setIsHovering(false)}
119+
className="relative p-4 rounded-lg border border-zinc-800 bg-zinc-900/30 overflow-hidden transition-all duration-300 group"
120+
style={{
121+
boxShadow: isHovering
122+
? '0 0 20px rgba(16, 185, 129, 0.15)'
123+
: 'none',
124+
}}
125+
>
126+
{isHovering && (
127+
<div
128+
className="absolute inset-0 rounded-lg pointer-events-none"
129+
style={{
130+
background: `radial-gradient(600px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.15), transparent 40%)`,
131+
}}
132+
/>
133+
)}
134+
<div
135+
className="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"
136+
style={{
137+
background: `radial-gradient(250px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(16, 185, 129, 0.4), transparent 100%)`,
138+
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
139+
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
140+
maskComposite: 'exclude',
141+
WebkitMaskComposite: 'xor',
142+
padding: '1px',
143+
}}
144+
/>
145+
146+
<div className="relative z-10">
147+
<code className="text-emerald-400 text-sm font-mono">{command}</code>
148+
<p className="text-zinc-500 text-xs mt-2">{description}</p>
149+
</div>
150+
</motion.div>
151+
);
152+
}

todo.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [ ] realtime team colaboration
1414
- [ ] Also a "Send feedback" button to directly send feedback to claudecode
1515
- [ ] Auto call/ open web when plan is created by claudecode
16+
- [ ] AI anotations ? ( my say is no)
1617

1718

1819

0 commit comments

Comments
 (0)