24 | {children}
25 |
26 |
34 | {children}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/components/ThemeColorUpdater.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect } from 'react';
4 |
5 | export default function ThemeColorUpdater() {
6 | useEffect(() => {
7 | const html = document.documentElement;
8 | let meta = document.querySelector('meta[name="theme-color"]');
9 | if (!meta) {
10 | meta = document.createElement('meta');
11 | meta.setAttribute('name', 'theme-color');
12 | document.head.appendChild(meta);
13 | }
14 |
15 | const LIGHT_THEME_COLOR = 'hsl(0 0% 100%)';
16 | const DARK_THEME_COLOR = 'hsl(240deg 10% 3.92%)';
17 |
18 | function updateThemeColor() {
19 | const isDark = html.classList.contains('dark');
20 | (meta as HTMLMetaElement).setAttribute('content', isDark ? DARK_THEME_COLOR : LIGHT_THEME_COLOR);
21 | }
22 |
23 | const observer = new MutationObserver(updateThemeColor);
24 | observer.observe(html, { attributes: true, attributeFilter: ['class'] });
25 | updateThemeColor();
26 |
27 | return () => observer.disconnect();
28 | }, []);
29 |
30 | return null;
31 | }
32 |
--------------------------------------------------------------------------------
/app/(chat)/api/history/route.ts:
--------------------------------------------------------------------------------
1 | import { auth } from '@/app/(auth)/auth';
2 | import { NextRequest } from 'next/server';
3 | import { getChatsByUserId } from '@/lib/db/queries';
4 |
5 | export async function GET(request: NextRequest) {
6 | const { searchParams } = request.nextUrl;
7 |
8 | const limit = parseInt(searchParams.get('limit') || '10');
9 | const startingAfter = searchParams.get('starting_after');
10 | const endingBefore = searchParams.get('ending_before');
11 |
12 | if (startingAfter && endingBefore) {
13 | return Response.json(
14 | 'Only one of starting_after or ending_before can be provided!',
15 | { status: 400 },
16 | );
17 | }
18 |
19 | const session = await auth();
20 |
21 | if (!session?.user?.id) {
22 | return Response.json('Unauthorized!', { status: 401 });
23 | }
24 |
25 | try {
26 | const chats = await getChatsByUserId({
27 | id: session.user.id,
28 | limit,
29 | startingAfter,
30 | endingBefore,
31 | });
32 |
33 | return Response.json(chats);
34 | } catch (_) {
35 | return Response.json('Failed to fetch chats!', { status: 500 });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/ai/providers.ts:
--------------------------------------------------------------------------------
1 | import {
2 | customProvider,
3 | extractReasoningMiddleware,
4 | wrapLanguageModel,
5 | } from 'ai';
6 | import { xai } from '@ai-sdk/xai';
7 | import { isTestEnvironment } from '../constants';
8 | import {
9 | artifactModel,
10 | chatModel,
11 | reasoningModel,
12 | titleModel,
13 | } from './models.test';
14 |
15 | export const myProvider = isTestEnvironment
16 | ? customProvider({
17 | languageModels: {
18 | 'chat-model': chatModel,
19 | 'chat-model-reasoning': reasoningModel,
20 | 'title-model': titleModel,
21 | 'artifact-model': artifactModel,
22 | },
23 | })
24 | : customProvider({
25 | languageModels: {
26 | 'chat-model': xai('grok-2-1212'),
27 | 'chat-model-reasoning': wrapLanguageModel({
28 | model: xai('grok-3-mini-beta'),
29 | middleware: extractReasoningMiddleware({ tagName: 'think' }),
30 | }),
31 | 'title-model': xai('grok-2-1212'),
32 | 'artifact-model': xai('grok-2-1212'),
33 | },
34 | imageModels: {
35 | 'small-model': xai.image('grok-2-image'),
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/lib/db/migrations/meta/_journal.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "7",
3 | "dialect": "postgresql",
4 | "entries": [
5 | {
6 | "idx": 0,
7 | "version": "7",
8 | "when": 1728598022383,
9 | "tag": "0000_keen_devos",
10 | "breakpoints": true
11 | },
12 | {
13 | "idx": 1,
14 | "version": "7",
15 | "when": 1730207363999,
16 | "tag": "0001_sparkling_blue_marvel",
17 | "breakpoints": true
18 | },
19 | {
20 | "idx": 2,
21 | "version": "7",
22 | "when": 1730725226313,
23 | "tag": "0002_wandering_riptide",
24 | "breakpoints": true
25 | },
26 | {
27 | "idx": 3,
28 | "version": "7",
29 | "when": 1733403031014,
30 | "tag": "0003_cloudy_glorian",
31 | "breakpoints": true
32 | },
33 | {
34 | "idx": 4,
35 | "version": "7",
36 | "when": 1733945232355,
37 | "tag": "0004_odd_slayback",
38 | "breakpoints": true
39 | },
40 | {
41 | "idx": 5,
42 | "version": "7",
43 | "when": 1741934630596,
44 | "tag": "0005_wooden_whistler",
45 | "breakpoints": true
46 | }
47 | ]
48 | }
--------------------------------------------------------------------------------
/app/(chat)/page.tsx:
--------------------------------------------------------------------------------
1 | import { cookies } from 'next/headers';
2 |
3 | import { Chat } from '@/components/chat';
4 | import { DEFAULT_CHAT_MODEL } from '@/lib/ai/models';
5 | import { generateUUID } from '@/lib/utils';
6 | import { DataStreamHandler } from '@/components/data-stream-handler';
7 |
8 | export default async function Page() {
9 | const id = generateUUID();
10 |
11 | const cookieStore = await cookies();
12 | const modelIdFromCookie = cookieStore.get('chat-model');
13 |
14 | if (!modelIdFromCookie) {
15 | return (
16 | <>
17 | 54 | Use your email and password to sign in 55 |
56 |60 | {"Don't have an account? "} 61 | 65 | Sign up 66 | 67 | {' for free.'} 68 |
69 |55 | Create an account with your email and password 56 |
57 |61 | {'Already have an account? '} 62 | 66 | Sign in 67 | 68 | {' instead.'} 69 |
70 |