chore(web): Upgrade to NextJS 15 (#477)

This commit is contained in:
Brendan Kellam 2025-08-22 14:48:29 -04:00 committed by GitHub
parent b36de3412d
commit d9fa221d72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 1346 additions and 741 deletions

View file

@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Updated NextJS to version 15. [#477](https://github.com/sourcebot-dev/sourcebot/pull/477)
## [4.6.4] - 2025-08-11
### Added

View file

@ -24,5 +24,8 @@
"dotenv-cli": "^8.0.0",
"npm-run-all": "^4.1.5"
},
"packageManager": "yarn@4.7.0"
"packageManager": "yarn@4.7.0",
"resolutions": {
"prettier": "3.5.3"
}
}

View file

@ -15,7 +15,7 @@
"@types/micromatch": "^4.0.9",
"@types/node": "^22.7.5",
"cross-env": "^7.0.3",
"json-schema-to-typescript": "^15.0.2",
"json-schema-to-typescript": "^15.0.4",
"tsc-watch": "^6.2.0",
"tsx": "^4.19.1",
"typescript": "^5.6.2",

View file

@ -1,2 +1,3 @@
# shadcn components
src/components/
next-env.d.ts

View file

@ -38,6 +38,8 @@ const nextConfig = {
},
]
},
turbopack: {}
};
export default withSentryConfig(nextConfig, {

View file

@ -3,10 +3,10 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev --turbopack",
"build": "cross-env SKIP_ENV_VALIDATION=1 next build",
"start": "next start",
"lint": "cross-env SKIP_ENV_VALIDATION=1 next lint",
"lint": "cross-env SKIP_ENV_VALIDATION=1 eslint .",
"test": "vitest",
"dev:emails": "email dev --dir ./src/emails",
"stripe:listen": "stripe listen --forward-to http://localhost:3000/api/stripe"
@ -146,7 +146,7 @@
"langfuse-vercel": "^3.38.4",
"lucide-react": "^0.517.0",
"micromatch": "^4.0.8",
"next": "14.2.30",
"next": "15.5.0",
"next-auth": "^5.0.0-beta.25",
"next-navigation-guard": "^0.2.0",
"next-themes": "^0.3.0",
@ -157,9 +157,9 @@
"posthog-js": "^1.161.5",
"pretty-bytes": "^6.1.1",
"psl": "^1.15.0",
"react": "^18",
"react": "19.1.1",
"react-device-detect": "^2.2.3",
"react-dom": "^18",
"react-dom": "19.1.1",
"react-hook-form": "^7.53.0",
"react-hotkeys-hook": "^4.5.1",
"react-icons": "^5.3.0",
@ -187,21 +187,23 @@
"zod-to-json-schema": "^3.24.5"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tanstack/eslint-plugin-query": "^5.74.7",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/micromatch": "^4.0.9",
"@types/node": "^20",
"@types/nodemailer": "^6.4.17",
"@types/psl": "^1.1.3",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^8.3.0",
"@typescript-eslint/parser": "^8.3.0",
"@types/react": "19.1.10",
"@types/react-dom": "19.1.7",
"@typescript-eslint/eslint-plugin": "^8.40.0",
"@typescript-eslint/parser": "^8.40.0",
"cross-env": "^7.0.3",
"eslint": "^8",
"eslint-config-next": "14.2.6",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-config-next": "15.5.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"jsdom": "^25.0.1",
"npm-run-all": "^4.1.5",
"postcss": "^8",
@ -211,5 +213,9 @@
"typescript": "^5",
"vite-tsconfig-paths": "^5.1.3",
"vitest": "^2.1.5"
},
"resolutions": {
"@types/react": "19.1.10",
"@types/react-dom": "19.1.7"
}
}

View file

