├── components ├── markdown │ ├── index.ts │ ├── elements │ │ ├── code │ │ │ ├── index.ts │ │ │ ├── code.type.ts │ │ │ └── code.tsx │ │ └── heading │ │ │ ├── index.ts │ │ │ ├── heading.type.ts │ │ │ └── heading.tsx │ ├── markdown-element │ │ ├── index.ts │ │ ├── markdown-element.type.ts │ │ └── markdown-element.tsx │ ├── markdown.type.ts │ └── markdown.tsx ├── providers │ └── modal-provider.tsx ├── ui │ ├── action-tooltip.tsx │ ├── label.tsx │ ├── textarea.tsx │ ├── toaster.tsx │ ├── input.tsx │ ├── tooltip.tsx │ ├── modal.tsx │ ├── avatar.tsx │ ├── scroll-area.tsx │ ├── button.tsx │ ├── card-spotlight.tsx │ ├── dialog.tsx │ ├── use-toast.ts │ ├── form.tsx │ ├── toast.tsx │ ├── select.tsx │ └── dropdown-menu.tsx ├── copy-button.tsx ├── header.tsx └── modals │ └── feedback-modal.tsx ├── .eslintrc.json ├── public ├── og.png ├── ss.png ├── logo.png ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── mstile-150x150.png ├── apple-touch-icon.png ├── logo-transparent.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── browserconfig.xml ├── site.webmanifest ├── safari-pinned-tab.svg └── logo.svg ├── types └── supabase.ts ├── next.config.mjs ├── .env.example ├── postcss.config.js ├── .prettierrc ├── app ├── (auth) │ ├── layout.tsx │ ├── sign-up │ │ └── page.tsx │ ├── sign-in │ │ └── page.tsx │ ├── auth │ │ └── confirm │ │ │ └── route.ts │ └── _components │ │ └── auth-form.tsx ├── chat │ ├── page.tsx │ └── _components │ │ ├── index.tsx │ │ ├── chat-feed.tsx │ │ ├── chat-welcome.tsx │ │ └── chat-form.tsx ├── api │ ├── feedback │ │ └── route.ts │ └── chat │ │ └── route.ts ├── layout.tsx ├── globals.css └── page.tsx ├── lib ├── supabase │ ├── client.ts │ ├── server.ts │ └── middleware.ts ├── config │ ├── site-config.ts │ └── metadata.ts └── utils.ts ├── components.json ├── hooks ├── use-modal-store.tsx ├── use-copy.tsx └── use-chat-scroll.tsx ├── .gitignore ├── middleware.ts ├── tsconfig.json ├── .prettierignore ├── README.md ├── package.json └── tailwind.config.ts /components/markdown/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./markdown"; 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /components/markdown/elements/code/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./code"; 2 | -------------------------------------------------------------------------------- /public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/og.png -------------------------------------------------------------------------------- /public/ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/ss.png -------------------------------------------------------------------------------- /components/markdown/elements/heading/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./heading"; 2 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /types/supabase.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/types/supabase.ts -------------------------------------------------------------------------------- /components/markdown/markdown-element/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./markdown-element"; 2 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/logo-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/logo-transparent.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudipfyi/ai/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | GOOGLE_API_KEY= 2 | 3 | USER= 4 | PASS= 5 | 6 | NEXT_PUBLIC_SUPABASE_URL= 7 | NEXT_PUBLIC_SUPABASE_ANON_KEY= 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /components/markdown/elements/code/code.type.ts: -------------------------------------------------------------------------------- 1 | export type MarkdownCodeProps = { 2 | lang?: string | null; 3 | value: string | null; 4 | }; 5 | -------------------------------------------------------------------------------- /components/markdown/elements/heading/heading.type.ts: -------------------------------------------------------------------------------- 1 | export type MarkdownHeadingProps = { 2 | text: string; 3 | depth: number; 4 | className?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /components/markdown/markdown.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export type MarkdownProps = React.ComponentPropsWithoutRef<"div"> & { 4 | source: string; 5 | }; 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "singleQuote": false, 5 | "semi": true, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true, 8 | "arrowParens": "always", 9 | "endOfLine": "lf" 10 | } 11 | -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | export default async function AuthLayout({ children }: { children: React.ReactNode }) { 2 | return ( 3 |
{children}
4 | ); 5 | } 6 | -------------------------------------------------------------------------------- /components/markdown/markdown-element/markdown-element.type.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import type { Content } from "mdast-util-from-markdown/lib"; 3 | 4 | export type MarkdownElementProps = { 5 | element: Content; 6 | parent?: Content | null; 7 | }; 8 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/supabase/client.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from "@supabase/ssr"; 2 | 3 | import { Database } from "@/types/supabase"; 4 | 5 | export const createClient = () => 6 | createBrowserClient( 7 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 8 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 9 | ); 10 | -------------------------------------------------------------------------------- /lib/config/site-config.ts: -------------------------------------------------------------------------------- 1 | export const siteConfig = { 2 | name: "AI", 3 | url: "https://ai.sudipbiswas.dev", 4 | ogImage: "https://ai.sudipbiswas.dev/og.png", 5 | description: "Experience AI like never before.", 6 | links: { 7 | twitter: "https://twitter.com/sudipb7_", 8 | github: "https://github.com/sudipb7/ai", 9 | }, 10 | }; 11 | 12 | export type SiteConfig = typeof siteConfig; 13 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AI", 3 | "short_name": "AI", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /components/providers/modal-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | 5 | import { FeedbackModal } from "@/components/modals/feedback-modal"; 6 | 7 | export const ModalProvider = () => { 8 | const [isMounted, setIsMounted] = useState(false); 9 | 10 | useEffect(() => { 11 | setIsMounted(true); 12 | }, []); 13 | 14 | if (!isMounted) return null; 15 | 16 | return ( 17 | <> 18 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /hooks/use-modal-store.tsx: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | type ModalType = "feedback"; 4 | 5 | interface ModalStoreProps { 6 | type: ModalType | null; 7 | isOpen: boolean; 8 | onOpen: (type: ModalType) => void; 9 | onClose: () => void; 10 | } 11 | 12 | export const useModal = create((set) => ({ 13 | isOpen: false, 14 | onOpen: (type) => set({ isOpen: true, type }), 15 | onClose: () => set({ isOpen: false, type: null }), 16 | type: null, 17 | })); 18 | -------------------------------------------------------------------------------- /app/chat/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | 3 | import Chat from "./_components"; 4 | import { createClient } from "@/lib/supabase/server"; 5 | 6 | export const metadata = { 7 | title: "Chat", 8 | description: "Chat with the AI.", 9 | }; 10 | 11 | export default async function ChatPage() { 12 | const supabase = createClient(); 13 | const { 14 | data: { user }, 15 | } = await supabase.auth.getUser(); 16 | 17 | if (!user?.email) { 18 | return redirect("/sign-in"); 19 | } 20 | 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /components/markdown/markdown.tsx: -------------------------------------------------------------------------------- 1 | import { fromMarkdown } from "mdast-util-from-markdown"; 2 | 3 | import { MarkdownProps } from "./markdown.type"; 4 | import { MarkdownElement } from "./markdown-element"; 5 | 6 | export const Markdown = ({ source, ...props }: MarkdownProps) => { 7 | const markdownTree = fromMarkdown(source).children; 8 | 9 | return ( 10 |
11 | {markdownTree.map((node, index) => ( 12 | 13 | ))} 14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /hooks/use-copy.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export function useCopy(timeout = 2000) { 4 | const [isCopied, setIsCopied] = useState(false); 5 | 6 | const copyToClipboard = (value: string) => { 7 | if (!value) return; 8 | 9 | if (typeof window === "undefined" || !navigator.clipboard?.writeText) return; 10 | 11 | navigator.clipboard.writeText(value).then(() => { 12 | setIsCopied(true); 13 | 14 | setTimeout(() => { 15 | setIsCopied(false); 16 | }, timeout); 17 | }); 18 | }; 19 | 20 | return { isCopied, copyToClipboard }; 21 | } 22 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | import { z } from "zod"; 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)); 7 | } 8 | 9 | export const feedbackSchema = z.object({ 10 | email: z.string().email("Invalid email"), 11 | description: z.string().min(10, "Please enter atleast 10 characters."), 12 | }); 13 | 14 | export const authSchema = z.object({ 15 | email: z.string().email("Invalid email"), 16 | password: z.string().min(6, "Please enter atleast 6 characters."), 17 | }); 18 | 19 | export type AuthSchema = z.infer; 20 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest } from "next/server"; 2 | import { updateSession } from "@/lib/supabase/middleware"; 3 | 4 | export async function middleware(request: NextRequest) { 5 | return await updateSession(request); 6 | } 7 | 8 | export const config = { 9 | matcher: [ 10 | /* 11 | * Match all request paths except: 12 | * - _next/static (static files) 13 | * - _next/image (image optimization files) 14 | * - favicon.ico (favicon file) 15 | * - images - .svg, .png, .jpg, .jpeg, .gif, .webp 16 | */ 17 | "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /components/ui/action-tooltip.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; 4 | 5 | interface ActionTooltipProps { 6 | label: string; 7 | children: React.ReactNode; 8 | side?: "top" | "right" | "bottom" | "left"; 9 | align?: "start" | "center" | "end"; 10 | } 11 | 12 | export const ActionTooltip = ({ label, children, side, align }: ActionTooltipProps) => { 13 | return ( 14 | 15 | 16 | {children} 17 | 18 |

{label}

19 |
20 |
21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & VariantProps 16 | >(({ className, ...props }, ref) => ( 17 | 18 | )); 19 | Label.displayName = LabelPrimitive.Root.displayName; 20 | 21 | export { Label }; 22 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface TextareaProps extends React.TextareaHTMLAttributes {} 6 | 7 | const Textarea = React.forwardRef( 8 | ({ className, ...props }, ref) => { 9 | return ( 10 |