Spaces:
Running
Running
bradduy
commited on
Commit
·
eb0c817
1
Parent(s):
ae01397
new login
Browse files
src/app/(auth)/layout.tsx
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ClerkProvider } from '@clerk/nextjs';
|
2 |
+
import { setRequestLocale } from 'next-intl/server';
|
3 |
+
export default async function AuthLayout(props: {
|
4 |
+
children: React.ReactNode;
|
5 |
+
params: Promise<{ locale: string }>;
|
6 |
+
}) {
|
7 |
+
const { locale } = await props.params;
|
8 |
+
setRequestLocale(locale);
|
9 |
+
let signInUrl = '/sign-in';
|
10 |
+
let signUpUrl = '/sign-up';
|
11 |
+
let dashboardUrl = '/dashboard';
|
12 |
+
let afterSignOutUrl = '/';
|
13 |
+
signInUrl = `/${signInUrl}`;
|
14 |
+
signUpUrl = `/${signUpUrl}`;
|
15 |
+
dashboardUrl = `/`;
|
16 |
+
afterSignOutUrl = `/${afterSignOutUrl}`;
|
17 |
+
return (
|
18 |
+
<ClerkProvider
|
19 |
+
localization={{
|
20 |
+
signIn: {
|
21 |
+
start: {
|
22 |
+
title: 'Sign In',
|
23 |
+
},
|
24 |
+
},
|
25 |
+
signUp: {
|
26 |
+
start: {
|
27 |
+
title: 'Sign Up',
|
28 |
+
},
|
29 |
+
},
|
30 |
+
}}
|
31 |
+
signInUrl={signInUrl}
|
32 |
+
signUpUrl={signUpUrl}
|
33 |
+
signInFallbackRedirectUrl={dashboardUrl}
|
34 |
+
signUpFallbackRedirectUrl={dashboardUrl}
|
35 |
+
afterSignOutUrl={afterSignOutUrl}
|
36 |
+
>
|
37 |
+
<div className="mx-auto flex w-full items-center justify-center pt-32">
|
38 |
+
{props.children}
|
39 |
+
</div>
|
40 |
+
</ClerkProvider>
|
41 |
+
);
|
42 |
+
}
|
src/app/(auth)/sign-in/page.tsx
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { SignIn } from '@clerk/nextjs';
|
2 |
+
type ISignInPageProps = {
|
3 |
+
params: Promise<{ locale: string }>;
|
4 |
+
};
|
5 |
+
export default async function SignInPage(_props: ISignInPageProps) {
|
6 |
+
return (
|
7 |
+
<SignIn
|
8 |
+
path="/sign-in"
|
9 |
+
signUpUrl="/sign-up"
|
10 |
+
/>
|
11 |
+
);
|
12 |
+
};
|
src/components/atoms/Card/index.tsx
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { cn } from '@/utils/Helpers';
|
2 |
+
import * as React from 'react';
|
3 |
+
|
4 |
+
const Card = ({ ref, className, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref?: React.RefObject<HTMLDivElement> }) => (
|
5 |
+
<div ref={ref} className={cn('rounded-lg border bg-card text-card-foreground shadow-sm', className)} {...props} />
|
6 |
+
);
|
7 |
+
Card.displayName = 'Card';
|
8 |
+
|
9 |
+
const CardHeader = ({ ref, className, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref: React.RefObject<HTMLDivElement> }) => (
|
10 |
+
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
11 |
+
);
|
12 |
+
CardHeader.displayName = 'CardHeader';
|
13 |
+
|
14 |
+
const CardTitle = ({ ref, className, ...props }: React.HTMLAttributes<HTMLHeadingElement> & { ref: React.RefObject<HTMLParagraphElement> }) => (
|
15 |
+
// eslint-disable-next-line jsx-a11y/heading-has-content
|
16 |
+
<h3 ref={ref} className={cn('text-2xl font-semibold leading-none tracking-tight', className)} {...props} />
|
17 |
+
);
|
18 |
+
CardTitle.displayName = 'CardTitle';
|
19 |
+
|
20 |
+
const CardDescription = ({ ref, className, ...props }: React.HTMLAttributes<HTMLParagraphElement> & { ref: React.RefObject<HTMLParagraphElement> }) => (
|
21 |
+
<p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
|
22 |
+
);
|
23 |
+
CardDescription.displayName = 'CardDescription';
|
24 |
+
|
25 |
+
const CardContent = ({ ref, className, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref?: React.RefObject<HTMLDivElement> }) => <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />;
|
26 |
+
CardContent.displayName = 'CardContent';
|
27 |
+
|
28 |
+
const CardFooter = ({ ref, className, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref: React.RefObject<HTMLDivElement> }) => (
|
29 |
+
<div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
|
30 |
+
);
|
31 |
+
CardFooter.displayName = 'CardFooter';
|
32 |
+
|
33 |
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|
src/components/homepage/AnnouncementBanner.tsx
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Badge, Button } from '@/components/atoms';
|
2 |
+
import { ChevronDown, Download } from 'lucide-react';
|
3 |
+
import Image from 'next/image';
|
4 |
+
|
5 |
+
export function AnnouncementBanner() {
|
6 |
+
return (
|
7 |
+
<>
|
8 |
+
<div className="container flex flex-col items-center gap-4 text-center">
|
9 |
+
<Badge className="rounded-lg" variant="secondary">
|
10 |
+
NEW
|
11 |
+
{' '}
|
12 |
+
<span className="mx-1">+</span>
|
13 |
+
{' '}
|
14 |
+
v0.5.14 is now live on GitHub. Check it out!
|
15 |
+
</Badge>
|
16 |
+
|
17 |
+
<span className="text-4xl">❝</span>
|
18 |
+
|
19 |
+
<h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl">
|
20 |
+
Chat with AI
|
21 |
+
<br />
|
22 |
+
without privacy concerns
|
23 |
+
</h1>
|
24 |
+
|
25 |
+
<p className="max-w-2xl leading-normal text-muted-foreground sm:text-xl sm:leading-8">
|
26 |
+
Jan is an open source ChatGPT-alternative that runs 100% offline.
|
27 |
+
</p>
|
28 |
+
|
29 |
+
<div className="space-y-4">
|
30 |
+
<Button className="h-11 px-8" size="lg">
|
31 |
+
<Download className="mr-2 size-4" />
|
32 |
+
Download for Windows
|
33 |
+
<ChevronDown className="ml-2 size-4" />
|
34 |
+
</Button>
|
35 |
+
<p className="text-xs text-muted-foreground">
|
36 |
+
<span className="font-semibold text-yellow-500">2.5M+</span>
|
37 |
+
{' '}
|
38 |
+
downloads | Free & Open Source
|
39 |
+
</p>
|
40 |
+
</div>
|
41 |
+
</div>
|
42 |
+
|
43 |
+
<div className="container">
|
44 |
+
<div className="relative mx-auto aspect-video max-w-5xl overflow-hidden rounded-xl border bg-background shadow-xl">
|
45 |
+
<Image
|
46 |
+
src="https://sjc.microlink.io/-ax0tIqUfnMYpO1Y6sFNuRcGN_Oe6cQwpzrnQR5q5pzkpfA29UGKZ228lDnpeQCpNANORBcNBmQgFoOtLn18vw.jpeg"
|
47 |
+
alt="Jan AI Interface"
|
48 |
+
fill
|
49 |
+
className="object-cover"
|
50 |
+
priority
|
51 |
+
/>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</>
|
55 |
+
);
|
56 |
+
}
|
src/components/homepage/CustomizationSection.tsx
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Badge, Card } from '@/components/atoms';
|
2 |
+
import Image from 'next/image';
|
3 |
+
|
4 |
+
export function CustomizationSection() {
|
5 |
+
return (
|
6 |
+
<section className="container space-y-16 py-24">
|
7 |
+
<div className="space-y-4 text-center">
|
8 |
+
<h2 className="font-heading text-4xl md:text-5xl lg:text-6xl">Highly Customizable</h2>
|
9 |
+
<p className="text-xl text-muted-foreground">Customize Jan to match your needs and preferences.</p>
|
10 |
+
</div>
|
11 |
+
|
12 |
+
<div className="grid gap-8 md:grid-cols-2">
|
13 |
+
{/* Assistants & Memory Card */}
|
14 |
+
<Card className="space-y-4 p-6">
|
15 |
+
<div className="space-y-2">
|
16 |
+
<div className="flex items-center gap-2">
|
17 |
+
<h3 className="text-2xl font-semibold">Assistants & Memory</h3>
|
18 |
+
<Badge variant="secondary" className="bg-blue-100 text-blue-700 hover:bg-blue-100">
|
19 |
+
Coming Soon
|
20 |
+
</Badge>
|
21 |
+
</div>
|
22 |
+
<p className="text-muted-foreground">
|
23 |
+
Create personalized AI assistants that remember your conversations and execute specific tasks across your
|
24 |
+
systems.
|
25 |
+
</p>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<Image
|
29 |
+
src="https://jan.ai/assets/images/homepage/assistant-light.png"
|
30 |
+
alt="App Screenshot Feature"
|
31 |
+
width={800}
|
32 |
+
height={800}
|
33 |
+
className="object-contain"
|
34 |
+
/>
|
35 |
+
</Card>
|
36 |
+
|
37 |
+
{/* Extensions Card */}
|
38 |
+
<Card className="space-y-4 p-6">
|
39 |
+
<div className="space-y-2">
|
40 |
+
<h3 className="text-2xl font-semibold">Extensions</h3>
|
41 |
+
<p className="text-muted-foreground">
|
42 |
+
Customize Jan with Extensions, that range from Cloud AI connectors, tools, data connectors.
|
43 |
+
</p>
|
44 |
+
</div>
|
45 |
+
|
46 |
+
<Image
|
47 |
+
src="https://jan.ai/assets/images/homepage/extension-light.png"
|
48 |
+
alt="App Screenshot Feature"
|
49 |
+
width={800}
|
50 |
+
height={800}
|
51 |
+
className="object-contain"
|
52 |
+
/>
|
53 |
+
</Card>
|
54 |
+
</div>
|
55 |
+
</section>
|
56 |
+
);
|
57 |
+
}
|
src/components/homepage/FeatureSection.tsx
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Badge } from '@/components/atoms';
|
2 |
+
import { Cloud, FileText, Grid, MessageCircle, Server } from 'lucide-react';
|
3 |
+
import Image from 'next/image';
|
4 |
+
|
5 |
+
const features = [
|
6 |
+
{
|
7 |
+
number: '01',
|
8 |
+
title: 'Chat with AI',
|
9 |
+
description: 'Ask your questions, brainstorm, and learn from the AI running on your device to be more productive.',
|
10 |
+
icon: MessageCircle,
|
11 |
+
},
|
12 |
+
{
|
13 |
+
number: '02',
|
14 |
+
title: 'Model Hub',
|
15 |
+
description: 'Access and manage various AI models directly from your device.',
|
16 |
+
icon: Grid,
|
17 |
+
},
|
18 |
+
{
|
19 |
+
number: '03',
|
20 |
+
title: 'Connect to Cloud AIs',
|
21 |
+
description: 'Seamlessly integrate with cloud-based AI services when needed.',
|
22 |
+
icon: Cloud,
|
23 |
+
},
|
24 |
+
{
|
25 |
+
number: '04',
|
26 |
+
title: 'Local API Server',
|
27 |
+
description: 'Run your own API server locally for complete control and privacy.',
|
28 |
+
icon: Server,
|
29 |
+
},
|
30 |
+
{
|
31 |
+
number: '05',
|
32 |
+
title: 'Chat with your files',
|
33 |
+
description: 'Interact with your documents and files using natural language.',
|
34 |
+
icon: FileText,
|
35 |
+
experimental: true,
|
36 |
+
},
|
37 |
+
];
|
38 |
+
|
39 |
+
export function FeaturesSection() {
|
40 |
+
return (
|
41 |
+
<section className="container space-y-16 py-24">
|
42 |
+
<h2 className="font-heading text-4xl md:text-5xl lg:text-6xl">Features</h2>
|
43 |
+
|
44 |
+
<div className="grid items-start gap-8 lg:grid-cols-2">
|
45 |
+
<div className="space-y-8">
|
46 |
+
{features.map(feature => (
|
47 |
+
<div key={feature.number} className="flex items-center gap-4">
|
48 |
+
<div className="font-heading text-3xl text-muted-foreground/50">{feature.number}</div>
|
49 |
+
<div className="space-y-2">
|
50 |
+
<div className="flex items-center gap-2">
|
51 |
+
<h3 className="text-xl font-semibold">{feature.title}</h3>
|
52 |
+
{feature.experimental && (
|
53 |
+
<Badge variant="secondary" className="text-xs">
|
54 |
+
Experimental
|
55 |
+
</Badge>
|
56 |
+
)}
|
57 |
+
</div>
|
58 |
+
<p className="text-muted-foreground">{feature.description}</p>
|
59 |
+
</div>
|
60 |
+
</div>
|
61 |
+
))}
|
62 |
+
</div>
|
63 |
+
|
64 |
+
<Image
|
65 |
+
src="https://jan.ai/assets/images/homepage/features01.png"
|
66 |
+
alt="App Screenshot Feature"
|
67 |
+
width={800}
|
68 |
+
height={800}
|
69 |
+
/>
|
70 |
+
</div>
|
71 |
+
</section>
|
72 |
+
);
|
73 |
+
}
|