From 82907b46015ec6c9ac9a45af601bfd69c29d99fb Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sun, 14 Jun 2026 02:18:53 -0700 Subject: [PATCH] [eric] onboarding polish: exaggerated cursor pop + longer wait then prompt click; faster greeting, no caret; chips slide in smoothly (no instant pop); title 'First chat with OpenSwarm', no draft label --- .../Onboarding/ac/AgenticCursor.tsx | 8 +++--- .../Onboarding/steps/step00_welcomeNudge.ts | 4 +-- .../pages/AgentChat/WelcomeQuickReplies.tsx | 25 +++++++++++-------- .../app/pages/Dashboard/cards/AgentCard.tsx | 5 ++-- frontend/src/shared/state/agentsSlice.ts | 2 +- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/components/Onboarding/ac/AgenticCursor.tsx b/frontend/src/app/components/Onboarding/ac/AgenticCursor.tsx index 85edbcf0..d7207a5b 100644 --- a/frontend/src/app/components/Onboarding/ac/AgenticCursor.tsx +++ b/frontend/src/app/components/Onboarding/ac/AgenticCursor.tsx @@ -120,13 +120,13 @@ const AgenticCursor = forwardRef((_props, ref) => { controls.set({ x: from.x, y: from.y, opacity: 0, scale: 0.3 }); setVisible(true); setBurstKey((k) => k + 1); // replay the orange spark on each pop - // A little "pop!": scale overshoots past 1 then settles, while opacity snaps in. + // An exaggerated "POP!": scale shoots way past 1 then settles back, opacity snaps in. await controls.start({ opacity: 1, - scale: [0.3, 1.15, 1], + scale: [0.1, 1.45, 0.92, 1], transition: { - opacity: { duration: 0.16, ease: 'easeOut' }, - scale: { duration: 0.36, ease: 'easeOut', times: [0, 0.62, 1] }, + opacity: { duration: 0.14, ease: 'easeOut' }, + scale: { duration: 0.5, ease: 'easeOut', times: [0, 0.55, 0.8, 1] }, }, }); }, diff --git a/frontend/src/app/components/Onboarding/steps/step00_welcomeNudge.ts b/frontend/src/app/components/Onboarding/steps/step00_welcomeNudge.ts index c126f813..f5b21c15 100644 --- a/frontend/src/app/components/Onboarding/steps/step00_welcomeNudge.ts +++ b/frontend/src/app/components/Onboarding/steps/step00_welcomeNudge.ts @@ -11,10 +11,10 @@ export const welcomeOpenStep: OnboardingStep = { title: 'Welcome', description: '', ops: [ - { kind: 'delay', ms: 400 }, // brief beat after the pop + spark + { kind: 'delay', ms: 900 }, // let the big POP land + breathe { kind: 'move_to', target: S.newAgentButton }, { kind: 'popup', text: 'Let me open a chat for you.' }, - { kind: 'delay', ms: 650 }, // a moment to read, then click + { kind: 'delay', ms: 350 }, // quick read, then click promptly { kind: 'click', target: S.newAgentButton, simulate: true }, // spawns the welcome chat { kind: 'outro' }, ], diff --git a/frontend/src/app/pages/AgentChat/WelcomeQuickReplies.tsx b/frontend/src/app/pages/AgentChat/WelcomeQuickReplies.tsx index 75b5cb52..e2d2c6af 100644 --- a/frontend/src/app/pages/AgentChat/WelcomeQuickReplies.tsx +++ b/frontend/src/app/pages/AgentChat/WelcomeQuickReplies.tsx @@ -36,9 +36,9 @@ const WelcomeQuickReplies: React.FC<{ onPick: (prompt: string) => void; onPickBuilder: (prompt: string) => void; }> = ({ c, onPick, onPickBuilder }) => { - // Slow + delayed so it reads as a calm sequence: card pops, the header title streams, THEN - // the greeting types out unhurried, THEN the chips pop in. - const { shown: greeting, done: greetingDone } = useTypewriter(GREETING, 78, 850); + // Sequence: card pops, the header title streams, THEN the greeting types out, THEN the chips + // slide in. Brisk but smooth. + const { shown: greeting, done: greetingDone } = useTypewriter(GREETING, 42, 450); const [expanded, setExpanded] = React.useState(null); const currentCategory = STARTER_CATEGORIES.find((cat) => cat.id === expanded); const isAppBuilder = currentCategory?.target === 'app-builder'; @@ -65,14 +65,17 @@ const WelcomeQuickReplies: React.FC<{ }} > {greeting} - {!greetingDone && ( - - )} - {/* Chips reveal only once the greeting finishes, each popping in with a stagger. */} + {/* The chips block slides up + fades in once the greeting finishes (the outer motion.div), + then each chip springs in staggered (inner). */} {greetingDone && ( - + {expanded === null ? ( @@ -86,9 +89,9 @@ const WelcomeQuickReplies: React.FC<{ setExpanded(cat.id)} - initial={{ opacity: 0, scale: 0.82 }} + initial={{ opacity: 0, scale: 0.85 }} animate={{ opacity: 1, scale: 1 }} - transition={{ type: 'spring', stiffness: 420, damping: 22, delay: 0.25 + i * 0.18 }} + transition={{ type: 'spring', stiffness: 480, damping: 24, delay: 0.12 + i * 0.08 }} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px', @@ -155,7 +158,7 @@ const WelcomeQuickReplies: React.FC<{ )} - + )} ); diff --git a/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx b/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx index 7fab5de5..adaaf2be 100644 --- a/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx @@ -756,8 +756,9 @@ const AgentCard: React.FC = ({ )} - {/* Status speaks only when it needs the user; finished work sits quiet. */} - {session.status !== 'completed' && session.status !== 'stopped' && ( + {/* Status speaks only when it needs the user; finished work sits quiet. The welcome + chat hides its 'draft' label so the title reads clean. */} + {session.status !== 'completed' && session.status !== 'stopped' && !session.is_welcome_draft && ( {friendlyStatusLabel(session.status)} diff --git a/frontend/src/shared/state/agentsSlice.ts b/frontend/src/shared/state/agentsSlice.ts index 48020937..d64f52df 100644 --- a/frontend/src/shared/state/agentsSlice.ts +++ b/frontend/src/shared/state/agentsSlice.ts @@ -523,7 +523,7 @@ const agentsSlice = createSlice({ const { draftId, mode, setActive, targetDirectory, model, provider, thinkingLevel, seededMessages, welcome, dashboardId } = action.payload; state.sessions[draftId] = { id: draftId, - name: 'New chat', + name: welcome === true ? 'First chat with OpenSwarm' : 'New chat', status: 'draft', provider: provider || 'anthropic', model: model || 'sonnet',