2024-09-19 05:26:00 +00:00
|
|
|
import { listRepositories } from "@/lib/server/searchService";
|
|
|
|
|
import { isServiceError } from "@/lib/utils";
|
2024-08-26 00:57:34 +00:00
|
|
|
import Image from "next/image";
|
2024-09-19 05:26:00 +00:00
|
|
|
import { Suspense } from "react";
|
2024-09-03 01:46:43 +00:00
|
|
|
import logoDark from "../../public/sb_logo_dark_large.png";
|
|
|
|
|
import logoLight from "../../public/sb_logo_light_large.png";
|
2024-09-11 04:55:00 +00:00
|
|
|
import { NavigationMenu } from "./navigationMenu";
|
2024-09-19 05:26:00 +00:00
|
|
|
import { RepositoryCarousel } from "./repositoryCarousel";
|
2024-08-28 00:28:35 +00:00
|
|
|
import { SearchBar } from "./searchBar";
|
2024-09-19 05:26:00 +00:00
|
|
|
import { Separator } from "@/components/ui/separator";
|
2024-09-25 03:34:06 +00:00
|
|
|
import { SymbolIcon } from "@radix-ui/react-icons";
|
2024-08-28 04:00:59 +00:00
|
|
|
|
2024-09-19 05:26:00 +00:00
|
|
|
|
|
|
|
|
export default async function Home() {
|
2024-08-25 00:45:44 +00:00
|
|
|
return (
|
2024-09-24 05:30:58 +00:00
|
|
|
<div className="flex flex-col items-center overflow-hidden">
|
2024-08-28 00:28:35 +00:00
|
|
|
{/* TopBar */}
|
2024-09-11 04:55:00 +00:00
|
|
|
<NavigationMenu />
|
2024-09-19 05:26:00 +00:00
|
|
|
|
2024-09-24 05:30:58 +00:00
|
|
|
<div className="flex flex-col justify-center items-center mt-8 mb-8 md:mt-18 max-w-[90%]">
|
2024-09-03 01:46:43 +00:00
|
|
|
<div className="max-h-44 w-auto">
|
|
|
|
|
<Image
|
|
|
|
|
src={logoDark}
|
2024-09-24 05:30:58 +00:00
|
|
|
className="h-18 md:h-40 w-auto hidden dark:block"
|
2024-09-03 01:46:43 +00:00
|
|
|
alt={"Sourcebot logo"}
|
2024-09-19 05:26:00 +00:00
|
|
|
priority={true}
|
2024-08-28 00:28:35 +00:00
|
|
|
/>
|
2024-09-03 01:46:43 +00:00
|
|
|
<Image
|
|
|
|
|
src={logoLight}
|
2024-09-24 05:30:58 +00:00
|
|
|
className="h-18 md:h-40 w-auto block dark:hidden"
|
2024-09-03 01:46:43 +00:00
|
|
|
alt={"Sourcebot logo"}
|
2024-09-19 05:26:00 +00:00
|
|
|
priority={true}
|
2024-08-28 00:28:35 +00:00
|
|
|
/>
|
2024-09-03 01:46:43 +00:00
|
|
|
</div>
|
|
|
|
|
<div className="w-full flex flex-row mt-4">
|
2024-09-03 02:19:22 +00:00
|
|
|
<SearchBar
|
|
|
|
|
autoFocus={true}
|
|
|
|
|
/>
|
2024-09-03 01:46:43 +00:00
|
|
|
</div>
|
2024-09-19 05:26:00 +00:00
|
|
|
<div className="mt-8">
|
|
|
|
|
<Suspense fallback={<div>...</div>}>
|
|
|
|
|
<RepositoryList />
|
|
|
|
|
</Suspense>
|
|
|
|
|
</div>
|
|
|
|
|
<Separator className="mt-5 mb-8" />
|
|
|
|
|
<div className="flex flex-col items-center w-fit gap-6">
|
|
|
|
|
<span className="font-semibold">How to search</span>
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
|
|
|
<HowToSection
|
|
|
|
|
title="Search in files or paths"
|
|
|
|
|
>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="test todo">test todo</Query> <QueryExplanation>(both test and todo)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="test or todo">test <Highlight>or</Highlight> todo</Query> <QueryExplanation>(either test or todo)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query={`"exit boot"`}>{`"exit boot"`}</Query> <QueryExplanation>(exact match)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="TODO case:yes">TODO <Highlight>case:</Highlight>yes</Query> <QueryExplanation>(case sensitive)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
</HowToSection>
|
|
|
|
|
<HowToSection
|
|
|
|
|
title="Filter results"
|
|
|
|
|
>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="file:README setup"><Highlight>file:</Highlight>README setup</Query> <QueryExplanation>(by filename)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="repo:torvalds/linux test"><Highlight>repo:</Highlight>torvalds/linux test</Query> <QueryExplanation>(by repo)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="lang:typescript"><Highlight>lang:</Highlight>typescript</Query> <QueryExplanation>(by language)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="branch:HEAD"><Highlight>branch:</Highlight>HEAD</Query> <QueryExplanation>(by branch)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
</HowToSection>
|
|
|
|
|
<HowToSection
|
|
|
|
|
title="Advanced"
|
|
|
|
|
>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="file:\.py$"><Highlight>file:</Highlight>{`\\.py$`}</Query> <QueryExplanation>{`(files that end in ".py")`}</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="sym:main"><Highlight>sym:</Highlight>main</Query> <QueryExplanation>{`(symbols named "main")`}</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="todo -lang:c">todo <Highlight>-lang:c</Highlight></Query> <QueryExplanation>(negate filter)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
<QueryExample>
|
|
|
|
|
<Query query="content:README"><Highlight>content:</Highlight>README</Query> <QueryExplanation>(search content only)</QueryExplanation>
|
|
|
|
|
</QueryExample>
|
|
|
|
|
</HowToSection>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-09-03 01:46:43 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
2024-08-25 00:45:44 +00:00
|
|
|
}
|
2024-09-19 05:26:00 +00:00
|
|
|
|
|
|
|
|
const RepositoryList = async () => {
|
|
|
|
|
const _repos = await listRepositories();
|
|
|
|
|
|
|
|
|
|
if (isServiceError(_repos)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const repos = _repos.List.Repos.map((repo) => repo.Repository);
|
|
|
|
|
|
|
|
|
|
if (repos.length === 0) {
|
2024-09-25 03:34:06 +00:00
|
|
|
return (
|
|
|
|
|
<div className="flex flex-row items-center gap-3">
|
|
|
|
|
<SymbolIcon className="h-4 w-4 animate-spin" />
|
|
|
|
|
<span className="text-sm">indexing in progress...</span>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
2024-09-19 05:26:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex flex-col items-center gap-3">
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
{`Search ${repos.length} `}
|
|
|
|
|
<a
|
|
|
|
|
href="/repos"
|
|
|
|
|
className="text-blue-500"
|
|
|
|
|
>
|
|
|
|
|
{repos.length > 1 ? 'repositories' : 'repository'}
|
|
|
|
|
</a>
|
|
|
|
|
</span>
|
|
|
|
|
<RepositoryCarousel repos={repos} />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const HowToSection = ({ title, children }: { title: string, children: React.ReactNode }) => {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex flex-col gap-1">
|
|
|
|
|
<span className="dark:text-gray-300 text-sm mb-2 underline">{title}</span>
|
|
|
|
|
{children}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Highlight = ({ children }: { children: React.ReactNode }) => {
|
|
|
|
|
return (
|
|
|
|
|
<span className="text-blue-700 dark:text-blue-500">
|
|
|
|
|
{children}
|
|
|
|
|
</span>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QueryExample = ({ children }: { children: React.ReactNode }) => {
|
|
|
|
|
return (
|
|
|
|
|
<span className="text-sm font-mono">
|
|
|
|
|
{children}
|
|
|
|
|
</span>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QueryExplanation = ({ children }: { children: React.ReactNode }) => {
|
|
|
|
|
return (
|
|
|
|
|
<span className="text-gray-500 dark:text-gray-400 ml-3">
|
|
|
|
|
{children}
|
|
|
|
|
</span>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Query = ({ query, children }: { query: string, children: React.ReactNode }) => {
|
|
|
|
|
return (
|
|
|
|
|
<a
|
|
|
|
|
href={`/search?query=${query}`}
|
|
|
|
|
className="cursor-pointer hover:underline"
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</a>
|
|
|
|
|
)
|
|
|
|
|
}
|