├── .eslintignore ├── public ├── favicon.ico ├── merc-logo-og.webp ├── logo-down-indigo.webp ├── merc-logo-down-aqua.webp ├── merc-logo-down-purple.webp ├── merc-logo-down-violet.webp ├── logo-og.svg └── logo-down.svg ├── postcss.config.js ├── .env.example ├── .prettierignore ├── components ├── ui │ ├── aspect-ratio.tsx │ ├── collapsible.tsx │ ├── label.tsx │ ├── textarea.tsx │ ├── separator.tsx │ ├── progress.tsx │ ├── toaster.tsx │ ├── hover-card.tsx │ ├── checkbox.tsx │ ├── popover.tsx │ ├── slider.tsx │ ├── tooltip.tsx │ ├── switch.tsx │ ├── avatar.tsx │ ├── toggle.tsx │ ├── radio-group.tsx │ ├── scroll-area.tsx │ ├── tabs.tsx │ ├── accordion.tsx │ ├── input.tsx │ ├── select.tsx │ ├── button.tsx │ ├── dialog.tsx │ ├── codeblock.tsx │ ├── toast.tsx │ ├── navigation-menu.tsx │ ├── command.tsx │ ├── alert-dialog.tsx │ ├── sheet.tsx │ ├── context-menu.tsx │ └── dropdown-menu.tsx ├── MarkdownRenderer.tsx ├── animations │ ├── FadeIn.tsx │ └── ResizablePanel.tsx ├── Card.tsx ├── NamespaceInput.tsx ├── query │ ├── SearchInput.tsx │ └── DocumentQA.tsx ├── train │ ├── UrlScraper.tsx │ └── FileUpload.tsx ├── NavHeader.tsx ├── CommandMenu.tsx ├── Layouts.tsx ├── perplexity │ ├── Answer.tsx │ └── Sources.tsx └── icons.tsx ├── .editorconfig ├── next-env.d.ts ├── next.config.mjs ├── hooks ├── use-toggle.tsx ├── use-copy-to-clipboard.tsx ├── use-pinecone-stats.tsx ├── use-scrape-embed.tsx ├── use-query.tsx └── use-toast.ts ├── pages ├── _document.tsx ├── api │ ├── stats.ts │ ├── embed-file.ts │ ├── embed-webpage.ts │ └── query.ts ├── _app.tsx └── index.tsx ├── config ├── site.ts └── pinecone.ts ├── .eslintrc.json ├── .gitignore ├── tsconfig.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── types └── index.ts ├── prettier.config.js ├── lib ├── webpage.ts ├── chain.ts ├── utils.ts ├── prompts.ts └── file.ts ├── LICENSE ├── tailwind.config.js ├── package.json ├── styles └── globals.css └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | .cache 3 | public 4 | node_modules 5 | *.esm.js 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/merc-logo-og.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/merc-logo-og.webp -------------------------------------------------------------------------------- /public/logo-down-indigo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/logo-down-indigo.webp -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/merc-logo-down-aqua.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/merc-logo-down-aqua.webp -------------------------------------------------------------------------------- /public/merc-logo-down-purple.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/merc-logo-down-purple.webp -------------------------------------------------------------------------------- /public/merc-logo-down-violet.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jordan-Gilliam/ai-template/HEAD/public/merc-logo-down-violet.webp -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # OpenAI 2 | OPENAI_API_KEY="" 3 | # Pinecone 4 | PINECONE_API_KEY="" 5 | PINECONE_ENVIRONMENT="" 6 | PINECONE_INDEX_NAME="" 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | cache 2 | .cache 3 | package.json 4 | package-lock.json 5 | public 6 | CHANGELOG.md 7 | .yarn 8 | dist 9 | node_modules 10 | .next 11 | build 12 | .contentlayer -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | // experimental: { 5 | // fontLoaders: [ 6 | // { 7 | // loader: "@next/font/google", 8 | // }, 9 | // ], 10 | // }, 11 | } 12 | 13 | export default nextConfig 14 | -------------------------------------------------------------------------------- /components/MarkdownRenderer.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from "react" 2 | import ReactMarkdown, {Options} from "react-markdown" 3 | 4 | export const MemoizedReactMarkdown: FC = memo( 5 | ReactMarkdown, 6 | (prevProps, nextProps) => 7 | prevProps.children === nextProps.children && 8 | prevProps.className === nextProps.className 9 | ) 10 | -------------------------------------------------------------------------------- /components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /hooks/use-toggle.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react" 2 | 3 | export const useToggle = (initialState: boolean = false): [boolean, any] => { 4 | // Initialize the state 5 | const [state, setState] = useState(initialState) 6 | // Define and memoize toggler function in case we pass down the component, 7 | const toggle = useCallback((): void => setState((state) => !state), []) 8 | return [state, toggle] 9 | } 10 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from "next/document" 2 | import { FancyBackground } from "@/components/Layouts" 3 | 4 | export default function Document() { 5 | return ( 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /config/site.ts: -------------------------------------------------------------------------------- 1 | import { NavItem } from "@/types" 2 | 3 | interface SiteConfig { 4 | name: string 5 | description: string 6 | mainNav: NavItem[] 7 | links: { 8 | twitter: string 9 | github: string 10 | } 11 | } 12 | 13 | export const siteConfig: SiteConfig = { 14 | name: "Mercury", 15 | description: "Unlock the secrets of any website", 16 | mainNav: [], 17 | links: { 18 | twitter: "https://twitter.com/nolansym", 19 | github: "https://github.com/Jordan-Gilliam/ai-template", 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /config/pinecone.ts: -------------------------------------------------------------------------------- 1 | import { PineconeClient } from "@pinecone-database/pinecone" 2 | 3 | export async function initPinecone() { 4 | try { 5 | const pinecone = new PineconeClient() 6 | 7 | await pinecone.init({ 8 | environment: process.env.PINECONE_ENVIRONMENT ?? "", //this is in the dashboard 9 | apiKey: process.env.PINECONE_API_KEY ?? "", 10 | }) 11 | 12 | return pinecone 13 | } catch (error) { 14 | console.log("error", error) 15 | throw new Error("Failed to initialize Pinecone Client") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/eslintrc", 3 | "root": true, 4 | "extends": [ 5 | "next/core-web-vitals", 6 | "prettier", 7 | "plugin:tailwindcss/recommended" 8 | ], 9 | "plugins": ["tailwindcss"], 10 | "rules": { 11 | "@next/next/no-html-link-for-pages": "off", 12 | "react/jsx-key": "off", 13 | "tailwindcss/no-custom-classname": "off", 14 | "react/jsx-no-target-blank": "warn" 15 | }, 16 | "settings": { 17 | "tailwindcss": { 18 | "callees": ["cn"] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | .contentlayer 36 | .env -------------------------------------------------------------------------------- /components/animations/FadeIn.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react" 2 | import { motion } from "framer-motion" 3 | 4 | interface MotionProps extends PropsWithChildren { 5 | className?: string 6 | delay?: number 7 | } 8 | 9 | const FadeIn = (props: MotionProps) => ( 10 | 16 | {props.children} 17 | 18 | ) 19 | 20 | FadeIn.displayName = "FadeOut" 21 | export { FadeIn } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "forceConsistentCasingInFileNames": true, 8 | "noEmit": true, 9 | "incremental": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "baseUrl": ".", 17 | "paths": { 18 | "@/*": ["./*"] 19 | } 20 | }, 21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 22 | "exclude": ["node_modules"] 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 { cn } from "@/lib/utils" 6 | 7 | const Label = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, ...props }, ref) => ( 11 | 19 | )) 20 | Label.displayName = LabelPrimitive.Root.displayName 21 | 22 | export { Label } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /hooks/use-copy-to-clipboard.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | 5 | export interface useCopyToClipboardProps { 6 | timeout?: number 7 | } 8 | 9 | export function useCopyToClipboard({ 10 | timeout = 2000 11 | }: useCopyToClipboardProps) { 12 | const [isCopied, setIsCopied] = React.useState(false) 13 | 14 | const copyToClipboard = (value: string) => { 15 | if (typeof window === 'undefined' || !navigator.clipboard?.writeText) { 16 | return 17 | } 18 | 19 | if (!value) { 20 | return 21 | } 22 | 23 | navigator.clipboard.writeText(value).then(() => { 24 | setIsCopied(true) 25 | 26 | setTimeout(() => { 27 | setIsCopied(false) 28 | }, timeout) 29 | }) 30 | } 31 | 32 | return { isCopied, copyToClipboard } 33 | } -------------------------------------------------------------------------------- /hooks/use-pinecone-stats.tsx: -------------------------------------------------------------------------------- 1 | import useSWR from "swr" 2 | 3 | export const fetcher = (...args: Parameters) => 4 | fetch(...args).then((res) => res.json()) 5 | 6 | export const usePineconeStats = () => { 7 | const { data, error, isLoading } = useSWR("/api/stats", fetcher, { 8 | revalidateOnFocus: true, 9 | }) 10 | 11 | return { 12 | loading: isLoading, 13 | data, 14 | error, 15 | } 16 | } 17 | 18 | interface IndexDescription { 19 | namespaces: { 20 | [key: string]: { 21 | vectorCount: number 22 | } 23 | } 24 | dimension: number 25 | indexFullness: number 26 | totalVectorCount: number 27 | } 28 | 29 | export const getNamespaceKeys = ( 30 | indexDescription: IndexDescription 31 | ): string[] => { 32 | return Object.keys(indexDescription.namespaces) 33 | } 34 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cn } from "@/lib/utils" 3 | 4 | export interface TextareaProps 5 | extends React.TextareaHTMLAttributes {} 6 | 7 | const Textarea = React.forwardRef( 8 | ({ className, ...props }, ref) => { 9 | return ( 10 |