Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/(auth)/api/auth/guest/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { NextResponse } from 'next/server';

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const redirectUrl = searchParams.get('redirectUrl') || '/';
const redirectUrl = searchParams.get('redirectUrl') || '/chat';

const token = await getToken({
req: request,
Expand All @@ -14,7 +14,7 @@ export async function GET(request: Request) {
});

if (token) {
return NextResponse.redirect(new URL('/', request.url));
return NextResponse.redirect(new URL('/chat', request.url));
}

return signIn('guest', { redirect: true, redirectTo: redirectUrl });
Expand Down
2 changes: 1 addition & 1 deletion app/(auth)/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { NextAuthConfig } from 'next-auth';
export const authConfig = {
pages: {
signIn: '/login',
newUser: '/',
newUser: '/chat',
},
providers: [
// added later in auth.ts since it requires bcrypt which is only compatible with Node.js
Expand Down
4 changes: 2 additions & 2 deletions app/(chat)/page.tsx → app/(chat)/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Chat } from '@/components/chat';
import { DEFAULT_CHAT_MODEL } from '@/lib/ai/models';
import { generateUUID } from '@/lib/utils';
import { DataStreamHandler } from '@/components/data-stream-handler';
import { auth } from '../(auth)/auth';
import { auth } from '../../(auth)/auth';
import { redirect } from 'next/navigation';

export default async function Page() {
Expand All @@ -31,7 +31,7 @@ export default async function Page() {
initialVisibilityType="private"
isReadonly={false}
session={session}
autoResume={false}
autoResume={false}
initialApiKey={apiKeyFromCookie?.value ?? ''}
/>
<DataStreamHandler id={id} />
Expand Down
59 changes: 30 additions & 29 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,40 @@

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--card: 0 0% 100%;
--card-foreground: 222 47% 11%;
--popover: 0 0% 100%;
--popover-foreground: 222 47% 11%;
--primary: 221 83% 53%;
--primary-foreground: 0 0% 100%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 46%;
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
/* Dark green palette */
--background: 0 0% 2%;
--foreground: 170 12% 72%;
--card: 199 67% 8%;
--card-foreground: 170 12% 72%;
--popover: 199 67% 8%;
--popover-foreground: 170 12% 72%;
--primary: 180 84% 10%;
--primary-foreground: 170 12% 72%;
--secondary: 178 75% 34%;
--secondary-foreground: 0 0% 100%;
--muted: 0 0% 17%;
--muted-foreground: 170 12% 72%;
--accent: 185 89% 28%;
--accent-foreground: 0 0% 100%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--border: 180 5% 37%;
--input: 180 5% 37%;
--ring: 178 75% 34%;
--chart-1: 178 75% 34%;
--chart-2: 174 86% 20%;
--chart-3: 185 89% 28%;
--chart-4: 188 89% 14%;
--chart-5: 170 12% 72%;
--radius: 0.5rem;
--sidebar-background: 0 0% 100%;
--sidebar-foreground: 222 47% 11%;
--sidebar-primary: 221 83% 53%;
--sidebar-background: 0 0% 2%;
--sidebar-foreground: 170 12% 72%;
--sidebar-primary: 178 75% 34%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 210 40% 96%;
--sidebar-accent-foreground: 222 47% 11%;
--sidebar-border: 214 32% 91%;
--sidebar-ring: 221 83% 53%;
--sidebar-accent: 174 86% 20%;
--sidebar-accent-foreground: 0 0% 100%;
--sidebar-border: 180 5% 37%;
--sidebar-ring: 178 75% 34%;
}
}

Expand Down
27 changes: 27 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Link from 'next/link';

import { Button } from '@/components/ui/button';
import SplineScene from '@/components/SplineScene';

export default function Home() {
return (
<main className="relative h-screen w-screen overflow-hidden bg-background text-foreground">
<SplineScene className="absolute inset-0" />
<div className="absolute left-16 top-1/4 max-w-xl space-y-6">
<h1 className="text-5xl font-bold leading-tight">
Search that{' '}
<span className="text-primary">feels like a conversation</span>
</h1>
<p className="text-lg text-muted-foreground">
Autonomous agents entity fit, dedupe, enrich contacts, and score leads—so you get data you can actually
use.
</p>
<Link href="/chat">
<Button size="lg" className="bg-primary text-primary-foreground hover:bg-primary/80">
Try Demo
</Button>
</Link>
</div>
</main>
);
}
17 changes: 17 additions & 0 deletions components/SplineScene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

// eslint-disable-next-line import/no-unresolved
import Spline from '@splinetool/react-spline/next';

interface SplineSceneProps {
className?: string;
}

export default function SplineScene({ className }: SplineSceneProps) {
return (
<Spline
scene="https://prod.spline.design/DS0UgrDOifhNt9T6/scene.splinecode"
className={className}
/>
);
}
4 changes: 2 additions & 2 deletions components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function AppSidebar({ user }: { user: User | undefined }) {
<SidebarMenu>
<div className="flex flex-row justify-between items-center">
<Link
href="/"
href="/chat"
onClick={() => {
setOpenMobile(false);
}}
Expand All @@ -46,7 +46,7 @@ export function AppSidebar({ user }: { user: User | undefined }) {
className="p-2 h-fit"
onClick={() => {
setOpenMobile(false);
router.push('/');
router.push('/chat');
router.refresh();
}}
>
Expand Down
3 changes: 1 addition & 2 deletions components/chat-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function PureChatHeader({
variant="outline"
className="order-2 md:order-1 md:px-2 px-2 md:h-fit ml-auto md:ml-0"
onClick={() => {
router.push('/');
router.push('/chat');
router.refresh();
}}
>
Expand All @@ -60,4 +60,3 @@ function PureChatHeader({
}

export const ChatHeader = memo(PureChatHeader);

2 changes: 1 addition & 1 deletion components/sidebar-history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
setShowDeleteDialog(false);

if (deleteId === id) {
router.push('/');
router.push('/chat');
}
};

Expand Down
17 changes: 11 additions & 6 deletions lib/googleTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,29 @@ export async function getFreshAccessToken(): Promise<FreshTokens> {
const tokenSet = getTokenSet(sessionId);

let accessToken = jar.get('gc_access_token')?.value || tokenSet?.access_token;
let refreshToken = jar.get('gc_refresh_token')?.value || tokenSet?.refresh_token;
let refreshToken =
jar.get('gc_refresh_token')?.value || tokenSet?.refresh_token;
const expiresAtStr =
jar.get('gc_expires_at')?.value ||
(tokenSet?.expires_at != null ? String(tokenSet.expires_at) : undefined);
let expiresAt = expiresAtStr ? Number(expiresAtStr) : undefined;

const now = Date.now();
const isExpiringSoon = expiresAt != null && now > expiresAt - EXPIRY_SKEW_MS;
const shouldRefresh = Boolean(refreshToken && (!accessToken || isExpiringSoon));
const shouldRefresh = Boolean(
refreshToken && (!accessToken || isExpiringSoon),
);

if (shouldRefresh) {
try {
const config = await getGoogleClient();
const refreshed = await refreshTokenGrant(config, refreshToken!);
const refreshed = await refreshTokenGrant(config, refreshToken as string);
accessToken = refreshed.access_token || accessToken;
refreshToken = refreshed.refresh_token || refreshToken;
expiresAt =
refreshed.expires_in != null ? now + refreshed.expires_in * 1000 : expiresAt;
refreshed.expires_in != null
? now + refreshed.expires_in * 1000
: expiresAt;

// Persist refreshed tokens
const cookieOptions = {
Expand All @@ -46,7 +51,8 @@ export async function getFreshAccessToken(): Promise<FreshTokens> {
maxAge: 60 * 60 * 24 * 7,
};
if (accessToken) jar.set('gc_access_token', accessToken, cookieOptions);
if (refreshToken) jar.set('gc_refresh_token', refreshToken, cookieOptions);
if (refreshToken)
jar.set('gc_refresh_token', refreshToken, cookieOptions);
if (expiresAt) jar.set('gc_expires_at', String(expiresAt), cookieOptions);
if (sessionId) {
const existing = tokenSet || {};
Expand All @@ -64,4 +70,3 @@ export async function getFreshAccessToken(): Promise<FreshTokens> {

return { accessToken, refreshToken, expiresAt };
}

8 changes: 6 additions & 2 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { guestRegex, isDevelopmentEnvironment } from './lib/constants';
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;

if (pathname === '/') {
return NextResponse.next();
}

/*
* Playwright starts the dev server and requires a 200 status to
* begin the tests, so this ensures that the tests can start
Expand Down Expand Up @@ -34,15 +38,15 @@ export async function middleware(request: NextRequest) {
const isGuest = guestRegex.test(token?.email ?? '');

if (token && !isGuest && ['/login', '/register'].includes(pathname)) {
return NextResponse.redirect(new URL('/', request.url));
return NextResponse.redirect(new URL('/chat', request.url));
}

return NextResponse.next();
}

export const config = {
matcher: [
'/',
'/chat',
'/chat/:id',
'/api/:path*',
'/login',
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"test": "export PLAYWRIGHT=True && pnpm exec playwright test"
},
"dependencies": {
"@ai-sdk/react": "^1.2.11",
"@ai-sdk/openai": "^1.2.15",
"@ai-sdk/react": "^1.2.11",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-python": "^6.1.6",
"@codemirror/state": "^6.5.0",
Expand All @@ -33,11 +33,13 @@
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
"@radix-ui/react-visually-hidden": "^1.1.0",
"@splinetool/react-spline": "^4.1.0",
"@vercel/analytics": "^1.3.1",
"@vercel/blob": "^0.24.1",
"@vercel/functions": "^2.0.0",
Expand All @@ -61,6 +63,7 @@
"next": "15.3.0-canary.31",
"next-auth": "5.0.0-beta.25",
"next-themes": "^0.3.0",
"openid-client": "^6.6.4",
"orderedmap": "^2.1.1",
"papaparse": "^5.5.2",
"postgres": "^3.4.4",
Expand All @@ -86,9 +89,7 @@
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^3.1.0",
"zod": "^3.23.8",
"@radix-ui/react-popover": "^1.1.2",
"openid-client": "^6.6.4"
"zod": "^3.23.8"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
Expand Down
Loading