From e236bbb3894a8aa85d02b37e7a70d6fd51a84412 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:02:37 -0500 Subject: [PATCH 01/70] Initial smoother chat session loading behavior --- .../components/welcome-experience.tsx | 136 ++++++++++++++++++ .../[name]/sessions/[sessionName]/page.tsx | 83 ++++------- .../app/projects/[name]/sessions/new/page.tsx | 1 - .../src/components/create-session-dialog.tsx | 1 - .../src/components/session/MessagesTab.tsx | 19 ++- 5 files changed, 179 insertions(+), 61 deletions(-) create mode 100644 components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx new file mode 100644 index 000000000..9789e77d2 --- /dev/null +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Sparkles } from "lucide-react"; +import type { WorkflowConfig } from "../lib/types"; + +type WelcomeExperienceProps = { + ootbWorkflows: WorkflowConfig[]; + onWorkflowSelect: (workflowId: string) => void; + onUserInteraction: () => void; + userHasInteracted: boolean; + sessionPhase?: string; +}; + +const WELCOME_MESSAGE = `Welcome to Ambient AI! Your workspace and all of its context have been loaded. Please select a workflow below to get started, or type a message to begin chatting.`; + +export function WelcomeExperience({ + ootbWorkflows, + onWorkflowSelect, + onUserInteraction, + userHasInteracted, + sessionPhase, +}: WelcomeExperienceProps) { + const [displayedText, setDisplayedText] = useState(""); + const [isTypingComplete, setIsTypingComplete] = useState(false); + const [selectedWorkflowId, setSelectedWorkflowId] = useState(null); + + // Determine if we should show workflow cards (only for Pending/Creating phases) + const isInitialPhase = sessionPhase === "Pending" || sessionPhase === "Creating"; + const shouldShowAnimation = isInitialPhase && !userHasInteracted; + const shouldShowWorkflowCards = isInitialPhase && !userHasInteracted; + + // Streaming text effect + useEffect(() => { + if (!shouldShowAnimation) { + // Skip animation if session is already running or user has interacted + setDisplayedText(WELCOME_MESSAGE); + setIsTypingComplete(true); + return; + } + + let currentIndex = 0; + const interval = setInterval(() => { + if (currentIndex < WELCOME_MESSAGE.length) { + setDisplayedText(WELCOME_MESSAGE.slice(0, currentIndex + 1)); + currentIndex++; + } else { + setIsTypingComplete(true); + clearInterval(interval); + } + }, 50); // 50ms per character + + return () => clearInterval(interval); + }, [shouldShowAnimation]); + + const handleWorkflowSelect = (workflowId: string) => { + setSelectedWorkflowId(workflowId); + onWorkflowSelect(workflowId); + onUserInteraction(); + }; + + const enabledWorkflows = ootbWorkflows.filter((w) => w.enabled); + + return ( +
+ {/* Static welcome message styled like a chat message */} +
+
+ +
+
+
+ Ambient AI + just now +
+
+ {displayedText} + {shouldShowAnimation && !isTypingComplete && ( + + )} +
+
+
+ + {/* Workflow cards - show after typing completes (only for initial phases) */} + {shouldShowWorkflowCards && isTypingComplete && enabledWorkflows.length > 0 && ( +
+

+ Suggested workflows: +

+
+ {enabledWorkflows.map((workflow) => ( + { + if (selectedWorkflowId === null) { + handleWorkflowSelect(workflow.id); + } + }} + > + +
+

{workflow.name}

+ {selectedWorkflowId === workflow.id && ( + + Selected + + )} +
+

+ {workflow.description} +

+ {workflow.agentCount !== undefined && workflow.agentCount > 0 && ( + + {workflow.agentCount} agent{workflow.agentCount !== 1 ? "s" : ""} + + )} +
+
+ ))} +
+
+ )} +
+ ); +} + diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx index 8c964eeb2..70e3aa218 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx @@ -16,8 +16,6 @@ import { Cloud, FolderSync, Download, - LibraryBig, - MessageSquare, SlidersHorizontal, ArrowLeft, } from "lucide-react"; @@ -73,7 +71,7 @@ import { WorkflowsAccordion } from "./components/accordions/workflows-accordion" import { RepositoriesAccordion } from "./components/accordions/repositories-accordion"; import { ArtifactsAccordion } from "./components/accordions/artifacts-accordion"; import { McpIntegrationsAccordion } from "./components/accordions/mcp-integrations-accordion"; - +import { WelcomeExperience } from "./components/welcome-experience"; // Extracted hooks and utilities import { useGitOperations } from "./hooks/use-git-operations"; import { useWorkflowManagement } from "./hooks/use-workflow-management"; @@ -150,12 +148,12 @@ export default function ProjectSessionDetailPage({ const [sessionName, setSessionName] = useState(""); const [chatInput, setChatInput] = useState(""); const [backHref, setBackHref] = useState(null); - const [openAccordionItems, setOpenAccordionItems] = useState(["workflows"]); + const [openAccordionItems, setOpenAccordionItems] = useState([]); const [contextModalOpen, setContextModalOpen] = useState(false); const [uploadModalOpen, setUploadModalOpen] = useState(false); const [repoChanging, setRepoChanging] = useState(false); - const [firstMessageLoaded, setFirstMessageLoaded] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const [userHasInteracted, setUserHasInteracted] = useState(false); // Directory browser state (unified for artifacts, repos, and workflow) const [selectedDirectory, setSelectedDirectory] = useState({ @@ -504,13 +502,12 @@ export default function ProjectSessionDetailPage({ // Track if we've already initialized from session const initializedFromSessionRef = useRef(false); - // Track when first message loads + // Track user interaction (any message sent or workflow selected from welcome screen) useEffect(() => { - if (aguiState.messages && aguiState.messages.length > 0 && !firstMessageLoaded) { - setFirstMessageLoaded(true); + if (aguiState.messages && aguiState.messages.length > 0 && !userHasInteracted) { + setUserHasInteracted(true); } - }, [aguiState.messages, firstMessageLoaded]); - + }, [aguiState.messages, userHasInteracted]); // Load active workflow and remotes from session useEffect(() => { if (initializedFromSessionRef.current || !session) return; @@ -594,6 +591,15 @@ export default function ProjectSessionDetailPage({ ); }; + // Handle workflow selection from welcome experience + const handleWelcomeWorkflowSelect = (workflowId: string) => { + handleWorkflowChange(workflowId); + // Trigger activation immediately + setTimeout(() => { + workflowManagement.activateWorkflow(); + }, 100); + }; + // Convert AG-UI messages to display format with hierarchical tool call rendering const streamMessages: Array = useMemo(() => { @@ -1024,7 +1030,6 @@ export default function ProjectSessionDetailPage({ } }; }, [session?.status?.phase, refetchArtifactsFiles]); - // Session action handlers const handleStop = () => { stopMutation.mutate( @@ -1086,12 +1091,14 @@ export default function ProjectSessionDetailPage({ const finalMessage = chatInput.trim(); setChatInput(""); + // Mark user interaction when they send first message + setUserHasInteracted(true); + try { await aguiSendMessage(finalMessage); } catch (err) { errorToast(err instanceof Error ? err.message : "Failed to send message"); } - }; const handleCommandClick = async (slashCommand: string) => { try { @@ -1297,25 +1304,7 @@ export default function ProjectSessionDetailPage({ - {/* Blocking overlay when first message hasn't loaded and session is pending */} - {!firstMessageLoaded && - session?.status?.phase === "Pending" && ( -
-
- -
- -