@ -186,7 +186,7 @@ export const withTenancyModeEnforcement = async<T>(mode: TenancyMode, fn: () =>
////// Actions ///////
export const createOrg = (name: string, domain: string): Promise<{ id: number } | ServiceError> => sew(() =>
export const createOrg = async (name: string, domain: string): Promise<{ id: number } | ServiceError> => sew(() =>
withTenancyModeEnforcement('multi', () =>
withAuth(async (userId) => {
const org = await prisma.org.create({
@ -293,7 +293,7 @@ export const completeOnboarding = async (domain: string): Promise<{ success: boo
})
));
export const getSecrets = (domain: string): Promise<{ createdAt: Date; key: string; }[] | ServiceError> => sew(() =>
export const getSecrets = async (domain: string): Promise<{ createdAt: Date; key: string; }[] | ServiceError> => sew(() =>
withAuth((userId) =>
withOrgMembership(userId, domain, async ({ org }) => {
const secrets = await prisma.secret.findMany({
@ -1990,7 +1990,7 @@ export const rejectAccountRequest = async (requestId: string, domain: string) =>
));
export const dismissMobileUnsupportedSplashScreen = async () => sew(async () => {
await cookies().set(MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, 'true');
await (await cookies()).set(MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, 'true');
return true;
});

View file

@ -13,7 +13,13 @@ const agents = [
},
];
export default function AgentsPage({ params: { domain } }: { params: { domain: string } }) {
export default async function AgentsPage(props: { params: Promise<{ domain: string }> }) {
const params = await props.params;
const {
domain
} = params;
return (
<div className="flex flex-col items-center overflow-hidden min-h-screen">
<NavigationMenu domain={domain} />

View file

@ -5,13 +5,20 @@ import { Loader2 } from "lucide-react";
import { TreePreviewPanel } from "./components/treePreviewPanel";
interface BrowsePageProps {
params: {
params: Promise<{
path: string[];
domain: string;
};
}>;
}
export default async function BrowsePage({ params: { path: _rawPath, domain } }: BrowsePageProps) {
export default async function BrowsePage(props: BrowsePageProps) {
const params = await props.params;
const {
path: _rawPath,
domain
} = params;
const rawPath = decodeURIComponent(_rawPath.join('/'));
const { repoName, revisionName, path, pathType } = getBrowseParamsFromPathParam(rawPath);

View file

@ -13,13 +13,14 @@ import { ChatSidePanel } from '../components/chatSidePanel';
import { ResizablePanelGroup } from '@/components/ui/resizable';
interface PageProps {
params: {
params: Promise<{
domain: string;
id: string;
};
}>;
}
export default async function Page({ params }: PageProps) {
export default async function Page(props: PageProps) {
const params = await props.params;
const languageModels = await getConfiguredLanguageModelsInfo();
const repos = await getRepos(params.domain);
const searchContexts = await getSearchContexts(params.domain);

View file

@ -10,12 +10,13 @@ import { auth } from "@/auth";
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
interface PageProps {
params: {
params: Promise<{
domain: string;
};
}>;
}
export default async function Page({ params }: PageProps) {
export default async function Page(props: PageProps) {
const params = await props.params;
const languageModels = await getConfiguredLanguageModelsInfo();
const repos = await getRepos(params.domain);
const searchContexts = await getSearchContexts(params.domain);

View file

@ -57,43 +57,48 @@ export const NavigationMenu = async ({
<NavigationMenuBase>
<NavigationMenuList>
<NavigationMenuItem>
<Link href={`/${domain}`} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<NavigationMenuLink
href={`/${domain}`}
className={navigationMenuTriggerStyle()}
>
Search
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={`/${domain}/repos`} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<NavigationMenuLink
href={`/${domain}/repos`}
className={navigationMenuTriggerStyle()}
>
Repositories
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
{isAuthenticated && (
<>
{env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT === undefined && (
<NavigationMenuItem>
<Link href={`/${domain}/agents`} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<NavigationMenuLink
href={`/${domain}/agents`}
className={navigationMenuTriggerStyle()}
>
Agents
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
)}
<NavigationMenuItem>
<Link href={`/${domain}/connections`} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<NavigationMenuLink
href={`/${domain}/connections`}
className={navigationMenuTriggerStyle()}
>
Connections
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={`/${domain}/settings`} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<NavigationMenuLink
href={`/${domain}/settings`}
className={navigationMenuTriggerStyle()}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</>
)}

View file

@ -25,16 +25,18 @@ import { CodeHostType } from "@/lib/utils"
import { env } from "@/env.mjs"
interface ConnectionManagementPageProps {
params: {
params: Promise<{
domain: string
id: string
},
searchParams: {
}>,
searchParams: Promise<{
tab: string
}
}>
}
export default async function ConnectionManagementPage({ params, searchParams }: ConnectionManagementPageProps) {
export default async function ConnectionManagementPage(props: ConnectionManagementPageProps) {
const searchParams = await props.searchParams;
const params = await props.params;
const connection = await getConnectionByDomain(Number(params.id), params.domain);
if (!connection) {
return <NotFound className="flex w-full h-full items-center justify-center" message="Connection not found" />

View file

@ -2,13 +2,24 @@ import { auth } from "@/auth";
import { NavigationMenu } from "../components/navigationMenu";
import { redirect } from "next/navigation";
export default async function Layout({
children,
params: { domain },
}: Readonly<{
interface LayoutProps {
children: React.ReactNode;
params: { domain: string };
}>) {
params: Promise<{ domain: string }>;
}
export default async function Layout(
props: LayoutProps
) {
const params = await props.params;
const {
domain
} = params;
const {
children
} = props;
const session = await auth();
if (!session) {
return redirect(`/${domain}`);

View file

@ -9,12 +9,11 @@ import {
BitbucketCloudConnectionCreationForm,
BitbucketDataCenterConnectionCreationForm
} from "@/app/[domain]/components/connectionCreationForms";
import { useCallback } from "react";
import { useCallback, use } from "react";
import { useDomain } from "@/hooks/useDomain";
export default function NewConnectionPage({
params
}: { params: { type: string } }) {
export default function NewConnectionPage(props: { params: Promise<{ type: string }> }) {
const params = use(props.params);
const { type } = params;
const router = useRouter();
const domain = useDomain();

View file

@ -7,7 +7,13 @@ import { notFound, ServiceErrorException } from "@/lib/serviceError";
import { OrgRole } from "@sourcebot/db";
import { env } from "@/env.mjs";
export default async function ConnectionsPage({ params: { domain } }: { params: { domain: string } }) {
export default async function ConnectionsPage(props: { params: Promise<{ domain: string }> }) {
const params = await props.params;
const {
domain
} = params;
const connections = await getConnections(domain);
if (isServiceError(connections)) {
throw new ServiceErrorException(connections);
@ -15,7 +21,7 @@ export default async function ConnectionsPage({ params: { domain } }: { params:
const membership = await getOrgMembership(domain);
if (isServiceError(membership)) {
return notFound();
throw new ServiceErrorException(notFound());
}
return (

View file

@ -25,13 +25,20 @@ import { GitHubStarToast } from "./components/githubStarToast";
interface LayoutProps {
children: React.ReactNode,
params: { domain: string }
params: Promise<{ domain: string }>
}
export default async function Layout({
children,
params: { domain },
}: LayoutProps) {
export default async function Layout(props: LayoutProps) {
const params = await props.params;
const {
domain
} = params;
const {
children
} = props;
const org = await getOrgFromDomain(domain);
if (!org) {
@ -39,7 +46,18 @@ export default async function Layout({
}
const session = await auth();
const anonymousAccessEnabled = hasEntitlement("anonymous-access") && await getAnonymousAccessStatus(domain);
const anonymousAccessEnabled = await (async () => {
if (!hasEntitlement("anonymous-access")) {
return false;
}
const status = await getAnonymousAccessStatus(domain);
if (isServiceError(status)) {
return false;
}
return status;
})();
// If the user is authenticated, we must check if they're a member of the org
if (session) {

View file

@ -15,7 +15,13 @@ import { env } from "@/env.mjs";
import { loadJsonFile } from "@sourcebot/shared";
import { DemoExamples, demoExamplesSchema } from "@/types";
export default async function Home({ params: { domain } }: { params: { domain: string } }) {
export default async function Home(props: { params: Promise<{ domain: string }> }) {
const params = await props.params;
const {
domain
} = params;
const org = await getOrgFromDomain(domain);
if (!org) {
return <PageNotFound />

View file

@ -1,12 +1,22 @@
import { NavigationMenu } from "../components/navigationMenu";
export default function Layout({
children,
params: { domain },
}: Readonly<{
interface LayoutProps {
children: React.ReactNode;
params: { domain: string };
}>) {
params: Promise<{ domain: string }>;
}
export default async function Layout(
props: LayoutProps
) {
const params = await props.params;
const {
domain
} = params;
const {
children
} = props;
return (
<div className="min-h-screen flex flex-col">

View file

@ -4,7 +4,13 @@ import { PageNotFound } from "../components/pageNotFound";
import { Header } from "../components/header";
import { env } from "@/env.mjs";
export default async function ReposPage({ params: { domain } }: { params: { domain: string } }) {
export default async function ReposPage(props: { params: Promise<{ domain: string }> }) {
const params = await props.params;
const {
domain
} = params;
const org = await getOrgFromDomain(domain);
if (!org) {
return <PageNotFound />

View file

@ -8,12 +8,18 @@ import { ErrorCode } from "@/lib/errorCodes";
import { headers } from "next/headers";
interface GeneralSettingsPageProps {
params: {
params: Promise<{
domain: string;
}
}>
}
export default async function GeneralSettingsPage({ params: { domain } }: GeneralSettingsPageProps) {
export default async function GeneralSettingsPage(props: GeneralSettingsPageProps) {
const params = await props.params;
const {
domain
} = params;
const currentUserRole = await getCurrentUserRole(domain)
if (isServiceError(currentUserRole)) {
throw new ServiceErrorException(currentUserRole);

View file

@ -7,12 +7,18 @@ import { OrgRole } from "@sourcebot/db";
import { redirect } from "next/navigation";
interface AccessPageProps {
params: {
params: Promise<{
domain: string;
}
}>
}
export default async function AccessPage({ params: { domain } }: AccessPageProps) {
export default async function AccessPage(props: AccessPageProps) {
const params = await props.params;
const {
domain
} = params;
const org = await getOrgFromDomain(domain);
if (!org) {
throw new Error("Organization not found");

View file

@ -16,14 +16,18 @@ export const metadata: Metadata = {
}
interface BillingPageProps {
params: {
params: Promise<{
domain: string
}
}>
}
export default async function BillingPage({
params: { domain },
}: BillingPageProps) {
export default async function BillingPage(props: BillingPageProps) {
const params = await props.params;
const {
domain
} = params;
if (!IS_BILLING_ENABLED) {
notFound();
}

View file

@ -13,17 +13,28 @@ import { getOrgFromDomain } from "@/data/org";
import { OrgRole } from "@prisma/client";
import { env } from "@/env.mjs";
interface LayoutProps {
children: React.ReactNode;
params: Promise<{ domain: string }>;
}
export const metadata: Metadata = {
title: "Settings",
}
export default async function SettingsLayout({
children,
params: { domain },
}: Readonly<{
children: React.ReactNode;
params: { domain: string };
}>) {
export default async function SettingsLayout(
props: LayoutProps
) {
const params = await props.params;
const {
domain
} = params;
const {
children
} = props;
const session = await auth();
if (!session) {
return redirect(`/${domain}`);

View file

@ -7,12 +7,18 @@ import { notFound, ServiceErrorException } from "@/lib/serviceError";
import { env } from "@/env.mjs";
interface LicensePageProps {
params: {
params: Promise<{
domain: string;
}
}>
}
export default async function LicensePage({ params: { domain } }: LicensePageProps) {
export default async function LicensePage(props: LicensePageProps) {
const params = await props.params;
const {
domain
} = params;
if (env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined) {
notFound();
}

View file

@ -15,15 +15,27 @@ import { OrgRole } from "@prisma/client";
import { redirect } from "next/navigation";
interface MembersSettingsPageProps {
params: {
params: Promise<{
domain: string
},
searchParams: {
}>,
searchParams: Promise<{
tab?: string
}
}>
}
export default async function MembersSettingsPage({ params: { domain }, searchParams: { tab } }: MembersSettingsPageProps) {
export default async function MembersSettingsPage(props: MembersSettingsPageProps) {
const searchParams = await props.searchParams;
const {
tab
} = searchParams;
const params = await props.params;
const {
domain
} = params;
const org = await getOrgFromDomain(domain);
if (!org) {
throw new Error("Organization not found");

View file

@ -5,12 +5,18 @@ import { ImportSecretCard } from "./components/importSecretCard";
import { ServiceErrorException } from "@/lib/serviceError";
interface SecretsPageProps {
params: {
params: Promise<{
domain: string;
}
}>
}
export default async function SecretsPage({ params: { domain } }: SecretsPageProps) {
export default async function SecretsPage(props: SecretsPageProps) {
const params = await props.params;
const {
domain
} = params;
const secrets = await getSecrets(domain);
if (isServiceError(secrets)) {
throw new ServiceErrorException(secrets);

View file

@ -12,7 +12,13 @@ import { env } from "@/env.mjs";
import { IS_BILLING_ENABLED } from "@/ee/features/billing/stripe";
import { getSubscriptionInfo } from "@/ee/features/billing/actions";
export default async function Upgrade({ params: { domain } }: { params: { domain: string } }) {
export default async function Upgrade(props: { params: Promise<{ domain: string }> }) {
const params = await props.params;
const {
domain
} = params;
if (!IS_BILLING_ENABLED) {
redirect(`/${domain}`);
}

View file

@ -11,7 +11,7 @@ const logger = createLogger('stripe-webhook');
export async function POST(req: NextRequest) {
const body = await req.text();
const signature = headers().get('stripe-signature');
const signature = (await headers()).get('stripe-signature');
if (!signature) {
return new Response('No signature', { status: 400 });

View file

@ -4,8 +4,9 @@ import { NextRequest } from "next/server";
export async function GET(
request: NextRequest,
{ params }: { params: { domain: string; repoId: string } }
props: { params: Promise<{ domain: string; repoId: string }> }
) {
const params = await props.params;
const { domain, repoId } = params;
const repoIdNum = parseInt(repoId);

View file

@ -17,7 +17,7 @@ export async function OrganizationAccessSettings() {
const metadata = getOrgMetadata(org);
const anonymousAccessEnabled = metadata?.anonymousAccessEnabled ?? false;
const headersList = headers();
const headersList = await headers();
const baseUrl = getBaseUrl(headersList);
const inviteLink = createInviteLink(baseUrl, org.inviteLinkId)

View file

@ -1,9 +1,9 @@
@import "./codemirror-styles.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "./codemirror-styles.css";
@layer base {
html {
overflow-y: scroll;

View file

@ -9,7 +9,7 @@ import { prisma } from "@/prisma";
import { StatusCodes } from "http-status-codes";
import { ErrorCode } from "@/lib/errorCodes";
export const joinOrganization = (orgId: number, inviteLinkId?: string) => sew(async () =>
export const joinOrganization = async (orgId: number, inviteLinkId?: string) => sew(async () =>
withAuth(async (userId) => {
const org = await prisma.org.findUnique({
where: {

View file

@ -11,12 +11,13 @@ import { getAuthProviders } from "@/lib/authProviders";
import { JoinOrganizationCard } from "@/app/components/joinOrganizationCard";
interface InvitePageProps {
searchParams: {
searchParams: Promise<{
id?: string;
};
}>;
}
export default async function InvitePage({ searchParams }: InvitePageProps) {
export default async function InvitePage(props: InvitePageProps) {
const searchParams = await props.searchParams;
const org = await getOrgFromDomain(SINGLE_TENANT_ORG_DOMAIN);
if (!org || !org.isOnboarded) {
return redirect("/onboard");

View file

@ -10,13 +10,14 @@ import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants";
const logger = createLogger('login-page');
interface LoginProps {
searchParams: {
searchParams: Promise<{
callbackUrl?: string;
error?: string;
}
}>
}
export default async function Login({ searchParams }: LoginProps) {
export default async function Login(props: LoginProps) {
const searchParams = await props.searchParams;
logger.info("Login page loaded");
const session = await auth();
if (session) {

View file

@ -1,4 +1,5 @@
import type React from "react"
import Link from "next/link"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
@ -20,7 +21,7 @@ import { env } from "@/env.mjs";
import { GcpIapAuth } from "@/app/[domain]/components/gcpIapAuth";
interface OnboardingProps {
searchParams?: { step?: string };
searchParams?: Promise<{ step?: string }>;
}
interface OnboardingStep {
@ -38,7 +39,8 @@ interface ResourceCard {
icon?: React.ReactNode
}
export default async function Onboarding({ searchParams }: OnboardingProps) {
export default async function Onboarding(props: OnboardingProps) {
const searchParams = await props.searchParams;
const providers = getAuthProviders();
const org = await getOrgFromDomain(SINGLE_TENANT_ORG_DOMAIN);
const session = await auth();
@ -118,7 +120,7 @@ export default async function Onboarding({ searchParams }: OnboardingProps) {
component: (
<div className="space-y-6">
<Button asChild className="w-full">
<a href="/onboard?step=1">Get Started </a>
<Link href="/onboard?step=1">Get Started </Link>
</Button>
</div>
),
@ -170,7 +172,7 @@ export default async function Onboarding({ searchParams }: OnboardingProps) {
<div className="space-y-6">
<OrganizationAccessSettings />
<Button asChild className="w-full">
<a href="/onboard?step=3">Continue </a>
<Link href="/onboard?step=3">Continue </Link>
</Button>
</div>
),

View file

@ -9,12 +9,13 @@ import { getOrgFromDomain } from '@/data/org';
import { SINGLE_TENANT_ORG_DOMAIN } from '@/lib/constants';
interface RedeemPageProps {
searchParams: {
searchParams: Promise<{
invite_id?: string;
};
}>;
}
export default async function RedeemPage({ searchParams }: RedeemPageProps) {
export default async function RedeemPage(props: RedeemPageProps) {
const searchParams = await props.searchParams;
const org = await getOrgFromDomain(SINGLE_TENANT_ORG_DOMAIN);
if (!org || !org.isOnboarded) {
return redirect("/onboard");

View file

@ -10,13 +10,14 @@ import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants";
const logger = createLogger('signup-page');
interface LoginProps {
searchParams: {
searchParams: Promise<{
callbackUrl?: string;
error?: string;
}
}>
}
export default async function Signup({ searchParams }: LoginProps) {
export default async function Signup(props: LoginProps) {
const searchParams = await props.searchParams;
const session = await auth();
if (session) {
logger.info("Session found in signup page, redirecting to home");

View file

@ -245,7 +245,9 @@ export const ReferencedSourcesListView = ({
repoWebUrl={fileData.repositoryWebUrl}
fileName={fileData.path}
references={referencesInFile}
ref={(ref) => setEditorRef(fileId, ref)}
ref={ref => {
setEditorRef(fileId, ref);
}}
onSelectedReferenceChanged={onSelectedReferenceChanged}
onHoveredReferenceChanged={onHoveredReferenceChanged}
selectedReference={selectedReference}
@ -280,6 +282,5 @@ export const ReferencedSourcesListView = ({
})}
</div>
</ScrollArea>
);
}

View file

@ -1,5 +1,5 @@
import { expect, test } from 'vitest'
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
import { useExtractReferences } from './useExtractReferences';
import { getFileReferenceId } from './utils';
import { TextUIPart } from 'ai';

View file

@ -1,7 +1,7 @@
import { expect, test } from 'vitest'
import { SBChatMessage } from './types';
import { useMessagePairs } from './useMessagePairs';
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
test('useMessagePairs pairs user and assistant messages', () => {
const userMessage: SBChatMessage = {

View file

@ -22,7 +22,7 @@ export const FileTreeItemComponent = ({
isCollapsed?: boolean,
isCollapseChevronVisible?: boolean,
onClick: () => void,
parentRef: React.RefObject<HTMLDivElement>,
parentRef: React.RefObject<HTMLDivElement | null>,
}) => {
const ref = useRef<HTMLDivElement>(null);

View file

@ -1,6 +1,10 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -18,10 +22,23 @@
}
],
"paths": {
"@/*": ["./src/*"],
"@/public/*": ["./public/*"]
}
"@/*": [
"./src/*"
],
"@/public/*": [
"./public/*"
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/env.mjs"],
"exclude": ["node_modules"]
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"src/env.mjs"
],
"exclude": [
"node_modules"
]
}

1300
yarn.lock

File diff suppressed because it is too large Load diff