Skip to content

Commit 979e84e

Browse files
home view settings
1 parent 1f481da commit 979e84e

File tree

8 files changed

+253
-147
lines changed

8 files changed

+253
-147
lines changed

packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { cookies } from "next/headers";
12
import { auth } from "@/auth";
3+
import { HOME_VIEW_COOKIE_NAME } from "@/lib/constants";
4+
import { HomeView } from "@/hooks/useHomeView";
25
import { getConnectionStats, getOrgAccountRequests } from "@/actions";
36
import { isServiceError } from "@/lib/utils";
47
import { ServiceErrorException } from "@/lib/serviceError";
@@ -15,6 +18,8 @@ const SIDEBAR_CHAT_LIMIT = 30;
1518

1619
export async function DefaultSidebar() {
1720
const session = await auth();
21+
const cookieStore = await cookies();
22+
const homeView = (cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value ?? "search") as HomeView;
1823

1924
const chatHistory = session ? await getUserChatHistory() : [];
2025
if (isServiceError(chatHistory)) {
@@ -43,7 +48,13 @@ export async function DefaultSidebar() {
4348
<SidebarBase
4449
session={session}
4550
collapsible="icon"
46-
headerContent={<Nav isSettingsNotificationVisible={isSettingsNotificationVisible} isSignedIn={!!session} />}
51+
headerContent={
52+
<Nav
53+
isSettingsNotificationVisible={isSettingsNotificationVisible}
54+
isSignedIn={!!session}
55+
homeView={homeView}
56+
/>
57+
}
4758
>
4859
<ChatHistory
4960
chatHistory={chatHistory.slice(0, SIDEBAR_CHAT_LIMIT)}

packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
} from "@/components/ui/sidebar";
88
import { BookMarkedIcon, type LucideIcon, MessageCircleIcon, MessagesSquareIcon, SearchIcon, SettingsIcon } from "lucide-react";
99
import { usePathname } from "next/navigation";
10+
import { HomeView } from "@/hooks/useHomeView";
1011
import { NotificationDot } from "../../../components/notificationDot";
12+
import { useMemo } from "react";
1113

1214
interface NavItem {
1315
title: string;
@@ -17,53 +19,76 @@ interface NavItem {
1719
requiresAuth?: boolean;
1820
}
1921

20-
const baseItems: NavItem[] = [
21-
{
22-
title: "Code Search",
23-
href: "/search",
24-
icon: SearchIcon,
25-
key: "search",
26-
},
27-
{
28-
title: "Ask",
29-
href: "/chat",
30-
icon: MessageCircleIcon,
31-
key: "chat"
32-
},
33-
{
34-
title: "Chats",
35-
href: "/chats",
36-
icon: MessagesSquareIcon,
37-
key: "chats",
38-
requiresAuth: true,
39-
},
40-
{
41-
title: "Repositories",
42-
href: "/repos",
43-
icon: BookMarkedIcon,
44-
key: "repos"
45-
},
46-
{
47-
title: "Settings",
48-
href: "/settings",
49-
icon: SettingsIcon,
50-
key: "settings",
51-
requiresAuth: true
52-
},
53-
];
54-
5522
interface NavProps {
5623
isSettingsNotificationVisible?: boolean;
5724
isSignedIn?: boolean;
25+
homeView: HomeView;
5826
}
5927

60-
export function Nav({ isSettingsNotificationVisible, isSignedIn }: NavProps) {
28+
export function Nav({ isSettingsNotificationVisible, isSignedIn, homeView }: NavProps) {
6129
const pathname = usePathname();
6230

31+
const baseItems = useMemo((): NavItem[] => {
32+
33+
const searchItem: NavItem = {
34+
title: "Code Search",
35+
href: "/search",
36+
icon: SearchIcon,
37+
key: "search",
38+
}
39+
40+
const askItem: NavItem = {
41+
title: "Ask",
42+
href: "/chat",
43+
icon: MessageCircleIcon,
44+
key: "chat"
45+
}
46+
47+
return [
48+
...(homeView === "search" ? [
49+
searchItem,
50+
askItem,
51+
] : [
52+
askItem,
53+
searchItem,
54+
]),
55+
{
56+
title: "Chats",
57+
href: "/chats",
58+
icon: MessagesSquareIcon,
59+
key: "chats",
60+
requiresAuth: true,
61+
},
62+
{
63+
title: "Repositories",
64+
href: "/repos",
65+
icon: BookMarkedIcon,
66+
key: "repos"
67+
},
68+
{
69+
title: "Settings",
70+
href: "/settings",
71+
icon: SettingsIcon,
72+
key: "settings",
73+
requiresAuth: true
74+
},
75+
]
76+
77+
78+
}, [homeView]);
79+
6380
const isActive = (href: string) => {
81+
if (pathname === "/") {
82+
return (
83+
(homeView === "ask" && href === "/chat") ||
84+
(homeView === "search" && href === "/search")
85+
)
86+
}
87+
6488
if (href === "/search") {
65-
return pathname === "/" || pathname.startsWith("/search");
89+
return pathname.startsWith("/search");
6690
}
91+
6792
if (href === "/chat") {
6893
return pathname === "/chat";
6994
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { getRepos, getReposStats, getSearchContexts } from "@/actions";
2+
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
3+
import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server";
4+
import { CustomSlateEditor } from "@/features/chat/customSlateEditor";
5+
import { ServiceErrorException } from "@/lib/serviceError";
6+
import { isServiceError, measure } from "@/lib/utils";
7+
import { LandingPageChatBox } from "./components/landingPageChatBox";
8+
import { RepositoryCarousel } from "../components/repositoryCarousel";
9+
import { Separator } from "@/components/ui/separator";
10+
import { DemoCards } from "./components/demoCards";
11+
import { env } from "@sourcebot/shared";
12+
import { loadJsonFile } from "@sourcebot/shared";
13+
import { DemoExamples, demoExamplesSchema } from "@/types";
14+
import { auth } from "@/auth";
15+
16+
export async function ChatLandingPage() {
17+
const languageModels = await getConfiguredLanguageModelsInfo();
18+
const searchContexts = await getSearchContexts();
19+
const allRepos = await getRepos();
20+
const session = await auth();
21+
22+
const carouselRepos = await getRepos({
23+
where: {
24+
indexedAt: {
25+
not: null,
26+
},
27+
},
28+
take: 10,
29+
});
30+
31+
const repoStats = await getReposStats();
32+
33+
if (isServiceError(allRepos)) {
34+
throw new ServiceErrorException(allRepos);
35+
}
36+
37+
if (isServiceError(searchContexts)) {
38+
throw new ServiceErrorException(searchContexts);
39+
}
40+
41+
if (isServiceError(carouselRepos)) {
42+
throw new ServiceErrorException(carouselRepos);
43+
}
44+
45+
if (isServiceError(repoStats)) {
46+
throw new ServiceErrorException(repoStats);
47+
}
48+
49+
const demoExamples = env.SOURCEBOT_DEMO_EXAMPLES_PATH ? await (async () => {
50+
try {
51+
return (await measure(() => loadJsonFile<DemoExamples>(env.SOURCEBOT_DEMO_EXAMPLES_PATH!, demoExamplesSchema), 'loadExamplesJsonFile')).data;
52+
} catch (error) {
53+
console.error('Failed to load demo examples:', error);
54+
return undefined;
55+
}
56+
})() : undefined;
57+
58+
return (
59+
<div className="flex flex-col items-center h-full overflow-hidden">
60+
<div className="flex flex-col items-center h-full overflow-y-auto pt-8 pb-8 md:pt-16 w-full px-5">
61+
<div className="max-h-44 w-auto">
62+
<SourcebotLogo
63+
className="h-18 md:h-40 w-auto"
64+
/>
65+
</div>
66+
<CustomSlateEditor>
67+
<LandingPageChatBox
68+
languageModels={languageModels}
69+
repos={allRepos}
70+
searchContexts={searchContexts}
71+
isAuthenticated={!!session}
72+
/>
73+
</CustomSlateEditor>
74+
75+
<div className="mt-8">
76+
<RepositoryCarousel
77+
numberOfReposWithIndex={repoStats.numberOfReposWithIndex}
78+
displayRepos={carouselRepos}
79+
/>
80+
</div>
81+
82+
{demoExamples && (
83+
<>
84+
<div className="flex flex-col items-center w-fit gap-6">
85+
<Separator className="mt-5 w-[700px]" />
86+
</div>
87+
88+
<DemoCards
89+
demoExamples={demoExamples}
90+
/>
91+
</>
92+
)}
93+
</div>
94+
</div>
95+
)
96+
}
Lines changed: 3 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,5 @@
1-
import { getRepos, getReposStats, getSearchContexts } from "@/actions";
2-
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
3-
import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server";
4-
import { CustomSlateEditor } from "@/features/chat/customSlateEditor";
5-
import { ServiceErrorException } from "@/lib/serviceError";
6-
import { isServiceError, measure } from "@/lib/utils";
7-
import { LandingPageChatBox } from "./components/landingPageChatBox";
8-
import { RepositoryCarousel } from "../components/repositoryCarousel";
9-
import { Separator } from "@/components/ui/separator";
10-
import { DemoCards } from "./components/demoCards";
11-
import { env } from "@sourcebot/shared";
12-
import { loadJsonFile } from "@sourcebot/shared";
13-
import { DemoExamples, demoExamplesSchema } from "@/types";
14-
import { auth } from "@/auth";
1+
import { ChatLandingPage } from "./chatLandingPage";
152

163
export default async function Page() {
17-
const languageModels = await getConfiguredLanguageModelsInfo();
18-
const searchContexts = await getSearchContexts();
19-
const allRepos = await getRepos();
20-
const session = await auth();
21-
22-
const carouselRepos = await getRepos({
23-
where: {
24-
indexedAt: {
25-
not: null,
26-
},
27-
},
28-
take: 10,
29-
});
30-
31-
const repoStats = await getReposStats();
32-
33-
if (isServiceError(allRepos)) {
34-
throw new ServiceErrorException(allRepos);
35-
}
36-
37-
if (isServiceError(searchContexts)) {
38-
throw new ServiceErrorException(searchContexts);
39-
}
40-
41-
if (isServiceError(carouselRepos)) {
42-
throw new ServiceErrorException(carouselRepos);
43-
}
44-
45-
if (isServiceError(repoStats)) {
46-
throw new ServiceErrorException(repoStats);
47-
}
48-
49-
const demoExamples = env.SOURCEBOT_DEMO_EXAMPLES_PATH ? await (async () => {
50-
try {
51-
return (await measure(() => loadJsonFile<DemoExamples>(env.SOURCEBOT_DEMO_EXAMPLES_PATH!, demoExamplesSchema), 'loadExamplesJsonFile')).data;
52-
} catch (error) {
53-
console.error('Failed to load demo examples:', error);
54-
return undefined;
55-
}
56-
})() : undefined;
57-
58-
return (
59-
<div className="flex flex-col items-center h-full overflow-hidden">
60-
<div className="flex flex-col items-center h-full overflow-y-auto pt-8 pb-8 md:pt-16 w-full px-5">
61-
<div className="max-h-44 w-auto">
62-
<SourcebotLogo
63-
className="h-18 md:h-40 w-auto"
64-
/>
65-
</div>
66-
<CustomSlateEditor>
67-
<LandingPageChatBox
68-
languageModels={languageModels}
69-
repos={allRepos}
70-
searchContexts={searchContexts}
71-
isAuthenticated={!!session}
72-
/>
73-
</CustomSlateEditor>
74-
75-
<div className="mt-8">
76-
<RepositoryCarousel
77-
numberOfReposWithIndex={repoStats.numberOfReposWithIndex}
78-
displayRepos={carouselRepos}
79-
/>
80-
</div>
81-
82-
{demoExamples && (
83-
<>
84-
<div className="flex flex-col items-center w-fit gap-6">
85-
<Separator className="mt-5 w-[700px]" />
86-
</div>
87-
88-
<DemoCards
89-
demoExamples={demoExamples}
90-
/>
91-
</>
92-
)}
93-
</div>
94-
</div>
95-
)
96-
}
4+
return <ChatLandingPage />;
5+
}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1+
import { HOME_VIEW_COOKIE_NAME } from "@/lib/constants";
2+
import { cookies } from "next/headers";
3+
import { ChatLandingPage } from "./chat/chatLandingPage";
14
import SearchPage from "./search/page";
25

36
interface Props {
47
searchParams: Promise<{ query?: string }>;
58
}
69

710
export default async function Home(props: Props) {
11+
const cookieStore = await cookies();
12+
const homeView = cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value;
13+
if (homeView === "ask") {
14+
return <ChatLandingPage />;
15+
}
16+
817
return <SearchPage {...props} />;
9-
}
18+
}

0 commit comments

Comments
 (0)