Skip to content
Closed
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
86 changes: 67 additions & 19 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useActionState, useEffect, useState } from 'react';
import { toast } from '@/components/toast';
import { ArrowLeftIcon } from '@radix-ui/react-icons';

import { AuthForm } from '@/components/auth-form';
import { SubmitButton } from '@/components/submit-button';
import { Button } from '@/components/ui/button';
import Veil from '@/components/veil';

import { login, type LoginActionState } from '../actions';
import { useSession } from 'next-auth/react';
Expand Down Expand Up @@ -50,27 +53,72 @@ export default function Page() {
};

return (
<div className="flex h-dvh w-screen items-start justify-center bg-background pt-12 md:items-center md:pt-0">
<div className="flex w-full max-w-md flex-col gap-12 overflow-hidden rounded-2xl">
<div className="flex flex-col items-center justify-center gap-2 px-4 text-center sm:px-16">
<h3 className="font-semibold text-xl dark:text-zinc-50">Sign In</h3>
<p className="text-gray-500 text-sm dark:text-zinc-400">
Use your email and password to sign in
</p>
<div className="flex h-screen">
<div className="relative hidden flex-col items-start justify-between overflow-hidden p-12 md:flex md:w-1/2">
<div className="absolute inset-0">
<Veil
speed={3}
noiseIntensity={0.8}
/>
</div>
<AuthForm action={handleSubmit} defaultEmail={email}>
<SubmitButton isSuccessful={isSuccessful}>Sign in</SubmitButton>
<p className="mt-4 text-center text-gray-600 text-sm dark:text-zinc-400">
{"Don't have an account? "}
<Link
href="/register"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign up
</Link>
{' for free.'}

{/* Back button */}
<Button
asChild
variant="outline"
size="sm"
className="relative z-10 border-foreground/30 bg-background/80 text-foreground hover:bg-background hover:text-foreground backdrop-blur-sm"
>
<Link href="/">
<ArrowLeftIcon className="h-4 w-4" />
Back
</Link>
</Button>


{/* Bottom promotional section */}
<div className="relative z-10 text-foreground">
<h2 className="mb-2 font-bold text-2xl">
Chat SDK
</h2>
<p className="max-w-md text-foreground/70">
A powerful AI chatbot template built with Next.js, AI SDK, and Vercel AI Gateway for seamless conversations.
</p>
</AuthForm>
</div>
</div>

<div className="flex w-full flex-col overflow-auto bg-background md:w-1/2">
<div className="flex flex-1 items-center justify-center p-6 md:p-12">
<div className="w-full max-w-sm space-y-8">
{/* Header */}
<div className="space-y-3 text-center">
<h1 className="font-bold text-3xl tracking-tight">Welcome back</h1>
<p className="text-muted-foreground text-sm leading-relaxed">
Sign in to continue to Chat SDK
</p>
</div>

{/* Form */}
<div className="space-y-6">
<AuthForm action={handleSubmit} defaultEmail={email}>
<SubmitButton isSuccessful={isSuccessful}>Sign in</SubmitButton>
</AuthForm>

{/* Footer */}
<div className="text-center">
<p className="text-muted-foreground text-sm">
Don't have an account?{' '}
<Link
href="/register"
className="font-medium text-primary hover:underline underline-offset-4"
>
Sign up
</Link>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
Expand Down
83 changes: 64 additions & 19 deletions app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useActionState, useEffect, useState } from 'react';
import { ArrowLeftIcon } from '@radix-ui/react-icons';

import { AuthForm } from '@/components/auth-form';
import { SubmitButton } from '@/components/submit-button';
import { Button } from '@/components/ui/button';
import Veil from '@/components/veil';

import { register, type RegisterActionState } from '../actions';
import { toast } from '@/components/toast';
Expand Down Expand Up @@ -52,27 +55,69 @@ export default function Page() {
};

