MingruiZhang commited on
Commit
b841f1a
1 Parent(s): 7b2d417

google login

Browse files
app/sign-in/page.tsx CHANGED
@@ -1,17 +1,19 @@
1
- import { auth } from '@/auth'
2
- import { LoginButton } from '@/components/login-button'
3
- import { redirect } from 'next/navigation'
 
4
 
5
  export default async function SignInPage() {
6
- const session = await auth()
7
- // redirect to home if user is already logged in
8
- if (session?.user) {
9
- redirect('/')
10
- }
11
 
12
- return (
13
- <div className="flex h-[calc(100vh-theme(spacing.16))] items-center justify-center py-10">
14
- <LoginButton />
15
- </div>
16
- )
 
17
  }
 
1
+ import { auth } from '@/auth';
2
+ import { LoginButton } from '@/components/login-button';
3
+ import { redirect } from 'next/navigation';
4
+ import { ThemeToggle } from '../../components/theme-toggle';
5
 
6
  export default async function SignInPage() {
7
+ const session = await auth();
8
+ // redirect to home if user is already logged in
9
+ if (session?.user) {
10
+ redirect('/');
11
+ }
12
 
13
+ return (
14
+ <div className="flex flex-col h-[calc(100vh-theme(spacing.16))] items-center justify-center py-10 space-y-2">
15
+ <LoginButton oauth="google" />
16
+ <LoginButton oauth="github" />
17
+ </div>
18
+ );
19
  }
auth.ts CHANGED
@@ -1,39 +1,47 @@
1
- import NextAuth, { type DefaultSession } from 'next-auth'
2
- import GitHub from 'next-auth/providers/github'
 
3
 
4
  declare module 'next-auth' {
5
- interface Session {
6
- user: {
7
- /** The user's id. */
8
- id: string
9
- } & DefaultSession['user']
10
- }
11
  }
12
 
13
  export const {
14
- handlers: { GET, POST },
15
- auth
16
  } = NextAuth({
17
- providers: [GitHub],
18
- callbacks: {
19
- jwt({ token, profile }) {
20
- if (profile) {
21
- token.id = profile.id
22
- token.image = profile.avatar_url || profile.picture
23
- }
24
- return token
25
- },
26
- session: ({ session, token }) => {
27
- if (session?.user && token?.id) {
28
- session.user.id = String(token.id)
29
- }
30
- return session
31
- },
32
- authorized({ auth }) {
33
- return !!auth?.user // this ensures there is a logged in user for -every- request
34
- }
35
- },
36
- pages: {
37
- signIn: '/sign-in' // overrides the next-auth default signin page https://authjs.dev/guides/basics/pages
38
- }
39
- })
 
 
 
 
 
 
 
 
1
+ import NextAuth, { type DefaultSession } from 'next-auth';
2
+ import GitHub from 'next-auth/providers/github';
3
+ import Google from 'next-auth/providers/google';
4
 
5
  declare module 'next-auth' {
6
+ interface Session {
7
+ user: {
8
+ /** The user's id. */
9
+ id: string;
10
+ } & DefaultSession['user'];
11
+ }
12
  }
13
 
14
  export const {
15
+ handlers: { GET, POST },
16
+ auth,
17
  } = NextAuth({
18
+ providers: [
19
+ GitHub,
20
+ Google({
21
+ clientId: process.env.GOOGLE_CLIENT_ID!,
22
+ clientSecret: process.env.GOOGLE_SECRET!,
23
+ }),
24
+ ],
25
+ callbacks: {
26
+ jwt({ token, profile }) {
27
+ if (profile) {
28
+ // console.log('[Ming] ~ jwt ~ profile:', profile);
29
+ token.id = profile.id;
30
+ token.image = profile.avatar_url || profile.picture;
31
+ }
32
+ return token;
33
+ },
34
+ session: ({ session, token }) => {
35
+ if (session?.user && token?.id) {
36
+ session.user.id = String(token.id);
37
+ }
38
+ return session;
39
+ },
40
+ authorized({ auth }) {
41
+ return !!auth?.user; // this ensures there is a logged in user for -every- request
42
+ },
43
+ },
44
+ pages: {
45
+ signIn: '/sign-in', // overrides the next-auth default signin page https://authjs.dev/guides/basics/pages
46
+ },
47
+ });
components/login-button.tsx CHANGED
@@ -5,38 +5,34 @@ import { signIn } from 'next-auth/react';
5
 
6
  import { cn } from '@/lib/utils';
7
  import { Button, type ButtonProps } from '@/components/ui/button';
8
- import { IconGitHub, IconSpinner } from '@/components/ui/icons';
9
 
10
  interface LoginButtonProps extends ButtonProps {
11
- showGithubIcon?: boolean;
12
- text?: string;
13
  }
14
 
15
- export function LoginButton({
16
- text = 'Login with GitHub',
17
- showGithubIcon = true,
18
- className,
19
- ...props
20
- }: LoginButtonProps) {
21
  const [isLoading, setIsLoading] = React.useState(false);
 
 
 
 
 
 
 
22
  return (
23
  <Button
24
  variant="outline"
25
  onClick={() => {
26
  setIsLoading(true);
27
  // next-auth signIn() function doesn't work yet at Edge Runtime due to usage of BroadcastChannel
28
- signIn('github', { callbackUrl: `/` });
29
  }}
30
  disabled={isLoading}
31
- className={cn(className)}
32
  {...props}
33
  >
34
- {isLoading ? (
35
- <IconSpinner className="mr-2 animate-spin" />
36
- ) : showGithubIcon ? (
37
- <IconGitHub className="mr-2" />
38
- ) : null}
39
- {text}
40
  </Button>
41
  );
42
  }
 
5
 
6
  import { cn } from '@/lib/utils';
7
  import { Button, type ButtonProps } from '@/components/ui/button';
8
+ import { IconGitHub, IconSpinner, IconGoogle } from '@/components/ui/icons';
9
 
10
  interface LoginButtonProps extends ButtonProps {
11
+ oauth: 'github' | 'google';
 
12
  }
13
 
14
+ export function LoginButton({ oauth, ...props }: LoginButtonProps) {
 
 
 
 
 
15
  const [isLoading, setIsLoading] = React.useState(false);
16
+
17
+ const icon =
18
+ oauth === 'github' ? (
19
+ <IconGitHub className="mr-2" />
20
+ ) : (
21
+ <IconGoogle className="mr-2" />
22
+ );
23
  return (
24
  <Button
25
  variant="outline"
26
  onClick={() => {
27
  setIsLoading(true);
28
  // next-auth signIn() function doesn't work yet at Edge Runtime due to usage of BroadcastChannel
29
+ signIn(oauth, { callbackUrl: `/` });
30
  }}
31
  disabled={isLoading}
 
32
  {...props}
33
  >
34
+ {isLoading ? <IconSpinner className="mr-2 animate-spin" /> : icon}
35
+ Sign in with {oauth.charAt(0).toUpperCase() + oauth.slice(1)}
 
 
 
 
36
  </Button>
37
  );
38
  }
components/ui/icons.tsx CHANGED
@@ -1,507 +1,539 @@
1
- 'use client'
2
 
3
- import * as React from 'react'
4
 
