Enhance WelcomeStep component in onboarding flow with new feature highlights and animations. Introduce icons for memory, connectivity, and privacy features. Update logo display with ambient glow effect and improve user feedback during connection states.

This commit is contained in:
tusharmagar 2026-03-16 21:49:19 +05:30
parent 74d4172f54
commit da001e5a24
2 changed files with 132 additions and 53 deletions

View file

@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "renderer-dev",
"runtimeExecutable": "/Users/tusharmagar/Rowboat/rowboat-V2/apps/x/apps/renderer/node_modules/.bin/vite",
"runtimeArgs": ["--port", "5173"],
"port": 5173
}
]
}

View file

@ -1,4 +1,5 @@
import { Loader2, CheckCircle2 } from "lucide-react"
import { Loader2, CheckCircle2, Brain, Plug, ShieldCheck } from "lucide-react"
import { motion } from "motion/react"
import { Button } from "@/components/ui/button"
import type { OnboardingState } from "../use-onboarding-state"
@ -6,78 +7,145 @@ interface WelcomeStepProps {
state: OnboardingState
}
const features = [
{
icon: Brain,
label: "Memory",
desc: "Builds a knowledge graph from your work",
},
{
icon: Plug,
label: "Connected",
desc: "Syncs email, calendar, and meetings",
},
{
icon: ShieldCheck,
label: "Private",
desc: "Your data stays local on your machine",
},
]
export function WelcomeStep({ state }: WelcomeStepProps) {
const rowboatState = state.providerStates['rowboat'] || { isConnected: false, isLoading: false, isConnecting: false }
return (
<div className="flex flex-col items-center justify-center text-center flex-1">
{/* Logo */}
<img src="/logo-only.png" alt="Rowboat" className="size-14 mb-6" />
{/* Logo with ambient glow */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
className="relative mb-6"
>
<div className="absolute inset-0 size-14 rounded-2xl bg-primary/10 blur-xl scale-[2]" />
<img src="/logo-only.png" alt="Rowboat" className="relative size-14" />
</motion.div>
{/* Tagline badge */}
<div className="inline-flex items-center gap-2 rounded-full border bg-muted/50 px-3.5 py-1.5 text-xs font-medium text-muted-foreground mb-6">
<motion.div
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.15 }}
className="inline-flex items-center gap-2 rounded-full border bg-muted/50 px-3.5 py-1.5 text-xs font-medium text-muted-foreground mb-6"
>
<span className="size-1.5 rounded-full bg-green-500 animate-pulse" />
Your AI coworker, with memory
</div>
</motion.div>
{/* Main heading */}
<h1 className="text-3xl font-bold tracking-tight mb-3">
<motion.h1
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="text-3xl font-bold tracking-tight mb-3"
>
Welcome to Rowboat
</h1>
<p className="text-base text-muted-foreground leading-relaxed max-w-sm mb-8">
</motion.h1>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className="text-base text-muted-foreground leading-relaxed max-w-sm mb-8"
>
Connect your Rowboat account for instant access to all models through our gateway no API keys needed.
</p>
</motion.p>
{/* Product preview placeholder */}
<div className="w-full max-w-sm rounded-xl border-2 border-dashed border-muted-foreground/20 bg-muted/30 aspect-video flex items-center justify-center mb-8">
<span className="text-sm text-muted-foreground/50">Product Preview</span>
{/* Feature highlights */}
<div className="grid grid-cols-3 gap-3 w-full max-w-md mb-8">
{features.map((f, i) => (
<motion.div
key={f.label}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.35 + i * 0.08 }}
className="flex flex-col items-center gap-2 rounded-xl border bg-muted/30 p-4"
>
<div className="size-9 rounded-lg bg-primary/10 flex items-center justify-center">
<f.icon className="size-4.5 text-primary/80" />
</div>
<span className="text-xs font-semibold">{f.label}</span>
<span className="text-[11px] leading-tight text-muted-foreground">{f.desc}</span>
</motion.div>
))}
</div>
{/* Sign in / connected state */}
{rowboatState.isConnected ? (
<div className="flex flex-col items-center gap-4 w-full max-w-xs">
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
<CheckCircle2 className="size-5" />
<span className="text-sm font-medium">Connected to Rowboat</span>
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.6 }}
className="w-full max-w-xs"
>
{rowboatState.isConnected ? (
<div className="flex flex-col items-center gap-4">
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
<CheckCircle2 className="size-5" />
<span className="text-sm font-medium">Connected to Rowboat</span>
</div>
<Button
onClick={() => {
state.setOnboardingPath('rowboat')
state.setCurrentStep(2)
}}
size="lg"
className="w-full h-12 text-base font-medium"
>
Continue
</Button>
</div>
<Button
onClick={() => {
state.setOnboardingPath('rowboat')
state.setCurrentStep(2)
}}
size="lg"
className="w-full h-12 text-base font-medium"
>
Continue
</Button>
</div>
) : (
<div className="flex flex-col items-center gap-4 w-full max-w-xs">
<Button
onClick={() => {
state.setOnboardingPath('rowboat')
state.startConnect('rowboat')
}}
size="lg"
className="w-full h-12 text-base font-medium"
disabled={rowboatState.isConnecting}
>
{rowboatState.isConnecting ? (
<><Loader2 className="size-5 animate-spin mr-2" />Waiting for sign in...</>
) : (
"Sign in with Rowboat"
) : (
<div className="flex flex-col items-center gap-4">
<Button
onClick={() => {
state.setOnboardingPath('rowboat')
state.startConnect('rowboat')
}}
size="lg"
className="w-full h-12 text-base font-medium"
disabled={rowboatState.isConnecting}
>
{rowboatState.isConnecting ? (
<><Loader2 className="size-5 animate-spin mr-2" />Waiting for sign in...</>
) : (
"Sign in with Rowboat"
)}
</Button>
{rowboatState.isConnecting && (
<p className="text-xs text-muted-foreground animate-pulse">
Complete sign in in your browser, then return here.
</p>
)}
</Button>
{rowboatState.isConnecting && (
<p className="text-xs text-muted-foreground animate-pulse">
Complete sign in in your browser, then return here.
</p>
)}
</div>
)}
</div>
)}
</motion.div>
{/* BYOK link */}
<div className="mt-8">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.7 }}
className="mt-8"
>
<button
onClick={() => {
state.setOnboardingPath('byok')
@ -87,7 +155,7 @@ export function WelcomeStep({ state }: WelcomeStepProps) {
>
I want to bring my own API key
</button>
</div>
</motion.div>
</div>
)
}