Skip to content

Commit 1c756d2

Browse files
Add repo carousel and "how to search" section to homepage (#5)
1 parent 71717ac commit 1c756d2

File tree

8 files changed

+559
-5
lines changed

8 files changed

+559
-5
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"class-variance-authority": "^0.7.0",
3535
"client-only": "^0.0.1",
3636
"clsx": "^2.1.1",
37+
"embla-carousel-auto-scroll": "^8.3.0",
38+
"embla-carousel-react": "^8.3.0",
3739
"escape-string-regexp": "^5.0.0",
3840
"http-status-codes": "^2.3.0",
3941
"lucide-react": "^0.435.0",

src/app/navigationMenu.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ export const NavigationMenu = () => {
3030
src={logoDark}
3131
className="h-11 w-auto hidden dark:block"
3232
alt={"Sourcebot logo"}
33+
priority={true}
3334
/>
3435
<Image
3536
src={logoLight}
3637
className="h-11 w-auto block dark:hidden"
3738
alt={"Sourcebot logo"}
39+
priority={true}
3840
/>
3941
</div>
4042

src/app/page.tsx

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,183 @@
1+
import { listRepositories } from "@/lib/server/searchService";
2+
import { isServiceError } from "@/lib/utils";
13
import Image from "next/image";
4+
import { Suspense } from "react";
25
import logoDark from "../../public/sb_logo_dark_large.png";
36
import logoLight from "../../public/sb_logo_light_large.png";
47
import { NavigationMenu } from "./navigationMenu";
8+
import { RepositoryCarousel } from "./repositoryCarousel";
59
import { SearchBar } from "./searchBar";
10+
import { Separator } from "@/components/ui/separator";
611

7-
export default function Home() {
12+
13+
export default async function Home() {
814
return (
915
<div className="h-screen flex flex-col items-center">
1016
{/* TopBar */}
1117
<NavigationMenu />
12-
13-
<div className="flex flex-col justify-center items-center p-4 mt-48">
18+
19+
<div className="flex flex-col justify-center items-center mt-8 md:mt-32 max-w-[90%]">
1420
<div className="max-h-44 w-auto">
1521
<Image
1622
src={logoDark}
1723
className="w-full h-full hidden dark:block"
1824
alt={"Sourcebot logo"}
25+
priority={true}
1926
/>
2027
<Image
2128
src={logoLight}
2229
className="w-full h-full block dark:hidden"
2330
alt={"Sourcebot logo"}
31+
priority={true}
2432
/>
2533
</div>
2634
<div className="w-full flex flex-row mt-4">
2735
<SearchBar
2836
autoFocus={true}
2937
/>
3038
</div>
39+
<div className="mt-8">
40+
<Suspense fallback={<div>...</div>}>
41+
<RepositoryList />
42+
</Suspense>
43+
</div>
44+
<Separator className="mt-5 mb-8" />
45+
<div className="flex flex-col items-center w-fit gap-6">
46+
<span className="font-semibold">How to search</span>
47+
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
48+
<HowToSection
49+
title="Search in files or paths"
50+
>
51+
<QueryExample>
52+
<Query query="test todo">test todo</Query> <QueryExplanation>(both test and todo)</QueryExplanation>
53+
</QueryExample>
54+
<QueryExample>
55+
<Query query="test or todo">test <Highlight>or</Highlight> todo</Query> <QueryExplanation>(either test or todo)</QueryExplanation>
56+
</QueryExample>
57+
<QueryExample>
58+
<Query query={`"exit boot"`}>{`"exit boot"`}</Query> <QueryExplanation>(exact match)</QueryExplanation>
59+
</QueryExample>
60+
<QueryExample>
61+
<Query query="TODO case:yes">TODO <Highlight>case:</Highlight>yes</Query> <QueryExplanation>(case sensitive)</QueryExplanation>
62+
</QueryExample>
63+
</HowToSection>
64+
<HowToSection
65+
title="Filter results"
66+
>
67+
<QueryExample>
68+
<Query query="file:README setup"><Highlight>file:</Highlight>README setup</Query> <QueryExplanation>(by filename)</QueryExplanation>
69+
</QueryExample>
70+
<QueryExample>
71+
<Query query="repo:torvalds/linux test"><Highlight>repo:</Highlight>torvalds/linux test</Query> <QueryExplanation>(by repo)</QueryExplanation>
72+
</QueryExample>
73+
<QueryExample>
74+
<Query query="lang:typescript"><Highlight>lang:</Highlight>typescript</Query> <QueryExplanation>(by language)</QueryExplanation>
75+
</QueryExample>
76+
<QueryExample>
77+
<Query query="branch:HEAD"><Highlight>branch:</Highlight>HEAD</Query> <QueryExplanation>(by branch)</QueryExplanation>
78+
</QueryExample>
79+
</HowToSection>
80+
<HowToSection
81+
title="Advanced"
82+
>
83+
<QueryExample>
84+
<Query query="file:\.py$"><Highlight>file:</Highlight>{`\\.py$`}</Query> <QueryExplanation>{`(files that end in ".py")`}</QueryExplanation>
85+
</QueryExample>
86+
<QueryExample>
87+
<Query query="sym:main"><Highlight>sym:</Highlight>main</Query> <QueryExplanation>{`(symbols named "main")`}</QueryExplanation>
88+
</QueryExample>
89+
<QueryExample>
90+
<Query query="todo -lang:c">todo <Highlight>-lang:c</Highlight></Query> <QueryExplanation>(negate filter)</QueryExplanation>
91+
</QueryExample>
92+
<QueryExample>
93+
<Query query="content:README"><Highlight>content:</Highlight>README</Query> <QueryExplanation>(search content only)</QueryExplanation>
94+
</QueryExample>
95+
</HowToSection>
96+
</div>
97+
</div>
3198
</div>
3299
</div>
33100
)
34101
}
102+
103+
const RepositoryList = async () => {
104+
const _repos = await listRepositories();
105+
106+
if (isServiceError(_repos)) {
107+
return null;
108+
}
109+
110+
const repos = _repos.List.Repos.map((repo) => repo.Repository);
111+
112+
if (repos.length === 0) {
113+
return <span>
114+
Get started
115+
<a
116+
href="https://github.com/TaqlaAI/sourcebot/blob/main/README.md"
117+
className="text-blue-500"
118+
>
119+
{` configuring Sourcebot.`}
120+
</a>
121+
</span>;
122+
}
123+
124+
return (
125+
<div className="flex flex-col items-center gap-3">
126+
<span className="text-sm">
127+
{`Search ${repos.length} `}
128+
<a
129+
href="/repos"
130+
className="text-blue-500"
131+
>
132+
{repos.length > 1 ? 'repositories' : 'repository'}
133+
</a>
134+
</span>
135+
<RepositoryCarousel repos={repos} />
136+
</div>
137+
)
138+
}
139+
140+
const HowToSection = ({ title, children }: { title: string, children: React.ReactNode }) => {
141+
return (
142+
<div className="flex flex-col gap-1">
143+
<span className="dark:text-gray-300 text-sm mb-2 underline">{title}</span>
144+
{children}
145+
</div>
146+
)
147+
148+
}
149+
150+
const Highlight = ({ children }: { children: React.ReactNode }) => {
151+
return (
152+
<span className="text-blue-700 dark:text-blue-500">
153+
{children}
154+
</span>
155+
)
156+
}
157+
158+
const QueryExample = ({ children }: { children: React.ReactNode }) => {
159+
return (
160+
<span className="text-sm font-mono">
161+
{children}
162+
</span>
163+
)
164+
}
165+
166+
const QueryExplanation = ({ children }: { children: React.ReactNode }) => {
167+
return (
168+
<span className="text-gray-500 dark:text-gray-400 ml-3">
169+
{children}
170+
</span>
171+
)
172+
}
173+
174+
const Query = ({ query, children }: { query: string, children: React.ReactNode }) => {
175+
return (
176+
<a
177+
href={`/search?query=${query}`}
178+
className="cursor-pointer hover:underline"
179+
>
180+
{children}
181+
</a>
182+
)
183+
}

src/app/repos/columns.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
'use client';
22

33
import { Button } from "@/components/ui/button";
4+
import { getRepoCodeHostInfo } from "@/lib/utils";
45
import { Column, ColumnDef } from "@tanstack/react-table"
56
import { ArrowUpDown } from "lucide-react"
67

7-
8-
98
export type RepositoryColumnInfo = {
109
name: string;
1110
branches: {
@@ -25,6 +24,24 @@ export const columns: ColumnDef<RepositoryColumnInfo>[] = [
2524
{
2625
accessorKey: "name",
2726
header: "Name",
27+
cell: ({ row }) => {
28+
const repo = row.original;
29+
const info = getRepoCodeHostInfo(repo.name);
30+
return (
31+
<div className="flex flex-row items-center gap-2">
32+
<span
33+
className={info?.repoLink ? "cursor-pointer text-blue-500 hover:underline": ""}
34+
onClick={() => {
35+
if (info?.repoLink) {
36+
window.open(info.repoLink, "_blank");
37+
}
38+
}}
39+
>
40+
{repo.name}
41+
</span>
42+
</div>
43+
);
44+
}
2845
},
2946
{
3047
accessorKey: "branches",

src/app/repositoryCarousel.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use client';
2+
3+
import { Repository } from "@/lib/schemas";
4+
import {
5+
Carousel,
6+
CarouselContent,
7+
CarouselItem,
8+
} from "@/components/ui/carousel";
9+
import Autoscroll from "embla-carousel-auto-scroll";
10+
import { getRepoCodeHostInfo } from "@/lib/utils";
11+
import Image from "next/image";
12+
import { FileIcon } from "@radix-ui/react-icons";
13+
import clsx from "clsx";
14+
15+
interface RepositoryCarouselProps {
16+
repos: Repository[];
17+
}
18+
19+
export const RepositoryCarousel = ({
20+
repos,
21+
}: RepositoryCarouselProps) => {
22+
return (
23+
<Carousel
24+
opts={{
25+
align: "start",
26+
loop: true,
27+
}}
28+
className="w-full max-w-lg"
29+
plugins={[
30+
Autoscroll({
31+
startDelay: 0,
32+
speed: 1,
33+
stopOnMouseEnter: true,
34+
stopOnInteraction: false,
35+
}),
36+
]}
37+
>
38+
<CarouselContent>
39+
{repos.map((repo, index) => (
40+
<CarouselItem key={index} className="basis-auto">
41+
<RepositoryBadge
42+
key={index}
43+
repo={repo}
44+
/>
45+
</CarouselItem>
46+
))}
47+
</CarouselContent>
48+
</Carousel>
49+
)
50+
};
51+
52+
interface RepositoryBadgeProps {
53+
repo: Repository;
54+
}
55+
56+
const RepositoryBadge = ({
57+
repo
58+
}: RepositoryBadgeProps) => {
59+
const { repoIcon, repoName, repoLink } = (() => {
60+
const info = getRepoCodeHostInfo(repo.Name);
61+
62+
if (info) {
63+
return {
64+
repoIcon: <Image
65+
src={info.icon}
66+
alt={info.costHostName}
67+
className="w-4 h-4 dark:invert"
68+
/>,
69+
repoName: info.repoName,
70+
repoLink: info.repoLink,
71+
}
72+
}
73+
74+
return {
75+
repoIcon: <FileIcon className="w-4 h-4" />,
76+
repoName: repo.Name,
77+
repoLink: undefined,
78+
}
79+
})();
80+
81+
return (
82+
<div
83+
onClick={() => {
84+
if (repoLink !== undefined) {
85+
window.open(repoLink, "_blank");
86+
}
87+
}}
88+
className={clsx("flex flex-row items-center gap-2 border rounded-md p-2 text-clip", {
89+
"cursor-pointer": repoLink !== undefined,
90+
})}
91+
>
92+
{repoIcon}
93+
<span className="text-sm font-mono">
94+
{repoName}
95+
</span>
96+
</div>
97+
)
98+
}

0 commit comments

Comments
 (0)