No context yet

-
-

- Context will appear once the session starts... -

-
-
- )} -
+
)} - {/* Session starting overlay */} - {!firstMessageLoaded && - session?.status?.phase === "Pending" && ( -
-
- -
- -

No messages yet

-
-

- Messages will appear once the session starts... -

-
-
- )} - -
+
+ showWelcomeExperience={true} + welcomeExperienceComponent={ + setUserHasInteracted(true)} + userHasInteracted={userHasInteracted} + sessionPhase={session?.status?.phase} + /> + }
diff --git a/components/frontend/src/app/projects/[name]/sessions/new/page.tsx b/components/frontend/src/app/projects/[name]/sessions/new/page.tsx index 5ec1ad2b2..efb7f0404 100644 --- a/components/frontend/src/app/projects/[name]/sessions/new/page.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/new/page.tsx @@ -130,7 +130,6 @@ export default function NewProjectSessionPage({ params }: { params: Promise<{ na { onSuccess: (session) => { const sessionName = session.metadata.name; - successToast(`Session "${sessionName}" created successfully`); router.push(`/projects/${encodeURIComponent(projectName)}/sessions/${sessionName}`); }, onError: (error) => { diff --git a/components/frontend/src/components/create-session-dialog.tsx b/components/frontend/src/components/create-session-dialog.tsx index d33f1baee..d8d689dbd 100644 --- a/components/frontend/src/components/create-session-dialog.tsx +++ b/components/frontend/src/components/create-session-dialog.tsx @@ -97,7 +97,6 @@ export function CreateSessionDialog({ { onSuccess: (session) => { const sessionName = session.metadata.name; - successToast(`Session "${sessionName}" created successfully`); setOpen(false); form.reset(); router.push(`/projects/${encodeURIComponent(projectName)}/sessions/${sessionName}`); diff --git a/components/frontend/src/components/session/MessagesTab.tsx b/components/frontend/src/components/session/MessagesTab.tsx index 014defb4e..1c1fd582b 100644 --- a/components/frontend/src/components/session/MessagesTab.tsx +++ b/components/frontend/src/components/session/MessagesTab.tsx @@ -29,12 +29,13 @@ export type MessagesTabProps = { onContinue: () => void; workflowMetadata?: WorkflowMetadata; onCommandClick?: (slashCommand: string) => void; - isRunActive?: boolean; // NEW: Track if agent is actively processing + isRunActive?: boolean; // Track if agent is actively processing + showWelcomeExperience?: boolean; + welcomeExperienceComponent?: React.ReactNode; }; -const MessagesTab: React.FC = ({ session, streamMessages, chatInput, setChatInput, onSendChat, onInterrupt, onEndSession, onGoToResults, onContinue, workflowMetadata, onCommandClick, isRunActive = false }) => { - const [sendingChat, setSendingChat] = useState(false); +const MessagesTab: React.FC = ({ session, streamMessages, chatInput, setChatInput, onSendChat, onInterrupt, onEndSession, onGoToResults, onContinue, workflowMetadata, onCommandClick, isRunActive = false, showWelcomeExperience, welcomeExperienceComponent }) => { const [interrupting, setInterrupting] = useState(false); const [ending, setEnding] = useState(false); const [showSystemMessages, setShowSystemMessages] = useState(false); @@ -56,8 +57,8 @@ const MessagesTab: React.FC = ({ session, streamMessages, chat const phase = session?.status?.phase || ""; const isInteractive = session?.spec?.interactive; - // Only show chat interface when session is interactive AND in Running state - const showChatInterface = isInteractive && phase === "Running"; + // Show chat interface when session is interactive AND (in Running state OR showing welcome experience) + const showChatInterface = isInteractive && (phase === "Running" || showWelcomeExperience); // Determine if session is in a terminal state const isTerminalState = ["Completed", "Failed", "Stopped"].includes(phase); @@ -275,6 +276,10 @@ const MessagesTab: React.FC = ({ session, streamMessages, chat onScroll={handleScroll} className="flex-1 flex flex-col gap-2 overflow-y-auto px-3 pb-2 scrollbar-thin" > + {/* Show welcome experience if active */} + {showWelcomeExperience && welcomeExperienceComponent} + + {/* Show filtered messages */} {filteredMessages.map((m, idx) => ( ))} @@ -298,8 +303,8 @@ const MessagesTab: React.FC = ({ session, streamMessages, chat
)} - {filteredMessages.length === 0 && !isCreating && ( -
+ {/* Show empty state only if no welcome experience, no messages, and not creating */} + {!showWelcomeExperience && filteredMessages.length === 0 && !isCreating && (

No messages yet

From de9be83482d21eb015aa93f1c75056025fb685f7 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:06:18 -0500 Subject: [PATCH 02/70] Adjust workflow card selection behavior --- .../components/welcome-experience.tsx | 46 +++++++++++-------- .../[name]/sessions/[sessionName]/page.tsx | 1 + 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx index 9789e77d2..73d4f39d2 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { Sparkles } from "lucide-react"; +import { cn } from "@/lib/utils"; import type { WorkflowConfig } from "../lib/types"; type WelcomeExperienceProps = { @@ -27,10 +27,12 @@ export function WelcomeExperience({ const [isTypingComplete, setIsTypingComplete] = useState(false); const [selectedWorkflowId, setSelectedWorkflowId] = useState(null); - // Determine if we should show workflow cards (only for Pending/Creating phases) + // Determine if we should show workflow cards and animation const isInitialPhase = sessionPhase === "Pending" || sessionPhase === "Creating"; const shouldShowAnimation = isInitialPhase && !userHasInteracted; - const shouldShowWorkflowCards = isInitialPhase && !userHasInteracted; + // Show workflow cards until user interacts (regardless of session phase, unless it's a terminal state) + const isTerminalPhase = sessionPhase === "Completed" || sessionPhase === "Failed" || sessionPhase === "Stopped"; + const shouldShowWorkflowCards = !userHasInteracted && !isTerminalPhase; // Streaming text effect useEffect(() => { @@ -50,7 +52,7 @@ export function WelcomeExperience({ setIsTypingComplete(true); clearInterval(interval); } - }, 50); // 50ms per character + }, 25); // 25ms per character return () => clearInterval(interval); }, [shouldShowAnimation]); @@ -66,20 +68,28 @@ export function WelcomeExperience({ return (

{/* Static welcome message styled like a chat message */} -
-
- -
-
-
- Ambient AI - just now +
+
+ {/* Avatar */} +
+
+ AI +
-
- {displayedText} - {shouldShowAnimation && !isTypingComplete && ( - - )} + + {/* Message Content */} +
+ {/* Timestamp */} +
just now
+
+ {/* Content */} +
+ {displayedText} + {shouldShowAnimation && !isTypingComplete && ( + + )} +
+
@@ -90,7 +100,7 @@ export function WelcomeExperience({

Suggested workflows:

-
+
{enabledWorkflows.map((workflow) => ( { if (initializedFromSessionRef.current || !session) return; From 4f1ff457004c3a9eadf9b3fe5de1dfc319682068 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:11:52 -0500 Subject: [PATCH 03/70] Improve visual styling of switching between workflows in panel --- .../accordions/workflows-accordion.tsx | 165 +++++++++--------- 1 file changed, 80 insertions(+), 85 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx index 25c9ac85e..b43b03cea 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx @@ -72,98 +72,93 @@ export function WorkflowsAccordion({
) : (
- {/* Workflow selector - always visible except when activating */} - {!workflowActivating && ( - <> -

- Workflows provide agents with pre-defined context and structured steps to follow. -

- -
- -
- - {/* Show workflow preview and activate/switch button */} - {pendingWorkflow && ( - - - - Reload required - - -
-

- Please reload this chat session to switch to the new workflow. Your chat history will be preserved. -

- -
-
-
- )} - - )} + {/* Workflow selector - always visible */} +

+ Workflows provide agents with pre-defined context and structured steps to follow. +

- {/* Show active workflow info */} - {activeWorkflow && !workflowActivating && ( - <> - )} +
+ +
- {/* Show activating/switching state */} - {workflowActivating && ( - - - {activeWorkflow ? 'Switching Workflow...' : 'Activating Workflow...'} + {/* Show workflow preview and activate/switch button */} + {!workflowActivating && pendingWorkflow && ( + + + + Reload required + -
-

Please wait. This may take 10-20 seconds...

+
+

+ Please reload this chat session to switch to the new workflow. Your chat history will be preserved. +

+
)} + + {/* Show active workflow info */} + {activeWorkflow && !workflowActivating && ( + <> + )}
)} From abaa6ce04ff173c2e3d7d9d18681346e2ac2dd24 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:14:41 -0500 Subject: [PATCH 04/70] Switch workflows immediately on click --- .../accordions/workflows-accordion.tsx | 34 ++++--------------- .../[name]/sessions/[sessionName]/page.tsx | 10 +++--- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx index b43b03cea..f795785c9 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/workflows-accordion.tsx @@ -1,23 +1,26 @@ "use client"; -import { Play, Loader2, Workflow, AlertCircle } from "lucide-react"; +import { useState } from "react"; +import { Play, Loader2, Workflow, ChevronDown, ChevronRight, Info, AlertCircle } from "lucide-react"; import { AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import type { WorkflowConfig } from "../../lib/types"; type WorkflowsAccordionProps = { sessionPhase?: string; activeWorkflow: string | null; selectedWorkflow: string; - pendingWorkflow: WorkflowConfig | null; + pendingWorkflow?: string | null; workflowActivating: boolean; ootbWorkflows: WorkflowConfig[]; isExpanded: boolean; onWorkflowChange: (value: string) => void; onActivateWorkflow: () => void; + onCommandClick?: (slashCommand: string) => void; onResume?: () => void; }; @@ -31,6 +34,7 @@ export function WorkflowsAccordion({ isExpanded, onWorkflowChange, onActivateWorkflow, + onCommandClick, onResume, }: WorkflowsAccordionProps) { const isSessionStopped = sessionPhase === 'Stopped' || sessionPhase === 'Error' || sessionPhase === 'Completed'; @@ -130,31 +134,6 @@ export function WorkflowsAccordion({
- {/* Show workflow preview and activate/switch button */} - {!workflowActivating && pendingWorkflow && ( - - - - Reload required - - -
-

- Please reload this chat session to switch to the new workflow. Your chat history will be preserved. -

- -
-
-
- )} - {/* Show active workflow info */} {activeWorkflow && !workflowActivating && ( <> @@ -165,4 +144,3 @@ export function WorkflowsAccordion({ ); } - diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx index da3d02708..12ddf8df4 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx @@ -590,15 +590,15 @@ export default function ProjectSessionDetailPage({ workflowManagement.handleWorkflowChange(value, ootbWorkflows, () => setCustomWorkflowDialogOpen(true), ); + // Automatically trigger activation after workflow change + setTimeout(() => { + workflowManagement.activateWorkflow(); + }, 100); }; // Handle workflow selection from welcome experience const handleWelcomeWorkflowSelect = (workflowId: string) => { handleWorkflowChange(workflowId); - // Trigger activation immediately - setTimeout(() => { - workflowManagement.activateWorkflow(); - }, 100); }; // Convert AG-UI messages to display format with hierarchical tool call rendering @@ -1316,12 +1316,12 @@ export default function ProjectSessionDetailPage({ sessionPhase={session?.status?.phase} activeWorkflow={workflowManagement.activeWorkflow} selectedWorkflow={workflowManagement.selectedWorkflow} - pendingWorkflow={workflowManagement.pendingWorkflow} workflowActivating={workflowManagement.workflowActivating} ootbWorkflows={ootbWorkflows} isExpanded={openAccordionItems.includes("workflows")} onWorkflowChange={handleWorkflowChange} onActivateWorkflow={workflowManagement.activateWorkflow} + onCommandClick={handleCommandClick} onResume={handleContinue} /> From 4e4c3767ab1753dbae388b3330df9ce974dcb0a0 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:21:19 -0500 Subject: [PATCH 05/70] Remove toasts for starting and switching workflows --- .../sessions/[sessionName]/hooks/use-workflow-management.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/hooks/use-workflow-management.ts b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/hooks/use-workflow-management.ts index 654823911..fe830bf00 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/hooks/use-workflow-management.ts +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/hooks/use-workflow-management.ts @@ -1,7 +1,7 @@ "use client"; import { useState, useCallback } from "react"; -import { successToast, errorToast } from "@/hooks/use-toast"; +import { errorToast } from "@/hooks/use-toast"; import type { WorkflowConfig } from "../lib/types"; type UseWorkflowManagementProps = { @@ -51,7 +51,6 @@ export function useWorkflowManagement({ // Note: Workflow clone and restart handled by operator // Initial workflow prompt auto-executed via AG-UI pattern (POST /agui/run) - successToast(`Activating workflow: ${pendingWorkflow.name}`); setActiveWorkflow(pendingWorkflow.id); setPendingWorkflow(null); @@ -59,7 +58,6 @@ export function useWorkflowManagement({ await new Promise(resolve => setTimeout(resolve, 3000)); onWorkflowActivated?.(); - successToast("Workflow activated successfully"); return true; } catch (error) { From 738945a6ac4eedc3fe4d86ab2b14dcde0d755086 Mon Sep 17 00:00:00 2001 From: Andy Braren Date: Tue, 16 Dec 2025 17:59:17 -0500 Subject: [PATCH 06/70] Adjust workflow card styling --- .../components/welcome-experience.tsx | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx index 73d4f39d2..3f1b9551a 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/welcome-experience.tsx @@ -12,6 +12,7 @@ type WelcomeExperienceProps = { onUserInteraction: () => void; userHasInteracted: boolean; sessionPhase?: string; + hasRealMessages: boolean; }; const WELCOME_MESSAGE = `Welcome to Ambient AI! Your workspace and all of its context have been loaded. Please select a workflow below to get started, or type a message to begin chatting.`; @@ -22,17 +23,19 @@ export function WelcomeExperience({ onUserInteraction, userHasInteracted, sessionPhase, + hasRealMessages, }: WelcomeExperienceProps) { const [displayedText, setDisplayedText] = useState(""); const [isTypingComplete, setIsTypingComplete] = useState(false); const [selectedWorkflowId, setSelectedWorkflowId] = useState(null); + const [cardsEverShown, setCardsEverShown] = useState(false); // Determine if we should show workflow cards and animation const isInitialPhase = sessionPhase === "Pending" || sessionPhase === "Creating"; const shouldShowAnimation = isInitialPhase && !userHasInteracted; - // Show workflow cards until user interacts (regardless of session phase, unless it's a terminal state) + // Show workflow cards if they haven't been shown yet and session is not in terminal state const isTerminalPhase = sessionPhase === "Completed" || sessionPhase === "Failed" || sessionPhase === "Stopped"; - const shouldShowWorkflowCards = !userHasInteracted && !isTerminalPhase; + const shouldShowWorkflowCards = (cardsEverShown || !hasRealMessages) && !isTerminalPhase; // Streaming text effect useEffect(() => { @@ -57,6 +60,13 @@ export function WelcomeExperience({ return () => clearInterval(interval); }, [shouldShowAnimation]); + // Track when cards are shown for the first time + useEffect(() => { + if (isTypingComplete && !hasRealMessages && !isTerminalPhase) { + setCardsEverShown(true); + } + }, [isTypingComplete, hasRealMessages, isTerminalPhase]); + const handleWorkflowSelect = (workflowId: string) => { setSelectedWorkflowId(workflowId); onWorkflowSelect(workflowId); @@ -66,6 +76,21 @@ export function WelcomeExperience({ const enabledWorkflows = ootbWorkflows.filter((w) => w.enabled); return ( + <> +