diff --git a/docs/bun.lockb b/docs/bun.lockb new file mode 100755 index 0000000000..5ce8b72e7f Binary files /dev/null and b/docs/bun.lockb differ diff --git a/docs/package.json b/docs/package.json index 53a2ecad63..0c0513f68f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -21,11 +21,13 @@ "astro-mermaid": "^1.0.4", "autoprefixer": "^10.0.1", "axios": "^1.6.8", + "class-variance-authority": "^0.7.1", "date-fns": "^3.6.0", "embla-carousel-auto-height": "^8.0.0", "embla-carousel-auto-scroll": "^8.0.0", "embla-carousel-autoplay": "^8.0.0", "embla-carousel-react": "^8.0.0", + "framer-motion": "^12.23.18", "fs": "^0.0.1-security", "gray-matter": "^4.0.3", "lucide-react": "^0.522.0", @@ -45,7 +47,7 @@ "react-icons": "^5.0.1", "react-markdown": "^9.0.1", "react-share": "^5.1.0", - "react-tweet": "^3.2.0", + "react-tweet": "^3.2.2", "sass": "^1.72.0", "sharp": "^0.33.3", "tailwind-merge": "^2.2.2", diff --git a/docs/public/assets/fonts/StudioFeixenSans-Bold.otf b/docs/public/assets/fonts/StudioFeixenSans-Bold.otf new file mode 100644 index 0000000000..481b7b4139 Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Bold.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Book.otf b/docs/public/assets/fonts/StudioFeixenSans-Book.otf new file mode 100644 index 0000000000..80f60011f4 Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Book.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Light.otf b/docs/public/assets/fonts/StudioFeixenSans-Light.otf new file mode 100644 index 0000000000..c84da60927 Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Light.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Medium.otf b/docs/public/assets/fonts/StudioFeixenSans-Medium.otf new file mode 100644 index 0000000000..5a7ca79122 Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Medium.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Regular.otf b/docs/public/assets/fonts/StudioFeixenSans-Regular.otf new file mode 100644 index 0000000000..7b864f7dd5 Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Regular.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Semibold.otf b/docs/public/assets/fonts/StudioFeixenSans-Semibold.otf new file mode 100644 index 0000000000..7eca0c0fcd Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Semibold.otf differ diff --git a/docs/public/assets/fonts/StudioFeixenSans-Ultralight.otf b/docs/public/assets/fonts/StudioFeixenSans-Ultralight.otf new file mode 100644 index 0000000000..ffb5495d3b Binary files /dev/null and b/docs/public/assets/fonts/StudioFeixenSans-Ultralight.otf differ diff --git a/docs/public/assets/images/general/og-image-docs.png b/docs/public/assets/images/general/og-image-docs.png new file mode 100644 index 0000000000..5a4d700c4c Binary files /dev/null and b/docs/public/assets/images/general/og-image-docs.png differ diff --git a/docs/public/assets/images/general/og-image.png b/docs/public/assets/images/general/og-image.png index c601470791..e9646863bf 100644 Binary files a/docs/public/assets/images/general/og-image.png and b/docs/public/assets/images/general/og-image.png differ diff --git a/docs/src/assets/icons/Amazone.svg b/docs/src/assets/icons/Amazone.svg new file mode 100644 index 0000000000..df048950a8 --- /dev/null +++ b/docs/src/assets/icons/Amazone.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/ChatGPT.svg b/docs/src/assets/icons/ChatGPT.svg new file mode 100644 index 0000000000..4aadcec26f --- /dev/null +++ b/docs/src/assets/icons/ChatGPT.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/icons/Claude.svg b/docs/src/assets/icons/Claude.svg new file mode 100644 index 0000000000..f58252b4bb --- /dev/null +++ b/docs/src/assets/icons/Claude.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/icons/DeepSeek.svg b/docs/src/assets/icons/DeepSeek.svg new file mode 100644 index 0000000000..ddb8c31c08 --- /dev/null +++ b/docs/src/assets/icons/DeepSeek.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/icons/Figma.svg b/docs/src/assets/icons/Figma.svg new file mode 100644 index 0000000000..b95802f573 --- /dev/null +++ b/docs/src/assets/icons/Figma.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Gemini.svg b/docs/src/assets/icons/Gemini.svg new file mode 100644 index 0000000000..92331cd1ab --- /dev/null +++ b/docs/src/assets/icons/Gemini.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Gemma.svg b/docs/src/assets/icons/Gemma.svg new file mode 100644 index 0000000000..3f65e86c6e --- /dev/null +++ b/docs/src/assets/icons/Gemma.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/src/assets/icons/Gmail.svg b/docs/src/assets/icons/Gmail.svg new file mode 100644 index 0000000000..8732008928 --- /dev/null +++ b/docs/src/assets/icons/Gmail.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Google-drive.svg b/docs/src/assets/icons/Google-drive.svg new file mode 100644 index 0000000000..2d336a8186 --- /dev/null +++ b/docs/src/assets/icons/Google-drive.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Google.svg b/docs/src/assets/icons/Google.svg new file mode 100644 index 0000000000..a390de16fe --- /dev/null +++ b/docs/src/assets/icons/Google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/src/assets/icons/Jira.svg b/docs/src/assets/icons/Jira.svg new file mode 100644 index 0000000000..489c0d988a --- /dev/null +++ b/docs/src/assets/icons/Jira.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Kimi.svg b/docs/src/assets/icons/Kimi.svg new file mode 100644 index 0000000000..577aba7945 --- /dev/null +++ b/docs/src/assets/icons/Kimi.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/src/assets/icons/Meta.svg b/docs/src/assets/icons/Meta.svg new file mode 100644 index 0000000000..f52e08a4a3 --- /dev/null +++ b/docs/src/assets/icons/Meta.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Mistral AI.svg b/docs/src/assets/icons/Mistral AI.svg new file mode 100644 index 0000000000..76a05c6133 --- /dev/null +++ b/docs/src/assets/icons/Mistral AI.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/Notion.svg b/docs/src/assets/icons/Notion.svg new file mode 100644 index 0000000000..ff6c2f8c6b --- /dev/null +++ b/docs/src/assets/icons/Notion.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/src/assets/icons/Qwen.svg b/docs/src/assets/icons/Qwen.svg new file mode 100644 index 0000000000..49adcb9e53 --- /dev/null +++ b/docs/src/assets/icons/Qwen.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/src/assets/icons/Slack.svg b/docs/src/assets/icons/Slack.svg new file mode 100644 index 0000000000..46cac65a3b --- /dev/null +++ b/docs/src/assets/icons/Slack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/src/assets/icons/Youtube.svg b/docs/src/assets/icons/Youtube.svg new file mode 100644 index 0000000000..a8c5538e48 --- /dev/null +++ b/docs/src/assets/icons/Youtube.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/src/assets/icons/code.svg b/docs/src/assets/icons/code.svg new file mode 100644 index 0000000000..f397bcde38 --- /dev/null +++ b/docs/src/assets/icons/code.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/icons/huggingface.svg b/docs/src/assets/icons/huggingface.svg new file mode 100644 index 0000000000..11285a231a --- /dev/null +++ b/docs/src/assets/icons/huggingface.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/logo-jan.svg b/docs/src/assets/icons/logo-jan.svg new file mode 100644 index 0000000000..3faa0463b7 --- /dev/null +++ b/docs/src/assets/icons/logo-jan.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/src/assets/icons/robot.svg b/docs/src/assets/icons/robot.svg new file mode 100644 index 0000000000..350e3434f4 --- /dev/null +++ b/docs/src/assets/icons/robot.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/icons/share-android.svg b/docs/src/assets/icons/share-android.svg new file mode 100644 index 0000000000..85f5879ff9 --- /dev/null +++ b/docs/src/assets/icons/share-android.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/src/assets/landing/app-jan.png b/docs/src/assets/landing/app-jan.png new file mode 100644 index 0000000000..37b054975f Binary files /dev/null and b/docs/src/assets/landing/app-jan.png differ diff --git a/docs/src/assets/landing/avatar.png b/docs/src/assets/landing/avatar.png new file mode 100644 index 0000000000..bd5f6ac34b Binary files /dev/null and b/docs/src/assets/landing/avatar.png differ diff --git a/docs/src/assets/landing/cute-building-robot.png b/docs/src/assets/landing/cute-building-robot.png new file mode 100644 index 0000000000..1158924a65 Binary files /dev/null and b/docs/src/assets/landing/cute-building-robot.png differ diff --git a/docs/src/assets/landing/cute-robot-bg-mountain.png b/docs/src/assets/landing/cute-robot-bg-mountain.png new file mode 100644 index 0000000000..49d8a014ad Binary files /dev/null and b/docs/src/assets/landing/cute-robot-bg-mountain.png differ diff --git a/docs/src/assets/landing/cute-robot-flying.png b/docs/src/assets/landing/cute-robot-flying.png new file mode 100644 index 0000000000..f91bf6bec3 Binary files /dev/null and b/docs/src/assets/landing/cute-robot-flying.png differ diff --git a/docs/src/components/Blog/index.tsx b/docs/src/components/Blog/index.tsx index d7ec1cdb46..ca10cf408d 100644 --- a/docs/src/components/Blog/index.tsx +++ b/docs/src/components/Blog/index.tsx @@ -1,13 +1,11 @@ import { useData } from 'nextra/data' import { format } from 'date-fns' import { useRouter, useSearchParams } from 'next/navigation' - import Link from 'next/link' -import { Cards } from 'nextra/components' import { twMerge } from 'tailwind-merge' const Blog = () => { - const blogPost = useData() + const data = useData() const searchParams = useSearchParams() const search = searchParams?.get('category') const router = useRouter() @@ -24,8 +22,8 @@ const Blog = () => { ] return ( -
-
+
+

Blog

@@ -43,7 +41,7 @@ const Blog = () => {
-
    +
    • { router.push(`blog/`) @@ -76,63 +74,70 @@ const Blog = () => {
- - {blogPost - .filter((post: BlogPostsThumbnail) => { - if (search) { - return post.categories?.includes(String(search)) - } else { - return post - } - }) - .map((post: BlogPostsThumbnail, i: number) => { - return ( - -
-
-
- {post.categories?.map((cat, i) => { - return ( -

- {cat?.replaceAll('-', ' ')} -

- ) - })} -

- {format(String(post.date), 'MMMM do, yyyy')} -

-
+
+
+ {data + ?.filter((post: any) => { + if (search) { + return post.categories?.includes(String(search)) + } else { + return post + } + }) + .map((post: any, i: number) => { + return ( +
+
+

+ {format(post?.date, 'MMMM do, yyyy')} +

+ +
+
+
+
+
+
+ {post?.title} +
+ {post?.description && ( +

+ {post?.description} +

+ )} + {post?.categories && ( +
+ {post.categories.map( + (category: string, idx: number) => ( + + {category.replaceAll('-', ' ')} + + ) + )} +
+ )} + {post?.author && ( +

+ By {post?.author} +

+ )} +
+
+
+
+
-
-
- {post.title} -
-

- {post.description} -

-

- Read more... -

-
- - ) - })} - + ) + })} +
+
) diff --git a/docs/src/components/FavoriteModels.tsx b/docs/src/components/FavoriteModels.tsx new file mode 100644 index 0000000000..96b8a751ad --- /dev/null +++ b/docs/src/components/FavoriteModels.tsx @@ -0,0 +1,258 @@ +/* eslint-disable @next/next/no-img-element */ +import { Button } from '@/components/ui/button' +import { motion } from 'framer-motion' +import ChatGPTIcon from '@/assets/icons/ChatGPT.svg' +import ClaudeIcon from '@/assets/icons/Claude.svg' +import GeminiIcon from '@/assets/icons/Gemini.svg' +import MetaIcon from '@/assets/icons/Meta.svg' +import MistralIcon from '@/assets/icons/Mistral AI.svg' +import QwenIcon from '@/assets/icons/Qwen.svg' +import DeepSeekIcon from '@/assets/icons/DeepSeek.svg' +import GemmaIcon from '@/assets/icons/Gemma.svg' +import KimiIcon from '@/assets/icons/Kimi.svg' +import GmailIcon from '@/assets/icons/Gmail.svg' +import AmazonIcon from '@/assets/icons/Amazone.svg' +import GoogleIcon from '@/assets/icons/Google.svg' +import NotionIcon from '@/assets/icons/Notion.svg' +import FigmaIcon from '@/assets/icons/Figma.svg' +import YoutubeIcon from '@/assets/icons/Youtube.svg' +import SlackIcon from '@/assets/icons/Slack.svg' +import GoogleDriveIcon from '@/assets/icons/Google-drive.svg' +import JiraIcon from '@/assets/icons/Jira.svg' +import Avatar from '@/assets/landing/avatar.png' + +const models = [ + { name: 'ChatGPT', icon: ChatGPTIcon, company: 'OpenAI' }, + { name: 'Claude', icon: ClaudeIcon, company: 'Anthropic' }, + { name: 'Gemini', icon: GeminiIcon, company: 'Google' }, + { name: 'Llama', icon: MetaIcon, company: 'Meta' }, + { name: 'Mistral', icon: MistralIcon, company: 'Mistral AI' }, + { name: 'Qwen', icon: QwenIcon, company: 'Alibaba' }, + { name: 'DeepSeek', icon: DeepSeekIcon, company: 'DeepSeek' }, + { name: 'Gemma', icon: GemmaIcon, company: 'Google' }, + { name: 'Kimi', icon: KimiIcon, company: 'Moonshot AI' }, +] + +const apps = [ + { name: 'Gmail', icon: GmailIcon, description: 'Organize your inbox' }, + { name: 'Amazon', icon: AmazonIcon, description: 'Shop for products' }, + { name: 'Google', icon: GoogleIcon, description: 'Search the web' }, + { name: 'Notion', icon: NotionIcon, description: 'Write and organize' }, + { name: 'Figma', icon: FigmaIcon, description: 'Design with AI' }, + { name: 'YouTube', icon: YoutubeIcon, description: 'Look for videos' }, + { name: 'Slack', icon: SlackIcon, description: 'Read channel messages' }, + { + name: 'Google Drive', + icon: GoogleDriveIcon, + description: 'Find and fetch files', + }, + { name: 'Jira', icon: JiraIcon, description: 'Manage tickets' }, +] + +const thingsToRemember = [ + 'Minimalist UI tasted', + 'Currently on a portfolio refresh', + 'Wants brief, to-the-point answers', + 'Frequent Figma/prototyping questions', + 'Dark-mode sharer', + 'Curious about type trends (Mostly harmless)', +] + +export default function FavoriteModels() { + return ( +
+
+ + Best of open-source AI in one app + + + {/* Step 1: Use any model you want */} + +
+
+
+ 1 +
+

+ Models +

+

+ Choose from open models or plug in your favorite online models. +

+ {/* */} +
+
+
+ {models.map((model, index) => ( +
+
+ {model.name} +
+ + {model.name} + + {model.company && ( + + {model.company} + + )} +
+ ))} +
+
+
+
+ + {/* Step 2*/} + +
+
+
+ 2 +
+

+ Connectors +

+

+ Connect your email, files, notes and calendar. Jan works where + you work. +

+ {/* */} +
+
+
+ {apps.map((app) => ( +
+
+ {app.name} +
+ + {app.name} + + + {app.description} + +
+ ))} +
+
+
+
+ + {/* Step 3: Cross-platform */} + +
+
+
+ 3 +
+

+ Memory{' '} + + Coming Soon + +

+

+ Your context carries over, so you don’t repeat yourself. Jan + remembers your context and preferences. +

+ {/* */} +
+
+
+ {/* Layered cards background effect */} +
+
+ + {/* Main card */} +
+ {/* User profile section */} +
+
+ Joe's avatar +
+
+

Joe

+

+ Designer, Singapore +

+
+
+ + {/* Things to remember section */} +
+
+ Things Jan keeps in mind +
+
    + {thingsToRemember.map((item) => ( +
  • + • + + {item} + +
  • + ))} +
+
+
+
+
+
+
+
+
+ ) +} diff --git a/docs/src/components/FooterMenu/index.tsx b/docs/src/components/FooterMenu/index.tsx index 287c8a5175..1609430bfd 100644 --- a/docs/src/components/FooterMenu/index.tsx +++ b/docs/src/components/FooterMenu/index.tsx @@ -1,136 +1,54 @@ -import React, { useEffect, useState } from 'react' -import ThemeImage from '@/components/ThemeImage' -import { AiOutlineGithub } from 'react-icons/ai' -import { RiTwitterXFill } from 'react-icons/ri' - -import { BiLogoDiscordAlt } from 'react-icons/bi' +import { useState } from 'react' import { useForm } from 'react-hook-form' -import LogoMark from '@/components/LogoMark' -import { FaLinkedin } from 'react-icons/fa' -import posthog from 'posthog-js' -const socials = [ - { - icon: ( - - ), - href: 'https://twitter.com/jandotai', - }, - { - icon: ( - - ), - href: 'https://discord.com/invite/FTk2MvZwJH', - }, - { - icon: ( - - ), - href: 'https://github.com/menloresearch/jan', - }, +type FooterLink = { + name: string + href: string + comingSoon?: boolean +} + +type FooterMenu = { + title: string + links: FooterLink[] +} + +const FOOTER_MENUS: FooterMenu[] = [ { - icon: , - href: 'https://www.linkedin.com/company/homebrewltd', + title: 'Company', + links: [ + { name: 'Vision', href: '/', comingSoon: true }, + { name: 'Handbook', href: '/handbook' }, + { name: 'Community', href: 'https://discord.com/invite/FTk2MvZwJH' }, + { name: 'Careers', href: 'https://menlo.bamboohr.com/careers' }, + ], }, -] - -const menus = [ - // { - // name: 'Product', - // child: [ - // { - // menu: 'Download', - // path: '/download', - // }, - // { - // menu: 'Changelog', - // path: '/changelog', - // }, - // ], - // }, - // { - // name: 'For Developers', - // child: [ - // { - // menu: 'Documentation', - // path: '/docs', - // }, - // ], - // }, { - name: 'Community', - child: [ - { - menu: 'Github', - path: 'https://github.com/menloresearch/jan', - external: true, - }, - { - menu: 'Discord', - path: 'https://discord.gg/FTk2MvZwJH', - external: true, - }, - { - menu: 'X/Twitter', - path: 'https://twitter.com/jandotai', - external: true, - }, - { - menu: 'LinkedIn', - path: 'https://www.linkedin.com/company/menloresearch', - external: true, - }, + title: 'Resources', + links: [ + { name: 'Blog', href: '/blog' }, + { name: 'Docs', href: '/docs' }, + { name: 'Changelog', href: '/changelog' }, + { name: 'API Reference', href: '/api-reference' }, + { name: 'Jan Exam', href: '/', comingSoon: true }, ], }, { - name: 'Company', - child: [ - { - menu: 'Menlo', - path: 'https://menlo.ai', - }, - { - menu: 'Blog', - path: '/blog', - }, - { - menu: 'Careers', - path: 'https://menlo.bamboohr.com/careers', - external: true, - }, + title: 'Store', + links: [ + { name: 'Model Store', href: '/', comingSoon: true }, + { name: 'MCP Store', href: '/', comingSoon: true }, ], }, ] -const getCurrentYear = new Date().getFullYear() - export default function Footer() { - useEffect(() => { - if (typeof window !== 'undefined') { - posthog.init(process.env.POSTHOG_KEY as string, { - api_host: process.env.POSTHOG_HOST, - disable_session_recording: true, - person_profiles: 'always', - persistence: 'localStorage', - }) - - posthog.capture('web_page_view', { timestamp: new Date() }) - } - }, []) - - const { register, handleSubmit, reset } = useForm({ - defaultValues: { - email: '', - }, - }) - const [formMessage, setFormMessage] = useState('') + const { register, handleSubmit, reset } = useForm<{ email: string }>() const onSubmit = (data: { email: string }) => { const { email } = data const options = { method: 'POST', - body: JSON.stringify({ updateEnabled: false, email, @@ -157,113 +75,97 @@ export default function Footer() { } return ( -
-
-
-
- -

- Jan -

-
-
-
- The Soul of a New Machine -
-

- Subscribe to our newsletter on AI  -
- research and building Jan: -

- -
-
- - + +

+ Subscribe to our newsletter +

+
+ +
+ +
+ +
+
+ {formMessage && ( +

{formMessage}

+ )} - {formMessage &&

{formMessage}

}
-
-
- {menus.map((menu, i) => { - return ( -
-

- {menu.name} -

-
-
-
-
- {socials.map((social, i) => { - return ( - - {social.icon} - - ) - })} -
- ©{getCurrentYear} Menlo Research - + ))}
-
+ ) } diff --git a/docs/src/components/Home/index.tsx b/docs/src/components/Home/index.tsx index a907f320d9..286e51e7d2 100644 --- a/docs/src/components/Home/index.tsx +++ b/docs/src/components/Home/index.tsx @@ -1,29 +1,623 @@ -import { Fragment } from 'react' - -import Hero from '@/components/Home/Hero' -import BuiltWithLove from '@/components/Home/BuiltWithLove' -import WallOfLove from '@/components/Home/WallOfLove' -import Feature from '@/components/Home/Feature' -import Principles from './Principles' -import CTANewsletter from './CTANewsletter' -import Statistic from './Statistic' -import CTADownload from './CTADownload' -import Customizable from './Customizable' -// import APIStructure from './APIStructure' +'use client' +/* eslint-disable @next/next/no-img-element */ +import { Fragment, useEffect } from 'react' +import { FaDiscord, FaGithub } from 'react-icons/fa' +import HuggingFaceSVG from '@/assets/icons/huggingface.svg' +import CuteRobotBgMountainPNG from '@/assets/landing/cute-robot-bg-mountain.png' +import { Button } from '@/components/ui/button' +import CodeSVG from '@/assets/icons/code.svg' +import { IoMdPeople } from 'react-icons/io' +import CuteBuildingRobotPNG from '@/assets/landing/cute-building-robot.png' +import CuteRobotFlyingPNG from '@/assets/landing/cute-robot-flying.png' +import ShareSVG from '@/assets/icons/share-android.svg' +import RobotSVG from '@/assets/icons/robot.svg' +import LogoJanSVG from '@/assets/icons/logo-jan.svg' +import AppJanPNG from '@/assets/landing/app-jan.png' +import TweetSection from '@/components/TweetSection' +import FavoriteModels from '@/components/FavoriteModels' +import { DropdownButton } from '@/components/ui/dropdown-button' + +import { useData } from 'nextra/data' +import { useDiscordWidget } from '@/hooks/useDiscordWidget' +import { formatCompactNumber, totalDownload } from '@/utils/format' const Home = () => { + const { lastVersion, lastRelease, stars, release } = useData() + const { data: discordWidget } = useDiscordWidget() + + useEffect(() => { + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px', + } + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const element = entry.target as HTMLElement + const delay = element.dataset.delay || '0' + + setTimeout(() => { + element.classList.add('animate-in-view') + }, parseInt(delay)) + + observer.unobserve(element) + } + }) + }, observerOptions) + + // Observe all scroll-triggered animation elements + const animatedElements = document.querySelectorAll( + '.animate-on-scroll, .animate-on-scroll-left, .animate-on-scroll-right, .animate-on-scroll-scale, .animate-slide-up' + ) + + animatedElements.forEach((element) => { + observer.observe(element) + }) + + // Simple parallax effect for robot images + const handleScroll = () => { + const parallaxElements = document.querySelectorAll('.parallax-element') + + parallaxElements.forEach((el) => { + const element = el as HTMLElement + const rect = element.getBoundingClientRect() + + // Only apply parallax when element is visible + if (rect.top < window.innerHeight && rect.bottom > 0) { + const speed = parseFloat(element.getAttribute('data-speed') || '0.3') + // Simple calculation: how far the element has moved into/through viewport + const progress = Math.min( + 1, + Math.max(0, (window.innerHeight - rect.top) / window.innerHeight) + ) + // Move from 0 to -40px based on progress + const yPos = Math.round(progress * -100 * speed) + element.style.transform = `translateY(${yPos}px)` + } + }) + } + + window.addEventListener('scroll', handleScroll) + + // Cleanup function + return () => { + observer.disconnect() + window.removeEventListener('scroll', handleScroll) + } + }, []) + return ( - - - + {/* Hero */} +
+ + +
+
+ Jan App Interface +
+
+ +
+
+ Jan App Interface +
+
+
+ + {/* Statistic and social */} +
+
+

+ Over 4 million downloads +

+
+ +
+ + {/* Social tech */} +
+
+
+
+
+
+

+ Towards Open Superintelligence +

+

+ Jan takes the best of open source AI and packages it into an + easy-to-use product. +

+
+ +
+
+
+
+ +
+
+
+ + {/* Favorite Models Section */} + + + {/* Developer Community */} +
+
+
+
+

+ Built in Public +

+

+ Our core team believes that AI should be open,{' '} +
and Jan is built in public. +

+
+
+
+
+
+ +
+
+

+ Develop +

+
+

+ Submit PRs for UI, tooling, or edge optimizations. +

+ + + +
+
+ + + +
+ +
+
+

+ Train +

+
+

+ Add evals, safety tests, or training recipes. +

+ + + +
+
+
+
+
+
+
+ +
+
+
+ + {/* Call to action */} +
+
+
+
+
+
+

+ Ask Jan, +
+
+ get things done +

+
+
+ + + +{totalDownload(release)} downloads, Free & Open source + +
+
+
+
+
+ +
+
+
+ + {/* */} + {/* */} {/* */} - - - - - - + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */}
) } diff --git a/docs/src/components/Navbar.tsx b/docs/src/components/Navbar.tsx new file mode 100644 index 0000000000..51044e9c79 --- /dev/null +++ b/docs/src/components/Navbar.tsx @@ -0,0 +1,271 @@ +/* eslint-disable @next/next/no-img-element */ +import { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { cn } from '@/lib/utils' +import { FaDiscord, FaGithub } from 'react-icons/fa' +import { FiDownload } from 'react-icons/fi' +import { FaXTwitter } from 'react-icons/fa6' +import { Button } from './ui/button' +import LogoJanSVG from '@/assets/icons/logo-jan.svg' + +const MENU_ITEMS = [ + { name: 'Docs', href: '/docs' }, + { name: 'Changelog', href: '/changelog' }, + { name: 'Blog', href: '/blog' }, + { name: 'Handbook', href: '/handbook' }, +] + +const Navbar = ({ noScroll }: { noScroll?: boolean }) => { + const router = useRouter() + const [isScrolled, setIsScrolled] = useState(false) + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) + const currentPath = router.asPath + + const isLanding = currentPath === '/' + + useEffect(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > (isLanding ? 76 : 0)) + } + + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, [isLanding]) + + const toggleMobileMenu = () => { + setIsMobileMenuOpen(!isMobileMenuOpen) + } + + // Prevent body scroll when mobile menu is open + useEffect(() => { + if (isMobileMenuOpen) { + document.body.style.overflow = 'hidden' + } else { + document.body.style.overflow = 'unset' + } + + // Cleanup on unmount + return () => { + document.body.style.overflow = 'unset' + } + }, [isMobileMenuOpen]) + + return ( +