5
- import { cn } from '@/lib/utils'
6
 
7
  function IconNextChat({
8
- className,
9
- inverted,
10
- ...props
11
  }: React.ComponentProps<'svg'> & { inverted?: boolean }) {
12
- const id = React.useId()
13
-
14
- return (
15
- <svg
16
- viewBox="0 0 17 17"
17
- fill="none"
18
- xmlns="http://www.w3.org/2000/svg"
19
- className={cn('size-4', className)}
20
- {...props}
21
- >
22
- <defs>
23
- <linearGradient
24
- id={`gradient-${id}-1`}
25
- x1="10.6889"
26
- y1="10.3556"
27
- x2="13.8445"
28
- y2="14.2667"
29
- gradientUnits="userSpaceOnUse"
30
- >
31
- <stop stopColor={inverted ? 'white' : 'black'} />
32
- <stop
33
- offset={1}
34
- stopColor={inverted ? 'white' : 'black'}
35
- stopOpacity={0}
36
- />
37
- </linearGradient>
38
- <linearGradient
39
- id={`gradient-${id}-2`}
40
- x1="11.7555"
41
- y1="4.8"
42
- x2="11.7376"
43
- y2="9.50002"
44
- gradientUnits="userSpaceOnUse"
45
- >
46
- <stop stopColor={inverted ? 'white' : 'black'} />
47
- <stop
48
- offset={1}
49
- stopColor={inverted ? 'white' : 'black'}
50
- stopOpacity={0}
51
- />
52
- </linearGradient>
53
- </defs>
54
- <path
55
- d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z"
56
- fill={inverted ? 'black' : 'white'}
57
- stroke={inverted ? 'black' : 'white'}
58
- strokeWidth={2}
59
- strokeLinecap="round"
60
- strokeLinejoin="round"
61
- />
62
- <mask
63
- id="mask0_91_2047"
64
- style={{ maskType: 'alpha' }}
65
- maskUnits="userSpaceOnUse"
66
- x={1}
67
- y={0}
68
- width={16}
69
- height={16}
70
- >
71
- <circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
72
- </mask>
73
- <g mask="url(#mask0_91_2047)">
74
- <circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
75
- <path
76
- d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z"
77
- fill={`url(#gradient-${id}-1)`}
78
- />
79
- <rect
80
- x="11.2222"
81
- y="4.8"
82
- width="1.06667"
83
- height="6.4"
84
- fill={`url(#gradient-${id}-2)`}
85
- />
86
- </g>
87
- </svg>
88
- )
89
  }
90
 
91
  function IconOpenAI({ className, ...props }: React.ComponentProps<'svg'>) {
92
- return (
93
- <svg
94
- fill="currentColor"
95
- viewBox="0 0 24 24"
96
- role="img"
97
- xmlns="http://www.w3.org/2000/svg"
98
- className={cn('size-4', className)}
99
- {...props}
100
- >
101
- <title>OpenAI icon</title>
102
- <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
103
- </svg>
104
- )
105
  }
106
 
107
  function IconVercel({ className, ...props }: React.ComponentProps<'svg'>) {
108
- return (
109
- <svg
110
- aria-label="Vercel logomark"
111
- role="img"
112
- viewBox="0 0 74 64"
113
- className={cn('size-4', className)}
114
- {...props}
115
- >
116
- <path
117
- d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z"
118
- fill="currentColor"
119
- ></path>
120
- </svg>
121
- )
122
  }
123
 
