fix: Move search mode selection into a cookie to avoid SSR flashes

This commit is contained in:
bkellam 2025-07-23 14:45:55 -07:00
parent eb04422b9f
commit 11099695da
6 changed files with 37 additions and 9 deletions

View file

@ -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, SINGLE_TENANT_ORG_DOMAIN, SOURCEBOT_GUEST_USER_ID, SOURCEBOT_SUPPORT_EMAIL } from "./lib/constants";
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 { orgDomainSchema, orgNameSchema, repositoryQuerySchema } from "./lib/schemas";
import { TenancyMode, ApiKeyPayload } from "./lib/types";
import { decrementOrgSeatCount, getSubscriptionForOrg } from "./ee/features/billing/serverUtils";
@ -1998,6 +1998,13 @@ export const setAnonymousAccessStatus = async (domain: string, enabled: boolean)
});
});
export async function setSearchModeCookie(searchMode: "precise" | "agentic") {
const cookieStore = await cookies();
cookieStore.set(SEARCH_MODE_COOKIE_NAME, searchMode, {
httpOnly: false, // Allow client-side access
});
}
////// Helpers ///////
const parseConnectionConfig = (config: string) => {

View file

@ -22,7 +22,7 @@ export const NewChatPanel = ({
repos,
order,
}: NewChatPanelProps) => {
const [selectedRepos, setSelectedRepos] = useLocalStorage<string[]>("selectedRepos", []);
const [selectedRepos, setSelectedRepos] = useLocalStorage<string[]>("selectedRepos", [], { initializeWithValue: false });
const { createNewChatThread, isLoading } = useCreateNewChatThread();
const [isRepoSelectorOpen, setIsRepoSelectorOpen] = useState(false);

View file

@ -126,7 +126,7 @@ export const AgenticSearch = ({
const { createNewChatThread, isLoading } = useCreateNewChatThread();
const dropdownRef = useRef<HTMLDivElement>(null);
const editor = useSlate();
const [selectedRepos, setSelectedRepos] = useLocalStorage<string[]>("selectedRepos", []);
const [selectedRepos, setSelectedRepos] = useLocalStorage<string[]>("selectedRepos", [], { initializeWithValue: false });
const domain = useDomain();
const [isRepoSelectorOpen, setIsRepoSelectorOpen] = useState(false);

View file

@ -4,11 +4,12 @@ import { SourcebotLogo } from "@/app/components/sourcebotLogo";
import { LanguageModelInfo } from "@/features/chat/types";
import { RepositoryQuery } from "@/lib/types";
import { useHotkeys } from "react-hotkeys-hook";
import { useLocalStorage } from "usehooks-ts";
import { AgenticSearch } from "./agenticSearch";
import { PreciseSearch } from "./preciseSearch";
import { SearchMode } from "./toolbar";
import { CustomSlateEditor } from "@/features/chat/customSlateEditor";
import { setSearchModeCookie } from "@/actions";
import { useCallback, useState } from "react";
interface HomepageProps {
initialRepos: RepositoryQuery[];
@ -18,6 +19,7 @@ interface HomepageProps {
createdAt: Date;
name: string | null;
}[];
initialSearchMode: SearchMode;
}
@ -25,13 +27,19 @@ export const Homepage = ({
initialRepos,
languageModels,
chatHistory,
initialSearchMode,
}: HomepageProps) => {
const [searchMode, setSearchMode] = useLocalStorage<SearchMode>("search-mode", "precise", { initializeWithValue: false });
const [searchMode, setSearchMode] = useState<SearchMode>(initialSearchMode);
const isAgenticSearchEnabled = languageModels.length > 0;
const onSearchModeChanged = useCallback(async (newMode: SearchMode) => {
setSearchMode(newMode);
await setSearchModeCookie(newMode);
}, [setSearchMode]);
useHotkeys("mod+i", (e) => {
e.preventDefault();
setSearchMode("agentic");
onSearchModeChanged("agentic");
}, {
enableOnFormTags: true,
enableOnContentEditable: true,
@ -40,7 +48,7 @@ export const Homepage = ({
useHotkeys("mod+p", (e) => {
e.preventDefault();
setSearchMode("precise");
onSearchModeChanged("precise");
}, {
enableOnFormTags: true,
enableOnContentEditable: true,
@ -61,7 +69,7 @@ export const Homepage = ({
searchModeSelectorProps={{
searchMode: "precise",
isAgenticSearchEnabled,
onSearchModeChange: setSearchMode,
onSearchModeChange: onSearchModeChanged,
}}
/>
) : (
@ -70,7 +78,7 @@ export const Homepage = ({
searchModeSelectorProps={{
searchMode: "agentic",
isAgenticSearchEnabled,
onSearchModeChange: setSearchMode,
onSearchModeChange: onSearchModeChanged,
}}
languageModels={languageModels}
repos={initialRepos}

View file

@ -9,6 +9,8 @@ import { PageNotFound } from "./components/pageNotFound";
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";
export default async function Home({ params: { domain } }: { params: { domain: string } }) {
const org = await getOrgFromDomain(domain);
@ -32,6 +34,15 @@ export default async function Home({ params: { domain } }: { params: { domain: s
const indexedRepos = repos.filter((repo) => repo.indexedAt !== undefined);
// Read search mode from cookie, defaulting to agentic if not set
// (assuming a language model is configured).
const cookieStore = await cookies();
const searchModeCookie = cookieStore.get(SEARCH_MODE_COOKIE_NAME);
const initialSearchMode = (
searchModeCookie?.value === "agentic" ||
searchModeCookie?.value === "precise"
) ? searchModeCookie.value : models.length > 0 ? "agentic" : "precise";
return (
<div className="flex flex-col items-center overflow-hidden min-h-screen">
<NavigationMenu
@ -43,6 +54,7 @@ export default async function Home({ params: { domain } }: { params: { domain: s
initialRepos={indexedRepos}
languageModels={models}
chatHistory={chatHistory}
initialSearchMode={initialSearchMode}
/>
<Footer />
</div>

View file

@ -23,6 +23,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';
// 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