deepspace 0.3.10 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +331 -115
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +199 -16
- package/dist/index.js +354 -166
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +242 -73
- package/dist/server.js +985 -374
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +322 -73
- package/dist/worker.js +961 -343
- package/dist/worker.js.map +1 -1
- package/features/admin-page/feature.json +29 -0
- package/features/admin-page/src/AdminPage.tsx +292 -0
- package/features/admin-page/src/admin-schema.ts +20 -0
- package/features/ai-chat/feature.json +41 -0
- package/features/ai-chat/src/AiChatPage.tsx +692 -0
- package/features/ai-chat/src/ChatPanel.tsx +1098 -0
- package/features/ai-chat/src/ai-chat-schema.ts +11 -0
- package/features/canvas/feature.json +45 -0
- package/features/canvas/src/CanvasEditorPage.tsx +87 -0
- package/features/canvas/src/CanvasListPage.tsx +202 -0
- package/features/canvas/src/canvas-schema.ts +33 -0
- package/features/canvas/src/components/CanvasToolbar.tsx +170 -0
- package/features/canvas/src/components/CanvasView.tsx +369 -0
- package/features/canvas/src/components/ShapeRenderer.tsx +114 -0
- package/features/cron/feature.json +33 -0
- package/features/cron/src/CronLogPage.tsx +100 -0
- package/features/cron/src/cron.ts +25 -0
- package/features/docs/feature.json +96 -0
- package/features/docs/src/DocsEditorPage.tsx +1012 -0
- package/features/docs/src/DocsListPage.tsx +951 -0
- package/features/docs/src/DocsOutlinePanel.tsx +68 -0
- package/features/docs/src/DocsPresence.tsx +138 -0
- package/features/docs/src/InviteDialog.tsx +336 -0
- package/features/docs/src/LibrarySidebar.tsx +464 -0
- package/features/docs/src/docs-favorites.ts +18 -0
- package/features/docs/src/docs-library-types.ts +15 -0
- package/features/docs/src/docs-schema.ts +80 -0
- package/features/docs/src/docs-ui.css +309 -0
- package/features/docs/src/editor/DocEditorSurface.tsx +257 -0
- package/features/docs/src/editor/DocsTiptapToolbar.tsx +560 -0
- package/features/docs/src/editor/useDocEditor.ts +206 -0
- package/features/docs/src/use-yjs-room-with-awareness.ts +285 -0
- package/features/file-attachments/feature.json +42 -0
- package/features/file-attachments/src/AttachmentsPage.tsx +864 -0
- package/features/file-attachments/src/attachments-constants.ts +9 -0
- package/features/file-attachments/src/attachments-schema.ts +45 -0
- package/features/file-attachments/src/useFilePreview.ts +471 -0
- package/features/file-attachments/src/useMimeTypeDetection.ts +138 -0
- package/features/file-manager/feature.json +24 -0
- package/features/file-manager/src/FileManagerPage.tsx +330 -0
- package/features/integration-test/feature.json +18 -0
- package/features/integration-test/src/IntegrationTestPage.tsx +423 -0
- package/features/integration-test/src/integration.spec.ts +42 -0
- package/features/items/feature.json +33 -0
- package/features/items/src/ItemsPage.tsx +325 -0
- package/features/items/src/items-constants.ts +10 -0
- package/features/items/src/items-schema.ts +27 -0
- package/features/kanban/feature.json +18 -0
- package/features/kanban/src/Kanban.tsx +283 -0
- package/features/landing/feature.json +62 -0
- package/features/landing/src/LandingPage.tsx +627 -0
- package/features/landing/src/primitives.tsx +389 -0
- package/features/landing/src/sections/FAQSection.tsx +110 -0
- package/features/landing/src/sections/FeaturesGridSection.tsx +311 -0
- package/features/landing/src/sections/HowItWorksSection.tsx +62 -0
- package/features/landing/src/sections/LogoCloudSection.tsx +58 -0
- package/features/landing/src/sections/ShowcaseSection.tsx +63 -0
- package/features/landing/src/sections/StatsSection.tsx +31 -0
- package/features/landing/src/sections/TeamSection.tsx +86 -0
- package/features/landing/src/sections/TestimonialsSection.tsx +68 -0
- package/features/landing/src/sections/VideoSection.tsx +71 -0
- package/features/leaderboard/feature.json +33 -0
- package/features/leaderboard/src/LeaderboardPage.tsx +372 -0
- package/features/leaderboard/src/leaderboard-constants.ts +21 -0
- package/features/leaderboard/src/leaderboard-schema.ts +36 -0
- package/features/messaging/feature.json +111 -0
- package/features/messaging/src/ChatMultiPage.tsx +300 -0
- package/features/messaging/src/ChatPage.tsx +75 -0
- package/features/messaging/src/components/chat/ChatHeader.tsx +76 -0
- package/features/messaging/src/components/chat/MessageActionSheet.tsx +168 -0
- package/features/messaging/src/components/chat/MessageInput.tsx +65 -0
- package/features/messaging/src/components/chat/MessageItem.tsx +352 -0
- package/features/messaging/src/components/chat/MessageList.tsx +248 -0
- package/features/messaging/src/components/chat/ReactionPicker.tsx +2 -0
- package/features/messaging/src/components/chat/ThreadPanel.tsx +107 -0
- package/features/messaging/src/components/chat/UserProfilePopover.tsx +158 -0
- package/features/messaging/src/components/chat-multi/AddMemberModal.tsx +188 -0
- package/features/messaging/src/components/chat-multi/BrowseGroupsModal.tsx +163 -0
- package/features/messaging/src/components/chat-multi/ChannelListItem.tsx +147 -0
- package/features/messaging/src/components/chat-multi/ChannelSettingsPanel.tsx +344 -0
- package/features/messaging/src/components/chat-multi/ChannelSidebar.tsx +460 -0
- package/features/messaging/src/components/chat-multi/CreateGroupModal.tsx +190 -0
- package/features/messaging/src/components/chat-multi/InvitationList.tsx +149 -0
- package/features/messaging/src/components/chat-multi/NewMessageModal.tsx +159 -0
- package/features/messaging/src/hooks/useChatChannel.ts +64 -0
- package/features/messaging/src/hooks/useLongPress.ts +74 -0
- package/features/messaging/src/hooks/useMultiChannel.ts +142 -0
- package/features/messaging/src/messaging-schema.ts +25 -0
- package/features/presence-test/feature.json +21 -0
- package/features/presence-test/src/PresenceTestPage.tsx +120 -0
- package/features/sidebar/feature.json +19 -0
- package/features/sidebar/src/AppSidebar.tsx +558 -0
- package/features/tasks/feature.json +33 -0
- package/features/tasks/src/TasksPage.tsx +598 -0
- package/features/tasks/src/tasks-constants.ts +43 -0
- package/features/tasks/src/tasks-schema.ts +56 -0
- package/features/testing/feature.json +49 -0
- package/features/testing/src/TestPage.tsx +145 -0
- package/features/testing/src/cpu-burn-action.ts +51 -0
- package/features/testing/src/subrequest-burn-action.ts +53 -0
- package/features/testing/src/test-schema.ts +23 -0
- package/features/topbar/feature.json +18 -0
- package/features/topbar/src/Topbar.tsx +353 -0
- package/features/tree/feature.json +26 -0
- package/features/tree/src/TreeSidebar.tsx +607 -0
- package/features/tree/src/tree-constants.ts +68 -0
- package/features/tree/src/tree-hooks.ts +258 -0
- package/package.json +4 -2
- package/scripts/add-feature.cjs +654 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Features Grid
|
|
3
|
+
*
|
|
4
|
+
* Bento-style 2-column grid with 4 feature cards.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type ReactNode } from 'react'
|
|
8
|
+
import { motion } from 'framer-motion'
|
|
9
|
+
import { Zap, Shield, BarChart3, Sparkles, Check } from 'lucide-react'
|
|
10
|
+
import {
|
|
11
|
+
ScrollReveal,
|
|
12
|
+
StaggerContainer,
|
|
13
|
+
staggerChild,
|
|
14
|
+
GlassCard,
|
|
15
|
+
PlaceholderImage,
|
|
16
|
+
SectionHeading,
|
|
17
|
+
cn,
|
|
18
|
+
} from '../primitives'
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Configuration
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
const FEATURES: Array<{
|
|
25
|
+
icon: typeof Zap
|
|
26
|
+
title: string
|
|
27
|
+
description: string
|
|
28
|
+
gradient: string
|
|
29
|
+
iconColor: string
|
|
30
|
+
borderGlow: string
|
|
31
|
+
image: string
|
|
32
|
+
Visual: (() => ReactNode) | null
|
|
33
|
+
}> = [
|
|
34
|
+
{
|
|
35
|
+
icon: Zap,
|
|
36
|
+
title: 'Lightning Fast',
|
|
37
|
+
description: 'Real-time sync and instant updates across all devices.',
|
|
38
|
+
gradient: 'from-amber-500/20 to-orange-500/20',
|
|
39
|
+
iconColor: 'text-amber-400',
|
|
40
|
+
borderGlow: 'group-hover:shadow-[0_0_30px_rgba(245,158,11,0.15)]',
|
|
41
|
+
image: '',
|
|
42
|
+
Visual: null,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
icon: Shield,
|
|
46
|
+
title: 'Secure by Default',
|
|
47
|
+
description: 'Enterprise-grade security with role-based access and encryption.',
|
|
48
|
+
gradient: 'from-emerald-500/20 to-teal-500/20',
|
|
49
|
+
iconColor: 'text-emerald-400',
|
|
50
|
+
borderGlow: 'group-hover:shadow-[0_0_30px_rgba(16,185,129,0.15)]',
|
|
51
|
+
image: '',
|
|
52
|
+
Visual: ShieldVisual,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
icon: BarChart3,
|
|
56
|
+
title: 'Powerful Analytics',
|
|
57
|
+
description: 'Built-in dashboards and real-time insights for better decisions.',
|
|
58
|
+
gradient: 'from-blue-500/20 to-cyan-500/20',
|
|
59
|
+
iconColor: 'text-blue-400',
|
|
60
|
+
borderGlow: 'group-hover:shadow-[0_0_30px_rgba(59,130,246,0.15)]',
|
|
61
|
+
image: '',
|
|
62
|
+
Visual: BarChartVisual,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
icon: Sparkles,
|
|
66
|
+
title: 'AI-Powered',
|
|
67
|
+
description: 'Smart automation that learns from your workflow.',
|
|
68
|
+
gradient: 'from-violet-500/20 to-purple-500/20',
|
|
69
|
+
iconColor: 'text-violet-400',
|
|
70
|
+
borderGlow: 'group-hover:shadow-[0_0_30px_rgba(139,92,246,0.15)]',
|
|
71
|
+
image: '',
|
|
72
|
+
Visual: null,
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Decorative Visuals
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
function SpeedPulseVisual() {
|
|
81
|
+
return (
|
|
82
|
+
<div className="relative h-24 w-full overflow-hidden rounded-lg bg-foreground/[0.02]">
|
|
83
|
+
<div className="absolute inset-0 flex items-center justify-center gap-[3px]">
|
|
84
|
+
{[0.3, 0.7, 0.5, 1, 0.6, 0.85, 0.45, 0.9, 0.35, 0.75, 0.55, 0.95, 0.4, 0.8, 0.65].map((h, i) => (
|
|
85
|
+
<motion.div
|
|
86
|
+
key={i}
|
|
87
|
+
className="w-[4px] rounded-full bg-gradient-to-t from-amber-500/60 to-orange-400/30"
|
|
88
|
+
initial={{ height: '10%' }}
|
|
89
|
+
whileInView={{ height: `${h * 80}%` }}
|
|
90
|
+
viewport={{ once: true }}
|
|
91
|
+
transition={{ delay: i * 0.04, duration: 0.6, ease: 'easeOut' }}
|
|
92
|
+
/>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
<div className="absolute bottom-0 inset-x-0 h-8 bg-gradient-to-t from-background/90 to-transparent" />
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function ShieldVisual() {
|
|
101
|
+
const checkItems = ['Encryption', 'RBAC', 'Audit Log']
|
|
102
|
+
return (
|
|
103
|
+
<div className="relative h-24 w-full overflow-hidden rounded-lg bg-foreground/[0.02]">
|
|
104
|
+
<div className="flex flex-col gap-2 px-4 py-3">
|
|
105
|
+
{checkItems.map((item, i) => (
|
|
106
|
+
<motion.div
|
|
107
|
+
key={item}
|
|
108
|
+
className="flex items-center gap-2.5"
|
|
109
|
+
initial={{ opacity: 0, x: -12 }}
|
|
110
|
+
whileInView={{ opacity: 1, x: 0 }}
|
|
111
|
+
viewport={{ once: true }}
|
|
112
|
+
transition={{ delay: 0.15 + i * 0.12, duration: 0.4 }}
|
|
113
|
+
>
|
|
114
|
+
<div className="w-5 h-5 rounded-md bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center shrink-0">
|
|
115
|
+
<Check className="w-3 h-3 text-emerald-400" />
|
|
116
|
+
</div>
|
|
117
|
+
<span className="text-xs text-muted-foreground/60 font-medium">{item}</span>
|
|
118
|
+
<div className="flex-1 h-px bg-foreground/[0.04]" />
|
|
119
|
+
<span className="text-[10px] text-emerald-400/60 font-medium">Active</span>
|
|
120
|
+
</motion.div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function BarChartVisual() {
|
|
128
|
+
const bars = [
|
|
129
|
+
{ h: 35, label: 'Mon' },
|
|
130
|
+
{ h: 58, label: 'Tue' },
|
|
131
|
+
{ h: 42, label: 'Wed' },
|
|
132
|
+
{ h: 78, label: 'Thu' },
|
|
133
|
+
{ h: 65, label: 'Fri' },
|
|
134
|
+
{ h: 88, label: 'Sat' },
|
|
135
|
+
{ h: 52, label: 'Sun' },
|
|
136
|
+
]
|
|
137
|
+
return (
|
|
138
|
+
<div className="relative h-24 w-full overflow-hidden rounded-lg bg-foreground/[0.02]">
|
|
139
|
+
<div className="absolute inset-0 flex items-end justify-center gap-2 px-4 pb-5 pt-2">
|
|
140
|
+
{bars.map((bar, i) => (
|
|
141
|
+
<div key={bar.label} className="flex flex-col items-center gap-1.5 flex-1">
|
|
142
|
+
<motion.div
|
|
143
|
+
className="w-full max-w-[24px] rounded-t-sm bg-gradient-to-t from-blue-500/50 to-cyan-400/30"
|
|
144
|
+
initial={{ height: 0 }}
|
|
145
|
+
whileInView={{ height: `${bar.h}%` }}
|
|
146
|
+
viewport={{ once: true }}
|
|
147
|
+
transition={{ delay: i * 0.06, duration: 0.5, ease: 'easeOut' }}
|
|
148
|
+
/>
|
|
149
|
+
<span className="text-[8px] text-muted-foreground/60 font-medium">{bar.label}</span>
|
|
150
|
+
</div>
|
|
151
|
+
))}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function SparkleVisual() {
|
|
158
|
+
const nodes = [
|
|
159
|
+
{ x: 20, y: 30, delay: 0 },
|
|
160
|
+
{ x: 50, y: 15, delay: 0.1 },
|
|
161
|
+
{ x: 80, y: 35, delay: 0.2 },
|
|
162
|
+
{ x: 35, y: 65, delay: 0.15 },
|
|
163
|
+
{ x: 65, y: 55, delay: 0.25 },
|
|
164
|
+
{ x: 50, y: 80, delay: 0.3 },
|
|
165
|
+
]
|
|
166
|
+
const connections = [
|
|
167
|
+
[0, 1], [1, 2], [0, 3], [1, 4], [2, 4], [3, 5], [4, 5],
|
|
168
|
+
]
|
|
169
|
+
return (
|
|
170
|
+
<div className="relative h-24 w-full overflow-hidden rounded-lg bg-foreground/[0.02]">
|
|
171
|
+
<svg className="absolute inset-0 w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
|
172
|
+
{connections.map(([a, b], i) => (
|
|
173
|
+
<motion.line
|
|
174
|
+
key={i}
|
|
175
|
+
x1={nodes[a].x}
|
|
176
|
+
y1={nodes[a].y}
|
|
177
|
+
x2={nodes[b].x}
|
|
178
|
+
y2={nodes[b].y}
|
|
179
|
+
stroke="url(#sparkleGrad)"
|
|
180
|
+
strokeWidth="0.5"
|
|
181
|
+
initial={{ opacity: 0 }}
|
|
182
|
+
whileInView={{ opacity: 0.4 }}
|
|
183
|
+
viewport={{ once: true }}
|
|
184
|
+
transition={{ delay: 0.2 + i * 0.05, duration: 0.5 }}
|
|
185
|
+
/>
|
|
186
|
+
))}
|
|
187
|
+
<defs>
|
|
188
|
+
<linearGradient id="sparkleGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
189
|
+
<stop offset="0%" stopColor="rgb(139,92,246)" stopOpacity="0.6" />
|
|
190
|
+
<stop offset="100%" stopColor="rgb(168,85,247)" stopOpacity="0.3" />
|
|
191
|
+
</linearGradient>
|
|
192
|
+
</defs>
|
|
193
|
+
{nodes.map((node, i) => (
|
|
194
|
+
<motion.circle
|
|
195
|
+
key={i}
|
|
196
|
+
cx={node.x}
|
|
197
|
+
cy={node.y}
|
|
198
|
+
r="3"
|
|
199
|
+
fill="rgba(139,92,246,0.4)"
|
|
200
|
+
stroke="rgba(139,92,246,0.6)"
|
|
201
|
+
strokeWidth="0.5"
|
|
202
|
+
initial={{ scale: 0, opacity: 0 }}
|
|
203
|
+
whileInView={{ scale: 1, opacity: 1 }}
|
|
204
|
+
viewport={{ once: true }}
|
|
205
|
+
transition={{ delay: node.delay + 0.1, duration: 0.4, ease: 'easeOut' }}
|
|
206
|
+
/>
|
|
207
|
+
))}
|
|
208
|
+
{nodes.map((node, i) => (
|
|
209
|
+
<motion.circle
|
|
210
|
+
key={`glow-${i}`}
|
|
211
|
+
cx={node.x}
|
|
212
|
+
cy={node.y}
|
|
213
|
+
r="5"
|
|
214
|
+
fill="none"
|
|
215
|
+
stroke="rgba(139,92,246,0.15)"
|
|
216
|
+
strokeWidth="1"
|
|
217
|
+
initial={{ scale: 0 }}
|
|
218
|
+
whileInView={{ scale: [1, 1.8, 1] }}
|
|
219
|
+
viewport={{ once: true }}
|
|
220
|
+
transition={{ delay: node.delay + 0.5, duration: 2, repeat: Infinity, ease: 'easeInOut' }}
|
|
221
|
+
/>
|
|
222
|
+
))}
|
|
223
|
+
</svg>
|
|
224
|
+
</div>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Feature Card
|
|
230
|
+
// ============================================================================
|
|
231
|
+
|
|
232
|
+
function BentoFeatureCard({
|
|
233
|
+
feature,
|
|
234
|
+
size = 'normal',
|
|
235
|
+
}: {
|
|
236
|
+
feature: (typeof FEATURES)[number]
|
|
237
|
+
size?: 'large' | 'normal'
|
|
238
|
+
}) {
|
|
239
|
+
const isLarge = size === 'large'
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<motion.div variants={staggerChild} className={isLarge ? 'md:col-span-2' : ''}>
|
|
243
|
+
<GlassCard className={cn('h-full overflow-hidden', feature.borderGlow)}>
|
|
244
|
+
<div
|
|
245
|
+
className={cn(
|
|
246
|
+
'absolute inset-0 opacity-[0.15] group-hover:opacity-100 transition-opacity duration-700',
|
|
247
|
+
'bg-gradient-to-br',
|
|
248
|
+
feature.gradient,
|
|
249
|
+
)}
|
|
250
|
+
/>
|
|
251
|
+
<div className={cn('relative z-10 flex flex-col', isLarge ? 'p-8 md:p-10' : 'p-7')}>
|
|
252
|
+
<div className="flex items-start gap-5">
|
|
253
|
+
<div
|
|
254
|
+
className={cn(
|
|
255
|
+
'shrink-0 rounded-xl flex items-center justify-center',
|
|
256
|
+
'bg-foreground/[0.06] border border-foreground/[0.08]',
|
|
257
|
+
'group-hover:scale-110 transition-transform duration-500',
|
|
258
|
+
isLarge ? 'w-14 h-14' : 'w-11 h-11',
|
|
259
|
+
)}
|
|
260
|
+
>
|
|
261
|
+
<feature.icon className={cn(feature.iconColor, isLarge ? 'w-7 h-7' : 'w-5 h-5')} />
|
|
262
|
+
</div>
|
|
263
|
+
<div className="flex-1 min-w-0">
|
|
264
|
+
<h3 className={cn('text-foreground font-semibold mb-2', isLarge ? 'text-xl' : 'text-base')}>
|
|
265
|
+
{feature.title}
|
|
266
|
+
</h3>
|
|
267
|
+
<p className={cn('text-muted-foreground leading-relaxed', isLarge ? 'text-base' : 'text-sm')}>
|
|
268
|
+
{feature.description}
|
|
269
|
+
</p>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
{feature.Visual ? (
|
|
273
|
+
<div className={cn(isLarge ? 'mt-8' : 'mt-5')}>
|
|
274
|
+
<feature.Visual />
|
|
275
|
+
</div>
|
|
276
|
+
) : isLarge ? (
|
|
277
|
+
<div className="mt-8">
|
|
278
|
+
<PlaceholderImage label="Add Image" src={feature.image || undefined} aspectRatio="aspect-[16/10]" />
|
|
279
|
+
</div>
|
|
280
|
+
) : null}
|
|
281
|
+
</div>
|
|
282
|
+
</GlassCard>
|
|
283
|
+
</motion.div>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// Section
|
|
289
|
+
// ============================================================================
|
|
290
|
+
|
|
291
|
+
export default function FeaturesGridSection() {
|
|
292
|
+
return (
|
|
293
|
+
<section id="features" className="relative py-28 md:py-36">
|
|
294
|
+
<div className="max-w-6xl mx-auto px-6">
|
|
295
|
+
<SectionHeading
|
|
296
|
+
tag="Features"
|
|
297
|
+
title="Everything you need"
|
|
298
|
+
titleHighlight="all in one place"
|
|
299
|
+
subtitle="Powerful features designed to streamline your workflow."
|
|
300
|
+
/>
|
|
301
|
+
|
|
302
|
+
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-5" staggerDelay={0.08}>
|
|
303
|
+
<BentoFeatureCard feature={FEATURES[0]} size="large" />
|
|
304
|
+
<BentoFeatureCard feature={FEATURES[1]} />
|
|
305
|
+
<BentoFeatureCard feature={FEATURES[2]} />
|
|
306
|
+
<BentoFeatureCard feature={FEATURES[3]} size="large" />
|
|
307
|
+
</StaggerContainer>
|
|
308
|
+
</div>
|
|
309
|
+
</section>
|
|
310
|
+
)
|
|
311
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: How It Works
|
|
3
|
+
*
|
|
4
|
+
* Numbered 3-step horizontal flow.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { motion } from 'framer-motion'
|
|
8
|
+
import { StaggerContainer, staggerChild, SectionHeading } from '../primitives'
|
|
9
|
+
|
|
10
|
+
const STEPS = [
|
|
11
|
+
{
|
|
12
|
+
number: '01',
|
|
13
|
+
title: 'Sign Up',
|
|
14
|
+
description: 'Create your account in seconds. No credit card required, no complicated setup.',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
number: '02',
|
|
18
|
+
title: 'Set Up',
|
|
19
|
+
description: 'Configure your workspace and invite your team. Everything is guided step by step.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
number: '03',
|
|
23
|
+
title: 'Go',
|
|
24
|
+
description: 'Start using the app right away. Everything you need is ready from day one.',
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
export default function HowItWorksSection() {
|
|
29
|
+
return (
|
|
30
|
+
<section id="how-it-works" className="relative py-28 md:py-36">
|
|
31
|
+
<div className="max-w-5xl mx-auto px-6">
|
|
32
|
+
<SectionHeading
|
|
33
|
+
tag="How It Works"
|
|
34
|
+
title="Up and running in minutes"
|
|
35
|
+
subtitle="Three simple steps to get started."
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-6 relative" staggerDelay={0.15}>
|
|
39
|
+
<div className="hidden md:block absolute top-[36px] left-[16.67%] right-[16.67%] h-px">
|
|
40
|
+
<div className="w-full h-full bg-gradient-to-r from-primary/40 via-violet-400/40 to-primary/40" />
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
{STEPS.map(step => (
|
|
44
|
+
<motion.div key={step.number} variants={staggerChild}>
|
|
45
|
+
<div className="relative text-center">
|
|
46
|
+
<div className="relative z-10 w-[72px] h-[72px] mx-auto mb-6 rounded-2xl bg-background/80 border border-foreground/[0.1] flex items-center justify-center shadow-lg">
|
|
47
|
+
<span className="text-2xl font-bold bg-gradient-to-br from-primary to-violet-400 bg-clip-text text-transparent">
|
|
48
|
+
{step.number}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
<h3 className="text-xl font-semibold text-foreground mb-3">{step.title}</h3>
|
|
52
|
+
<p className="text-muted-foreground text-sm leading-relaxed max-w-[280px] mx-auto">
|
|
53
|
+
{step.description}
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
</motion.div>
|
|
57
|
+
))}
|
|
58
|
+
</StaggerContainer>
|
|
59
|
+
</div>
|
|
60
|
+
</section>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Logo Cloud
|
|
3
|
+
*
|
|
4
|
+
* Responsive grid of partner/integration logos.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ScrollReveal, SectionHeading, cn } from '../primitives'
|
|
8
|
+
|
|
9
|
+
interface Logo {
|
|
10
|
+
name: string
|
|
11
|
+
imageUrl?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const LOGOS: Logo[] = [
|
|
15
|
+
{ name: 'Acme Corp' },
|
|
16
|
+
{ name: 'Globex' },
|
|
17
|
+
{ name: 'Initech' },
|
|
18
|
+
{ name: 'Umbrella' },
|
|
19
|
+
{ name: 'Stark Ind.' },
|
|
20
|
+
{ name: 'Wayne Ent.' },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export default function LogoCloudSection() {
|
|
24
|
+
return (
|
|
25
|
+
<section className="relative py-20 md:py-28">
|
|
26
|
+
<div className="max-w-5xl mx-auto px-6">
|
|
27
|
+
<SectionHeading tag="Trusted By" title="Used by teams you know" />
|
|
28
|
+
|
|
29
|
+
<ScrollReveal>
|
|
30
|
+
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-6">
|
|
31
|
+
{LOGOS.map(logo => (
|
|
32
|
+
<div
|
|
33
|
+
key={logo.name}
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex items-center justify-center h-20 rounded-xl',
|
|
36
|
+
'bg-foreground/[0.03] border border-foreground/[0.06]',
|
|
37
|
+
'opacity-50 hover:opacity-100 transition-opacity duration-300',
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
{logo.imageUrl ? (
|
|
41
|
+
<img
|
|
42
|
+
src={logo.imageUrl}
|
|
43
|
+
alt={logo.name}
|
|
44
|
+
className="max-h-10 max-w-[80%] object-contain brightness-0 invert opacity-60"
|
|
45
|
+
/>
|
|
46
|
+
) : (
|
|
47
|
+
<span className="text-sm font-medium text-muted-foreground tracking-wide">
|
|
48
|
+
{logo.name}
|
|
49
|
+
</span>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
</ScrollReveal>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Showcase
|
|
3
|
+
*
|
|
4
|
+
* Alternating left/right product blocks.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ArrowRight } from 'lucide-react'
|
|
8
|
+
import { ScrollReveal, BrowserMockup, cn } from '../primitives'
|
|
9
|
+
|
|
10
|
+
const SHOWCASE_ITEMS = [
|
|
11
|
+
{
|
|
12
|
+
label: 'Overview',
|
|
13
|
+
title: 'See everything at a glance',
|
|
14
|
+
description: 'A clear, organized view of what matters most. Stay on top of your work without the clutter.',
|
|
15
|
+
image: '',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'Workflow',
|
|
19
|
+
title: 'Work the way you want',
|
|
20
|
+
description: 'Flexible tools that adapt to your process. Set things up once and let the app handle the rest.',
|
|
21
|
+
image: '',
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export default function ShowcaseSection() {
|
|
26
|
+
return (
|
|
27
|
+
<section className="relative py-28 md:py-36">
|
|
28
|
+
<div className="max-w-6xl mx-auto px-6 space-y-24 md:space-y-32">
|
|
29
|
+
{SHOWCASE_ITEMS.map((item, idx) => {
|
|
30
|
+
const isReversed = idx % 2 !== 0
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
key={item.title}
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex flex-col gap-10 md:gap-16 items-center',
|
|
36
|
+
isReversed ? 'md:flex-row-reverse' : 'md:flex-row',
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
<ScrollReveal className="flex-1 w-full" direction={isReversed ? 'right' : 'left'} delay={0.1}>
|
|
40
|
+
<BrowserMockup label={item.label} src={item.image || undefined} />
|
|
41
|
+
</ScrollReveal>
|
|
42
|
+
<ScrollReveal className="flex-1 w-full md:max-w-md" direction={isReversed ? 'left' : 'right'} delay={0.2}>
|
|
43
|
+
<span className="inline-block text-xs font-semibold tracking-[0.2em] uppercase text-primary/80 mb-3">
|
|
44
|
+
{item.label}
|
|
45
|
+
</span>
|
|
46
|
+
<h3 className="text-2xl sm:text-3xl md:text-4xl font-bold text-foreground leading-tight tracking-[-0.01em]">
|
|
47
|
+
{item.title}
|
|
48
|
+
</h3>
|
|
49
|
+
<p className="mt-4 text-muted-foreground text-base md:text-lg leading-relaxed">
|
|
50
|
+
{item.description}
|
|
51
|
+
</p>
|
|
52
|
+
<button className="mt-6 inline-flex items-center gap-2 text-sm font-medium text-primary hover:text-primary/80 transition-colors group">
|
|
53
|
+
Learn more
|
|
54
|
+
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
|
|
55
|
+
</button>
|
|
56
|
+
</ScrollReveal>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
})}
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Stats Band
|
|
3
|
+
*
|
|
4
|
+
* Full-width gradient band with animated counter stats.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { StaggerContainer, AnimatedStat } from '../primitives'
|
|
8
|
+
|
|
9
|
+
const STATS = [
|
|
10
|
+
{ value: '99.9%', label: 'Uptime' },
|
|
11
|
+
{ value: '10x', label: 'Faster' },
|
|
12
|
+
{ value: '50K+', label: 'Users' },
|
|
13
|
+
{ value: '<50ms', label: 'Response Time' },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
export default function StatsSection() {
|
|
17
|
+
return (
|
|
18
|
+
<section className="relative py-20 overflow-hidden">
|
|
19
|
+
<div className="absolute inset-0 bg-gradient-to-r from-primary/[0.08] via-violet-500/[0.06] to-primary/[0.08]" />
|
|
20
|
+
<div className="absolute inset-0 border-y border-foreground/[0.06]" />
|
|
21
|
+
|
|
22
|
+
<div className="relative max-w-5xl mx-auto px-6">
|
|
23
|
+
<StaggerContainer className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-4" staggerDelay={0.08}>
|
|
24
|
+
{STATS.map(stat => (
|
|
25
|
+
<AnimatedStat key={stat.label} value={stat.value} label={stat.label} />
|
|
26
|
+
))}
|
|
27
|
+
</StaggerContainer>
|
|
28
|
+
</div>
|
|
29
|
+
</section>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Team / About
|
|
3
|
+
*
|
|
4
|
+
* Responsive grid of team member cards.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { motion } from 'framer-motion'
|
|
8
|
+
import { Twitter, Github, Linkedin } from 'lucide-react'
|
|
9
|
+
import { StaggerContainer, staggerChild, GlassCard, SectionHeading, cn } from '../primitives'
|
|
10
|
+
|
|
11
|
+
interface TeamMember {
|
|
12
|
+
name: string
|
|
13
|
+
role: string
|
|
14
|
+
socials?: {
|
|
15
|
+
twitter?: string
|
|
16
|
+
github?: string
|
|
17
|
+
linkedin?: string
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const TEAM_MEMBERS: TeamMember[] = [
|
|
22
|
+
{ name: 'Alex Rivera', role: 'Founder & CEO', socials: { twitter: '#', linkedin: '#' } },
|
|
23
|
+
{ name: 'Jordan Lee', role: 'Head of Engineering', socials: { github: '#', linkedin: '#' } },
|
|
24
|
+
{ name: 'Sam Patel', role: 'Lead Designer', socials: { twitter: '#' } },
|
|
25
|
+
{ name: 'Morgan Chen', role: 'Product Manager', socials: { linkedin: '#' } },
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const SOCIAL_ICONS = { twitter: Twitter, github: Github, linkedin: Linkedin } as const
|
|
29
|
+
|
|
30
|
+
export default function TeamSection() {
|
|
31
|
+
return (
|
|
32
|
+
<section id="team" className="relative py-28 md:py-36">
|
|
33
|
+
<div className="max-w-6xl mx-auto px-6">
|
|
34
|
+
<SectionHeading
|
|
35
|
+
tag="Team"
|
|
36
|
+
title="Meet the people behind the product"
|
|
37
|
+
subtitle="A small team with big ambitions, building something we believe in."
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
<StaggerContainer className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6" staggerDelay={0.08}>
|
|
41
|
+
{TEAM_MEMBERS.map(member => (
|
|
42
|
+
<motion.div key={member.name} variants={staggerChild}>
|
|
43
|
+
<GlassCard className="p-6 text-center" hoverEffect>
|
|
44
|
+
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-gradient-to-br from-primary/30 to-violet-500/30 border border-foreground/[0.1] flex items-center justify-center">
|
|
45
|
+
<span className="text-lg font-semibold text-muted-foreground">
|
|
46
|
+
{member.name.split(' ').map(n => n[0]).join('')}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<h3 className="text-base font-semibold text-foreground">{member.name}</h3>
|
|
50
|
+
<p className="text-sm text-muted-foreground mt-1">{member.role}</p>
|
|
51
|
+
|
|
52
|
+
{member.socials && Object.keys(member.socials).length > 0 && (
|
|
53
|
+
<div className="flex items-center justify-center gap-2 mt-4">
|
|
54
|
+
{(Object.entries(member.socials) as Array<[keyof typeof SOCIAL_ICONS, string]>).map(
|
|
55
|
+
([platform, url]) => {
|
|
56
|
+
const Icon = SOCIAL_ICONS[platform]
|
|
57
|
+
if (!url) return null
|
|
58
|
+
return (
|
|
59
|
+
<a
|
|
60
|
+
key={platform}
|
|
61
|
+
href={url}
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
className={cn(
|
|
65
|
+
'w-8 h-8 rounded-lg flex items-center justify-center',
|
|
66
|
+
'bg-foreground/[0.04] border border-foreground/[0.08]',
|
|
67
|
+
'text-muted-foreground/60 hover:text-muted-foreground hover:bg-foreground/[0.08]',
|
|
68
|
+
'transition-all duration-200',
|
|
69
|
+
)}
|
|
70
|
+
aria-label={platform}
|
|
71
|
+
>
|
|
72
|
+
<Icon className="w-3.5 h-3.5" />
|
|
73
|
+
</a>
|
|
74
|
+
)
|
|
75
|
+
},
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
</GlassCard>
|
|
80
|
+
</motion.div>
|
|
81
|
+
))}
|
|
82
|
+
</StaggerContainer>
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing Section: Testimonials
|
|
3
|
+
*
|
|
4
|
+
* 3-column grid of testimonial cards.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { motion } from 'framer-motion'
|
|
8
|
+
import { Star } from 'lucide-react'
|
|
9
|
+
import { StaggerContainer, staggerChild, GlassCard, SectionHeading } from '../primitives'
|
|
10
|
+
|
|
11
|
+
const TESTIMONIALS = [
|
|
12
|
+
{
|
|
13
|
+
quote: 'This completely changed how I work. Everything is faster and more organized now.',
|
|
14
|
+
name: 'Alex Johnson',
|
|
15
|
+
role: 'Product Manager',
|
|
16
|
+
rating: 5,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
quote: 'The simplicity is what sold me. I was up and running in minutes, not hours.',
|
|
20
|
+
name: 'Sarah Chen',
|
|
21
|
+
role: 'Designer',
|
|
22
|
+
rating: 5,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
quote: 'Finally an app that works the way I think. The flexibility is incredible.',
|
|
26
|
+
name: 'Marcus Williams',
|
|
27
|
+
role: 'Developer',
|
|
28
|
+
rating: 5,
|
|
29
|
+
},
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
export default function TestimonialsSection() {
|
|
33
|
+
return (
|
|
34
|
+
<section id="testimonials" className="relative py-28 md:py-36">
|
|
35
|
+
<div className="max-w-6xl mx-auto px-6">
|
|
36
|
+
<SectionHeading tag="Testimonials" title="Loved by people everywhere" />
|
|
37
|
+
|
|
38
|
+
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-6" staggerDelay={0.12}>
|
|
39
|
+
{TESTIMONIALS.map(t => (
|
|
40
|
+
<motion.div key={t.name} variants={staggerChild}>
|
|
41
|
+
<GlassCard className="p-7 h-full flex flex-col" hoverEffect={false}>
|
|
42
|
+
<div className="flex gap-1 mb-5">
|
|
43
|
+
{Array.from({ length: t.rating }).map((_, i) => (
|
|
44
|
+
<Star key={i} className="w-4 h-4 fill-amber-400 text-amber-400" />
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
<p className="text-muted-foreground text-sm leading-relaxed flex-1">
|
|
48
|
+
“{t.quote}”
|
|
49
|
+
</p>
|
|
50
|
+
<div className="flex items-center gap-3 mt-6 pt-5 border-t border-foreground/[0.06]">
|
|
51
|
+
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary/30 to-violet-500/30 border border-foreground/[0.1] flex items-center justify-center">
|
|
52
|
+
<span className="text-xs font-semibold text-muted-foreground">
|
|
53
|
+
{t.name.split(' ').map(n => n[0]).join('')}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<div className="text-sm font-medium text-foreground">{t.name}</div>
|
|
58
|
+
<div className="text-xs text-muted-foreground/60">{t.role}</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</GlassCard>
|
|
62
|
+
</motion.div>
|
|
63
|
+
))}
|
|
64
|
+
</StaggerContainer>
|
|
65
|
+
</div>
|
|
66
|
+
</section>
|
|
67
|
+
)
|
|
68
|
+
}
|