diff --git a/frontend/components/navigation.tsx b/frontend/components/navigation.tsx index 2d4d64df..28422445 100644 --- a/frontend/components/navigation.tsx +++ b/frontend/components/navigation.tsx @@ -594,8 +594,8 @@ export function Navigation({ No documents yet ) : ( - newConversationFiles?.map((file) => ( -
+ newConversationFiles?.map((file, index) => ( +
{file}
diff --git a/frontend/src/app/chat/components/user-message.tsx b/frontend/src/app/chat/components/user-message.tsx index 21c97f4f..bb3ff10f 100644 --- a/frontend/src/app/chat/components/user-message.tsx +++ b/frontend/src/app/chat/components/user-message.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"; import { Message } from "./message"; interface UserMessageProps { - content: string; + content: string | undefined; isCompleted?: boolean; animate?: boolean; files?: string; diff --git a/frontend/src/app/chat/page.tsx b/frontend/src/app/chat/page.tsx index 81144644..f34b1efb 100644 --- a/frontend/src/app/chat/page.tsx +++ b/frontend/src/app/chat/page.tsx @@ -9,7 +9,7 @@ import { type EndpointType, useChat } from "@/contexts/chat-context"; import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context"; import { useTask } from "@/contexts/task-context"; import { useChatStreaming } from "@/hooks/useChatStreaming"; -import { FILES_REGEX } from "@/lib/constants"; +import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants"; import { useLoadingStore } from "@/stores/loadingStore"; import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery"; import { AssistantMessage } from "./components/assistant-message"; @@ -911,9 +911,9 @@ function ChatPage() { } // Only send message if there's input text - if (input.trim()) { + if (input.trim() || uploadedFile) { // Pass the responseId from upload (if any) to handleSendMessage - handleSendMessage(input, uploadedResponseId || undefined); + handleSendMessage(!input.trim() ? FILE_CONFIRMATION : input, uploadedResponseId || undefined); } }; @@ -1154,6 +1154,8 @@ function ChatPage() { } }; + console.log(messages) + return ( <> {/* Debug header - only show in debug mode */} @@ -1236,7 +1238,10 @@ function ChatPage() { ? message.source !== "langflow" : false } - content={message.content} + content={index >= 2 + && (messages[index - 2]?.content.match( + FILES_REGEX, + )?.[0] ?? undefined) && message.content === FILE_CONFIRMATION ? undefined : message.content} files={ index >= 2 ? messages[index - 2]?.content.match( diff --git a/frontend/src/app/onboarding/components/animated-provider-steps.tsx b/frontend/src/app/onboarding/components/animated-provider-steps.tsx index 0dc334aa..b97113dc 100644 --- a/frontend/src/app/onboarding/components/animated-provider-steps.tsx +++ b/frontend/src/app/onboarding/components/animated-provider-steps.tsx @@ -19,34 +19,30 @@ export function AnimatedProviderSteps({ setCurrentStep, steps, storageKey = "provider-steps", + processingStartTime, }: { currentStep: number; isCompleted: boolean; setCurrentStep: (step: number) => void; steps: string[]; storageKey?: string; + processingStartTime?: number | null; }) { const [startTime, setStartTime] = useState(null); const [elapsedTime, setElapsedTime] = useState(0); - // Initialize start time from local storage or set new one + // Initialize start time from prop or local storage useEffect(() => { - const storedStartTime = localStorage.getItem(`${storageKey}-start`); const storedElapsedTime = localStorage.getItem(`${storageKey}-elapsed`); if (isCompleted && storedElapsedTime) { // If completed, use stored elapsed time setElapsedTime(parseFloat(storedElapsedTime)); - } else if (storedStartTime) { - // If in progress, use stored start time - setStartTime(parseInt(storedStartTime)); - } else { - // First time, set new start time - const now = Date.now(); - setStartTime(now); - localStorage.setItem(`${storageKey}-start`, now.toString()); + } else if (processingStartTime) { + // Use the start time passed from parent (when user clicked Complete) + setStartTime(processingStartTime); } - }, [storageKey, isCompleted]); + }, [storageKey, isCompleted, processingStartTime]); // Progress through steps useEffect(() => { @@ -64,7 +60,6 @@ export function AnimatedProviderSteps({ const elapsed = Date.now() - startTime; setElapsedTime(elapsed); localStorage.setItem(`${storageKey}-elapsed`, elapsed.toString()); - localStorage.removeItem(`${storageKey}-start`); } }, [isCompleted, startTime, storageKey]); diff --git a/frontend/src/app/onboarding/components/onboarding-card.tsx b/frontend/src/app/onboarding/components/onboarding-card.tsx index e379976b..4414b601 100644 --- a/frontend/src/app/onboarding/components/onboarding-card.tsx +++ b/frontend/src/app/onboarding/components/onboarding-card.tsx @@ -32,12 +32,11 @@ interface OnboardingCardProps { setLoadingStatus?: (status: string[]) => void; } - const STEP_LIST = [ - "Setting up your model provider", - "Defining schema", - "Configuring Langflow", - "Ingesting sample data", + "Setting up your model provider", + "Defining schema", + "Configuring Langflow", + "Ingesting sample data", ]; const TOTAL_PROVIDER_STEPS = STEP_LIST.length; @@ -106,7 +105,13 @@ const OnboardingCard = ({ llm_model: "", }); - const [currentStep, setCurrentStep] = useState(isCompleted ? TOTAL_PROVIDER_STEPS : null); + const [currentStep, setCurrentStep] = useState( + isCompleted ? TOTAL_PROVIDER_STEPS : null, + ); + + const [processingStartTime, setProcessingStartTime] = useState( + null, + ); // Query tasks to track completion const { data: tasks } = useGetTasksQuery({ @@ -131,8 +136,8 @@ const OnboardingCard = ({ // If no active tasks and we've started onboarding, complete it if ( (!activeTasks || (activeTasks.processed_files ?? 0) > 0) && - tasks.length > 0 - && !isCompleted + tasks.length > 0 && + !isCompleted ) { // Set to final step to show "Done" setCurrentStep(TOTAL_PROVIDER_STEPS); @@ -189,6 +194,8 @@ const OnboardingCard = ({ onboardingData.project_id = settings.project_id; } + // Record the start time when user clicks Complete + setProcessingStartTime(Date.now()); onboardingMutation.mutate(onboardingData); setCurrentStep(0); }; @@ -211,30 +218,55 @@ const OnboardingCard = ({ onValueChange={handleSetModelProvider} > - -
- + +
+
OpenAI
- -
- + +
+
IBM watsonx.ai
- -
+ +
@@ -285,11 +317,13 @@ const OnboardingCard = ({ {!isComplete && ( - {isLoadingModels ? "Loading models..." : (!!settings.llm_model && - !!settings.embedding_model && - !isDoclingHealthy - ? "docling-serve must be running to continue" - : "Please fill in all required fields")} + {isLoadingModels + ? "Loading models..." + : !!settings.llm_model && + !!settings.embedding_model && + !isDoclingHealthy + ? "docling-serve must be running to continue" + : "Please fill in all required fields"} )} @@ -303,11 +337,12 @@ const OnboardingCard = ({ transition={{ duration: 0.4, ease: "easeInOut" }} > + currentStep={currentStep} + isCompleted={isCompleted} + setCurrentStep={setCurrentStep} + steps={STEP_LIST} + processingStartTime={processingStartTime} + /> )} diff --git a/frontend/src/lib/constants.ts b/frontend/src/lib/constants.ts index dfd7358a..4b74a5db 100644 --- a/frontend/src/lib/constants.ts +++ b/frontend/src/lib/constants.ts @@ -35,4 +35,6 @@ export const TOTAL_ONBOARDING_STEPS = 3; export const ONBOARDING_STEP_KEY = "onboarding_current_step"; export const FILES_REGEX = - /(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/; \ No newline at end of file + /(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/; + +export const FILE_CONFIRMATION = "Confirm that you received this file."; \ No newline at end of file