diff --git a/CHANGELOG.md b/CHANGELOG.md index 0762823ad..df792a756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Removed the Ask Sourcebot first-visit tutorial banner. [#1675](https://github.com/sourcebot-dev/sourcebot/pull/1675) - Removed suggested example queries from the Ask landing page. [#1674](https://github.com/sourcebot-dev/sourcebot/pull/1674) - + ### Fixed +- Made the default home page configurable with `DEFAULT_HOME_VIEW_PAGE`, defaulting to Code Search and supporting Ask. [#1677](https://github.com/sourcebot-dev/sourcebot/pull/1677) - Require authentication for the streaming and blocking Ask APIs in Public SaaS deployments. [#1679](https://github.com/sourcebot-dev/sourcebot/pull/1679) ## [5.1.14] - 2026-09-17 diff --git a/docs/docs/configuration/environment-variables.mdx b/docs/docs/configuration/environment-variables.mdx index 9bbe31de4..6316d6c9f 100644 --- a/docs/docs/configuration/environment-variables.mdx +++ b/docs/docs/configuration/environment-variables.mdx @@ -39,6 +39,7 @@ The following environment variables allow you to configure your Sourcebot deploy | `SOURCEBOT_STRUCTURED_LOGGING_FILE` | - |

Optional file to log to if structured logging is enabled

| | `SOURCEBOT_TELEMETRY_DISABLED` | `false` |

Enables/disables telemetry collection in Sourcebot. See [this doc](/docs/misc/telemetry) for more info.

| | `DEFAULT_MAX_MATCH_COUNT` | `10000` |

The default maximum number of search results to return when using search in the web app.

| +| `DEFAULT_HOME_VIEW_PAGE` | `search` |

Sets the default home page when no user home page preference is set. Use `ask` to default to Ask Sourcebot or `search` to default to Code Search.

| | `ALWAYS_INDEX_FILE_PATTERNS` | - |

A comma separated list of glob patterns matching file paths that should always be indexed, regardless of size or number of trigrams.

| | `SOURCEBOT_CHAT_ATTACHMENT_MAX_IMAGE_BYTES` | `10485760` (10 MiB) |

Maximum size in bytes of a single image attachment uploaded to Ask Sourcebot. Enforced server-side at upload time.

| | `SOURCEBOT_CHAT_ATTACHMENT_ORPHAN_TTL_HOURS` | `24` |

How long in hours an uploaded-but-unsent attachment is retained before being deleted by the orphan sweep. Set to `0` to disable the sweep.

| diff --git a/packages/shared/src/env.server.test.ts b/packages/shared/src/env.server.test.ts index 7f9bf0bca..967e9bbdb 100644 --- a/packages/shared/src/env.server.test.ts +++ b/packages/shared/src/env.server.test.ts @@ -82,3 +82,25 @@ describe('SOURCEBOT_MCP_TOOL_CALL_TIMEOUT_MS', () => { await expect(import('./env.server.js')).rejects.toThrow(); }); }); + +describe('DEFAULT_HOME_VIEW_PAGE', () => { + beforeEach(() => { + vi.resetModules(); + delete process.env.DEFAULT_HOME_VIEW_PAGE; + }); + + afterEach(() => { + delete process.env.DEFAULT_HOME_VIEW_PAGE; + }); + + test('defaults to search when not set', async () => { + const { env } = await import('./env.server.js'); + expect(env.DEFAULT_HOME_VIEW_PAGE).toBe('search'); + }); + + test('accepts ask', async () => { + process.env.DEFAULT_HOME_VIEW_PAGE = 'ask'; + const { env } = await import('./env.server.js'); + expect(env.DEFAULT_HOME_VIEW_PAGE).toBe('ask'); + }); +}); diff --git a/packages/shared/src/env.server.ts b/packages/shared/src/env.server.ts index 618e1a857..733442726 100644 --- a/packages/shared/src/env.server.ts +++ b/packages/shared/src/env.server.ts @@ -362,6 +362,7 @@ const options = { // @NOTE: Take care to update actions.ts when changing the name of this. EXPERIMENT_SELF_SERVE_REPO_INDEXING_GITHUB_TOKEN: z.string().optional(), PERMISSION_SYNC_REPO_DRIVEN_ENABLED: booleanSchema.default('true'), + DEFAULT_HOME_VIEW_PAGE: z.enum(["search", "ask"]).default("search"), EXPERIMENT_ASK_GH_ENABLED: booleanSchema.default('false'), EXPERIMENT_ASK_GH_GITHUB_TOKEN: z.string().optional(), diff --git a/packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx b/packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx index 85880f908..0779b7a24 100644 --- a/packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx +++ b/packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx @@ -1,7 +1,7 @@ import { cookies } from "next/headers"; import { auth } from "@/auth"; import { HOME_VIEW_COOKIE_NAME } from "@/lib/constants"; -import { HomeView } from "@/hooks/useHomeView"; +import type { HomeView } from "@/hooks/useHomeView"; import { getOrgAccountRequests } from "@/features/membership/actions"; import { isServiceError } from "@/lib/utils"; import { ServiceErrorException } from "@/lib/serviceError"; @@ -21,7 +21,8 @@ export const SIDEBAR_REPO_VISITS_LIMIT = 10; export async function DefaultSidebar() { const session = await auth(); const cookieStore = await cookies(); - const homeView = (cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value ?? "search") as HomeView; + const cookieValue = cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value as HomeView | undefined; + const homeView = cookieValue ?? env.DEFAULT_HOME_VIEW_PAGE; // Chat history is part of the Ask experience; hide it when the deployment // is not on a plan that includes Ask. diff --git a/packages/web/src/app/(app)/page.tsx b/packages/web/src/app/(app)/page.tsx index 998aef646..d9d71c645 100644 --- a/packages/web/src/app/(app)/page.tsx +++ b/packages/web/src/app/(app)/page.tsx @@ -1,7 +1,9 @@ import { HOME_VIEW_COOKIE_NAME } from "@/lib/constants"; +import type { HomeView } from "@/hooks/useHomeView"; import { cookies } from "next/headers"; import { ChatLandingPage } from "./chat/chatLandingPage"; import SearchPage from "./search/page"; +import { env } from "@sourcebot/shared"; interface Props { searchParams: Promise<{ query?: string }>; @@ -9,7 +11,8 @@ interface Props { export default async function Home(props: Props) { const cookieStore = await cookies(); - const homeView = cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value; + const cookieValue = cookieStore.get(HOME_VIEW_COOKIE_NAME)?.value as HomeView | undefined; + const homeView = cookieValue ?? env.DEFAULT_HOME_VIEW_PAGE; if (homeView === "ask") { return ; } diff --git a/packages/web/src/app/(app)/settings/general/generalPage.tsx b/packages/web/src/app/(app)/settings/general/generalPage.tsx index 5e3d9655a..a7d714043 100644 --- a/packages/web/src/app/(app)/settings/general/generalPage.tsx +++ b/packages/web/src/app/(app)/settings/general/generalPage.tsx @@ -29,11 +29,17 @@ interface GeneralPageProps { userName?: string; userEmail?: string; userImage?: string; + defaultHomeView: HomeView; } -export function GeneralPage({ userName, userEmail, userImage }: GeneralPageProps) { +export function GeneralPage({ + userName, + userEmail, + userImage, + defaultHomeView, +}: GeneralPageProps) { const { theme: _theme, setTheme } = useTheme(); - const [homeView, setHomeView] = useHomeView(); + const [homeView, setHomeView] = useHomeView(defaultHomeView); const [keymapType, setKeymapType] = useKeymapType(); const theme = useMemo(() => { diff --git a/packages/web/src/app/(app)/settings/general/page.tsx b/packages/web/src/app/(app)/settings/general/page.tsx index ffeaed5dc..7b469f9bc 100644 --- a/packages/web/src/app/(app)/settings/general/page.tsx +++ b/packages/web/src/app/(app)/settings/general/page.tsx @@ -1,4 +1,5 @@ import { authenticatedPage } from "@/middleware/authenticatedPage"; +import { env } from "@sourcebot/shared"; import { GeneralPage } from "./generalPage"; export default authenticatedPage(async ({ user }) => { @@ -7,6 +8,7 @@ export default authenticatedPage(async ({ user }) => { userName={user.name ?? undefined} userEmail={user.email ?? undefined} userImage={user.image ?? undefined} + defaultHomeView={env.DEFAULT_HOME_VIEW_PAGE} /> ); }); diff --git a/packages/web/src/hooks/useHomeView.ts b/packages/web/src/hooks/useHomeView.ts index 4c0c64de6..62969e2ba 100644 --- a/packages/web/src/hooks/useHomeView.ts +++ b/packages/web/src/hooks/useHomeView.ts @@ -7,17 +7,14 @@ export type HomeView = "search" | "ask"; const COOKIE_NAME = HOME_VIEW_COOKIE_NAME; -function getHomeViewFromCookie(): HomeView { +function getHomeViewFromCookie(defaultHomeView: HomeView): HomeView { if (typeof document === "undefined") { - return "search"; + return defaultHomeView; } const cookies = document.cookie.split(';').map(c => c.trim()); const cookie = cookies.find(c => c.startsWith(`${COOKIE_NAME}=`)); - if (!cookie) { - return "search"; - } - const value = cookie.substring(`${COOKIE_NAME}=`.length); - return value === "ask" ? "ask" : "search"; + const value = cookie?.substring(`${COOKIE_NAME}=`.length) as HomeView | undefined; + return value ?? defaultHomeView; } function setHomeViewCookie(value: HomeView) { @@ -29,8 +26,8 @@ function setHomeViewCookie(value: HomeView) { document.cookie = `${COOKIE_NAME}=${value}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`; } -export const useHomeView = (): [HomeView, (value: HomeView) => void] => { - const [homeView, setHomeViewState] = useState(getHomeViewFromCookie); +export const useHomeView = (defaultHomeView: HomeView): [HomeView, (value: HomeView) => void] => { + const [homeView, setHomeViewState] = useState(() => getHomeViewFromCookie(defaultHomeView)); const setHomeView = useCallback((value: HomeView) => { setHomeViewState(value);