return (
<div className="flex h-dvh w-screen items-start justify-center bg-background pt-12 md:items-center md:pt-0">
<div className="flex w-full max-w-md flex-col gap-12 overflow-hidden rounded-2xl">
<div className="flex flex-col items-center justify-center gap-2 px-4 text-center sm:px-16">
<h3 className="font-semibold text-xl dark:text-zinc-50">Sign Up</h3>
<p className="text-gray-500 text-sm dark:text-zinc-400">
Create an account with your email and password
</p>
<div className="flex h-screen">
<div className="relative hidden flex-col items-start justify-between overflow-hidden p-12 md:flex md:w-1/2">
<div className="absolute inset-0">
<Veil
speed={3}
noiseIntensity={0.8}
/>
</div>
<AuthForm action={handleSubmit} defaultEmail={email}>
<SubmitButton isSuccessful={isSuccessful}>Sign Up</SubmitButton>
<p className="mt-4 text-center text-gray-600 text-sm dark:text-zinc-400">
{'Already have an account? '}
<Link
href="/login"
className="font-semibold text-gray-800 hover:underline dark:text-zinc-200"
>
Sign in
</Link>
{' instead.'}

<Button
asChild
variant="outline"
size="sm"
className="relative z-10 border-foreground/30 bg-background/80 text-foreground hover:bg-background hover:text-foreground backdrop-blur-sm"
>
<Link href="/">
<ArrowLeftIcon className="h-4 w-4" />
Back
</Link>
</Button>

<div className="relative z-10 text-foreground">
<h2 className="mb-2 font-bold text-2xl">
Chat SDK
</h2>
<p className="max-w-md text-foreground/70">
A powerful AI chatbot template built with Next.js, AI SDK, and Vercel AI Gateway for seamless conversations.
</p>
</AuthForm>
</div>
</div>

<div className="flex w-full flex-col overflow-auto bg-background md:w-1/2">
<div className="flex flex-1 items-center justify-center p-6 md:p-12">
<div className="w-full max-w-sm space-y-8">
{/* Header */}
<div className="space-y-3 text-center">
<h1 className="font-bold text-3xl tracking-tight">Create account</h1>
<p className="text-muted-foreground text-sm leading-relaxed">
Join Chat SDK to get started
</p>
</div>

{/* Form */}
<div className="space-y-6">
<AuthForm action={handleSubmit} defaultEmail={email}>
<SubmitButton isSuccessful={isSuccessful}>Create account</SubmitButton>
</AuthForm>

{/* Footer */}
<div className="text-center">
<p className="text-muted-foreground text-sm">
Already have an account?{' '}
<Link
href="/login"
className="font-medium text-primary hover:underline underline-offset-4"
>
Sign in
</Link>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion components/elements/suggestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const Suggestion = ({

return (
<Button
className={cn('cursor-pointer rounded-full px-4', className)}
className={cn('cursor-pointer rounded-xl px-4', className)}
onClick={handleClick}
size={size}
type="button"
Expand Down
168 changes: 168 additions & 0 deletions components/iridescence.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
'use client';

import { Color, Mesh, Program, Renderer, Triangle } from 'ogl';
import { useTheme } from 'next-themes';
import { useEffect, useRef } from 'react';

const vertexShader = `
attribute vec2 uv;
attribute vec2 position;

varying vec2 vUv;

void main() {
vUv = uv;
gl_Position = vec4(position, 0, 1);
}
`;

const fragmentShader = `
precision highp float;

uniform float uTime;
uniform vec3 uColor;
uniform vec3 uResolution;
uniform vec2 uMouse;
uniform float uAmplitude;
uniform float uSpeed;

varying vec2 vUv;

void main() {
float mr = min(uResolution.x, uResolution.y);
vec2 uv = (vUv.xy * 2.0 - 1.0) * uResolution.xy / mr;

// Add a subtle offset based on the mouse position
uv += (uMouse - vec2(0.5)) * uAmplitude;

float d = -uTime * 0.5 * uSpeed;
float a = 0.0;
for (float i = 0.0; i < 8.0; ++i) {
a += cos(i - d - a * uv.x);
d += sin(uv.y * i + a);
}
d += uTime * 0.5 * uSpeed;

float intensity = cos(length(uv * vec2(d, a))) * 0.15 + 0.85;
vec3 col = vec3(intensity) * uColor;
gl_FragColor = vec4(col, 1.0);
}
`;

interface IridescenceProps {
color?: [number, number, number];
speed?: number;
amplitude?: number;
mouseReact?: boolean;
}

export default function Iridescence({
color,
speed = 1.0,
amplitude = 0.1,
mouseReact = true,
...rest
}: IridescenceProps) {
const { theme } = useTheme();
const ctnDom = useRef<HTMLDivElement>(null);
const mousePos = useRef({ x: 0.5, y: 0.5 });

const themeColor = color || (theme === 'dark' ? [0.1, 0.1, 0.1] : [1.0, 1.0, 1.0]);

useEffect(() => {
if (!ctnDom.current) {
return;
}
const ctn = ctnDom.current;
const renderer = new Renderer();
const gl = renderer.gl;

if (!gl) {
console.error('WebGL context not available');
return;
}

gl.clearColor(1, 1, 1, 1);

let program: Program | null = null;

function resize() {
const scale = 1;
renderer.setSize(ctn.offsetWidth * scale, ctn.offsetHeight * scale);
if (program) {
program.uniforms.uResolution.value = new Color(
gl.canvas.width,
gl.canvas.height,
gl.canvas.width / gl.canvas.height
);
}
}
window.addEventListener('resize', resize, false);
resize();

const geometry = new Triangle(gl);
program = new Program(gl, {
vertex: vertexShader,
fragment: fragmentShader,
uniforms: {
uTime: { value: 0 },
uColor: { value: new Color(...themeColor) },
uResolution: {
value: new Color(
gl.canvas.width,
gl.canvas.height,
gl.canvas.width / gl.canvas.height
),
},
uMouse: {
value: new Float32Array([mousePos.current.x, mousePos.current.y]),
},
uAmplitude: { value: amplitude },
uSpeed: { value: speed },
},
});

const mesh = new Mesh(gl, { geometry, program });
let animateId: number;

function update(t: number) {
animateId = requestAnimationFrame(update);
if (program) {
program.uniforms.uTime.value = t * 0.001;
renderer.render({ scene: mesh });
}
}
animateId = requestAnimationFrame(update);
if (gl.canvas) {
ctn.appendChild(gl.canvas);
}

function handleMouseMove(e: MouseEvent) {
const rect = ctn.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = 1.0 - (e.clientY - rect.top) / rect.height;
mousePos.current = { x, y };
if (program) {
program.uniforms.uMouse.value[0] = x;
program.uniforms.uMouse.value[1] = y;
}
}
if (mouseReact) {
ctn.addEventListener('mousemove', handleMouseMove);
}

return () => {
cancelAnimationFrame(animateId);
window.removeEventListener('resize', resize);
if (mouseReact) {
ctn.removeEventListener('mousemove', handleMouseMove);
}
if (gl.canvas && ctn.contains(gl.canvas)) {
ctn.removeChild(gl.canvas);
}
gl.getExtension('WEBGL_lose_context')?.loseContext();
};
}, [themeColor, speed, amplitude, mouseReact]);

return <div className="h-full w-full" ref={ctnDom} {...rest} />;
}
2 changes: 1 addition & 1 deletion components/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const PurePreviewMessage = ({
<MessageContent
data-testid="message-content"
className={cn({
'w-fit break-words rounded-2xl px-3 py-2 text-right text-white':
'w-fit break-words rounded-xl px-3 py-2 text-right text-white':
message.role === 'user',
'bg-transparent px-0 py-0 text-left':
message.role === 'assistant',
Expand Down
5 changes: 5 additions & 0 deletions components/veil.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.veil-canvas {
width: 100%;
height: 100%;
display: block;
}
Loading
Loading