Merge branch 'main' into docs-check-onboarding

This commit is contained in:
Mendon Kissling 2025-10-29 13:15:58 -04:00 committed by GitHub
commit e77821dcc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 332 additions and 348 deletions

View file

@ -7,29 +7,29 @@ const DogIcon = ({ disabled = false, stroke, ...props }: DogIconProps) => {
// CSS for the stepped animation states // CSS for the stepped animation states
const animationCSS = ` const animationCSS = `
.state1 { animation: showState1 600ms infinite; } .state1 { animation: showDogState1 600ms infinite; }
.state2 { animation: showState2 600ms infinite; } .state2 { animation: showDogState2 600ms infinite; }
.state3 { animation: showState3 600ms infinite; } .state3 { animation: showDogState3 600ms infinite; }
.state4 { animation: showState4 600ms infinite; } .state4 { animation: showDogState4 600ms infinite; }
@keyframes showState1 { @keyframes showDogState1 {
0%, 24.99% { opacity: 1; } 0%, 24.99% { opacity: 1; }
25%, 100% { opacity: 0; } 25%, 100% { opacity: 0; }
} }
@keyframes showState2 { @keyframes showDogState2 {
0%, 24.99% { opacity: 0; } 0%, 24.99% { opacity: 0; }
25%, 49.99% { opacity: 1; } 25%, 49.99% { opacity: 1; }
50%, 100% { opacity: 0; } 50%, 100% { opacity: 0; }
} }
@keyframes showState3 { @keyframes showDogState3 {
0%, 49.99% { opacity: 0; } 0%, 49.99% { opacity: 0; }
50%, 74.99% { opacity: 1; } 50%, 74.99% { opacity: 1; }
75%, 100% { opacity: 0; } 75%, 100% { opacity: 0; }
} }
@keyframes showState4 { @keyframes showDogState4 {
0%, 74.99% { opacity: 0; } 0%, 74.99% { opacity: 0; }
75%, 100% { opacity: 1; } 75%, 100% { opacity: 1; }
} }

View file

@ -46,7 +46,7 @@ export function AssistantMessage({
> >
<Message <Message
icon={ icon={
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none"> <div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
<DogIcon <DogIcon
className="h-6 w-6 transition-colors duration-300" className="h-6 w-6 transition-colors duration-300"
disabled={isCompleted || isInactive} disabled={isCompleted || isInactive}

View file

@ -1,5 +1,5 @@
import { ArrowRight, Check, Funnel, Loader2, Plus } from "lucide-react"; import { ArrowRight, Check, Funnel, Loader2, Plus } from "lucide-react";
import { forwardRef, useImperativeHandle, useRef } from "react"; import { forwardRef, useImperativeHandle, useRef, useState } from "react";
import TextareaAutosize from "react-textarea-autosize"; import TextareaAutosize from "react-textarea-autosize";
import type { FilterColor } from "@/components/filter-icon-popover"; import type { FilterColor } from "@/components/filter-icon-popover";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -9,7 +9,6 @@ import {
PopoverContent, PopoverContent,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import type { KnowledgeFilterData } from "../types"; import type { KnowledgeFilterData } from "../types";
import { useState } from "react";
import { FilePreview } from "./file-preview"; import { FilePreview } from "./file-preview";
import { SelectedKnowledgeFilter } from "./selected-knowledge-filter"; import { SelectedKnowledgeFilter } from "./selected-knowledge-filter";
@ -107,13 +106,17 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
)} )}
{/* Main Input Container - flex-row or flex-col based on textarea height */} {/* Main Input Container - flex-row or flex-col based on textarea height */}
<div className={`relative flex w-full gap-2 ${ <div
textareaHeight > 40 ? 'flex-col' : 'flex-row items-center' className={`relative flex w-full gap-2 ${
}`}> textareaHeight > 40 ? "flex-col" : "flex-row items-center"
}`}
>
{/* Filter + Textarea Section */} {/* Filter + Textarea Section */}
<div className={`flex items-center gap-2 ${textareaHeight > 40 ? 'w-full' : 'flex-1'}`}> <div
{textareaHeight <= 40 && ( className={`flex items-center gap-2 ${textareaHeight > 40 ? "w-full" : "flex-1"}`}
selectedFilter ? ( >
{textareaHeight <= 40 &&
(selectedFilter ? (
<SelectedKnowledgeFilter <SelectedKnowledgeFilter
selectedFilter={selectedFilter} selectedFilter={selectedFilter}
parsedFilterData={parsedFilterData} parsedFilterData={parsedFilterData}
@ -136,8 +139,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
> >
<Funnel className="h-4 w-4" /> <Funnel className="h-4 w-4" />
</Button> </Button>
) ))}
)}
<div <div
className="relative flex-1" className="relative flex-1"
style={{ height: `${textareaHeight}px` }} style={{ height: `${textareaHeight}px` }}
@ -149,6 +151,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
onHeightChange={(height) => setTextareaHeight(height)} onHeightChange={(height) => setTextareaHeight(height)}
maxRows={7} maxRows={7}
autoComplete="off"
minRows={1} minRows={1}
placeholder="Ask a question..." placeholder="Ask a question..."
disabled={loading} disabled={loading}
@ -159,9 +162,11 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
</div> </div>
{/* Action Buttons Section */} {/* Action Buttons Section */}
<div className={`flex items-center gap-2 ${textareaHeight > 40 ? 'justify-between w-full' : ''}`}> <div
{textareaHeight > 40 && ( className={`flex items-center gap-2 ${textareaHeight > 40 ? "justify-between w-full" : ""}`}
selectedFilter ? ( >
{textareaHeight > 40 &&
(selectedFilter ? (
<SelectedKnowledgeFilter <SelectedKnowledgeFilter
selectedFilter={selectedFilter} selectedFilter={selectedFilter}
parsedFilterData={parsedFilterData} parsedFilterData={parsedFilterData}
@ -184,8 +189,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
> >
<Funnel className="h-4 w-4" /> <Funnel className="h-4 w-4" />
</Button> </Button>
) ))}
)}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
type="button" type="button"

View file

@ -1,44 +0,0 @@
"use client";
import { Suspense, useState } from "react";
import { DoclingHealthBanner } from "@/components/docling-health-banner";
import { ProtectedRoute } from "@/components/protected-route";
import { OnboardingContent } from "./components/onboarding-content";
import { ProgressBar } from "./components/progress-bar";
const TOTAL_STEPS = 4;
function NewOnboardingPage() {
const [currentStep, setCurrentStep] = useState(0);
const handleStepComplete = () => {
if (currentStep < TOTAL_STEPS - 1) {
setCurrentStep(currentStep + 1);
}
};
return (
<div className="min-h-dvh w-full flex gap-5 flex-col items-center justify-center bg-primary-foreground relative p-4">
<DoclingHealthBanner className="absolute top-0 left-0 right-0 w-full z-20" />
{/* Chat-like content area */}
<div className="flex flex-col items-center gap-5 w-full max-w-3xl z-10">
<div className="w-full h-[872px] bg-background border rounded-lg p-4 shadow-sm overflow-y-auto">
<OnboardingContent handleStepComplete={handleStepComplete} currentStep={currentStep} />
</div>
<ProgressBar currentStep={currentStep} totalSteps={TOTAL_STEPS} />
</div>
</div>
);
}
export default function ProtectedNewOnboardingPage() {
return (
<ProtectedRoute>
<Suspense fallback={<div>Loading...</div>}>
<NewOnboardingPage />
</Suspense>
</ProtectedRoute>
);
}

View file

@ -37,7 +37,6 @@ export function AdvancedOnboarding({
languageModel !== undefined && languageModel !== undefined &&
setLanguageModel !== undefined; setLanguageModel !== undefined;
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
return ( return (
<Accordion type="single" collapsible> <Accordion type="single" collapsible>
<AccordionItem value="item-1"> <AccordionItem value="item-1">

View file

@ -80,7 +80,7 @@ export function AnimatedProviderSteps({
<div <div
className={cn( className={cn(
"transition-all duration-150 relative", "transition-all duration-150 relative",
isDone ? "w-3.5 h-3.5" : "w-3.5 h-2.5", isDone ? "w-3.5 h-3.5" : "w-6 h-6",
)} )}
> >
<CheckIcon <CheckIcon
@ -110,7 +110,7 @@ export function AnimatedProviderSteps({
transition={{ duration: 0.4, ease: "easeInOut" }} transition={{ duration: 0.4, ease: "easeInOut" }}
className="flex items-center gap-4 overflow-y-hidden relative h-6" className="flex items-center gap-4 overflow-y-hidden relative h-6"
> >
<div className="w-px h-6 bg-border ml-2" /> <div className="w-px h-6 bg-border ml-3" />
<div className="relative h-5 w-full"> <div className="relative h-5 w-full">
<AnimatePresence mode="sync" initial={false}> <AnimatePresence mode="sync" initial={false}>
<motion.span <motion.span

View file

@ -24,6 +24,7 @@ import { AnimatedProviderSteps } from "./animated-provider-steps";
import { IBMOnboarding } from "./ibm-onboarding"; import { IBMOnboarding } from "./ibm-onboarding";
import { OllamaOnboarding } from "./ollama-onboarding"; import { OllamaOnboarding } from "./ollama-onboarding";
import { OpenAIOnboarding } from "./openai-onboarding"; import { OpenAIOnboarding } from "./openai-onboarding";
import { TabTrigger } from "./tab-trigger";
interface OnboardingCardProps { interface OnboardingCardProps {
onComplete: () => void; onComplete: () => void;
@ -191,6 +192,10 @@ const OnboardingCard = ({
> >
<TabsList className="mb-4"> <TabsList className="mb-4">
<TabsTrigger value="openai"> <TabsTrigger value="openai">
<TabTrigger
selected={modelProvider === "openai"}
isLoading={isLoadingModels}
>
<div <div
className={cn( className={cn(
"flex items-center justify-center gap-2 w-8 h-8 rounded-md", "flex items-center justify-center gap-2 w-8 h-8 rounded-md",
@ -207,12 +212,19 @@ const OnboardingCard = ({
/> />
</div> </div>
OpenAI OpenAI
</TabTrigger>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="watsonx"> <TabsTrigger value="watsonx">
<TabTrigger
selected={modelProvider === "watsonx"}
isLoading={isLoadingModels}
>
<div <div
className={cn( className={cn(
"flex items-center justify-center gap-2 w-8 h-8 rounded-md", "flex items-center justify-center gap-2 w-8 h-8 rounded-md",
modelProvider === "watsonx" ? "bg-[#1063FE]" : "bg-muted", modelProvider === "watsonx"
? "bg-[#1063FE]"
: "bg-muted",
)} )}
> >
<IBMLogo <IBMLogo
@ -225,8 +237,13 @@ const OnboardingCard = ({
/> />
</div> </div>
IBM watsonx.ai IBM watsonx.ai
</TabTrigger>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="ollama"> <TabsTrigger value="ollama">
<TabTrigger
selected={modelProvider === "ollama"}
isLoading={isLoadingModels}
>
<div <div
className={cn( className={cn(
"flex items-center justify-center gap-2 w-8 h-8 rounded-md", "flex items-center justify-center gap-2 w-8 h-8 rounded-md",
@ -243,32 +260,9 @@ const OnboardingCard = ({
/> />
</div> </div>
Ollama Ollama
</TabTrigger>
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
<AnimatePresence>
{isLoadingModels && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.1, ease: "easeInOut" }}
className="overflow-hidden"
>
<div className="py-3">
<AnimatedProviderSteps
currentStep={loadingStep}
isCompleted={false}
setCurrentStep={setLoadingStep}
steps={[
"Connecting to the provider",
"Fetching language models",
"Fetching embedding models",
]}
storageKey="model-loading-steps"
/></div>
</motion.div>
)}
</AnimatePresence>
<TabsContent value="openai"> <TabsContent value="openai">
<OpenAIOnboarding <OpenAIOnboarding
setSettings={setSettings} setSettings={setSettings}
@ -295,8 +289,6 @@ const OnboardingCard = ({
</TabsContent> </TabsContent>
</Tabs> </Tabs>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div> <div>

View file

@ -74,7 +74,7 @@ export function OnboardingStep({
<div className="w-8 h-8 rounded-lg flex-shrink-0" /> <div className="w-8 h-8 rounded-lg flex-shrink-0" />
) : ( ) : (
icon || ( icon || (
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none"> <div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
<DogIcon <DogIcon
className="h-6 w-6 text-accent-foreground transition-colors duration-300" className="h-6 w-6 text-accent-foreground transition-colors duration-300"
disabled={isCompleted} disabled={isCompleted}

View file

@ -76,7 +76,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
onClick={handleUploadClick} onClick={handleUploadClick}
disabled={isUploading} disabled={isUploading}
> >
{isUploading ? "Uploading..." : "Add a Document"} <div>{isUploading ? "Uploading..." : "Add a document"}</div>
</Button> </Button>
<input <input
ref={fileInputRef} ref={fileInputRef}

View file

@ -30,12 +30,12 @@ export function ProgressBar({ currentStep, totalSteps, onSkip }: ProgressBarProp
<div className="flex-1 flex justify-end"> <div className="flex-1 flex justify-end">
{currentStep > 0 && onSkip && ( {currentStep > 0 && onSkip && (
<Button <Button
variant="ghost" variant="link"
size="sm" size="sm"
onClick={onSkip} onClick={onSkip}
className="flex items-center gap-2 text-xs text-muted-foreground" className="flex items-center gap-2 text-mmd !text-placeholder-foreground hover:!text-foreground hover:!no-underline"
> >
Skip onboarding Skip overview
<ArrowRight className="w-4 h-4" /> <ArrowRight className="w-4 h-4" />
</Button> </Button>
)} )}

View file

@ -0,0 +1,33 @@
import AnimatedProcessingIcon from "@/components/ui/animated-processing-icon";
import { cn } from "@/lib/utils";
export function TabTrigger({
children,
selected,
isLoading,
}: {
children: React.ReactNode;
selected: boolean;
isLoading: boolean;
}) {
return (
<div className="flex flex-col relative items-start justify-between gap-4 h-full w-full">
<div
className={cn(
"flex absolute items-center justify-center h-full w-full transition-opacity duration-200",
isLoading && selected ? "opacity-100" : "opacity-0",
)}
>
<AnimatedProcessingIcon className="text-current shrink-0 h-10 w-10" />
</div>
<div
className={cn(
"flex flex-col items-start justify-between gap-4 h-full w-full transition-opacity duration-200",
isLoading && selected ? "opacity-0" : "opacity-100",
)}
>
{children}
</div>
</div>
);
}

View file

@ -8,8 +8,8 @@ import {
useGetConversationsQuery, useGetConversationsQuery,
} from "@/app/api/queries/useGetConversationsQuery"; } from "@/app/api/queries/useGetConversationsQuery";
import type { Settings } from "@/app/api/queries/useGetSettingsQuery"; import type { Settings } from "@/app/api/queries/useGetSettingsQuery";
import { OnboardingContent } from "@/app/new-onboarding/components/onboarding-content"; import { OnboardingContent } from "@/app/onboarding/components/onboarding-content";
import { ProgressBar } from "@/app/new-onboarding/components/progress-bar"; import { ProgressBar } from "@/app/onboarding/components/progress-bar";
import { AnimatedConditional } from "@/components/animated-conditional"; import { AnimatedConditional } from "@/components/animated-conditional";
import { Header } from "@/components/header"; import { Header } from "@/components/header";
import { Navigation } from "@/components/navigation"; import { Navigation } from "@/components/navigation";
@ -156,7 +156,7 @@ export function ChatRenderer({
}} }}
className={cn( className={cn(
"flex h-full w-full max-w-full max-h-full items-center justify-center overflow-hidden", "flex h-full w-full max-w-full max-h-full items-center justify-center overflow-hidden",
!showLayout && "absolute", !showLayout && "absolute max-h-[calc(100vh-190px)]",
showLayout && !isOnChatPage && "bg-background", showLayout && !isOnChatPage && "bg-background",
)} )}
> >
@ -199,7 +199,7 @@ export function ChatRenderer({
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: showLayout ? 0 : 1, y: showLayout ? 20 : 0 }} animate={{ opacity: showLayout ? 0 : 1, y: showLayout ? 20 : 0 }}
transition={{ duration: ANIMATION_DURATION, ease: "easeOut" }} transition={{ duration: ANIMATION_DURATION, ease: "easeOut" }}
className={cn("absolute bottom-10 left-0 right-0")} className={cn("absolute bottom-6 left-0 right-0")}
> >
<ProgressBar <ProgressBar
currentStep={currentStep} currentStep={currentStep}

View file

@ -51,8 +51,8 @@ const AnimatedProcessingIcon = ({
return ( return (
<svg <svg
width="16" width="24"
height="16" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"