mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-16 18:25:17 +02:00
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:
parent
74d4172f54
commit
da001e5a24
2 changed files with 132 additions and 53 deletions
11
apps/x/.claude/launch.json
Normal file
11
apps/x/.claude/launch.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue