diff --git a/gui/src/App.tsx b/gui/src/App.tsx index fcf79b1810..75e68be9bb 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -20,9 +20,10 @@ import Onboarding from "./pages/onboarding/Onboarding"; import SettingsPage from "./pages/settings"; import Stats from "./pages/stats"; import Inventory from "./pages/inventory"; -import AiderGUI from "./integrations/aider/aidergui"; -import PerplexityGUI from "./integrations/perplexity/perplexitygui"; - +import AiderGUI from "./integrations/aider/aiderGui"; +import AiderOnboarding from "./integrations/aider/aiderOnboarding"; +import PerplexityGUI from "./integrations/perplexity/perplexityGui"; +import PerplexityOnboarding from "./integrations/perplexity/perplexityOnboarding"; declare global { interface Window { @@ -49,10 +50,18 @@ const router = createMemoryRouter( path: "/aiderMode", element: , }, + { + path: "/aiderOnboarding", + element: , + }, { path: "/perplexityMode", element: , }, + { + path: "/perplexityOnboarding", + element: , + }, { path: "/history", element: , diff --git a/gui/src/integrations/aider/aiderOnboarding.tsx b/gui/src/integrations/aider/aiderOnboarding.tsx new file mode 100644 index 0000000000..b2eceb039c --- /dev/null +++ b/gui/src/integrations/aider/aiderOnboarding.tsx @@ -0,0 +1,144 @@ +import React, { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import styled, { keyframes } from 'styled-components'; +import { Button } from '../../components'; +import { getLocalStorage, setLocalStorage } from '../../util/localStorage'; + +const rumbleAnimation = keyframes` + 0% { transform: translate(0, 0); } + 25% { transform: translate(-5px, 5px); } + 50% { transform: translate(5px, -5px); } + 75% { transform: translate(-3px, -3px); } + 100% { transform: translate(0, 0); } +`; + +const fadeInUp = keyframes` + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +const glowPulse = keyframes` + 0% { box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); } + 50% { box-shadow: 0 0 20px rgba(255, 255, 255, 0.8); } + 100% { box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); } +`; + +const OnboardingContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 80vh; + padding: 2rem; + text-align: center; + max-width: 800px; + margin: 0 auto; + animation: ${rumbleAnimation} 0.5s ease-in-out; +`; + +const Title = styled.h1` + font-size: 2.5rem; + margin-bottom: 2rem; + color: ${props => props.theme.textColor}; + animation: ${fadeInUp} 0.8s ease-out forwards; + opacity: 0; +`; + +const Description = styled.p` + font-size: 1.2rem; + line-height: 1.6; + margin-bottom: 2rem; + color: ${props => props.theme.secondaryText}; + animation: ${fadeInUp} 0.8s ease-out 0.3s forwards; + opacity: 0; +`; + +const FeatureList = styled.ul` + list-style-type: none; + padding: 0; + margin-bottom: 2rem; + text-align: left; + animation: ${fadeInUp} 0.8s ease-out 0.6s forwards; + opacity: 0; +`; + +const FeatureItem = styled.li` + font-size: 1.1rem; + margin-bottom: 1rem; + padding-left: 1.5rem; + position: relative; + transition: transform 0.3s ease; + + &:before { + content: "→"; + position: absolute; + left: 0; + transition: transform 0.3s ease; + } + + &:hover { + transform: translateX(10px); + + &:before { + transform: rotate(90deg); + } + } +`; + +const StartButton = styled(Button)` + padding: 1rem 2rem; + font-size: 1.2rem; + animation: ${fadeInUp} 0.8s ease-out 0.9s forwards, ${glowPulse} 2s infinite; + opacity: 0; + transition: transform 0.3s ease; + + &:hover { + transform: scale(1.05); + } +`; + +const AiderOnboarding = () => { + const navigate = useNavigate(); + + useEffect(() => { + // Play a subtle rumble sound when the component mounts + const audio = new Audio('data:audio/wav;base64,UklGRjIAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAAABmYWN0BAAAAAAAAABkYXRhAAAAAA=='); + audio.play().catch(() => { + // Ignore audio play errors + }); + }, []); + + const handleStart = () => { + setLocalStorage('hasSeenAiderOnboarding', true); + navigate('/aiderMode'); + }; + + return ( + + Welcome to PearAI Creator + + Your AI-powered coding companion that helps you build and modify your projects effortlessly. + Just describe what you want to create or change, and we'll handle the implementation. + + + + Create new features by describing them in plain English + Fix bugs and improve existing code naturally + Refactor and optimize your codebase effortlessly + Get intelligent suggestions and solutions + + + + Okay! Let's start creating + + + ); +}; + +export default AiderOnboarding; diff --git a/gui/src/integrations/aider/aidergui.tsx b/gui/src/integrations/aider/aidergui.tsx index 590505157b..9e8f0b98e9 100644 --- a/gui/src/integrations/aider/aidergui.tsx +++ b/gui/src/integrations/aider/aidergui.tsx @@ -63,9 +63,16 @@ import { function AiderGUI() { + const navigate = useNavigate(); + useEffect(() => { + const hasSeenOnboarding = getLocalStorage('hasSeenAiderOnboarding'); + if (!hasSeenOnboarding) { + navigate('./aiderOnboarding'); + } + }, [navigate]); + const posthog = usePostHog(); const dispatch = useDispatch(); - const navigate = useNavigate(); const ideMessenger = useContext(IdeMessengerContext); const isBetaAccess = useSelector((state: RootState) => state.state.config.isBetaAccess); @@ -177,8 +184,10 @@ function AiderGUI() { return ( <> +
+

PearAI Creator - Beta

@@ -197,6 +206,13 @@ function AiderGUI() {

Ask for a feature, describe a bug, or ask for a change to your project. We'll take care of everything for you!

+ { + dispatch(newSession()); // Reset the state + setLocalStorage('hasSeenAiderOnboarding', false); // Reset onboarding flag + navigate("/aiderOnboarding"); + }} + >Return to Onboarding (Remove when in prod)
{state.history.map((item, index: number) => ( @@ -327,6 +343,8 @@ function AiderGUI() { Inventory } + + ); } diff --git a/gui/src/integrations/perplexity/perplexityOnboarding.tsx b/gui/src/integrations/perplexity/perplexityOnboarding.tsx new file mode 100644 index 0000000000..27909389c6 --- /dev/null +++ b/gui/src/integrations/perplexity/perplexityOnboarding.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import styled from 'styled-components'; +import { Button } from '../../components'; +import { getLocalStorage, setLocalStorage } from '../../util/localStorage'; + +const OnboardingContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 80vh; + padding: 2rem; + text-align: center; + max-width: 800px; + margin: 0 auto; +`; + +const Title = styled.h1` + font-size: 2.5rem; + margin-bottom: 2rem; + color: ${props => props.theme.textColor}; +`; + +const Description = styled.p` + font-size: 1.2rem; + line-height: 1.6; + margin-bottom: 2rem; + color: ${props => props.theme.secondaryText}; +`; + +const FeatureList = styled.ul` + list-style-type: none; + padding: 0; + margin-bottom: 2rem; + text-align: left; +`; + +const FeatureItem = styled.li` + font-size: 1.1rem; + margin-bottom: 1rem; + padding-left: 1.5rem; + position: relative; + + &:before { + content: "→"; + position: absolute; + left: 0; + } +`; + +const StartButton = styled(Button)` + padding: 1rem 2rem; + font-size: 1.2rem; +`; + +const PerplexityOnboarding = () => { + const navigate = useNavigate(); + + const handleStart = () => { + setLocalStorage('hasSeenPerplexityOnboarding', true); + navigate('/perplexityMode'); + }; + + return ( + + Welcome to PearAI Creator + + Your AI-powered coding companion that helps you build and modify your projects effortlessly. + Just describe what you want to create or change, and we'll handle the implementation. + + + + Create new features by describing them in plain English + Fix bugs and improve existing code naturally + Refactor and optimize your codebase effortlessly + Get intelligent suggestions and solutions + + + + Okay! Let's start creating + + + ); +}; + +export default PerplexityOnboarding; diff --git a/gui/src/integrations/perplexity/perplexitygui.tsx b/gui/src/integrations/perplexity/perplexitygui.tsx index a74eafd79c..894f113137 100644 --- a/gui/src/integrations/perplexity/perplexitygui.tsx +++ b/gui/src/integrations/perplexity/perplexitygui.tsx @@ -62,9 +62,16 @@ import { import { TopGuiDiv, StopButton, StepsDiv, NewSessionButton, fallbackRender } from "../../pages/gui"; function PerplexityGUI() { + const navigate = useNavigate(); + useEffect(() => { + const hasSeenOnboarding = getLocalStorage('hasSeenPerplexityOnboarding'); + if (!hasSeenOnboarding) { + navigate('./perplexityOnboarding'); + } + }, [navigate]); + const posthog = usePostHog(); const dispatch = useDispatch(); - const navigate = useNavigate(); const ideMessenger = useContext(IdeMessengerContext); const isBetaAccess = useSelector((state: RootState) => state.state.config.isBetaAccess); diff --git a/gui/src/util/localStorage.ts b/gui/src/util/localStorage.ts index 1eecda3637..ba61b08927 100644 --- a/gui/src/util/localStorage.ts +++ b/gui/src/util/localStorage.ts @@ -17,6 +17,8 @@ type LocalStorageTypes = { showTutorialCard: boolean; shownProfilesIntroduction: boolean; disableIndexing: boolean; + hasSeenAiderOnboarding: boolean; + hasSeenPerplexityOnboarding: boolean; }; export function getLocalStorage(