124
  function IconGitHub({ className, ...props }: React.ComponentProps<'svg'>) {
125
- return (
126
- <svg
127
- role="img"
128
- viewBox="0 0 24 24"
129
- xmlns="http://www.w3.org/2000/svg"
130
- fill="currentColor"
131
- className={cn('size-4', className)}
132
- {...props}
133
- >
134
- <title>GitHub</title>
135
- <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
136
- </svg>
137
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
 
140
  function IconSeparator({ className, ...props }: React.ComponentProps<'svg'>) {
141
- return (
142
- <svg
143
- fill="none"
144
- shapeRendering="geometricPrecision"
145
- stroke="currentColor"
146
- strokeLinecap="round"
147
- strokeLinejoin="round"
148
- strokeWidth="1"
149
- viewBox="0 0 24 24"
150
- aria-hidden="true"
151
- className={cn('size-4', className)}
152
- {...props}
153
- >
154
- <path d="M16.88 3.549L7.12 20.451"></path>
155
- </svg>
156
- )
157
  }
158
 
159
  function IconArrowDown({ className, ...props }: React.ComponentProps<'svg'>) {
160
- return (
161
- <svg
162
- xmlns="http://www.w3.org/2000/svg"
163
- viewBox="0 0 256 256"
164
- fill="currentColor"
165
- className={cn('size-4', className)}
166
- {...props}
167
- >
168
- <path d="m205.66 149.66-72 72a8 8 0 0 1-11.32 0l-72-72a8 8 0 0 1 11.32-11.32L120 196.69V40a8 8 0 0 1 16 0v156.69l58.34-58.35a8 8 0 0 1 11.32 11.32Z" />
169
- </svg>
170
- )
171
  }
172
 
173
  function IconArrowRight({ className, ...props }: React.ComponentProps<'svg'>) {
174
- return (
175
- <svg
176
- xmlns="http://www.w3.org/2000/svg"
177
- viewBox="0 0 256 256"
178
- fill="currentColor"
179
- className={cn('size-4', className)}
180
- {...props}
181
- >
182
- <path d="m221.66 133.66-72 72a8 8 0 0 1-11.32-11.32L196.69 136H40a8 8 0 0 1 0-16h156.69l-58.35-58.34a8 8 0 0 1 11.32-11.32l72 72a8 8 0 0 1 0 11.32Z" />
183
- </svg>
184
- )
185
  }
186
 
187
  function IconUser({ className, ...props }: React.ComponentProps<'svg'>) {
188
- return (
189
- <svg
190
- xmlns="http://www.w3.org/2000/svg"
191
- viewBox="0 0 256 256"
192
- fill="currentColor"
193
- className={cn('size-4', className)}
194
- {...props}
195
- >
196
- <path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z" />
197
- </svg>
198
- )
199
  }
200
 
201
  function IconPlus({ className, ...props }: React.ComponentProps<'svg'>) {
202
- return (
203
- <svg
204
- xmlns="http://www.w3.org/2000/svg"
205
- viewBox="0 0 256 256"
206
- fill="currentColor"
207
- className={cn('size-4', className)}
208
- {...props}
209
- >
210
- <path d="M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8Z" />
211
- </svg>
212
- )
213
  }
214
 
215
  function IconArrowElbow({ className, ...props }: React.ComponentProps<'svg'>) {
216
- return (
217
- <svg
218
- xmlns="http://www.w3.org/2000/svg"
219
- viewBox="0 0 256 256"
220
- fill="currentColor"
221
- className={cn('size-4', className)}
222
- {...props}
223
- >
224
- <path d="M200 32v144a8 8 0 0 1-8 8H67.31l34.35 34.34a8 8 0 0 1-11.32 11.32l-48-48a8 8 0 0 1 0-11.32l48-48a8 8 0 0 1 11.32 11.32L67.31 168H184V32a8 8 0 0 1 16 0Z" />
225
- </svg>
226
- )
227
  }
228
 
229
  function IconSpinner({ className, ...props }: React.ComponentProps<'svg'>) {
230
- return (
231
- <svg
232
- xmlns="http://www.w3.org/2000/svg"
233
- viewBox="0 0 256 256"
234
- fill="currentColor"
235
- className={cn('size-4 animate-spin', className)}
236
- {...props}
237
- >
238
- <path d="M232 128a104 104 0 0 1-208 0c0-41 23.81-78.36 60.66-95.27a8 8 0 0 1 6.68 14.54C60.15 61.59 40 93.27 40 128a88 88 0 0 0 176 0c0-34.73-20.15-66.41-51.34-80.73a8 8 0 0 1 6.68-14.54C208.19 49.64 232 87 232 128Z" />
239
- </svg>
240
- )
241
  }
242
 
243
  function IconMessage({ className, ...props }: React.ComponentProps<'svg'>) {
244
- return (
245
- <svg
246
- xmlns="http://www.w3.org/2000/svg"
247
- viewBox="0 0 256 256"
248
- fill="currentColor"
249
- className={cn('size-4', className)}
250
- {...props}
251
- >
252
- <path d="M216 48H40a16 16 0 0 0-16 16v160a15.84 15.84 0 0 0 9.25 14.5A16.05 16.05 0 0 0 40 240a15.89 15.89 0 0 0 10.25-3.78.69.69 0 0 0 .13-.11L82.5 208H216a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16ZM40 224Zm176-32H82.5a16 16 0 0 0-10.3 3.75l-.12.11L40 224V64h176Z" />
253
- </svg>
254
- )
255
  }
256
 
257
  function IconTrash({ className, ...props }: React.ComponentProps<'svg'>) {
258
- return (
259
- <svg
260
- xmlns="http://www.w3.org/2000/svg"
261
- viewBox="0 0 256 256"
262
- fill="currentColor"
263
- className={cn('size-4', className)}
264
- {...props}
265
- >
266
- <path d="M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16ZM96 40a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8v8H96Zm96 168H64V64h128Zm-80-104v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm48 0v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Z" />
267
- </svg>
268
- )
269
  }
270
 
271
  function IconRefresh({ className, ...props }: React.ComponentProps<'svg'>) {
272
- return (
273
- <svg
274
- xmlns="http://www.w3.org/2000/svg"
275
- viewBox="0 0 256 256"
276
- fill="currentColor"
277
- className={cn('size-4', className)}
278
- {...props}
279
- >
280
- <path d="M197.67 186.37a8 8 0 0 1 0 11.29C196.58 198.73 170.82 224 128 224c-37.39 0-64.53-22.4-80-39.85V208a8 8 0 0 1-16 0v-48a8 8 0 0 1 8-8h48a8 8 0 0 1 0 16H55.44C67.76 183.35 93 208 128 208c36 0 58.14-21.46 58.36-21.68a8 8 0 0 1 11.31.05ZM216 40a8 8 0 0 0-8 8v23.85C192.53 54.4 165.39 32 128 32c-42.82 0-68.58 25.27-69.66 26.34a8 8 0 0 0 11.3 11.34C69.86 69.46 92 48 128 48c35 0 60.24 24.65 72.56 40H168a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8V48a8 8 0 0 0-8-8Z" />
281
- </svg>
282
- )
283
  }
284
 
285
  function IconStop({ className, ...props }: React.ComponentProps<'svg'>) {
286
- return (
287
- <svg
288
- xmlns="http://www.w3.org/2000/svg"
289
- viewBox="0 0 256 256"
290
- fill="currentColor"
291
- className={cn('size-4', className)}
292
- {...props}
293
- >
294
- <path d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm0 192a88 88 0 1 1 88-88 88.1 88.1 0 0 1-88 88Zm24-120h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8Zm-8 48h-32v-32h32Z" />
295
- </svg>
296
- )
297
  }
298
 
299
  function IconSidebar({ className, ...props }: React.ComponentProps<'svg'>) {
300
- return (
301
- <svg
302
- xmlns="http://www.w3.org/2000/svg"
303
- viewBox="0 0 256 256"
304
- fill="currentColor"
305
- className={cn('size-4', className)}
306
- {...props}
307
- >
308
- <path d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16ZM40 56h40v144H40Zm176 144H96V56h120v144Z" />
309
- </svg>
310
- )
311
  }
312
 
313
  function IconMoon({ className, ...props }: React.ComponentProps<'svg'>) {
314
- return (
315
- <svg
316
- xmlns="http://www.w3.org/2000/svg"
317
- viewBox="0 0 256 256"
318
- fill="currentColor"
319
- className={cn('size-4', className)}
320
- {...props}
321
- >
322
- <path d="M233.54 142.23a8 8 0 0 0-8-2 88.08 88.08 0 0 1-109.8-109.8 8 8 0 0 0-10-10 104.84 104.84 0 0 0-52.91 37A104 104 0 0 0 136 224a103.09 103.09 0 0 0 62.52-20.88 104.84 104.84 0 0 0 37-52.91 8 8 0 0 0-1.98-7.98Zm-44.64 48.11A88 88 0 0 1 65.66 67.11a89 89 0 0 1 31.4-26A106 106 0 0 0 96 56a104.11 104.11 0 0 0 104 104 106 106 0 0 0 14.92-1.06 89 89 0 0 1-26.02 31.4Z" />
323
- </svg>
324
- )
325
  }
326
 
327
  function IconSun({ className, ...props }: React.ComponentProps<'svg'>) {
328
- return (
329
- <svg
330
- xmlns="http://www.w3.org/2000/svg"
331
- viewBox="0 0 256 256"
332
- fill="currentColor"
333
- className={cn('size-4', className)}
334
- {...props}
335
- >
336
- <path d="M120 40V16a8 8 0 0 1 16 0v24a8 8 0 0 1-16 0Zm72 88a64 64 0 1 1-64-64 64.07 64.07 0 0 1 64 64Zm-16 0a48 48 0 1 0-48 48 48.05 48.05 0 0 0 48-48ZM58.34 69.66a8 8 0 0 0 11.32-11.32l-16-16a8 8 0 0 0-11.32 11.32Zm0 116.68-16 16a8 8 0 0 0 11.32 11.32l16-16a8 8 0 0 0-11.32-11.32ZM192 72a8 8 0 0 0 5.66-2.34l16-16a8 8 0 0 0-11.32-11.32l-16 16A8 8 0 0 0 192 72Zm5.66 114.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32-11.32ZM48 128a8 8 0 0 0-8-8H16a8 8 0 0 0 0 16h24a8 8 0 0 0 8-8Zm80 80a8 8 0 0 0-8 8v24a8 8 0 0 0 16 0v-24a8 8 0 0 0-8-8Zm112-88h-24a8 8 0 0 0 0 16h24a8 8 0 0 0 0-16Z" />
337
- </svg>
338
- )
339
  }
340
 
341
  function IconCopy({ className, ...props }: React.ComponentProps<'svg'>) {
342
- return (
343
- <svg
344
- xmlns="http://www.w3.org/2000/svg"
345
- viewBox="0 0 256 256"
346
- fill="currentColor"
347
- className={cn('size-4', className)}
348
- {...props}
349
- >
350
- <path d="M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8Zm-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z" />
351
- </svg>
352
- )
353
  }
354
 
355
  function IconCheck({ className, ...props }: React.ComponentProps<'svg'>) {
356
- return (
357
- <svg
358
- xmlns="http://www.w3.org/2000/svg"
359
- viewBox="0 0 256 256"
360
- fill="currentColor"
361
- className={cn('size-4', className)}
362
- {...props}
363
- >
364
- <path d="m229.66 77.66-128 128a8 8 0 0 1-11.32 0l-56-56a8 8 0 0 1 11.32-11.32L96 188.69 218.34 66.34a8 8 0 0 1 11.32 11.32Z" />
365
- </svg>
366
- )
367
  }
368
 
369
  function IconDownload({ className, ...props }: React.ComponentProps<'svg'>) {
370
- return (
371
- <svg
372
- xmlns="http://www.w3.org/2000/svg"
373
- viewBox="0 0 256 256"
374
- fill="currentColor"
375
- className={cn('size-4', className)}
376
- {...props}
377
- >
378
- <path d="M224 152v56a16 16 0 0 1-16 16H48a16 16 0 0 1-16-16v-56a8 8 0 0 1 16 0v56h160v-56a8 8 0 0 1 16 0Zm-101.66 5.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0-11.32-11.32L136 132.69V40a8 8 0 0 0-16 0v92.69l-26.34-26.35a8 8 0 0 0-11.32 11.32Z" />
379
- </svg>
380
- )
381
  }
382
 
383
  function IconClose({ className, ...props }: React.ComponentProps<'svg'>) {
384
- return (
385
- <svg
386
- xmlns="http://www.w3.org/2000/svg"
387
- viewBox="0 0 256 256"
388
- fill="currentColor"
389
- className={cn('size-4', className)}
390
- {...props}
391
- >
392
- <path d="M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128 50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z" />
393
- </svg>
394
- )
395
  }
396
 
397
  function IconEdit({ className, ...props }: React.ComponentProps<'svg'>) {
398
- return (
399
- <svg
400
- xmlns="http://www.w3.org/2000/svg"
401
- fill="none"
402
- viewBox="0 0 24 24"
403
- strokeWidth={1.5}
404
- stroke="currentColor"
405
- className={cn('size-4', className)}
406
- {...props}
407
- >
408
- <path
409
- strokeLinecap="round"
410
- strokeLinejoin="round"
411
- d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
412
- />
413
- </svg>
414
- )
415
  }
416
 
417
  function IconShare({ className, ...props }: React.ComponentProps<'svg'>) {
418
- return (
419
- <svg
420
- xmlns="http://www.w3.org/2000/svg"
421
- fill="currentColor"
422
- className={cn('size-4', className)}
423
- viewBox="0 0 256 256"
424
- {...props}
425
- >
426
- <path d="m237.66 106.35-80-80A8 8 0 0 0 144 32v40.35c-25.94 2.22-54.59 14.92-78.16 34.91-28.38 24.08-46.05 55.11-49.76 87.37a12 12 0 0 0 20.68 9.58c11-11.71 50.14-48.74 107.24-52V192a8 8 0 0 0 13.66 5.65l80-80a8 8 0 0 0 0-11.3ZM160 172.69V144a8 8 0 0 0-8-8c-28.08 0-55.43 7.33-81.29 21.8a196.17 196.17 0 0 0-36.57 26.52c5.8-23.84 20.42-46.51 42.05-64.86C99.41 99.77 127.75 88 152 88a8 8 0 0 0 8-8V51.32L220.69 112Z" />
427
- </svg>
428
- )
429
  }
430
 
431
  function IconUsers({ className, ...props }: React.ComponentProps<'svg'>) {
432
- return (
433
- <svg
434
- xmlns="http://www.w3.org/2000/svg"
435
- fill="currentColor"
436
- className={cn('size-4', className)}
437
- viewBox="0 0 256 256"
438
- {...props}
439
- >
440
- <path d="M117.25 157.92a60 60 0 1 0-66.5 0 95.83 95.83 0 0 0-47.22 37.71 8 8 0 1 0 13.4 8.74 80 80 0 0 1 134.14 0 8 8 0 0 0 13.4-8.74 95.83 95.83 0 0 0-47.22-37.71ZM40 108a44 44 0 1 1 44 44 44.05 44.05 0 0 1-44-44Zm210.14 98.7a8 8 0 0 1-11.07-2.33A79.83 79.83 0 0 0 172 168a8 8 0 0 1 0-16 44 44 0 1 0-16.34-84.87 8 8 0 1 1-5.94-14.85 60 60 0 0 1 55.53 105.64 95.83 95.83 0 0 1 47.22 37.71 8 8 0 0 1-2.33 11.07Z" />
441
- </svg>
442
- )
443
  }
444
 
445
  function IconExternalLink({
446
- className,
447
- ...props
448
  }: React.ComponentProps<'svg'>) {
449
- return (
450
- <svg
451
- xmlns="http://www.w3.org/2000/svg"
452
- fill="currentColor"
453
- className={cn('size-4', className)}
454
- viewBox="0 0 256 256"
455
- {...props}
456
- >
457
- <path d="M224 104a8 8 0 0 1-16 0V59.32l-66.33 66.34a8 8 0 0 1-11.32-11.32L196.68 48H152a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8Zm-40 24a8 8 0 0 0-8 8v72H48V80h72a8 8 0 0 0 0-16H48a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-72a8 8 0 0 0-8-8Z" />
458
- </svg>
459
- )
460
  }
461
 
462
  function IconChevronUpDown({
463
- className,
464
- ...props
465
  }: React.ComponentProps<'svg'>) {
466
- return (
467
- <svg
468
- xmlns="http://www.w3.org/2000/svg"
469
- fill="currentColor"
470
- className={cn('size-4', className)}
471
- viewBox="0 0 256 256"
472
- {...props}
473
- >
474
- <path d="M181.66 170.34a8 8 0 0 1 0 11.32l-48 48a8 8 0 0 1-11.32 0l-48-48a8 8 0 0 1 11.32-11.32L128 212.69l42.34-42.35a8 8 0 0 1 11.32 0Zm-96-84.68L128 43.31l42.34 42.35a8 8 0 0 0 11.32-11.32l-48-48a8 8 0 0 0-11.32 0l-48 48a8 8 0 0 0 11.32 11.32Z" />
475
- </svg>
476
- )
477
  }
478
 
479
  export {
480
- IconEdit,
481
- IconNextChat,
482
- IconOpenAI,
483
- IconVercel,
484
- IconGitHub,
485
- IconSeparator,
486
- IconArrowDown,
487
- IconArrowRight,
488
- IconUser,
489
- IconPlus,
490
- IconArrowElbow,
491
- IconSpinner,
492
- IconMessage,
493
- IconTrash,
494
- IconRefresh,
495
- IconStop,
496
- IconSidebar,
497
- IconMoon,
498
- IconSun,
499
- IconCopy,
500
- IconCheck,
501
- IconDownload,
502
- IconClose,
503
- IconShare,
504
- IconUsers,
505
- IconExternalLink,
506
- IconChevronUpDown
507
- }
 
 
1
+ 'use client';
2
 
3
+ import * as React from 'react';
4
 
5
+ import { cn } from '@/lib/utils';
6
 
7
  function IconNextChat({
8
+ className,
9
+ inverted,
10
+ ...props
11
  }: React.ComponentProps<'svg'> & { inverted?: boolean }) {
12
+ const id = React.useId();
13
+
14
+ return (
15
+ <svg
16
+ viewBox="0 0 17 17"
17
+ fill="none"
18
+ xmlns="http://www.w3.org/2000/svg"
19
+ className={cn('size-4', className)}
20
+ {...props}
21
+ >
22
+ <defs>
23
+ <linearGradient
24
+ id={`gradient-${id}-1`}
25
+ x1="10.6889"
26
+ y1="10.3556"
27
+ x2="13.8445"
28
+ y2="14.2667"
29
+ gradientUnits="userSpaceOnUse"
30
+ >
31
+ <stop stopColor={inverted ? 'white' : 'black'} />
32
+ <stop
33
+ offset={1}
34
+ stopColor={inverted ? 'white' : 'black'}
35
+ stopOpacity={0}
36
+ />
37
+ </linearGradient>
38
+ <linearGradient
39
+ id={`gradient-${id}-2`}
40
+ x1="11.7555"
41
+ y1="4.8"
42
+ x2="11.7376"
43
+ y2="9.50002"
44
+ gradientUnits="userSpaceOnUse"
45
+ >
46
+ <stop stopColor={inverted ? 'white' : 'black'} />
47
+ <stop
48
+ offset={1}
49
+ stopColor={inverted ? 'white' : 'black'}
50
+ stopOpacity={0}
51
+ />
52
+ </linearGradient>
53
+ </defs>
54
+ <path
55
+ d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z"
56
+ fill={inverted ? 'black' : 'white'}
57
+ stroke={inverted ? 'black' : 'white'}
58
+ strokeWidth={2}
59
+ strokeLinecap="round"
60
+ strokeLinejoin="round"
61
+ />
62
+ <mask
63
+ id="mask0_91_2047"
64
+ style={{ maskType: 'alpha' }}
65
+ maskUnits="userSpaceOnUse"
66
+ x={1}
67
+ y={0}
68
+ width={16}
69
+ height={16}
70
+ >
71
+ <circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
72
+ </mask>
73
+ <g mask="url(#mask0_91_2047)">
74
+ <circle cx={9} cy={8} r={8} fill={inverted ? 'black' : 'white'} />
75
+ <path
76
+ d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z"
77
+ fill={`url(#gradient-${id}-1)`}
78
+ />
79
+ <rect
80
+ x="11.2222"
81
+ y="4.8"
82
+ width="1.06667"
83
+ height="6.4"
84
+ fill={`url(#gradient-${id}-2)`}
85
+ />
86
+ </g>
87
+ </svg>
88
+ );
89
  }
90
 
91
  function IconOpenAI({ className, ...props }: React.ComponentProps<'svg'>) {
92
+ return (
93
+ <svg
94
+ fill="currentColor"
95
+ viewBox="0 0 24 24"
96
+ role="img"
97
+ xmlns="http://www.w3.org/2000/svg"
98
+ className={cn('size-4', className)}
99
+ {...props}
100
+ >
101
+ <title>OpenAI icon</title>
102
+ <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
103
+ </svg>
104
+ );
105
  }
106
 
107
  function IconVercel({ className, ...props }: React.ComponentProps<'svg'>) {
108
+ return (
109
+ <svg
110
+ aria-label="Vercel logomark"
111
+ role="img"
112
+ viewBox="0 0 74 64"
113
+ className={cn('size-4', className)}
114
+ {...props}
115
+ >
116
+ <path
117
+ d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z"
118
+ fill="currentColor"
119
+ ></path>
120
+ </svg>
121
+ );
122
  }
123
 
124
  function IconGitHub({ className, ...props }: React.ComponentProps<'svg'>) {
125
+ return (
126
+ <svg
127
+ role="img"
128
+ viewBox="0 0 24 24"
129
+ xmlns="http://www.w3.org/2000/svg"
130
+ fill="currentColor"
131
+ className={cn('size-4', className)}
132
+ {...props}
133
+ >
134
+ <title>GitHub</title>
135
+ <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
136
+ </svg>
137
+ );
138
+ }
139
+
140
+ function IconGoogle({ className, ...props }: React.ComponentProps<'svg'>) {
141
+ return (
142
+ <svg
143
+ xmlns="http://www.w3.org/2000/svg"
144
+ width="2443"
145
+ height="2500"
146
+ preserveAspectRatio="xMidYMid"
147
+ viewBox="0 0 256 262"
148
+ id="google"
149
+ className={cn('size-4', className)}
150
+ >
151
+ <path
152
+ fill="#4285F4"
153
+ d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
154
+ ></path>
155
+ <path
156
+ fill="#34A853"
157
+ d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
158
+ ></path>
159
+ <path
160
+ fill="#FBBC05"
161
+ d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782"
162
+ ></path>
163
+ <path
164
+ fill="#EB4335"
165
+ d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
166
+ ></path>
167
+ </svg>
168
+ );
169
  }
170
 
171
  function IconSeparator({ className, ...props }: React.ComponentProps<'svg'>) {
172
+ return (
173
+ <svg
174
+ fill="none"
175
+ shapeRendering="geometricPrecision"
176
+ stroke="currentColor"
177
+ strokeLinecap="round"
178
+ strokeLinejoin="round"
179
+ strokeWidth="1"
180
+ viewBox="0 0 24 24"
181
+ aria-hidden="true"
182
+ className={cn('size-4', className)}
183
+ {...props}
184
+ >
185
+ <path d="M16.88 3.549L7.12 20.451"></path>
186
+ </svg>
187
+ );
188
  }
189
 
190
  function IconArrowDown({ className, ...props }: React.ComponentProps<'svg'>) {
191
+ return (
192
+ <svg
193
+ xmlns="http://www.w3.org/2000/svg"
194
+ viewBox="0 0 256 256"
195
+ fill="currentColor"
196
+ className={cn('size-4', className)}
197
+ {...props}
198
+ >
199
+ <path d="m205.66 149.66-72 72a8 8 0 0 1-11.32 0l-72-72a8 8 0 0 1 11.32-11.32L120 196.69V40a8 8 0 0 1 16 0v156.69l58.34-58.35a8 8 0 0 1 11.32 11.32Z" />
200
+ </svg>
201
+ );
202
  }
203
 
204
  function IconArrowRight({ className, ...props }: React.ComponentProps<'svg'>) {
205
+ return (
206
+ <svg
207
+ xmlns="http://www.w3.org/2000/svg"
208
+ viewBox="0 0 256 256"
209
+ fill="currentColor"
210
+ className={cn('size-4', className)}
211
+ {...props}
212
+ >
213
+ <path d="m221.66 133.66-72 72a8 8 0 0 1-11.32-11.32L196.69 136H40a8 8 0 0 1 0-16h156.69l-58.35-58.34a8 8 0 0 1 11.32-11.32l72 72a8 8 0 0 1 0 11.32Z" />
214
+ </svg>
215
+ );
216
  }
217
 
218
  function IconUser({ className, ...props }: React.ComponentProps<'svg'>) {
219
+ return (
220
+ <svg
221
+ xmlns="http://www.w3.org/2000/svg"
222
+ viewBox="0 0 256 256"
223
+ fill="currentColor"
224
+ className={cn('size-4', className)}
225
+ {...props}
226
+ >
227
+ <path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z" />
228
+ </svg>
229
+ );
230
  }
231
 
232
  function IconPlus({ className, ...props }: React.ComponentProps<'svg'>) {
233
+ return (
234
+ <svg
235
+ xmlns="http://www.w3.org/2000/svg"
236
+ viewBox="0 0 256 256"
237
+ fill="currentColor"
238
+ className={cn('size-4', className)}
239
+ {...props}
240
+ >
241
+ <path d="M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8Z" />
242
+ </svg>
243
+ );
244
  }
245
 
246
  function IconArrowElbow({ className, ...props }: React.ComponentProps<'svg'>) {
247
+ return (
248
+ <svg
249
+ xmlns="http://www.w3.org/2000/svg"
250
+ viewBox="0 0 256 256"
251
+ fill="currentColor"
252
+ className={cn('size-4', className)}
253
+ {...props}
254
+ >
255
+ <path d="M200 32v144a8 8 0 0 1-8 8H67.31l34.35 34.34a8 8 0 0 1-11.32 11.32l-48-48a8 8 0 0 1 0-11.32l48-48a8 8 0 0 1 11.32 11.32L67.31 168H184V32a8 8 0 0 1 16 0Z" />
256
+ </svg>
257
+ );
258
  }
259
 
260
  function IconSpinner({ className, ...props }: React.ComponentProps<'svg'>) {
261
+ return (
262
+ <svg
263
+ xmlns="http://www.w3.org/2000/svg"
264
+ viewBox="0 0 256 256"
265
+ fill="currentColor"
266
+ className={cn('size-4 animate-spin', className)}
267
+ {...props}
268
+ >
269
+ <path d="M232 128a104 104 0 0 1-208 0c0-41 23.81-78.36 60.66-95.27a8 8 0 0 1 6.68 14.54C60.15 61.59 40 93.27 40 128a88 88 0 0 0 176 0c0-34.73-20.15-66.41-51.34-80.73a8 8 0 0 1 6.68-14.54C208.19 49.64 232 87 232 128Z" />
270
+ </svg>
271
+ );
272
  }
273
 
274
  function IconMessage({ className, ...props }: React.ComponentProps<'svg'>) {
275
+ return (
276
+ <svg
277
+ xmlns="http://www.w3.org/2000/svg"
278
+ viewBox="0 0 256 256"
279
+ fill="currentColor"
280
+ className={cn('size-4', className)}
281
+ {...props}
282
+ >
283
+ <path d="M216 48H40a16 16 0 0 0-16 16v160a15.84 15.84 0 0 0 9.25 14.5A16.05 16.05 0 0 0 40 240a15.89 15.89 0 0 0 10.25-3.78.69.69 0 0 0 .13-.11L82.5 208H216a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16ZM40 224Zm176-32H82.5a16 16 0 0 0-10.3 3.75l-.12.11L40 224V64h176Z" />
284
+ </svg>
285
+ );
286
  }
287
 
288
  function IconTrash({ className, ...props }: React.ComponentProps<'svg'>) {
289
+ return (
290
+ <svg
291
+ xmlns="http://www.w3.org/2000/svg"
292
+ viewBox="0 0 256 256"
293
+ fill="currentColor"
294
+ className={cn('size-4', className)}
295
+ {...props}
296
+ >
297
+ <path d="M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16ZM96 40a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8v8H96Zm96 168H64V64h128Zm-80-104v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm48 0v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Z" />
298
+ </svg>
299
+ );
300
  }
301
 
302
  function IconRefresh({ className, ...props }: React.ComponentProps<'svg'>) {
303
+ return (
304
+ <svg
305
+ xmlns="http://www.w3.org/2000/svg"
306
+ viewBox="0 0 256 256"
307
+ fill="currentColor"
308
+ className={cn('size-4', className)}
309
+ {...props}
310
+ >
311
+ <path d="M197.67 186.37a8 8 0 0 1 0 11.29C196.58 198.73 170.82 224 128 224c-37.39 0-64.53-22.4-80-39.85V208a8 8 0 0 1-16 0v-48a8 8 0 0 1 8-8h48a8 8 0 0 1 0 16H55.44C67.76 183.35 93 208 128 208c36 0 58.14-21.46 58.36-21.68a8 8 0 0 1 11.31.05ZM216 40a8 8 0 0 0-8 8v23.85C192.53 54.4 165.39 32 128 32c-42.82 0-68.58 25.27-69.66 26.34a8 8 0 0 0 11.3 11.34C69.86 69.46 92 48 128 48c35 0 60.24 24.65 72.56 40H168a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8V48a8 8 0 0 0-8-8Z" />
312
+ </svg>
313
+ );
314
  }
315
 
316
  function IconStop({ className, ...props }: React.ComponentProps<'svg'>) {
317
+ return (
318
+ <svg
319
+ xmlns="http://www.w3.org/2000/svg"
320
+ viewBox="0 0 256 256"
321
+ fill="currentColor"
322
+ className={cn('size-4', className)}
323
+ {...props}
324
+ >
325
+ <path d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm0 192a88 88 0 1 1 88-88 88.1 88.1 0 0 1-88 88Zm24-120h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8Zm-8 48h-32v-32h32Z" />
326
+ </svg>
327
+ );
328
  }
329
 
330
  function IconSidebar({ className, ...props }: React.ComponentProps<'svg'>) {
331
+ return (
332
+ <svg
333
+ xmlns="http://www.w3.org/2000/svg"
334
+ viewBox="0 0 256 256"
335
+ fill="currentColor"
336
+ className={cn('size-4', className)}
337
+ {...props}
338
+ >
339
+ <path d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16ZM40 56h40v144H40Zm176 144H96V56h120v144Z" />
340
+ </svg>
341
+ );
342
  }
343
 
344
  function IconMoon({ className, ...props }: React.ComponentProps<'svg'>) {
345
+ return (
346
+ <svg
347
+ xmlns="http://www.w3.org/2000/svg"
348
+ viewBox="0 0 256 256"
349
+ fill="currentColor"
350
+ className={cn('size-4', className)}
351
+ {...props}
352
+ >
353
+ <path d="M233.54 142.23a8 8 0 0 0-8-2 88.08 88.08 0 0 1-109.8-109.8 8 8 0 0 0-10-10 104.84 104.84 0 0 0-52.91 37A104 104 0 0 0 136 224a103.09 103.09 0 0 0 62.52-20.88 104.84 104.84 0 0 0 37-52.91 8 8 0 0 0-1.98-7.98Zm-44.64 48.11A88 88 0 0 1 65.66 67.11a89 89 0 0 1 31.4-26A106 106 0 0 0 96 56a104.11 104.11 0 0 0 104 104 106 106 0 0 0 14.92-1.06 89 89 0 0 1-26.02 31.4Z" />
354
+ </svg>
355
+ );
356
  }
357
 
358
  function IconSun({ className, ...props }: React.ComponentProps<'svg'>) {
359
+ return (
360
+ <svg
361
+ xmlns="http://www.w3.org/2000/svg"
362
+ viewBox="0 0 256 256"
363
+ fill="currentColor"
364
+ className={cn('size-4', className)}
365
+ {...props}
366
+ >
367
+ <path d="M120 40V16a8 8 0 0 1 16 0v24a8 8 0 0 1-16 0Zm72 88a64 64 0 1 1-64-64 64.07 64.07 0 0 1 64 64Zm-16 0a48 48 0 1 0-48 48 48.05 48.05 0 0 0 48-48ZM58.34 69.66a8 8 0 0 0 11.32-11.32l-16-16a8 8 0 0 0-11.32 11.32Zm0 116.68-16 16a8 8 0 0 0 11.32 11.32l16-16a8 8 0 0 0-11.32-11.32ZM192 72a8 8 0 0 0 5.66-2.34l16-16a8 8 0 0 0-11.32-11.32l-16 16A8 8 0 0 0 192 72Zm5.66 114.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32-11.32ZM48 128a8 8 0 0 0-8-8H16a8 8 0 0 0 0 16h24a8 8 0 0 0 8-8Zm80 80a8 8 0 0 0-8 8v24a8 8 0 0 0 16 0v-24a8 8 0 0 0-8-8Zm112-88h-24a8 8 0 0 0 0 16h24a8 8 0 0 0 0-16Z" />
368
+ </svg>
369
+ );
370
  }
371
 
372
  function IconCopy({ className, ...props }: React.ComponentProps<'svg'>) {
373
+ return (
374
+ <svg
375
+ xmlns="http://www.w3.org/2000/svg"
376
+ viewBox="0 0 256 256"
377
+ fill="currentColor"
378
+ className={cn('size-4', className)}
379
+ {...props}
380
+ >
381
+ <path d="M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8Zm-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z" />
382
+ </svg>
383
+ );
384
  }
385
 
386
  function IconCheck({ className, ...props }: React.ComponentProps<'svg'>) {
387
+ return (
388
+ <svg
389
+ xmlns="http://www.w3.org/2000/svg"
390
+ viewBox="0 0 256 256"
391
+ fill="currentColor"
392
+ className={cn('size-4', className)}
393
+ {...props}
394
+ >
395
+ <path d="m229.66 77.66-128 128a8 8 0 0 1-11.32 0l-56-56a8 8 0 0 1 11.32-11.32L96 188.69 218.34 66.34a8 8 0 0 1 11.32 11.32Z" />
396
+ </svg>
397
+ );
398
  }
399
 
400
  function IconDownload({ className, ...props }: React.ComponentProps<'svg'>) {
401
+ return (
402
+ <svg
403
+ xmlns="http://www.w3.org/2000/svg"
404
+ viewBox="0 0 256 256"
405
+ fill="currentColor"
406
+ className={cn('size-4', className)}
407
+ {...props}
408
+ >
409
+ <path d="M224 152v56a16 16 0 0 1-16 16H48a16 16 0 0 1-16-16v-56a8 8 0 0 1 16 0v56h160v-56a8 8 0 0 1 16 0Zm-101.66 5.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0-11.32-11.32L136 132.69V40a8 8 0 0 0-16 0v92.69l-26.34-26.35a8 8 0 0 0-11.32 11.32Z" />
410
+ </svg>
411
+ );
412
  }
413
 
414
  function IconClose({ className, ...props }: React.ComponentProps<'svg'>) {
415
+ return (
416
+ <svg
417
+ xmlns="http://www.w3.org/2000/svg"
418
+ viewBox="0 0 256 256"
419
+ fill="currentColor"
420
+ className={cn('size-4', className)}
421
+ {...props}
422
+ >
423
+ <path d="M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128 50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z" />
424
+ </svg>
425
+ );
426
  }
427
 
428
  function IconEdit({ className, ...props }: React.ComponentProps<'svg'>) {
429
+ return (
430
+ <svg
431
+ xmlns="http://www.w3.org/2000/svg"
432
+ fill="none"
433
+ viewBox="0 0 24 24"
434
+ strokeWidth={1.5}
435
+ stroke="currentColor"
436
+ className={cn('size-4', className)}
437
+ {...props}
438
+ >
439
+ <path
440
+ strokeLinecap="round"
441
+ strokeLinejoin="round"
442
+ d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
443
+ />
444
+ </svg>
445
+ );
446
  }
447
 
448
  function IconShare({ className, ...props }: React.ComponentProps<'svg'>) {
449
+ return (
450
+ <svg
451
+ xmlns="http://www.w3.org/2000/svg"
452
+ fill="currentColor"
453
+ className={cn('size-4', className)}
454
+ viewBox="0 0 256 256"
455
+ {...props}
456
+ >
457
+ <path d="m237.66 106.35-80-80A8 8 0 0 0 144 32v40.35c-25.94 2.22-54.59 14.92-78.16 34.91-28.38 24.08-46.05 55.11-49.76 87.37a12 12 0 0 0 20.68 9.58c11-11.71 50.14-48.74 107.24-52V192a8 8 0 0 0 13.66 5.65l80-80a8 8 0 0 0 0-11.3ZM160 172.69V144a8 8 0 0 0-8-8c-28.08 0-55.43 7.33-81.29 21.8a196.17 196.17 0 0 0-36.57 26.52c5.8-23.84 20.42-46.51 42.05-64.86C99.41 99.77 127.75 88 152 88a8 8 0 0 0 8-8V51.32L220.69 112Z" />
458
+ </svg>
459
+ );
460
  }
461
 
462
  function IconUsers({ className, ...props }: React.ComponentProps<'svg'>) {
463
+ return (
464
+ <svg
465
+ xmlns="http://www.w3.org/2000/svg"
466
+ fill="currentColor"
467
+ className={cn('size-4', className)}
468
+ viewBox="0 0 256 256"
469
+ {...props}
470
+ >
471
+ <path d="M117.25 157.92a60 60 0 1 0-66.5 0 95.83 95.83 0 0 0-47.22 37.71 8 8 0 1 0 13.4 8.74 80 80 0 0 1 134.14 0 8 8 0 0 0 13.4-8.74 95.83 95.83 0 0 0-47.22-37.71ZM40 108a44 44 0 1 1 44 44 44.05 44.05 0 0 1-44-44Zm210.14 98.7a8 8 0 0 1-11.07-2.33A79.83 79.83 0 0 0 172 168a8 8 0 0 1 0-16 44 44 0 1 0-16.34-84.87 8 8 0 1 1-5.94-14.85 60 60 0 0 1 55.53 105.64 95.83 95.83 0 0 1 47.22 37.71 8 8 0 0 1-2.33 11.07Z" />
472
+ </svg>
473
+ );
474
  }
475
 
476
  function IconExternalLink({
477
+ className,
478
+ ...props
479
  }: React.ComponentProps<'svg'>) {
480
+ return (
481
+ <svg
482
+ xmlns="http://www.w3.org/2000/svg"
483
+ fill="currentColor"
484
+ className={cn('size-4', className)}
485
+ viewBox="0 0 256 256"
486
+ {...props}
487
+ >
488
+ <path d="M224 104a8 8 0 0 1-16 0V59.32l-66.33 66.34a8 8 0 0 1-11.32-11.32L196.68 48H152a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8Zm-40 24a8 8 0 0 0-8 8v72H48V80h72a8 8 0 0 0 0-16H48a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-72a8 8 0 0 0-8-8Z" />
489
+ </svg>
490
+ );
491
  }
492
 
493
  function IconChevronUpDown({
494
+ className,
495
+ ...props
496
  }: React.ComponentProps<'svg'>) {
497
+ return (
498
+ <svg
499
+ xmlns="http://www.w3.org/2000/svg"
500
+ fill="currentColor"
501
+ className={cn('size-4', className)}
502
+ viewBox="0 0 256 256"
503
+ {...props}
504
+ >
505
+ <path d="M181.66 170.34a8 8 0 0 1 0 11.32l-48 48a8 8 0 0 1-11.32 0l-48-48a8 8 0 0 1 11.32-11.32L128 212.69l42.34-42.35a8 8 0 0 1 11.32 0Zm-96-84.68L128 43.31l42.34 42.35a8 8 0 0 0 11.32-11.32l-48-48a8 8 0 0 0-11.32 0l-48 48a8 8 0 0 0 11.32 11.32Z" />
506
+ </svg>
507
+ );
508
  }
509
 
510
  export {
511
+ IconEdit,
512
+ IconNextChat,
513
+ IconOpenAI,
514
+ IconVercel,
515
+ IconGitHub,
516
+ IconSeparator,
517
+ IconArrowDown,
518
+ IconArrowRight,
519
+ IconUser,
520
+ IconPlus,
521
+ IconArrowElbow,
522
+ IconSpinner,
523
+ IconMessage,
524
+ IconTrash,
525
+ IconRefresh,
526
+ IconStop,
527
+ IconSidebar,
528
+ IconMoon,
529
+ IconSun,
530
+ IconCopy,
531
+ IconCheck,
532
+ IconDownload,
533
+ IconClose,
534
+ IconShare,
535
+ IconUsers,
536
+ IconExternalLink,
537
+ IconChevronUpDown,
538
+ IconGoogle,
539
+ };
components/user-menu.tsx CHANGED
@@ -1,68 +1,68 @@
1
- 'use client'
2
 
3
- import Image from 'next/image'
4
- import { type Session } from 'next-auth'
5
- import { signOut } from 'next-auth/react'
6
 
7
- import { Button } from '@/components/ui/button'
8
  import {
9
- DropdownMenu,
10
- DropdownMenuContent,
11
- DropdownMenuItem,
12
- DropdownMenuSeparator,
13
- DropdownMenuTrigger
14
- } from '@/components/ui/dropdown-menu'
15
- import { IconExternalLink } from '@/components/ui/icons'
16
 
17
  export interface UserMenuProps {
18
- user: Session['user']
19
  }
20
 
21
  function getUserInitials(name: string) {
22
- const [firstName, lastName] = name.split(' ')
23
- return lastName ? `${firstName[0]}${lastName[0]}` : firstName.slice(0, 2)
24
  }
25
 
26
  export function UserMenu({ user }: UserMenuProps) {
27
- return (
28
- <div className="flex items-center justify-between">
29
- <DropdownMenu>
30
- <DropdownMenuTrigger asChild>
31
- <Button variant="ghost">
32
- {user?.image ? (
33
- <Image
34
- className="size-6 transition-opacity duration-300 rounded-full select-none ring-1 ring-zinc-100/10 hover:opacity-80"
35
- src={user?.image ? `${user.image}&s=60` : ''}
36
- alt={user.name ?? 'Avatar'}
37
- height={48}
38
- width={48}
39
- />
40
- ) : (
41
- <div className="flex items-center justify-center text-xs font-medium uppercase rounded-full select-none size-7 shrink-0 bg-muted/50 text-muted-foreground">
42
- {user?.name ? getUserInitials(user?.name) : null}
43
- </div>
44
- )}
45
- <span className="ml-2">{user?.name}</span>
46
- </Button>
47
- </DropdownMenuTrigger>
48
- <DropdownMenuContent sideOffset={8} align="start" className="w-[180px]">
49
- <DropdownMenuItem className="flex-col items-start">
50
- <div className="text-xs font-medium">{user?.name}</div>
51
- <div className="text-xs text-zinc-500">{user?.email}</div>
52
- </DropdownMenuItem>
53
- <DropdownMenuSeparator />
54
- <DropdownMenuItem
55
- onClick={() =>
56
- signOut({
57
- callbackUrl: '/'
58
- })
59
- }
60
- className="text-xs"
61
- >
62
- Log Out
63
- </DropdownMenuItem>
64
- </DropdownMenuContent>
65
- </DropdownMenu>
66
- </div>
67
- )
68
  }
 
1
+ 'use client';
2
 
3
+ import Image from 'next/image';
4
+ import { type Session } from 'next-auth';
5
+ import { signOut } from 'next-auth/react';
6
 
7
+ import { Button } from '@/components/ui/button';
8
  import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger,
14
+ } from '@/components/ui/dropdown-menu';
15
+ import { IconExternalLink } from '@/components/ui/icons';
16
 
17
  export interface UserMenuProps {
18
+ user: Session['user'];
19
  }
20
 
21
  function getUserInitials(name: string) {
22
+ const [firstName, lastName] = name.split(' ');
23
+ return lastName ? `${firstName[0]}${lastName[0]}` : firstName.slice(0, 2);
24
  }
25
 
26
  export function UserMenu({ user }: UserMenuProps) {
27
+ return (
28
+ <div className="flex items-center justify-between">
29
+ <DropdownMenu>
30
+ <DropdownMenuTrigger asChild>
31
+ <Button variant="ghost">
32
+ {user?.image ? (
33
+ <Image
34
+ className="size-6 transition-opacity duration-300 rounded-full select-none ring-1 ring-zinc-100/10 hover:opacity-80"
35
+ src={user?.image ?? ''}
36
+ alt={user.name ?? 'Avatar'}
37
+ height={48}
38
+ width={48}
39
+ />
40
+ ) : (
41
+ <div className="flex items-center justify-center text-xs font-medium uppercase rounded-full select-none size-7 shrink-0 bg-muted/50 text-muted-foreground">
42
+ {user?.name ? getUserInitials(user?.name) : null}
43
+ </div>
44
+ )}
45
+ <span className="ml-2">{user?.name}</span>
46
+ </Button>
47
+ </DropdownMenuTrigger>
48
+ <DropdownMenuContent sideOffset={8} align="start" className="w-[180px]">
49
+ <DropdownMenuItem className="flex-col items-start">
50
+ <div className="text-xs font-medium">{user?.name}</div>
51
+ <div className="text-xs text-zinc-500">{user?.email}</div>
52
+ </DropdownMenuItem>
53
+ <DropdownMenuSeparator />
54
+ <DropdownMenuItem
55
+ onClick={() =>
56
+ signOut({
57
+ callbackUrl: '/',
58
+ })
59
+ }
60
+ className="text-xs"
61
+ >
62
+ Log Out
63
+ </DropdownMenuItem>
64
+ </DropdownMenuContent>
65
+ </DropdownMenu>
66
+ </div>
67
+ );
68
  }