-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add repo carousel and "how to search" section to homepage (#5)
- Loading branch information
1 parent
71717ac
commit 1c756d2
Showing
8 changed files
with
559 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,183 @@ | ||
import { listRepositories } from "@/lib/server/searchService"; | ||
import { isServiceError } from "@/lib/utils"; | ||
import Image from "next/image"; | ||
import { Suspense } from "react"; | ||
import logoDark from "../../public/sb_logo_dark_large.png"; | ||
import logoLight from "../../public/sb_logo_light_large.png"; | ||
import { NavigationMenu } from "./navigationMenu"; | ||
import { RepositoryCarousel } from "./repositoryCarousel"; | ||
import { SearchBar } from "./searchBar"; | ||
import { Separator } from "@/components/ui/separator"; | ||
|
||
export default function Home() { | ||
|
||
export default async function Home() { | ||
return ( | ||
<div className="h-screen flex flex-col items-center"> | ||
{/* TopBar */} | ||
<NavigationMenu /> | ||
<div className="flex flex-col justify-center items-center p-4 mt-48"> | ||
|
||
<div className="flex flex-col justify-center items-center mt-8 md:mt-32 max-w-[90%]"> | ||
<div className="max-h-44 w-auto"> | ||
<Image | ||
src={logoDark} | ||
className="w-full h-full hidden dark:block" | ||
alt={"Sourcebot logo"} | ||
priority={true} | ||
/> | ||
<Image | ||
src={logoLight} | ||
className="w-full h-full block dark:hidden" | ||
alt={"Sourcebot logo"} | ||
priority={true} | ||
/> | ||
</div> | ||
<div className="w-full flex flex-row mt-4"> | ||
<SearchBar | ||
autoFocus={true} | ||
/> | ||
</div> | ||
<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> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
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) { | ||
return <span> | ||
Get started | ||
<a | ||
href="https://github.com/TaqlaAI/sourcebot/blob/main/README.md" | ||
className="text-blue-500" | ||
> | ||
{` configuring Sourcebot.`} | ||
</a> | ||
</span>; | ||
} | ||
|
||
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
'use client'; | ||
|
||
import { Repository } from "@/lib/schemas"; | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
} from "@/components/ui/carousel"; | ||
import Autoscroll from "embla-carousel-auto-scroll"; | ||
import { getRepoCodeHostInfo } from "@/lib/utils"; | ||
import Image from "next/image"; | ||
import { FileIcon } from "@radix-ui/react-icons"; | ||
import clsx from "clsx"; | ||
|
||
interface RepositoryCarouselProps { | ||
repos: Repository[]; | ||
} | ||
|
||
export const RepositoryCarousel = ({ | ||
repos, | ||
}: RepositoryCarouselProps) => { | ||
return ( | ||
<Carousel | ||
opts={{ | ||
align: "start", | ||
loop: true, | ||
}} | ||
className="w-full max-w-lg" | ||
plugins={[ | ||
Autoscroll({ | ||
startDelay: 0, | ||
speed: 1, | ||
stopOnMouseEnter: true, | ||
stopOnInteraction: false, | ||
}), | ||
]} | ||
> | ||
<CarouselContent> | ||
{repos.map((repo, index) => ( | ||
<CarouselItem key={index} className="basis-auto"> | ||
<RepositoryBadge | ||
key={index} | ||
repo={repo} | ||
/> | ||
</CarouselItem> | ||
))} | ||
</CarouselContent> | ||
</Carousel> | ||
) | ||
}; | ||
|
||
interface RepositoryBadgeProps { | ||
repo: Repository; | ||
} | ||
|
||
const RepositoryBadge = ({ | ||
repo | ||
}: RepositoryBadgeProps) => { | ||
const { repoIcon, repoName, repoLink } = (() => { | ||
const info = getRepoCodeHostInfo(repo.Name); | ||
|
||
if (info) { | ||
return { | ||
repoIcon: <Image | ||
src={info.icon} | ||
alt={info.costHostName} | ||
className="w-4 h-4 dark:invert" | ||
/>, | ||
repoName: info.repoName, | ||
repoLink: info.repoLink, | ||
} | ||
} | ||
|
||
return { | ||
repoIcon: <FileIcon className="w-4 h-4" />, | ||
repoName: repo.Name, | ||
repoLink: undefined, | ||
} | ||
})(); | ||
|
||
return ( | ||
<div | ||
onClick={() => { | ||
if (repoLink !== undefined) { | ||
window.open(repoLink, "_blank"); | ||
} | ||
}} | ||
className={clsx("flex flex-row items-center gap-2 border rounded-md p-2 text-clip", { | ||
"cursor-pointer": repoLink !== undefined, | ||
})} | ||
> | ||
{repoIcon} | ||
<span className="text-sm font-mono"> | ||
{repoName} | ||
</span> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.