mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
feat(ask_sb): Add onboarding tutorial (#408)
This commit is contained in:
parent
45416a41d1
commit
4343b3c3d5
16 changed files with 385 additions and 28 deletions
|
|
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Add ability to include/exclude connection in search context. [#399](https://github.com/sourcebot-dev/sourcebot/pull/399)
|
||||
- Search context refactor to search scope and demo card UI changes. [#405](https://github.com/sourcebot-dev/sourcebot/pull/405)
|
||||
- Add GitHub star toast. [#409](https://github.com/sourcebot-dev/sourcebot/pull/409)
|
||||
- Added a onboarding modal when first visiting the homepage when `ask` mode is selected. [#408](https://github.com/sourcebot-dev/sourcebot/pull/408)
|
||||
|
||||
### Fixed
|
||||
- Fixed multiple writes race condition on config file watcher. [#398](https://github.com/sourcebot-dev/sourcebot/pull/398)
|
||||
|
|
|
|||
BIN
packages/web/public/ask_sb_tutorial_at_mentions.png
Normal file
BIN
packages/web/public/ask_sb_tutorial_at_mentions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 414 KiB |
BIN
packages/web/public/ask_sb_tutorial_citations.png
Normal file
BIN
packages/web/public/ask_sb_tutorial_citations.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
packages/web/public/ask_sb_tutorial_search_scope.png
Normal file
BIN
packages/web/public/ask_sb_tutorial_search_scope.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 381 KiB |
|
|
@ -25,7 +25,7 @@ import { auth } from "./auth";
|
|||
import { getConnection } from "./data/connection";
|
||||
import { IS_BILLING_ENABLED } from "./ee/features/billing/stripe";
|
||||
import InviteUserEmail from "./emails/inviteUserEmail";
|
||||
import { MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, SEARCH_MODE_COOKIE_NAME, SINGLE_TENANT_ORG_DOMAIN, SOURCEBOT_GUEST_USER_ID, SOURCEBOT_SUPPORT_EMAIL } from "./lib/constants";
|
||||
import { AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME, MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, SEARCH_MODE_COOKIE_NAME, SINGLE_TENANT_ORG_DOMAIN, SOURCEBOT_GUEST_USER_ID, SOURCEBOT_SUPPORT_EMAIL } from "./lib/constants";
|
||||
import { orgDomainSchema, orgNameSchema, repositoryQuerySchema } from "./lib/schemas";
|
||||
import { TenancyMode, ApiKeyPayload } from "./lib/types";
|
||||
import { decrementOrgSeatCount, getSubscriptionForOrg } from "./ee/features/billing/serverUtils";
|
||||
|
|
@ -2015,6 +2015,13 @@ export async function setSearchModeCookie(searchMode: "precise" | "agentic") {
|
|||
});
|
||||
}
|
||||
|
||||
export async function setAgenticSearchTutorialDismissedCookie(dismissed: boolean) {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set(AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME, dismissed ? "true" : "false", {
|
||||
httpOnly: false, // Allow client-side access
|
||||
});
|
||||
}
|
||||
|
||||
////// Helpers ///////
|
||||
|
||||
const parseConnectionConfig = (config: string) => {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolba
|
|||
import { LanguageModelInfo, SearchScope } from "@/features/chat/types";
|
||||
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
|
||||
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { SearchModeSelector, SearchModeSelectorProps } from "./toolbar";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { DemoExamples } from "@/types";
|
||||
import { AskSourcebotDemoCards } from "./askSourcebotDemoCards";
|
||||
import { AgenticSearchTutorialDialog } from "./agenticSearchTutorialDialog";
|
||||
import { setAgenticSearchTutorialDismissedCookie } from "@/actions";
|
||||
|
||||
interface AgenticSearchProps {
|
||||
searchModeSelectorProps: SearchModeSelectorProps;
|
||||
|
|
@ -23,6 +25,7 @@ interface AgenticSearchProps {
|
|||
name: string | null;
|
||||
}[];
|
||||
demoExamples: DemoExamples | undefined;
|
||||
isTutorialDismissed: boolean;
|
||||
}
|
||||
|
||||
export const AgenticSearch = ({
|
||||
|
|
@ -31,11 +34,18 @@ export const AgenticSearch = ({
|
|||
repos,
|
||||
searchContexts,
|
||||
demoExamples,
|
||||
isTutorialDismissed,
|
||||
}: AgenticSearchProps) => {
|
||||
const { createNewChatThread, isLoading } = useCreateNewChatThread();
|
||||
const [selectedSearchScopes, setSelectedSearchScopes] = useLocalStorage<SearchScope[]>("selectedSearchScopes", [], { initializeWithValue: false });
|
||||
const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false);
|
||||
|
||||
const [isTutorialOpen, setIsTutorialOpen] = useState(!isTutorialDismissed);
|
||||
const onTutorialDismissed = useCallback(() => {
|
||||
setIsTutorialOpen(false);
|
||||
setAgenticSearchTutorialDismissedCookie(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center w-full">
|
||||
<div className="mt-4 w-full border rounded-md shadow-sm max-w-[800px]">
|
||||
|
|
@ -75,6 +85,12 @@ export const AgenticSearch = ({
|
|||
demoExamples={demoExamples}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isTutorialOpen && (
|
||||
<AgenticSearchTutorialDialog
|
||||
onClose={onTutorialDismissed}
|
||||
/>
|
||||
)}
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
||||
import { ModelProviderLogo } from "@/features/chat/components/chatBox/modelProviderLogo"
|
||||
import { cn } from "@/lib/utils"
|
||||
import mentionsDemo from "@/public/ask_sb_tutorial_at_mentions.png"
|
||||
import citationsDemo from "@/public/ask_sb_tutorial_citations.png"
|
||||
import searchScopeDemo from "@/public/ask_sb_tutorial_search_scope.png"
|
||||
import logoDarkSmall from "@/public/sb_logo_dark_small.png"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import {
|
||||
ArrowLeftRightIcon,
|
||||
AtSignIcon,
|
||||
BookMarkedIcon,
|
||||
BookTextIcon,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
CircleCheckIcon,
|
||||
FileIcon,
|
||||
FolderIcon,
|
||||
GitCommitHorizontalIcon,
|
||||
LibraryBigIcon,
|
||||
ScanSearchIcon,
|
||||
StarIcon,
|
||||
TicketIcon,
|
||||
} from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { useState } from "react"
|
||||
|
||||
interface AgenticSearchTutorialDialogProps {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
|
||||
// Star button component that fetches GitHub star count
|
||||
const GitHubStarButton = () => {
|
||||
const { data: starCount, isLoading, isError } = useQuery({
|
||||
queryKey: ['github-stars', 'sourcebot-dev/sourcebot'],
|
||||
queryFn: async () => {
|
||||
const response = await fetch('https://api.github.com/repos/sourcebot-dev/sourcebot')
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch star count')
|
||||
}
|
||||
const data = await response.json()
|
||||
return data.stargazers_count as number;
|
||||
},
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
gcTime: 30 * 60 * 1000, // 30 minutes
|
||||
retry: 3,
|
||||
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
|
||||
})
|
||||
|
||||
const formatStarCount = (count: number) => {
|
||||
if (count >= 1000) {
|
||||
return `${(count / 1000).toFixed(1)}k`
|
||||
}
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => window.open('https://github.com/sourcebot-dev/sourcebot', '_blank')}
|
||||
>
|
||||
<StarIcon className="w-4 h-4" />
|
||||
<span className="font-medium">
|
||||
{
|
||||
!isLoading && !isError && starCount ? `Star (${formatStarCount(starCount)})` : 'Star'
|
||||
}
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const tutorialSteps = [
|
||||
{
|
||||
leftContent: (
|
||||
<div className="flex flex-col h-full p-8 justify-between gap-4">
|
||||
<div className="flex flex-col gap-6">
|
||||
<h2 className="text-5xl font-bold leading-tight">
|
||||
Ask Source<span className="text-[#851EE6]">bot.</span>
|
||||
</h2>
|
||||
<p className="text-lg">
|
||||
Ask questions about your <span className="font-bold">entire codebase</span> in natural language.
|
||||
Get back responses grounded in code with <span className="font-bold">inline citations</span>.
|
||||
</p>
|
||||
<p className="text-md text-muted-foreground">
|
||||
Ask Sourcebot is an agentic search tool that can answer questions about your codebase by searching, reading files, navigating references, and more. Supports any <Link href="https://docs.sourcebot.dev/docs/configuration/language-model-providers" className="underline">compatible LLM.</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-3 mx-auto flex flex-wrap justify-center gap-4">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<ModelProviderLogo provider="anthropic" />
|
||||
<ModelProviderLogo provider="openai" />
|
||||
<ModelProviderLogo provider="google-generative-ai" />
|
||||
<ModelProviderLogo provider="amazon-bedrock" />
|
||||
<ModelProviderLogo provider="azure" />
|
||||
<ModelProviderLogo provider="deepseek" />
|
||||
<ModelProviderLogo provider="mistral" />
|
||||
<ModelProviderLogo provider="openrouter" />
|
||||
<ModelProviderLogo provider="xai" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
rightContent: (
|
||||
<video
|
||||
src="https://storage.googleapis.com/sourcebot-assets/hero_final.mp4"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
leftContent: (
|
||||
<div className="flex flex-col h-full p-8 space-y-6">
|
||||
<h2 className="text-3xl font-bold leading-tight flex items-center gap-2">
|
||||
<ScanSearchIcon className="inline-block h-8 w-8 text-primary" />
|
||||
Search Scopes
|
||||
</h2>
|
||||
<p className="text-lg">
|
||||
{`When asking Sourcebot a question, you can select one or more scopes to focus the search.`}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col mb-2 text-muted-foreground">
|
||||
<p className="mb-4">There are two types of search scopes:</p>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<BookMarkedIcon className="h-4 w-4 text-muted-foreground flex-shrink-0 mt-1" />
|
||||
<span><strong>Repository</strong>: A single repository.</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<LibraryBigIcon className="h-4 w-4 text-muted-foreground flex-shrink-0 mt-1" />
|
||||
<span><strong>Reposet</strong>: A collection of repositories (<Link href="https://docs.sourcebot.dev/docs/features/search/search-contexts" className="underline">configuration docs</Link>).</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
rightContent: (
|
||||
<Image
|
||||
src={searchScopeDemo}
|
||||
alt="Search scope demo"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
leftContent: (
|
||||
<div className="flex flex-col h-full p-8 space-y-6">
|
||||
<h2 className="text-3xl font-bold leading-tight flex items-center gap-2">
|
||||
<AtSignIcon className="inline-block h-8 w-8 text-primary" />
|
||||
Mentions
|
||||
</h2>
|
||||
<p className="text-lg">
|
||||
@ mention specific <FileIcon className="inline-block h-4 w-4 mb-1 ml-0.5" /> files to add them to the {`model's`} context. Suggestions will be scoped to the selected search scopes.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<p className="mb-3 text-muted-foreground"><strong>Coming soon</strong></p>
|
||||
<div className="space-y-2 text-muted-foreground">
|
||||
<div className="flex gap-2">
|
||||
<FolderIcon className="h-4 w-4 flex-shrink-0 mt-1" />
|
||||
<span><strong>Directories</strong>: Include entire folders as context</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<GitCommitHorizontalIcon className="h-4 w-4 flex-shrink-0 mt-1" />
|
||||
<span><strong>Commits</strong>: Reference specific git commits</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<BookTextIcon className="h-4 w-4 flex-shrink-0 mt-1" />
|
||||
<span><strong>Docs</strong>: Link to external docs and wikis</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<TicketIcon className="h-4 w-4 flex-shrink-0 mt-1" />
|
||||
<span><strong>Issues</strong>: GitHub issues, Jira tickets, and more</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
rightContent: (
|
||||
<Image
|
||||
src={mentionsDemo}
|
||||
alt="Mentions demo"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
leftContent: (
|
||||
<div className="flex flex-col h-full p-8 space-y-6">
|
||||
<h2 className="text-3xl font-bold leading-tight flex items-center gap-2">
|
||||
<ArrowLeftRightIcon className="inline-block h-8 w-8 text-primary" />
|
||||
Inline Citations
|
||||
</h2>
|
||||
<p className="text-lg">
|
||||
{`Sourcebot searches your codebase and provides responses with clickable citations that link directly to relevant sections of code.`}
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
rightContent: (
|
||||
<Image
|
||||
src={citationsDemo}
|
||||
alt="Citations demo"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
leftContent: (
|
||||
<div className="flex flex-col h-full p-8 space-y-6">
|
||||
<h2 className="text-3xl font-bold leading-tight flex items-center gap-2">
|
||||
<CircleCheckIcon className="inline-block h-8 w-8 text-primary" />
|
||||
You're all set!
|
||||
</h2>
|
||||
<p className="text-lg">
|
||||
You can now ask Sourcebot any question about your codebase. Checkout the <Link href="https://docs.sourcebot.dev/docs/features/ask/overview" className="underline">docs</Link> for more information.
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
<span className="font-bold">Hit a bug?</span> Open up <Link href="https://github.com/sourcebot-dev/sourcebot/issues" className="underline">an issue</Link>.
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
<span className="font-bold">Feature request?</span> Open a <Link href="https://github.com/sourcebot-dev/sourcebot/discussions" className="underline">discussion</Link>.
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
<span className="font-bold">Anything else?</span> <Link href="https://www.sourcebot.dev/contact" className="underline">Contact us</Link>.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
rightContent: (
|
||||
<div className="flex flex-col h-full justify-center items-center gap-6 bg-[#020817]">
|
||||
<Image
|
||||
src={logoDarkSmall}
|
||||
width={150}
|
||||
height={150}
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
<GitHubStarButton />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
export const AgenticSearchTutorialDialog = ({ onClose }: AgenticSearchTutorialDialogProps) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
|
||||
const nextStep = () => {
|
||||
if (currentStep < tutorialSteps.length - 1) {
|
||||
setCurrentStep(currentStep + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const prevStep = () => {
|
||||
if (currentStep > 0) {
|
||||
setCurrentStep(currentStep - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const isLastStep = currentStep === tutorialSteps.length - 1
|
||||
const isFirstStep = currentStep === 0
|
||||
const currentStepData = tutorialSteps[currentStep];
|
||||
|
||||
return (
|
||||
<Dialog open={true} onOpenChange={onClose}>
|
||||
<DialogContent
|
||||
className="sm:max-w-[900px] p-0 flex flex-col h-[525px] overflow-hidden rounded-xl border-none bg-transparent"
|
||||
closeButtonClassName="text-white"
|
||||
>
|
||||
<div className="relative flex h-full">
|
||||
{/* Left Column (Text Content & Navigation) */}
|
||||
<div className="flex-1 flex flex-col justify-between bg-background">
|
||||
<div className="p-4 flex-1 overflow-y-auto">
|
||||
{currentStepData.leftContent}
|
||||
</div>
|
||||
|
||||
{/* Fixed bottom navigation for left column */}
|
||||
<div className="border-t p-6 flex items-center justify-between">
|
||||
{/* Left side: Previous button container */}
|
||||
<div className="w-36 flex justify-start">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={prevStep}
|
||||
className={cn(
|
||||
"flex items-center gap-2",
|
||||
isFirstStep && "opacity-0 pointer-events-none"
|
||||
)}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
Previous
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Center: Progress dots */}
|
||||
<div className="flex gap-2">
|
||||
{tutorialSteps.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
"w-2 h-2 rounded-full transition-colors",
|
||||
index === currentStep ? "bg-primary" : "bg-muted"
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right side: Next/Start/Get Started button container */}
|
||||
<div className="w-36 flex justify-end">
|
||||
{isLastStep ? (
|
||||
<Button onClick={onClose}>
|
||||
Get Started
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={nextStep}>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column (Image/Visual Content) */}
|
||||
<div className="flex-1 flex flex-col justify-between">
|
||||
<div className="flex-1 overflow-y-auto">{currentStepData.rightContent}</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ import { CardContent } from "@/components/ui/card";
|
|||
import { DemoExamples, DemoSearchExample, DemoSearchScope } from "@/types";
|
||||
import { cn, getCodeHostIcon } from "@/lib/utils";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { SearchScopeInfoCard } from "@/components/searchScopeInfoCard";
|
||||
import { SearchScopeInfoCard } from "@/features/chat/components/chatBox/searchScopeInfoCard";
|
||||
|
||||
interface AskSourcebotDemoCardsProps {
|
||||
demoExamples: DemoExamples;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ interface HomepageProps {
|
|||
}[];
|
||||
initialSearchMode: SearchMode;
|
||||
demoExamples: DemoExamples | undefined;
|
||||
isAgenticSearchTutorialDismissed: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ export const Homepage = ({
|
|||
chatHistory,
|
||||
initialSearchMode,
|
||||
demoExamples,
|
||||
isAgenticSearchTutorialDismissed,
|
||||
}: HomepageProps) => {
|
||||
const [searchMode, setSearchMode] = useState<SearchMode>(initialSearchMode);
|
||||
const isAgenticSearchEnabled = languageModels.length > 0;
|
||||
|
|
@ -90,6 +92,7 @@ export const Homepage = ({
|
|||
searchContexts={searchContexts}
|
||||
chatHistory={chatHistory}
|
||||
demoExamples={demoExamples}
|
||||
isTutorialDismissed={isAgenticSearchTutorialDismissed}
|
||||
/>
|
||||
</CustomSlateEditor>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { UpgradeToast } from "./components/upgradeToast";
|
|||
import { ServiceErrorException } from "@/lib/serviceError";
|
||||
import { auth } from "@/auth";
|
||||
import { cookies } from "next/headers";
|
||||
import { SEARCH_MODE_COOKIE_NAME } from "@/lib/constants";
|
||||
import { AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME, SEARCH_MODE_COOKIE_NAME } from "@/lib/constants";
|
||||
import { env } from "@/env.mjs";
|
||||
import { loadJsonFile } from "@sourcebot/shared";
|
||||
import { DemoExamples, demoExamplesSchema } from "@/types";
|
||||
|
|
@ -51,6 +51,8 @@ export default async function Home({ params: { domain } }: { params: { domain: s
|
|||
searchModeCookie?.value === "precise"
|
||||
) ? searchModeCookie.value : models.length > 0 ? "agentic" : "precise";
|
||||
|
||||
const isAgenticSearchTutorialDismissed = cookieStore.get(AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME)?.value === "true";
|
||||
|
||||
const demoExamples = env.SOURCEBOT_DEMO_EXAMPLES_PATH ? await (async () => {
|
||||
try {
|
||||
return await loadJsonFile<DemoExamples>(env.SOURCEBOT_DEMO_EXAMPLES_PATH!, demoExamplesSchema);
|
||||
|
|
@ -74,6 +76,7 @@ export default async function Home({ params: { domain } }: { params: { domain: s
|
|||
chatHistory={chatHistory}
|
||||
initialSearchMode={initialSearchMode}
|
||||
demoExamples={demoExamples}
|
||||
isAgenticSearchTutorialDismissed={isAgenticSearchTutorialDismissed}
|
||||
/>
|
||||
<Footer />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
|||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
closeButtonClassName?: string
|
||||
}
|
||||
>(({ className, children, closeButtonClassName, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
|
|
@ -45,7 +47,7 @@ const DialogContent = React.forwardRef<
|
|||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<X className={cn("h-4 w-4", closeButtonClassName)} />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ export const ChatBox = ({
|
|||
>
|
||||
<Editable
|
||||
className="w-full focus-visible:outline-none focus-visible:ring-0 bg-background text-base disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
|
||||
placeholder="Ask a question about the selected search scopes. @mention files to refine your query."
|
||||
placeholder="Ask a question about your code. @mention files or select search scopes to refine your query."
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
onKeyDown={onKeyDown}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import { ReactEditor, useSlate } from "slate-react";
|
|||
import { useSelectedLanguageModel } from "../../useSelectedLanguageModel";
|
||||
import { LanguageModelSelector } from "./languageModelSelector";
|
||||
import { SearchScopeSelector } from "./searchScopeSelector";
|
||||
import { SearchScopeInfoCard } from "@/components/searchScopeInfoCard";
|
||||
import { AtMentionInfoCard } from "@/components/atMentionInfoCard";
|
||||
import { SearchScopeInfoCard } from "@/features/chat/components/chatBox/searchScopeInfoCard";
|
||||
import { AtMentionInfoCard } from "@/features/chat/components/chatBox/atMentionInfoCard";
|
||||
|
||||
export interface ChatBoxToolbarProps {
|
||||
languageModels: LanguageModelInfo[];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import Image from "next/image";
|
||||
import { LibraryBigIcon, Code, ScanSearchIcon } from "lucide-react";
|
||||
import { cn, getCodeHostIcon } from "@/lib/utils";
|
||||
import { BookMarkedIcon, LibraryBigIcon, ScanSearchIcon } from "lucide-react";
|
||||
|
||||
export const SearchScopeInfoCard = () => {
|
||||
return (
|
||||
|
|
@ -10,24 +8,11 @@ export const SearchScopeInfoCard = () => {
|
|||
<h4 className="text-sm font-semibold text-popover-foreground">Search Scope</h4>
|
||||
</div>
|
||||
<div className="text-sm text-popover-foreground leading-relaxed">
|
||||
When asking Sourcebot a question, you can select one or more scopes to constrain the search.
|
||||
When asking Sourcebot a question, you can select one or more scopes to focus the search.
|
||||
There are two different types of search scopes:
|
||||
<div className="mt-3 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{(() => {
|
||||
const githubIcon = getCodeHostIcon("github");
|
||||
return githubIcon ? (
|
||||
<Image
|
||||
src={githubIcon.src}
|
||||
alt="GitHub icon"
|
||||
width={16}
|
||||
height={16}
|
||||
className={cn("h-4 w-4 flex-shrink-0", githubIcon.className)}
|
||||
/>
|
||||
) : (
|
||||
<Code className="h-4 w-4 text-muted-foreground flex-shrink-0" />
|
||||
);
|
||||
})()}
|
||||
<BookMarkedIcon className="h-4 w-4 text-muted-foreground flex-shrink-0" />
|
||||
<span><strong>Repository</strong>: A single repository, indicated by the code host icon.</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -24,6 +24,7 @@ export const TEAM_FEATURES = [
|
|||
|
||||
export const MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME = 'sb.mobile-unsupported-splash-screen-dismissed';
|
||||
export const SEARCH_MODE_COOKIE_NAME = 'sb.search-mode';
|
||||
export const AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME = 'sb.agentic-search-tutorial-dismissed';
|
||||
|
||||
// NOTE: changing SOURCEBOT_GUEST_USER_ID may break backwards compatibility since this value is used
|
||||
// to detect old guest users in the DB. If you change this value ensure it doesn't break upgrade flows
|
||||
|
|
|
|||
Loading…
Reference in a new issue