diff --git a/apps/www/components/ui/avatar.tsx b/apps/www/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/apps/www/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/apps/www/components/ui/hero-cards.tsx b/apps/www/components/ui/hero-cards.tsx new file mode 100644 index 0000000..531b697 --- /dev/null +++ b/apps/www/components/ui/hero-cards.tsx @@ -0,0 +1,625 @@ +"use client" + +import { useState, useEffect } from "react" +import { Card, CardContent } from "@/components/ui/card" +import { Avatar, AvatarFallback } from "@/components/ui/avatar" + +const emailContent = `Hi Sarah, + +Thanks for yesterday's meeting! Here are our 3 follow-up items: + +1. Technical Integration - Implementation roadmap by Friday +2. Custom Dashboard - Analytics mockups for your KPIs +3. Pricing Proposal - ROI analysis by next week + +Available for a call Tuesday to review? + +Best, +Alex` + +const slackMessage = `Hey Alex, thank you for helping me out with the OKRs, you are a lifesaver! 🙌` + +const jiraComment = `Bug resolved - closing the task. Please publish the changelog. Thanks!` + +const whatsappMessage = `Thanks for the wonderful evening 🙏, loved the pot roast 😋, see you next week at the game 🏈!` + +const cards = ["gmail", "slack", "jira", "whatsapp"] as const + +export default function HeroCards() { + const [currentCardIndex, setCurrentCardIndex] = useState(0) + const [isTransitioning, setIsTransitioning] = useState(false) + const [isMobile, setIsMobile] = useState(false) + + // Email states + const [typedContent, setTypedContent] = useState("") + const [currentIndex, setCurrentIndex] = useState(0) + const [isTyping, setIsTyping] = useState(true) + + // Slack states + const [slackTypedContent, setSlackTypedContent] = useState("") + const [slackCurrentIndex, setSlackCurrentIndex] = useState(0) + const [slackIsTyping, setSlackIsTyping] = useState(true) + + // Jira states + const [jiraTypedContent, setJiraTypedContent] = useState("") + const [jiraCurrentIndex, setJiraCurrentIndex] = useState(0) + const [jiraIsTyping, setJiraIsTyping] = useState(true) + + // WhatsApp states + const [whatsappTypedContent, setWhatsappTypedContent] = useState("") + const [whatsappCurrentIndex, setWhatsappCurrentIndex] = useState(0) + const [whatsappIsTyping, setWhatsappIsTyping] = useState(true) + + // Check for mobile screen size + useEffect(() => { + const checkScreenSize = () => { + setIsMobile(window.innerWidth < 640) + } + + checkScreenSize() + window.addEventListener('resize', checkScreenSize) + + return () => window.removeEventListener('resize', checkScreenSize) + }, []) + + // Carousel rotation function + const rotateCarousel = () => { + if (!isTransitioning) { + setIsTransitioning(true) + setTimeout(() => { + setCurrentCardIndex((prev) => (prev + 1) % cards.length) + setIsTransitioning(false) + }, 300) + } + } + + // Email typing animation + useEffect(() => { + if (cards[currentCardIndex] === "gmail") { + if (currentIndex < emailContent.length && isTyping) { + const timeout = setTimeout(() => { + setTypedContent(emailContent.slice(0, currentIndex + 1)) + setCurrentIndex(currentIndex + 1) + }, 3) + return () => clearTimeout(timeout) + } else if (currentIndex >= emailContent.length && isTyping) { + setIsTyping(false) + setTimeout(() => { + rotateCarousel() + setTimeout(() => { + setCurrentIndex(0) + setTypedContent("") + setIsTyping(true) + }, 400) + }, 1000) + } + } + }, [currentIndex, isTyping, currentCardIndex]) + + // Slack typing animation + useEffect(() => { + if (cards[currentCardIndex] === "slack") { + if (slackCurrentIndex < slackMessage.length && slackIsTyping) { + const timeout = setTimeout(() => { + setSlackTypedContent(slackMessage.slice(0, slackCurrentIndex + 1)) + setSlackCurrentIndex(slackCurrentIndex + 1) + }, 25) + return () => clearTimeout(timeout) + } else if (slackCurrentIndex >= slackMessage.length && slackIsTyping) { + setSlackIsTyping(false) + setTimeout(() => { + rotateCarousel() + setTimeout(() => { + setSlackCurrentIndex(0) + setSlackTypedContent("") + setSlackIsTyping(true) + }, 400) + }, 2000) + } + } + }, [slackCurrentIndex, slackIsTyping, currentCardIndex]) + + // Jira typing animation + useEffect(() => { + if (cards[currentCardIndex] === "jira") { + if (jiraCurrentIndex < jiraComment.length && jiraIsTyping) { + const timeout = setTimeout(() => { + setJiraTypedContent(jiraComment.slice(0, jiraCurrentIndex + 1)) + setJiraCurrentIndex(jiraCurrentIndex + 1) + }, 30) + return () => clearTimeout(timeout) + } else if (jiraCurrentIndex >= jiraComment.length && jiraIsTyping) { + setJiraIsTyping(false) + setTimeout(() => { + rotateCarousel() + setTimeout(() => { + setJiraCurrentIndex(0) + setJiraTypedContent("") + setJiraIsTyping(true) + }, 400) + }, 2000) + } + } + }, [jiraCurrentIndex, jiraIsTyping, currentCardIndex]) + + // WhatsApp typing animation + useEffect(() => { + if (cards[currentCardIndex] === "whatsapp") { + if (whatsappCurrentIndex < whatsappMessage.length && whatsappIsTyping) { + const timeout = setTimeout(() => { + setWhatsappTypedContent(whatsappMessage.slice(0, whatsappCurrentIndex + 1)) + setWhatsappCurrentIndex(whatsappCurrentIndex + 1) + }, 20) + return () => clearTimeout(timeout) + } else if (whatsappCurrentIndex >= whatsappMessage.length && whatsappIsTyping) { + setWhatsappIsTyping(false) + setTimeout(() => { + rotateCarousel() + setTimeout(() => { + setWhatsappCurrentIndex(0) + setWhatsappTypedContent("") + setWhatsappIsTyping(true) + }, 400) + }, 2000) + } + } + }, [whatsappCurrentIndex, whatsappIsTyping, currentCardIndex]) + + const renderGmailCard = () => ( + + {/* Gmail Header */} +
+
+ Gmail + New Email +
+
+
+
+
+
+
+ + + {/* Email Fields */} +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ + {/* Email Body */} +
+
+ {typedContent} + {isTyping && } +
+
+ + {/* Bottom Actions */} +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ ) + + const renderSlackCard = () => ( + + {/* Slack Header */} +
+
+ Slack + New Message +
+
+
+
+
+
+
+ + {/* Channel Header */} +
+
+ # dev-team +
+ 42 members +
+
+ + + {/* Messages Area */} +
+
+ + AM + +
+
+ Alex Miller + 2:34 PM +
+
+
+
+
+
+
+ +
+ + JD + +
+
+ Jane Doe + 2:35 PM +
+
+
+
+
+
+
+ + {/* Message Input */} +
+
+
+ + {slackTypedContent} + {slackIsTyping && } + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ ) + + const renderJiraCard = () => ( + + {/* Jira Header */} +
+
+ Jira + Add Comment +
+
+
+
+
+
+
+ + {/* Task Header */} +
+
+
+ 🐛 +
+ BUG-1234 +
+
+
+ + + {/* Task Details */} +
+
+ Status: +
Done
+
+ +
+ Assignee: + + JD + + John Developer +
+ +
+
+
+ + PM + + Product Manager + 2 hours ago +
+
+
+
+
+
+
+
+ + {/* Comment Input */} +
+
+
+ + {jiraTypedContent} + {jiraIsTyping && } + +
+
+
+
+ B +
+
+ I +
+
+ @ +
+
+
Comment
+
+
+
+
+
+ ) + + const renderWhatsAppCard = () => ( + + {/* WhatsApp Header */} +
+
+ WhatsApp + Send IM +
+
+
+
+
+
+
+ + {/* Chat Header */} +
+
+ Family Group +
+ 5 members +
+
+ + + {/* Messages Area */} +
+
+ + MO + +
+
+ Mom + 7:45 PM +
+
+
+
+
+
+
+ +
+ + DA + +
+
+ Dad + 8:12 PM +
+
+
+
+
+
+
+ + +
+ + {/* Message Input */} +
+
+
+ + {whatsappTypedContent} + {whatsappIsTyping && } + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ ) + + const renderCard = (cardType: string) => { + switch (cardType) { + case "gmail": + return renderGmailCard() + case "slack": + return renderSlackCard() + case "jira": + return renderJiraCard() + case "whatsapp": + return renderWhatsAppCard() + default: + return null + } + } + + const renderCurrentCardIcon = () => { + switch (cards[currentCardIndex]) { + case "gmail": + return ( + Gmail + ) + case "slack": + return ( + Slack + ) + case "jira": + return Jira + case "whatsapp": + return ( + WhatsApp + ) + default: + return null + } + } + + return ( +
+ {/* Carousel Container */} +
+
+ {/* Fade Gradients */} +
+
+ + {/* Cards Container with responsive scaling */} +
+ {cards.map((cardType, index) => ( +
+ {renderCard(cardType)} +
+ ))} +
+
+
+ + {/* Recording Pill - Fixed below cards with stronger indigo glow */} +
+
+ {/* Dynamic App Icon */} +
+ {renderCurrentCardIcon()} +
+ + {/* Extended Recording Wave Animation */} +
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => ( +
+ ))} +
+
+
+ + {/* CSS for wave animation */} + +
+ ) +} diff --git a/apps/www/components/ui/hero-demo.tsx b/apps/www/components/ui/hero-demo.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/apps/www/components/ui/hero-old.tsx b/apps/www/components/ui/hero-old.tsx new file mode 100644 index 0000000..d529f50 --- /dev/null +++ b/apps/www/components/ui/hero-old.tsx @@ -0,0 +1,29 @@ +"use client"; +import React from "react"; +import { WavyBackground } from "../ui/wavy-background"; + +export function Hero() { + return ( + +

+ Open Source AI Dictation App +

+

+ Type 10x faster, no keyboard needed. Fast, Accurate, Context-aware and Private. +

+ +
+ ); +} diff --git a/apps/www/components/ui/hero.tsx b/apps/www/components/ui/hero.tsx index d529f50..34986f7 100644 --- a/apps/www/components/ui/hero.tsx +++ b/apps/www/components/ui/hero.tsx @@ -1,29 +1,36 @@ "use client"; import React from "react"; -import { WavyBackground } from "../ui/wavy-background"; +import HeroCards from "./hero-cards"; export function Hero() { return ( - -

- Open Source AI Dictation App -

-

- Type 10x faster, no keyboard needed. Fast, Accurate, Context-aware and Private. -

-
- - Get Started - +
+
+ {/* Left side - Title and content */} +
+

+ Open Source
AI Dictation App +

+

+ Type 10x faster, no keyboard needed. Fast, Accurate, Context-aware and Private. +

+
- + + {/* Right side - Hero cards */} +
+ +
+
+
); } diff --git a/apps/www/package.json b/apps/www/package.json index eb88532..bacac39 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -13,6 +13,7 @@ "dependencies": { "@next/third-parties": "^15.3.2", "@radix-ui/react-accordion": "^1.2.10", + "@radix-ui/react-avatar": "^1.1.9", "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-dialog": "^1.1.13", "@radix-ui/react-label": "^2.1.6", diff --git a/apps/www/public/integrations/whatsapp.svg b/apps/www/public/integrations/whatsapp.svg index 9cbdb11..f618754 100644 --- a/apps/www/public/integrations/whatsapp.svg +++ b/apps/www/public/integrations/whatsapp.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eacb29f..3f87af2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -288,6 +288,9 @@ importers: '@radix-ui/react-accordion': specifier: ^1.2.10 version: 1.2.11(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-avatar': + specifier: ^1.1.9 + version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-checkbox': specifier: ^1.3.1 version: 1.3.2(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)