feat(ask_sb): Add back search scope requirement and other UI changes (#411)

* Revert "Remove search scope constraint"

This reverts commit e69ac0d806.

* add llm section to onboard final page

* add select all button

* add repo snapshot to agentic search and other ui nits

* refactor demo repo index cta into repo snapshop

* changelog
This commit is contained in:
Michael Sukkarieh 2025-07-29 15:50:36 -07:00 committed by GitHub
parent 211ad8fb12
commit 53edd44462
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 210 additions and 122 deletions

View file

@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bumped next version. [#406](https://github.com/sourcebot-dev/sourcebot/pull/406) - Bumped next version. [#406](https://github.com/sourcebot-dev/sourcebot/pull/406)
- [ask sb] Improved search code tool with filter options. [#400](https://github.com/sourcebot-dev/sourcebot/pull/400) - [ask sb] Improved search code tool with filter options. [#400](https://github.com/sourcebot-dev/sourcebot/pull/400)
- [ask sb] Removed search scope constraint. [#400](https://github.com/sourcebot-dev/sourcebot/pull/400) - [ask sb] Removed search scope constraint. [#400](https://github.com/sourcebot-dev/sourcebot/pull/400)
- [ask sb] Add back search scope requirement and other UI changes. [#411](https://github.com/sourcebot-dev/sourcebot/pull/411)
## [4.6.0] - 2025-07-25 ## [4.6.0] - 2025-07-25

View file

@ -51,6 +51,7 @@ export const NewChatPanel = ({
languageModels={languageModels} languageModels={languageModels}
selectedSearchScopes={selectedSearchScopes} selectedSearchScopes={selectedSearchScopes}
searchContexts={searchContexts} searchContexts={searchContexts}
onContextSelectorOpenChanged={setIsContextSelectorOpen}
/> />
<div className="w-full flex flex-row items-center bg-accent rounded-b-md px-2"> <div className="w-full flex flex-row items-center bg-accent rounded-b-md px-2">
<ChatBoxToolbar <ChatBoxToolbar

View file

@ -13,6 +13,7 @@ import { DemoExamples } from "@/types";
import { AskSourcebotDemoCards } from "./askSourcebotDemoCards"; import { AskSourcebotDemoCards } from "./askSourcebotDemoCards";
import { AgenticSearchTutorialDialog } from "./agenticSearchTutorialDialog"; import { AgenticSearchTutorialDialog } from "./agenticSearchTutorialDialog";
import { setAgenticSearchTutorialDismissedCookie } from "@/actions"; import { setAgenticSearchTutorialDismissedCookie } from "@/actions";
import { RepositorySnapshot } from "./repositorySnapshot";
interface AgenticSearchProps { interface AgenticSearchProps {
searchModeSelectorProps: SearchModeSelectorProps; searchModeSelectorProps: SearchModeSelectorProps;
@ -58,6 +59,7 @@ export const AgenticSearch = ({
languageModels={languageModels} languageModels={languageModels}
selectedSearchScopes={selectedSearchScopes} selectedSearchScopes={selectedSearchScopes}
searchContexts={searchContexts} searchContexts={searchContexts}
onContextSelectorOpenChanged={setIsContextSelectorOpen}
/> />
<Separator /> <Separator />
<div className="relative"> <div className="relative">
@ -79,6 +81,16 @@ export const AgenticSearch = ({
</div> </div>
</div> </div>
<div className="mt-8">
<RepositorySnapshot
repos={repos}
/>
</div>
<div className="flex flex-col items-center w-fit gap-6">
<Separator className="mt-5 w-[700px]" />
</div>
{demoExamples && ( {demoExamples && (
<AskSourcebotDemoCards <AskSourcebotDemoCards
demoExamples={demoExamples} demoExamples={demoExamples}

View file

@ -66,110 +66,93 @@ export const AskSourcebotDemoCards = ({
} }
return ( return (
<> <div className="w-full mt-8 space-y-12 px-4 max-w-[1200px]">
{process.env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT === "demo" && ( {/* Example Searches Row */}
<p className="text-sm text-muted-foreground text-center mt-6"> <div className="space-y-4">
Interested in using Sourcebot on your code? Check out our{' '} <div className="text-center mb-6">
<a <div className="flex items-center justify-center gap-3 mb-4">
href="https://docs.sourcebot.dev/docs/overview" <Search className="h-7 w-7 text-muted-foreground" />
target="_blank" <h3 className="text-2xl font-bold">Community Ask Results</h3>
rel="noopener noreferrer"
className="text-primary hover:underline"
onClick={() => captureEvent('wa_demo_docs_link_pressed', {})}
>
docs
</a>
</p>
)}
<div className="w-full mt-16 space-y-12 px-4 max-w-[1000px]">
{/* Example Searches Row */}
<div className="space-y-4">
<div className="text-center mb-6">
<div className="flex items-center justify-center gap-3 mb-4">
<Search className="h-7 w-7 text-muted-foreground" />
<h3 className="text-2xl font-bold">Community Ask Results</h3>
</div>
</div>
{/* Search Scope Filter */}
<div className="flex flex-wrap items-center justify-center gap-2 mb-6">
<div className="flex items-center gap-2 mr-2">
<div className="relative group">
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
<div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 pointer-events-none">
<SearchScopeInfoCard />
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-border"></div>
</div>
</div>
<span className="text-sm font-medium text-muted-foreground">Search Scope:</span>
</div>
<Badge
variant={selectedFilterSearchScope === null ? "default" : "secondary"}
className={`cursor-pointer transition-all duration-200 hover:shadow-sm ${selectedFilterSearchScope === null ? "bg-primary text-primary-foreground" : "hover:bg-secondary/80"
}`}
onClick={() => {
setSelectedFilterSearchScope(null);
}}
>
All
</Badge>
{demoExamples.searchScopes.map((searchScope) => (
<Badge
key={searchScope.id}
variant={selectedFilterSearchScope === searchScope.id ? "default" : "secondary"}
className={`cursor-pointer transition-all duration-200 hover:shadow-sm flex items-center gap-1 ${selectedFilterSearchScope === searchScope.id ? "bg-primary text-primary-foreground" : "hover:bg-secondary/80"
}`}
onClick={() => {
setSelectedFilterSearchScope(searchScope.id);
}}
>
{getSearchScopeIcon(searchScope, 12, selectedFilterSearchScope === searchScope.id)}
{searchScope.displayName}
</Badge>
))}
</div>
<div className="flex flex-wrap justify-center gap-3">
{demoExamples.searchExamples
.filter((example) => {
if (selectedFilterSearchScope === null) return true;
return example.searchScopes.includes(selectedFilterSearchScope);
})
.map((example) => {
const searchScopes = demoExamples.searchScopes.filter((searchScope) => example.searchScopes.includes(searchScope.id))
return (
<Card
key={example.url}
className="cursor-pointer transition-all duration-200 hover:shadow-md hover:scale-105 hover:border-primary/50 group w-full max-w-[350px]"
onClick={() => handleExampleClick(example)}
>
<CardContent className="p-4">
<div className="space-y-3">
<div className="flex items-center justify-between">
{searchScopes.map((searchScope) => (
<Badge key={searchScope.value} variant="secondary" className="text-[10px] px-1.5 py-0.5 h-4 flex items-center gap-1">
{getSearchScopeIcon(searchScope, 12)}
{searchScope.displayName}
</Badge>
))}
</div>
<div className="space-y-1">
<h4 className="font-semibold text-sm group-hover:text-primary transition-colors line-clamp-2">
{example.title}
</h4>
<p className="text-xs text-muted-foreground line-clamp-3 leading-relaxed">
{example.description}
</p>
</div>
</div>
</CardContent>
</Card>
)
})}
</div> </div>
</div> </div>
{/* Search Scope Filter */}
<div className="flex flex-wrap items-center justify-center gap-2 mb-6">
<div className="flex items-center gap-2 mr-2">
<div className="relative group">
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
<div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 pointer-events-none">
<SearchScopeInfoCard />
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-border"></div>
</div>
</div>
<span className="text-sm font-medium text-muted-foreground">Search Scope:</span>
</div>
<Badge
variant={selectedFilterSearchScope === null ? "default" : "secondary"}
className={`cursor-pointer transition-all duration-200 hover:shadow-sm ${selectedFilterSearchScope === null ? "bg-primary text-primary-foreground" : "hover:bg-secondary/80"
}`}
onClick={() => {
setSelectedFilterSearchScope(null);
}}
>
All
</Badge>
{demoExamples.searchScopes.map((searchScope) => (
<Badge
key={searchScope.id}
variant={selectedFilterSearchScope === searchScope.id ? "default" : "secondary"}
className={`cursor-pointer transition-all duration-200 hover:shadow-sm flex items-center gap-1 ${selectedFilterSearchScope === searchScope.id ? "bg-primary text-primary-foreground" : "hover:bg-secondary/80"
}`}
onClick={() => {
setSelectedFilterSearchScope(searchScope.id);
}}
>
{getSearchScopeIcon(searchScope, 12, selectedFilterSearchScope === searchScope.id)}
{searchScope.displayName}
</Badge>
))}
</div>
<div className="flex flex-wrap justify-center gap-3">
{demoExamples.searchExamples
.filter((example) => {
if (selectedFilterSearchScope === null) return true;
return example.searchScopes.includes(selectedFilterSearchScope);
})
.map((example) => {
const searchScopes = demoExamples.searchScopes.filter((searchScope) => example.searchScopes.includes(searchScope.id))
return (
<Card
key={example.url}
className="cursor-pointer transition-all duration-200 hover:shadow-md hover:scale-105 hover:border-primary/50 group w-full max-w-[350px]"
onClick={() => handleExampleClick(example)}
>
<CardContent className="p-4">
<div className="space-y-3">
<div className="flex items-center justify-between">
{searchScopes.map((searchScope) => (
<Badge key={searchScope.value} variant="secondary" className="text-[10px] px-1.5 py-0.5 h-4 flex items-center gap-1">
{getSearchScopeIcon(searchScope, 12)}
{searchScope.displayName}
</Badge>
))}
</div>
<div className="space-y-1">
<h4 className="font-semibold text-sm group-hover:text-primary transition-colors line-clamp-2">
{example.title}
</h4>
<p className="text-xs text-muted-foreground line-clamp-3 leading-relaxed">
{example.description}
</p>
</div>
</div>
</CardContent>
</Card>
)
})}
</div>
</div> </div>
</> </div>
); );
}; };

View file

@ -16,6 +16,7 @@ import {
import { RepoIndexingStatus } from "@sourcebot/db"; import { RepoIndexingStatus } from "@sourcebot/db";
import { SymbolIcon } from "@radix-ui/react-icons"; import { SymbolIcon } from "@radix-ui/react-icons";
import { RepositoryQuery } from "@/lib/types"; import { RepositoryQuery } from "@/lib/types";
import { captureEvent } from "@/hooks/useCaptureEvent";
interface RepositorySnapshotProps { interface RepositorySnapshotProps {
repos: RepositoryQuery[]; repos: RepositoryQuery[];
@ -68,15 +69,30 @@ export function RepositorySnapshot({
return ( return (
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<span className="text-sm"> <span className="text-sm">
{`Search ${indexedRepos.length} `} {`${indexedRepos.length} `}
<Link <Link
href={`${domain}/repos`} href={`${domain}/repos`}
className="text-link hover:underline" className="text-link hover:underline"
> >
{indexedRepos.length > 1 ? 'repositories' : 'repository'} {indexedRepos.length > 1 ? 'repositories' : 'repository'}
</Link> </Link>
{` indexed`}
</span> </span>
<RepositoryCarousel repos={indexedRepos} /> <RepositoryCarousel repos={indexedRepos} />
{process.env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT === "demo" && (
<p className="text-sm text-muted-foreground text-center">
Interested in using Sourcebot on your code? Check out our{' '}
<a
href="https://docs.sourcebot.dev/docs/overview"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
onClick={() => captureEvent('wa_demo_docs_link_pressed', {})}
>
docs
</a>
</p>
)}
</div> </div>
) )
} }

View file

@ -14,7 +14,7 @@ import { prisma } from "@/prisma";
import { OrgRole } from "@sourcebot/db"; import { OrgRole } from "@sourcebot/db";
import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch"; import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { BetweenHorizontalStart, GitBranchIcon, LockIcon } from "lucide-react"; import { BetweenHorizontalStart, Brain, GitBranchIcon, LockIcon } from "lucide-react";
import { hasEntitlement } from "@sourcebot/shared"; import { hasEntitlement } from "@sourcebot/shared";
import { env } from "@/env.mjs"; import { env } from "@/env.mjs";
import { GcpIapAuth } from "@/app/[domain]/components/gcpIapAuth"; import { GcpIapAuth } from "@/app/[domain]/components/gcpIapAuth";
@ -87,6 +87,13 @@ export default async function Onboarding({ searchParams }: OnboardingProps) {
href: "https://docs.sourcebot.dev/docs/connections/overview", href: "https://docs.sourcebot.dev/docs/connections/overview",
icon: <GitBranchIcon className="w-4 h-4" />, icon: <GitBranchIcon className="w-4 h-4" />,
}, },
{
id: "language-models",
title: "Language Models",
description: "Learn how to configure your language model providers to start using Ask Sourcebot",
href: "https://docs.sourcebot.dev/docs/configuration/language-model-providers",
icon: <Brain className="w-4 h-4" />,
},
{ {
id: "authentication-system", id: "authentication-system",
title: "Authentication System", title: "Authentication System",

View file

@ -5,10 +5,9 @@ import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { CustomEditor, LanguageModelInfo, MentionElement, RenderElementPropsFor, SearchScope } from "@/features/chat/types"; import { CustomEditor, LanguageModelInfo, MentionElement, RenderElementPropsFor, SearchScope } from "@/features/chat/types";
import { insertMention, slateContentToString } from "@/features/chat/utils"; import { insertMention, slateContentToString } from "@/features/chat/utils";
import { SearchContextQuery } from "@/lib/types";
import { cn, IS_MAC } from "@/lib/utils"; import { cn, IS_MAC } from "@/lib/utils";
import { computePosition, flip, offset, shift, VirtualElement } from "@floating-ui/react"; import { computePosition, flip, offset, shift, VirtualElement } from "@floating-ui/react";
import { ArrowUp, Loader2, StopCircleIcon } from "lucide-react"; import { ArrowUp, Loader2, StopCircleIcon, TriangleAlertIcon } from "lucide-react";
import { Fragment, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Fragment, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
import { Descendant, insertText } from "slate"; import { Descendant, insertText } from "slate";
@ -18,6 +17,8 @@ import { SuggestionBox } from "./suggestionsBox";
import { Suggestion } from "./types"; import { Suggestion } from "./types";
import { useSuggestionModeAndQuery } from "./useSuggestionModeAndQuery"; import { useSuggestionModeAndQuery } from "./useSuggestionModeAndQuery";
import { useSuggestionsData } from "./useSuggestionsData"; import { useSuggestionsData } from "./useSuggestionsData";
import { useToast } from "@/components/hooks/use-toast";
import { SearchContextQuery } from "@/lib/types";
interface ChatBoxProps { interface ChatBoxProps {
onSubmit: (children: Descendant[], editor: CustomEditor) => void; onSubmit: (children: Descendant[], editor: CustomEditor) => void;
@ -29,6 +30,7 @@ interface ChatBoxProps {
languageModels: LanguageModelInfo[]; languageModels: LanguageModelInfo[];
selectedSearchScopes: SearchScope[]; selectedSearchScopes: SearchScope[];
searchContexts: SearchContextQuery[]; searchContexts: SearchContextQuery[];
onContextSelectorOpenChanged: (isOpen: boolean) => void;
} }
export const ChatBox = ({ export const ChatBox = ({
@ -41,6 +43,7 @@ export const ChatBox = ({
languageModels, languageModels,
selectedSearchScopes, selectedSearchScopes,
searchContexts, searchContexts,
onContextSelectorOpenChanged,
}: ChatBoxProps) => { }: ChatBoxProps) => {
const suggestionsBoxRef = useRef<HTMLDivElement>(null); const suggestionsBoxRef = useRef<HTMLDivElement>(null);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
@ -67,6 +70,7 @@ export const ChatBox = ({
const { selectedLanguageModel } = useSelectedLanguageModel({ const { selectedLanguageModel } = useSelectedLanguageModel({
initialLanguageModel: languageModels.length > 0 ? languageModels[0] : undefined, initialLanguageModel: languageModels.length > 0 ? languageModels[0] : undefined,
}); });
const { toast } = useToast();
// Reset the index when the suggestion mode changes. // Reset the index when the suggestion mode changes.
useEffect(() => { useEffect(() => {
@ -97,9 +101,9 @@ export const ChatBox = ({
return <Leaf {...props} /> return <Leaf {...props} />
}, []); }, []);
const { isSubmitDisabled } = useMemo((): { const { isSubmitDisabled, isSubmitDisabledReason } = useMemo((): {
isSubmitDisabled: true, isSubmitDisabled: true,
isSubmitDisabledReason: "empty" | "redirecting" | "generating" | "no-language-model-selected" isSubmitDisabledReason: "empty" | "redirecting" | "generating" | "no-repos-selected" | "no-language-model-selected"
} | { } | {
isSubmitDisabled: false, isSubmitDisabled: false,
isSubmitDisabledReason: undefined, isSubmitDisabledReason: undefined,
@ -125,6 +129,13 @@ export const ChatBox = ({
} }
} }
if (selectedSearchScopes.length === 0) {
return {
isSubmitDisabled: true,
isSubmitDisabledReason: "no-repos-selected",
}
}
if (selectedLanguageModel === undefined) { if (selectedLanguageModel === undefined) {
return { return {
@ -138,11 +149,29 @@ export const ChatBox = ({
isSubmitDisabledReason: undefined, isSubmitDisabledReason: undefined,
} }
}, [editor.children, isRedirecting, isGenerating, selectedLanguageModel]) }, [
editor.children,
isRedirecting,
isGenerating,
selectedSearchScopes.length,
selectedLanguageModel,
])
const onSubmit = useCallback(() => { const onSubmit = useCallback(() => {
if (isSubmitDisabled) {
if (isSubmitDisabledReason === "no-repos-selected") {
toast({
description: "⚠️ You must select at least one search scope",
variant: "destructive",
});
onContextSelectorOpenChanged(true);
}
return;
}
_onSubmit(editor.children, editor); _onSubmit(editor.children, editor);
}, [_onSubmit, editor]); }, [_onSubmit, editor, isSubmitDisabled, isSubmitDisabledReason, toast, onContextSelectorOpenChanged]);
const onInsertSuggestion = useCallback((suggestion: Suggestion) => { const onInsertSuggestion = useCallback((suggestion: Suggestion) => {
switch (suggestion.type) { switch (suggestion.type) {
@ -281,15 +310,39 @@ export const ChatBox = ({
Stop Stop
</Button> </Button>
) : ( ) : (
<Button <Tooltip>
variant={isSubmitDisabled ? "outline" : "default"} <TooltipTrigger asChild>
size="sm" <div
className="w-6 h-6" onClick={() => {
onClick={onSubmit} // @hack: When submission is disabled, we still want to issue
disabled={isSubmitDisabled} // a warning to the user as to why the submission is disabled.
> // onSubmit on the Button will not be called because of the
<ArrowUp className="w-4 h-4" /> // disabled prop, hence the call here.
</Button> if (isSubmitDisabled) {
onSubmit();
}
}}
>
<Button
variant={isSubmitDisabled ? "outline" : "default"}
size="sm"
className="w-6 h-6"
onClick={onSubmit}
disabled={isSubmitDisabled}
>
<ArrowUp className="w-4 h-4" />
</Button>
</div>
</TooltipTrigger>
{(isSubmitDisabled && isSubmitDisabledReason === "no-repos-selected") && (
<TooltipContent>
<div className="flex flex-row items-center">
<TriangleAlertIcon className="h-4 w-4 text-warning mr-1" />
<span className="text-destructive">You must select at least one search scope</span>
</div>
</TooltipContent>
)}
</Tooltip>
)} )}
</div> </div>
{suggestionMode !== "none" && ( {suggestionMode !== "none" && (

View file

@ -57,6 +57,7 @@ export const SearchScopeSelector = React.forwardRef<
) => { ) => {
const scrollContainerRef = React.useRef<HTMLDivElement>(null); const scrollContainerRef = React.useRef<HTMLDivElement>(null);
const scrollPosition = React.useRef<number>(0); const scrollPosition = React.useRef<number>(0);
const [hasSearchInput, setHasSearchInput] = React.useState(false);
const handleInputKeyDown = ( const handleInputKeyDown = (
event: React.KeyboardEvent<HTMLInputElement> event: React.KeyboardEvent<HTMLInputElement>
@ -93,6 +94,10 @@ export const SearchScopeSelector = React.forwardRef<
onSelectedSearchScopesChange([]); onSelectedSearchScopesChange([]);
}; };
const handleSelectAll = () => {
onSelectedSearchScopesChange(allSearchScopeItems);
};
const handleTogglePopover = () => { const handleTogglePopover = () => {
onOpenChanged(!isOpen); onOpenChanged(!isOpen);
}; };
@ -180,10 +185,19 @@ export const SearchScopeSelector = React.forwardRef<
<CommandInput <CommandInput
placeholder="Search scopes..." placeholder="Search scopes..."
onKeyDown={handleInputKeyDown} onKeyDown={handleInputKeyDown}
onValueChange={(value) => setHasSearchInput(!!value)}
/> />
<CommandList ref={scrollContainerRef}> <CommandList ref={scrollContainerRef}>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup> <CommandGroup>
{!hasSearchInput && (
<div
onClick={handleSelectAll}
className="flex items-center px-2 py-1.5 text-sm text-muted-foreground hover:text-foreground cursor-pointer transition-colors"
>
<span className="text-xs">Select all</span>
</div>
)}
{sortedSearchScopeItems.map(({ item, isSelected }) => { {sortedSearchScopeItems.map(({ item, isSelected }) => {
return ( return (
<CommandItem <CommandItem

View file

@ -321,6 +321,7 @@ export const ChatThread = ({
languageModels={languageModels} languageModels={languageModels}
selectedSearchScopes={selectedSearchScopes} selectedSearchScopes={selectedSearchScopes}
searchContexts={searchContexts} searchContexts={searchContexts}
onContextSelectorOpenChanged={setIsContextSelectorOpen}
/> />
<div className="w-full flex flex-row items-center bg-accent rounded-b-md px-2"> <div className="w-full flex flex-row items-center bg-accent rounded-b-md px-2">
<ChatBoxToolbar <ChatBoxToolbar

View file

@ -6,7 +6,7 @@ import { Separator } from '@/components/ui/separator';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Brain, ChevronDown, ChevronRight, Clock, Cpu, InfoIcon, Loader2, ScanSearchIcon, Zap } from 'lucide-react'; import { Brain, ChevronDown, ChevronRight, Clock, Cpu, InfoIcon, Loader2, List, ScanSearchIcon, Zap } from 'lucide-react';
import { MarkdownRenderer } from './markdownRenderer'; import { MarkdownRenderer } from './markdownRenderer';
import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinitionsToolComponent'; import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinitionsToolComponent';
import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent'; import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent';
@ -89,7 +89,7 @@ export const DetailsCard = ({
)} )}
{metadata?.modelName && ( {metadata?.modelName && (
<div className="flex items-center text-xs"> <div className="flex items-center text-xs">
<Cpu className="w-3 h-3 mr-1 flex-shrink-0" /> <Brain className="w-3 h-3 mr-1 flex-shrink-0" />
{metadata?.modelName} {metadata?.modelName}
</div> </div>
)} )}
@ -106,7 +106,7 @@ export const DetailsCard = ({
</div> </div>
)} )}
<div className="flex items-center text-xs"> <div className="flex items-center text-xs">
<Brain className="w-3 h-3 mr-1 flex-shrink-0" /> <List className="w-3 h-3 mr-1 flex-shrink-0" />
{`${thinkingSteps.length} step${thinkingSteps.length === 1 ? '' : 's'}`} {`${thinkingSteps.length} step${thinkingSteps.length === 1 ? '' : 's'}`}
</div> </div>
</> </>