+
+
+
+ {/* Desktop Navigation */}
+
+
+ {/* Mobile Download Button and Hamburger */}
+
+
+
+ {/* Mobile Menu - Modal Card */}
+ {isMobileMenuOpen && (
+ <>
+ {/* Backdrop */}
+
setIsMobileMenuOpen(false)}
+ />
+
+ {/* Modal Card */}
+
+
+ {/* Header with close button */}
+
+
Jan
+
+
+
+ {/* Menu Items */}
+
+
+ {/* Social Icons */}
+
+
+ {/* Action Buttons */}
+
+
+
+ >
+ )}
+
+ )
+}
+
+export default Navbar
diff --git a/docs/src/components/TweetSection.tsx b/docs/src/components/TweetSection.tsx
new file mode 100644
index 0000000000..ef39f5ad7c
--- /dev/null
+++ b/docs/src/components/TweetSection.tsx
@@ -0,0 +1,135 @@
+import { ClientTweetCard } from '@/components/ui/tweet-card'
+import { useEffect } from 'react'
+
+const Tweets = [
+ { id: '1959360209970700621' },
+ { id: '1959018716219277654' },
+ { id: '1959410685093523580' },
+ { id: '1959003819196785143' },
+ { id: '1956547833999560863' },
+ { id: '1956616098885079434' },
+ { id: '1955283174340128809' },
+ { id: '1955680680261652896' },
+ { id: '1955624616560566446' },
+ { id: '1955633387038966112' },
+ { id: '1955326315160043918' },
+ { id: '1952305678497747137' },
+]
+
+export default function TweetSection() {
+ useEffect(() => {
+ const buttons = document.querySelectorAll('.tweet-nav-btn')
+
+ const handleClick = (event: Event) => {
+ const button = event.currentTarget as HTMLButtonElement
+ const direction = button.dataset.direction
+ const container = document.querySelector('.tweet-marquee-container')
+
+ if (direction === 'left') {
+ container?.scrollBy({ left: -300, behavior: 'smooth' })
+ } else {
+ container?.scrollBy({ left: 300, behavior: 'smooth' })
+ }
+ }
+
+ buttons.forEach((button) => {
+ button.addEventListener('click', handleClick)
+ })
+
+ return () => {
+ buttons.forEach((button) => {
+ button.removeEventListener('click', handleClick)
+ })
+ }
+ }, [])
+
+ return (
+
+ {/* Scrollable marquee container */}
+
+
+ {/* Multiple copies for infinite scroll */}
+ {[...Tweets, ...Tweets, ...Tweets].map((tweet, index) => (
+
+
+
+ ))}
+
+
+
+ {/* Navigation arrows at bottom - will need client-side JS */}
+
+
+
+
+ )
+}
diff --git a/docs/src/components/ui/button.tsx b/docs/src/components/ui/button.tsx
new file mode 100644
index 0000000000..ae58a6332b
--- /dev/null
+++ b/docs/src/components/ui/button.tsx
@@ -0,0 +1,67 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
+ {
+ variants: {
+ variant: {
+ "playful-green":
+ "bg-[#7EF19D] hover:bg-[#6BD689] hover:shadow-[0px_0px_0px_0px_rgba(0,0,0,1)] hover:translate-y-[2px] active:shadow-[0px_2px_0px_0px_rgba(0,0,0,1)] active:translate-y-[2px] text-black shadow-[0px_4px_0px_0px_rgba(0,0,0,1)] border border-black !rounded-2xl",
+ "playful-white":
+ "bg-white text-black hover:bg-gray-200 hover:shadow-[0px_0px_0px_0px_rgba(0,0,0,1)] hover:translate-y-[2px] active:shadow-[0px_2px_0px_0px_rgba(0,0,0,1)] active:translate-y-[2px] shadow-[0px_4px_0px_0px_rgba(0,0,0,1)] border border-black !rounded-2xl",
+ playful:
+ "text-black hover:shadow-[0px_0px_0px_0px_rgba(0,0,0,1)] hover:translate-y-[2px] active:shadow-[0px_2px_0px_0px_rgba(0,0,0,1)] active:translate-y-[2px] shadow-[0px_4px_0px_0px_rgba(0,0,0,1)] border border-black !rounded-2xl",
+ default:
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ xl: "h-12 rounded-md px-6 has-[>svg]:px-4 text-lg",
+ xxl: "h-16 !rounded-[20px] px-6 has-[>svg]:px-4 text-xl -tracking-[0.2px]",
+ icon: "size-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+);
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps
& {
+ asChild?: boolean;
+ }) {
+ const Comp = asChild ? Slot : "button";
+
+ return (
+
+ );
+}
+
+export { Button, buttonVariants };
diff --git a/docs/src/components/ui/dropdown-button.tsx b/docs/src/components/ui/dropdown-button.tsx
new file mode 100644
index 0000000000..2b200284d0
--- /dev/null
+++ b/docs/src/components/ui/dropdown-button.tsx
@@ -0,0 +1,255 @@
+import * as React from 'react'
+import { Button } from './button'
+import { cn } from '@/lib/utils'
+import { FaApple, FaWindows, FaLinux } from 'react-icons/fa'
+import { formatFileSize } from '@/utils/format'
+
+interface DownloadOption {
+ id: string
+ name: string
+ icon: React.ReactNode
+ size: string
+ href: string
+ isActive?: boolean
+}
+
+const downloadOptionsTemplate: DownloadOption[] = [
+ {
+ id: 'mac',
+ name: 'Download for Mac',
+ icon: ,
+ size: '',
+ href: '#',
+ isActive: true,
+ },
+ {
+ id: 'windows',
+ name: 'Download for Windows',
+ icon: ,
+ size: '',
+ href: '#',
+ },
+ {
+ id: 'linux-appimage',
+ name: 'Download for Linux (AppImage)',
+ icon: ,
+ size: '',
+ href: '#',
+ },
+ {
+ id: 'linux-deb',
+ name: 'Download for Linux (Deb)',
+ icon: ,
+ size: '',
+ href: '#',
+ },
+]
+
+const fileFormatMap: { [key: string]: string } = {
+ 'mac': 'Jan_{tag}_universal.dmg',
+ 'windows': 'Jan_{tag}_x64-setup.exe',
+ 'linux-appimage': 'Jan_{tag}_amd64.AppImage',
+ 'linux-deb': 'Jan_{tag}_amd64.deb',
+}
+
+interface DropdownButtonProps {
+ size?: 'default' | 'sm' | 'lg' | 'xl' | 'icon' | 'xxl'
+ className?: string
+ classNameButton?: string
+ lastRelease?: any
+}
+
+export function DropdownButton({
+ size = 'xl',
+ className,
+ classNameButton,
+ lastRelease,
+}: DropdownButtonProps) {
+ const [isOpen, setIsOpen] = React.useState(false)
+ const [downloadOptions, setDownloadOptions] = React.useState(
+ downloadOptionsTemplate
+ )
+ const [currentOption, setCurrentOption] = React.useState(
+ downloadOptions.find((opt) => opt.isActive) || downloadOptions[0]
+ )
+ const dropdownRef = React.useRef(null)
+
+ const toggleDropdown = () => setIsOpen(!isOpen)
+
+ const selectOption = (option: DownloadOption) => {
+ setCurrentOption(option)
+ setIsOpen(false)
+ }
+
+ const changeDefaultSystem = React.useCallback((systems: DownloadOption[]) => {
+ const userAgent = navigator.userAgent
+ if (userAgent.includes('Windows')) {
+ // windows user
+ const windowsOption = systems.find((opt) => opt.id === 'windows')
+ if (windowsOption) setCurrentOption(windowsOption)
+ } else if (userAgent.includes('Linux')) {
+ // linux user - prefer deb package
+ const linuxOption = systems.find((opt) => opt.id === 'linux-deb')
+ if (linuxOption) setCurrentOption(linuxOption)
+ } else if (userAgent.includes('Mac OS')) {
+ // mac user - always use universal build
+ const macOption = systems.find((opt) => opt.id === 'mac')
+ if (macOption) setCurrentOption(macOption)
+ } else {
+ // fallback to windows
+ const windowsOption = systems.find((opt) => opt.id === 'windows')
+ if (windowsOption) setCurrentOption(windowsOption)
+ }
+ }, [])
+
+ React.useEffect(() => {
+ if (lastRelease) {
+ try {
+ const tag = lastRelease.tag_name.startsWith('v')
+ ? lastRelease.tag_name.substring(1)
+ : lastRelease.tag_name
+
+ const updatedOptions = downloadOptionsTemplate.map((option) => {
+ const fileFormat = fileFormatMap[option.id]
+ const fileName = fileFormat.replace('{tag}', tag)
+
+ // Find the corresponding asset to get the file size
+ const asset = lastRelease.assets.find(
+ (asset: any) => asset.name === fileName
+ )
+
+ return {
+ ...option,
+ href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${fileName}`,
+ size: asset ? formatFileSize(asset.size) : 'N/A',
+ }
+ })
+
+ setDownloadOptions(updatedOptions)
+ changeDefaultSystem(updatedOptions)
+ } catch (error) {
+ console.error('Failed to update download links:', error)
+ }
+ }
+ }, [lastRelease, changeDefaultSystem])
+
+ React.useEffect(() => {
+ const handleEscapeKey = (event: KeyboardEvent) => {
+ if (event.key === 'Escape' && isOpen) {
+ setIsOpen(false)
+ }
+ }
+
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false)
+ }
+ }
+
+ if (isOpen) {
+ document.addEventListener('keydown', handleEscapeKey)
+ document.addEventListener('mousedown', handleClickOutside)
+ }
+
+ return () => {
+ document.removeEventListener('keydown', handleEscapeKey)
+ document.removeEventListener('mousedown', handleClickOutside)
+ }
+ }, [isOpen])
+
+ return (
+
+
+
+ {/* Dropdown Menu */}
+ {isOpen && (
+
+ )}
+
+ )
+}
diff --git a/docs/src/components/ui/tweet-card.tsx b/docs/src/components/ui/tweet-card.tsx
new file mode 100644
index 0000000000..358a787e42
--- /dev/null
+++ b/docs/src/components/ui/tweet-card.tsx
@@ -0,0 +1,293 @@
+/* eslint-disable @next/next/no-img-element */
+import { Suspense } from 'react'
+import { FaXTwitter } from 'react-icons/fa6'
+
+import {
+ enrichTweet,
+ useTweet,
+ type EnrichedTweet,
+ type TweetProps,
+ type TwitterComponents,
+} from 'react-tweet'
+import { getTweet, type Tweet } from 'react-tweet/api'
+
+import { cn } from '@/lib/utils'
+
+interface TwitterIconProps {
+ className?: string
+ [key: string]: unknown
+}
+const Twitter = ({ className, ...props }: TwitterIconProps) => (
+
+)
+
+const Verified = ({ className, ...props }: TwitterIconProps) => (
+
+)
+
+export const truncate = (str: string | null, length: number) => {
+ if (!str || str.length <= length) return str
+ return `${str.slice(0, length - 3)}...`
+}
+
+const Skeleton = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+
+export const TweetSkeleton = ({
+ className,
+ ...props
+}: {
+ className?: string
+ [key: string]: unknown
+}) => (
+
+)
+
+export const TweetNotFound = ({
+ className,
+ ...props
+}: {
+ className?: string
+ [key: string]: unknown
+}) => (
+
+
Tweet not found
+
+)
+
+export const TweetHeader = ({ tweet }: { tweet: EnrichedTweet }) => (
+
+)
+
+export const TweetBody = ({ tweet }: { tweet: EnrichedTweet }) => (
+
+ {tweet.entities.map((entity, idx) => {
+ switch (entity.type) {
+ case 'url':
+ case 'symbol':
+ case 'hashtag':
+ case 'mention':
+ return (
+
+ {entity.text}
+
+ )
+ case 'text':
+ return (
+
+ )
+ }
+ })}
+
+)
+
+export const TweetMedia = ({ tweet }: { tweet: EnrichedTweet }) => {
+ if (!tweet.video && !tweet.photos) return null
+ return (
+
+ {tweet.video && (
+
+ )}
+ {tweet.photos && (
+
+
+ {tweet.photos.map((photo) => (
+

+ ))}
+
+
+ )}
+ {!tweet.video &&
+ !tweet.photos &&
+ // @ts-ignore
+ tweet?.card?.binding_values?.thumbnail_image_large?.image_value.url && (
+

+ )}
+
+ )
+}
+
+export const MagicTweet = ({
+ tweet,
+ components,
+ className,
+ ...props
+}: {
+ tweet: Tweet
+ components?: TwitterComponents
+ className?: string
+}) => {
+ const enrichedTweet = enrichTweet(tweet)
+ return (
+
+
+
+ {/* */}
+
+ )
+}
+
+/**
+ * TweetCard (Server Side Only)
+ */
+export const TweetCard = async ({
+ id,
+ components,
+ fallback = ,
+ onError,
+ ...props
+}: TweetProps & {
+ className?: string
+}) => {
+ const tweet = id
+ ? await getTweet(id).catch((err) => {
+ if (onError) {
+ onError(err)
+ } else {
+ console.error(err)
+ }
+ })
+ : undefined
+
+ if (!tweet) {
+ const NotFound = components?.TweetNotFound || TweetNotFound
+ return
+ }
+
+ return (
+
+
+
+ )
+}
+
+export const ClientTweetCard = ({
+ id,
+ apiUrl,
+ fallback = ,
+ components,
+ fetchOptions,
+ onError,
+ ...props
+}: TweetProps & { className?: string }) => {
+ const { data, error, isLoading } = useTweet(id, apiUrl, fetchOptions)
+ if (isLoading) return fallback
+ if (error || !data) {
+ const NotFound = components?.TweetNotFound || TweetNotFound
+ return
+ }
+ return
+}
diff --git a/docs/src/lib/utils.ts b/docs/src/lib/utils.ts
new file mode 100644
index 0000000000..fed2fe91e4
--- /dev/null
+++ b/docs/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/docs/src/pages/_meta.json b/docs/src/pages/_meta.json
index bcecee260b..5d0a283c82 100644
--- a/docs/src/pages/_meta.json
+++ b/docs/src/pages/_meta.json
@@ -1,62 +1,67 @@
{
- "index": {
- "type": "page",
- "title": "Homepage",
- "display": "hidden",
- "theme": {
- "layout": "raw"
- }
- },
- "docs": {
- "type": "page",
- "title": "Docs"
- },
- "platforms": {
- "type": "page",
- "title": "Platforms",
- "display": "hidden"
- },
- "integrations": {
- "type": "page",
- "title": "Integrations",
- "display": "hidden"
- },
- "api-reference": {
- "type": "page",
- "title": "API reference",
- "display": "hidden"
- },
- "changelog": {
- "type": "page",
- "title": "Changelog",
- "theme": {
- "layout": "raw"
- }
- },
- "blog": {
- "type": "page",
- "title": "Blog",
- "theme": {
- "layout": "raw"
- }
- },
- "post": {
- "type": "page",
- "title": "Post Categories",
- "display": "hidden"
- },
- "download": {
- "type": "page",
- "theme": {
- "layout": "raw"
- }
- },
- "privacy": {
- "title": "Privacy",
- "display": "hidden"
- },
- "support": {
- "title": "Support",
- "display": "hidden"
- }
+ "index": {
+ "type": "page",
+ "title": "Homepage",
+ "display": "hidden",
+ "theme": {
+ "layout": "raw"
+ }
+ },
+ "docs": {
+ "type": "page",
+ "title": "Docs"
+ },
+ "platforms": {
+ "type": "page",
+ "title": "Platforms",
+ "display": "hidden"
+ },
+ "integrations": {
+ "type": "page",
+ "title": "Integrations",
+ "display": "hidden"
+ },
+ "api-reference": {
+ "type": "page",
+ "title": "API reference",
+ "display": "hidden"
+ },
+ "handbook": {
+ "type": "page",
+ "title": "Handbook",
+ "display": "hidden"
+ },
+ "changelog": {
+ "type": "page",
+ "title": "Changelog",
+ "theme": {
+ "layout": "raw"
+ }
+ },
+ "blog": {
+ "type": "page",
+ "title": "Blog",
+ "theme": {
+ "layout": "raw"
+ }
+ },
+ "post": {
+ "type": "page",
+ "title": "Post Categories",
+ "display": "hidden"
+ },
+ "download": {
+ "type": "page",
+ "theme": {
+ "layout": "raw"
+ }
+ },
+ "privacy": {
+ "title": "Privacy",
+ "display": "hidden"
+ },
+ "support": {
+ "title": "Support",
+ "display": "hidden"
+ }
}
diff --git a/docs/src/pages/docs/_meta.json b/docs/src/pages/docs/_meta.json
index 8a2778b7ea..5b6962032b 100644
--- a/docs/src/pages/docs/_meta.json
+++ b/docs/src/pages/docs/_meta.json
@@ -1,14 +1,18 @@
{
- "-- Switcher": {
- "type": "separator",
- "title": "Switcher"
- },
- "desktop": {
- "type": "page",
- "title": "Jan Desktop & Mobile"
- },
- "server": {
- "type": "page",
- "title": "Jan Server"
- }
-}
\ No newline at end of file
+ "-- Switcher": {
+ "type": "separator",
+ "title": "Switcher"
+ },
+ "index": {
+ "type": "page",
+ "title": "Jan Overview"
+ },
+ "desktop": {
+ "type": "page",
+ "title": "Jan Desktop & Mobile"
+ },
+ "server": {
+ "type": "page",
+ "title": "Jan Server"
+ }
+}
diff --git a/docs/src/pages/docs/desktop/_meta.json b/docs/src/pages/docs/desktop/_meta.json
index 96e076dcdb..36c70cf270 100644
--- a/docs/src/pages/docs/desktop/_meta.json
+++ b/docs/src/pages/docs/desktop/_meta.json
@@ -1,47 +1,47 @@
{
- "get-started-separator": {
- "title": "Get started",
- "type": "separator"
- },
- "index": "Overview",
- "quickstart": "Quickstart",
- "desktop": "Install 👋 Jan",
- "jan-models": "Models",
- "remote-models": "Cloud Providers",
- "mcp-examples": "Tutorials",
- "coreconcepts-separator": {
- "title": "Core concepts",
- "type": "separator"
- },
- "assistants": "Assistants",
- "llama-cpp": "Local AI Engine",
- "model-parameters": "Model Parameters",
- "privacy-policy": {
- "type": "page",
- "display": "hidden",
- "title": "Privacy Policy"
- },
- "advanced-separator": {
- "title": "ADVANCED",
- "type": "separator"
- },
- "manage-models": "Manage Models",
- "mcp": "Model Context Protocol",
- "localserver": {
- "title": "LOCAL SERVER",
- "type": "separator"
- },
- "api-server": "Server Setup",
- "llama-cpp-server": "LlamaCpp Server",
- "server-settings": "Server Settings",
- "server-troubleshooting": "Server Troubleshooting",
- "server-examples": "Integrations",
- "reference-separator": {
- "title": "REFERENCE",
- "type": "separator"
- },
- "settings": "Settings",
- "data-folder": "Jan Data Folder",
- "troubleshooting": "Troubleshooting",
- "privacy": "Privacy"
+ "get-started-separator": {
+ "title": "Get started",
+ "type": "separator"
+ },
+ "index": "Overview",
+ "quickstart": "Quickstart",
+ "install": "Install 👋 Jan",
+ "jan-models": "Models",
+ "remote-models": "Cloud Providers",
+ "mcp-examples": "Tutorials",
+ "coreconcepts-separator": {
+ "title": "Core concepts",
+ "type": "separator"
+ },
+ "assistants": "Assistants",
+ "llama-cpp": "Local AI Engine",
+ "model-parameters": "Model Parameters",
+ "privacy-policy": {
+ "type": "page",
+ "display": "hidden",
+ "title": "Privacy Policy"
+ },
+ "advanced-separator": {
+ "title": "ADVANCED",
+ "type": "separator"
+ },
+ "manage-models": "Manage Models",
+ "mcp": "Model Context Protocol",
+ "localserver": {
+ "title": "LOCAL SERVER",
+ "type": "separator"
+ },
+ "api-server": "Server Setup",
+ "llama-cpp-server": "LlamaCpp Server",
+ "server-settings": "Server Settings",
+ "server-troubleshooting": "Server Troubleshooting",
+ "server-examples": "Integrations",
+ "reference-separator": {
+ "title": "REFERENCE",
+ "type": "separator"
+ },
+ "settings": "Settings",
+ "data-folder": "Jan Data Folder",
+ "troubleshooting": "Troubleshooting",
+ "privacy": "Privacy"
}
diff --git a/docs/src/pages/docs/desktop/index.mdx b/docs/src/pages/docs/desktop/index.mdx
index 27b51f5c2b..5e37e76b36 100644
--- a/docs/src/pages/docs/desktop/index.mdx
+++ b/docs/src/pages/docs/desktop/index.mdx
@@ -1,5 +1,5 @@
---
-title: Jan
+title: Overview
description: Working towards open superintelligence through community-driven AI
keywords:
[
diff --git a/docs/src/pages/docs/desktop/linux.mdx b/docs/src/pages/docs/desktop/install/linux.mdx
similarity index 100%
rename from docs/src/pages/docs/desktop/linux.mdx
rename to docs/src/pages/docs/desktop/install/linux.mdx
diff --git a/docs/src/pages/docs/desktop/mac.mdx b/docs/src/pages/docs/desktop/install/mac.mdx
similarity index 100%
rename from docs/src/pages/docs/desktop/mac.mdx
rename to docs/src/pages/docs/desktop/install/mac.mdx
diff --git a/docs/src/pages/docs/desktop/windows.mdx b/docs/src/pages/docs/desktop/install/windows.mdx
similarity index 100%
rename from docs/src/pages/docs/desktop/windows.mdx
rename to docs/src/pages/docs/desktop/install/windows.mdx
diff --git a/docs/src/pages/docs/desktop/mcp-examples/browser/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/browser/_meta.json
index faa76ef2a8..da0e375eeb 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/browser/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/browser/_meta.json
@@ -1,6 +1,5 @@
{
"browserbase": {
- "title": "Browserbase",
- "href": "/docs/mcp-examples/browser/browserbase"
+ "title": "Browserbase"
}
}
diff --git a/docs/src/pages/docs/desktop/mcp-examples/data-analysis/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/data-analysis/_meta.json
index 43561ec1a1..bda882d7d2 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/data-analysis/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/data-analysis/_meta.json
@@ -1,10 +1,8 @@
{
"e2b": {
- "title": "E2B Code Sandbox",
- "href": "/docs/mcp-examples/data-analysis/e2b"
+ "title": "E2B Code Sandbox"
},
"jupyter": {
- "title": "Jupyter Notebooks",
- "href": "/docs/mcp-examples/data-analysis/jupyter"
+ "title": "Jupyter Notebooks"
}
}
diff --git a/docs/src/pages/docs/desktop/mcp-examples/deepresearch/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/deepresearch/_meta.json
index c746d8e335..4d2faafd44 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/deepresearch/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/deepresearch/_meta.json
@@ -1,6 +1,5 @@
{
"octagon": {
- "title": "Octagon Deep Research",
- "href": "/docs/mcp-examples/deepresearch/octagon"
+ "title": "Octagon Deep Research"
}
}
diff --git a/docs/src/pages/docs/desktop/mcp-examples/design/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/design/_meta.json
index d9f00c5d2b..9d9313837e 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/design/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/design/_meta.json
@@ -1,6 +1,5 @@
{
"canva": {
- "title": "Canva",
- "href": "/docs/mcp-examples/design/canva"
+ "title": "Canva"
}
}
diff --git a/docs/src/pages/docs/desktop/mcp-examples/productivity/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/productivity/_meta.json
index a739472a53..8bed55d0cc 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/productivity/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/productivity/_meta.json
@@ -1,10 +1,8 @@
{
"todoist": {
- "title": "Todoist",
- "href": "/docs/mcp-examples/productivity/todoist"
+ "title": "Todoist"
},
"linear": {
- "title": "Linear",
- "href": "/docs/mcp-examples/productivity/linear"
+ "title": "Linear"
}
}
diff --git a/docs/src/pages/docs/desktop/mcp-examples/search/_meta.json b/docs/src/pages/docs/desktop/mcp-examples/search/_meta.json
index 0ba01c4ace..649e3e15e1 100644
--- a/docs/src/pages/docs/desktop/mcp-examples/search/_meta.json
+++ b/docs/src/pages/docs/desktop/mcp-examples/search/_meta.json
@@ -1,10 +1,8 @@
{
"exa": {
- "title": "Exa Search",
- "href": "/docs/mcp-examples/search/exa"
+ "title": "Exa Search"
},
"serper": {
- "title": "Serper Search",
- "href": "/docs/mcp-examples/search/serper"
+ "title": "Serper Search"
}
}
diff --git a/docs/src/pages/docs/desktop/remote-models/_meta.json b/docs/src/pages/docs/desktop/remote-models/_meta.json
index 9ef524352d..60268b73c9 100644
--- a/docs/src/pages/docs/desktop/remote-models/_meta.json
+++ b/docs/src/pages/docs/desktop/remote-models/_meta.json
@@ -1,34 +1,26 @@
{
"anthropic": {
- "title": "Anthropic",
- "href": "/docs/remote-models/anthropic"
+ "title": "Anthropic"
},
"cohere": {
- "title": "Cohere",
- "href": "/docs/remote-models/cohere"
+ "title": "Cohere"
},
"google": {
- "title": "Gemini",
- "href": "/docs/remote-models/google"
+ "title": "Gemini"
},
"groq": {
- "title": "Groq",
- "href": "/docs/remote-models/groq"
+ "title": "Groq"
},
"mistralai": {
- "title": "Mistral AI",
- "href": "/docs/remote-models/mistralai"
+ "title": "Mistral AI"
},
"openai": {
- "title": "OpenAI",
- "href": "/docs/remote-models/openai"
+ "title": "OpenAI"
},
"openrouter": {
- "title": "OpenRouter",
- "href": "/docs/remote-models/openrouter"
+ "title": "OpenRouter"
},
"huggingface": {
- "title": "Hugging Face",
- "href": "/docs/remote-models/huggingface"
+ "title": "Hugging Face"
}
}
diff --git a/docs/src/pages/docs/server/overview.mdx b/docs/src/pages/docs/server/overview.mdx
index 2471119c9a..eade5bbe4d 100644
--- a/docs/src/pages/docs/server/overview.mdx
+++ b/docs/src/pages/docs/server/overview.mdx
@@ -1,5 +1,5 @@
---
-title: Jan Server
+title: Overview
description: A comprehensive self-hosted AI server platform that provides OpenAI-compatible APIs, multi-tenant organization management, and AI model inference capabilities.
keywords:
[
diff --git a/docs/src/pages/handbook/_meta.json b/docs/src/pages/handbook/_meta.json
new file mode 100644
index 0000000000..482ca4f93d
--- /dev/null
+++ b/docs/src/pages/handbook/_meta.json
@@ -0,0 +1,5 @@
+{
+ "index": "Overview",
+ "open-superintelligence": "Open Superintelligence",
+ "betting-on-open-source": "Betting on Open-Source"
+}
diff --git a/docs/src/pages/handbook/betting-on-open-source.mdx b/docs/src/pages/handbook/betting-on-open-source.mdx
new file mode 100644
index 0000000000..a0560d53e2
--- /dev/null
+++ b/docs/src/pages/handbook/betting-on-open-source.mdx
@@ -0,0 +1,33 @@
+---
+title: "Why Open-Source"
+description: "Why we're betting on open-source."
+---
+
+# Why Open-Source
+
+AI today is concentrated in the hands of a few companies. They ask for trust, while keeping the levers of control hidden. We think that's a mistake.
+
+When you depend on one vendor, your future is tied to their roadmap, their politics, their survival. If they get acquired, pivot, or shut down; you're stuck.
+
+Depending on a closed vendor means giving up more than flexibility:
+- Your tools only move when their priorities move
+- Their pivots become your pivots
+- Their acquisitions become your risks
+
+AI has become critical infrastructure. Nations, enterprises, even small teams rely on it to think and decide. And yet, control sits with a few vendors who decide the terms of access. We believe that's not control. That's dependency dressed up as convenience. One of the most powerful invention is being steered by a handful of executives. Their values shape what billions can say, build, or ask.
+
+*This cannot stand. It must be changed.*
+
+## Jan's Bet
+
+We don't believe the future of AI should be dictated by a few firms in San Francisco, Beijing, or anywhere else.
+
+AI is revolutionary like electricity. And like electricity, it must be open. Not locked behind trust-me promises. Not steered by a handful of companies.
+
+That's why we're building Jan, a full product suite:
+- Jan Models
+- Jan on Desktop, Browser, Mobile, Web
+- Jan Server
+- Hub, Store, evals, guardrails, the ecosystem around it
+
+The goal is to be the open-source replacement for ChatGPT and other BigAI products, with models and tools you can run, own, and trust.
diff --git a/docs/src/pages/handbook/index.mdx b/docs/src/pages/handbook/index.mdx
new file mode 100644
index 0000000000..2c64eff727
--- /dev/null
+++ b/docs/src/pages/handbook/index.mdx
@@ -0,0 +1,48 @@
+---
+title: "Jan Team Handbook"
+description: "Building superintelligence that you can own and run anywhere."
+---
+
+# Jan Handbook
+
+> Jan's Handbook is inspired by [Posthog](https://posthog.com/handbook) and [Gitlab](https://handbook.gitlab.com/).
+> Thank you for showing us the way.
+
+## Welcome
+
+This handbook explains how [Jan](https://jan.ai) works, and is public.
+
+We're building superintelligence that you can self-host and use locally. Not as a limitation, but as a feature. Your AI should work wherever you need it - on your laptop during a flight, on your company's servers for compliance, or in the cloud for scale.
+
+Jan's Handbook is a [living document](https://en.wikipedia.org/wiki/Living_document), constantly evolving as we build the future of AI ownership.
+
+## Why does Jan exist?
+
+### [Open Superintelligence](/handbook/open-superintelligence)
+Building superintelligence that belongs to everyone, not just a few tech giants. We believe the future of AI should be open, accessible, and owned by the people who use it.
+
+### [Betting on Open-Source](/handbook/betting-on-open-source)
+Why we're betting on open-source as the future of AI and technology. Open-source has consistently won in the long term, and AI will be no different.
+
+---
+
+## Quick Links
+
+- **For the curious**: Start with [Open Superintelligence](/handbook/open-superintelligence)
+- **For developers**: Learn about [Betting on Open-Source](/handbook/betting-on-open-source)
+- **For contributors**: Check out our [GitHub](https://github.com/menloresearch/jan) and [Discord](https://discord.gg/FTk2MvZwJH)
+
+## Our North Star
+
+We're building superintelligence that:
+
+- **Works anywhere**: From your laptop to your data center
+- **Belongs to you**: Download it, own it, modify it
+- **Scales infinitely**: One person or ten thousand, same platform
+- **Improves constantly**: Community-driven development
+
+This isn't just about making AI accessible. It's about ensuring the most transformative technology in human history can be owned by those who use it.
+
+---
+
+_"The future of AI isn't about choosing between local or cloud. It's about having both, and everything in between, working perfectly together."_
diff --git a/docs/src/pages/handbook/open-superintelligence.mdx b/docs/src/pages/handbook/open-superintelligence.mdx
new file mode 100644
index 0000000000..5174f712bd
--- /dev/null
+++ b/docs/src/pages/handbook/open-superintelligence.mdx
@@ -0,0 +1,49 @@
+---
+title: "Why Jan exists"
+description: "Short answer: Open Superintelligence."
+---
+
+# Why does Jan exist?
+
+> Short answer: Open Superintelligence.
+
+In 1879, Edison lit a single street in [Menlo Park](https://en.wikipedia.org/wiki/Menlo_Park,_California). What mattered wasn't the bulb. It was that power could reach homes, schools, and factories.
+
+Electricity changed the world only when it became universal. Standard plugs, cheap generation, lines everywhere. People stopped talking about electricity and started using light, cold chains, and machines.
+
+[Superintelligence](https://en.wikipedia.org/wiki/Superintelligence) is the same kind of story. The point is who gets the power, on what terms, and at what cost. If intelligence is open, portable, and cheap, it becomes a utility. If it isn’t, it becomes a gate.
+
+Jan exists to push intelligence toward the first path: Open Superintelligence you can run on your cheap laptop.
+
+## What history teaches
+
+> The world is made, and can be remade.
+
+Every industrial wave redefined critical aspects of our daily lives:
+- Factories introduced shift clocks and wage rhythms
+- Steam gave way to electricity and standardized parts
+- Rail, telegraph, and later networks changed how decisions travel
+- Each wave pulled new bargains into being skills, schools, safety nets, labor law
+
+So what we're interested in is who is going to write the new defaults and share in the gains.
+
+Technology doesn’t choose its path, people do. Power accrues to whoever designs, deploys, and profits from the system:
+- If intelligence is closed and centralized, the gains concentrate
+- If it is open, local, and participatory, the gains spread
+
+We choose the second.
+
+## What we're making at Jan
+Jan is building Open Superinteligence. It's one product that bundles models, tools, guardrails, and connectors in a way everybody can run on a laptop, a home server, or the web.
+
+- Open-source, so everyone can study, reproduce, improve
+- Building together, so progress compounds in public
+- AI for everyone, so it runs where people work, under their control
+
+### Co-operation > competition
+
+We believe progress is cooperative before it is competitive. People grow by working together. We've chosen [to stand on open shoulders](https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants). We use, contribute to, and fund open-source projects. When something exists and works, we don't reinvent the wheel to commercialize it - we upstream fixes, write docs, and make it easier to run.
+
+Open Superintelligence is how we make that true at the scale of intelligence.
+
+Happy to see you in this journey. Join our [community](https://discord.gg/Exe46xPMbK).
\ No newline at end of file
diff --git a/docs/src/pages/index.mdx b/docs/src/pages/index.mdx
index f830aaf039..ce0b3372b6 100644
--- a/docs/src/pages/index.mdx
+++ b/docs/src/pages/index.mdx
@@ -1,6 +1,6 @@
---
-title: "Jan: Open source ChatGPT-alternative that runs 100% offline"
-description: "Chat with AI without privacy concerns. Jan is an open-source ChatGPT-alternative, running AI models locally on your device."
+title: "Open source ChatGPT alternative that runs offline"
+description: "Jan is building Open Superintelligence. It’s the open-source ChatGPT alternative that leverages the best of open-source AI."
keywords:
[
Jan,
diff --git a/docs/src/styles/animations.css b/docs/src/styles/animations.css
new file mode 100644
index 0000000000..a1ca86de29
--- /dev/null
+++ b/docs/src/styles/animations.css
@@ -0,0 +1,232 @@
+/* Keyframe animations */
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes wave {
+ 0%, 100% {
+ transform: rotate(0deg);
+ }
+ 25% {
+ transform: rotate(-10deg);
+ }
+ 75% {
+ transform: rotate(10deg);
+ }
+}
+
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(8px);
+ }
+}
+
+@keyframes slideInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-50px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(50px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes scaleIn {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+/* Animation classes */
+.animate-fade-in-up {
+ animation: fadeInUp 0.8s ease-out forwards;
+}
+
+.animate-fade-in {
+ animation: fadeIn 0.6s ease-out forwards;
+}
+
+.animate-wave {
+ animation: wave 2s ease-in-out infinite;
+ transform-origin: 70% 70%;
+}
+
+.animate-float {
+ animation: float 3s ease-in-out infinite;
+}
+
+.animate-slide-in-left {
+ animation: slideInLeft 0.8s ease-out forwards;
+}
+
+.animate-slide-in-right {
+ animation: slideInRight 0.8s ease-out forwards;
+}
+
+.animate-scale-in {
+ animation: scaleIn 0.6s ease-out forwards;
+}
+
+/* Delay classes for staggered animations */
+.delay-100 {
+ animation-delay: 0.1s;
+}
+
+.delay-200 {
+ animation-delay: 0.2s;
+}
+
+.delay-300 {
+ animation-delay: 0.3s;
+}
+
+.delay-400 {
+ animation-delay: 0.4s;
+}
+
+.delay-500 {
+ animation-delay: 0.5s;
+}
+
+.delay-600 {
+ animation-delay: 0.6s;
+}
+
+/* Initial state for animated elements */
+.animate-fade-in-up,
+.animate-fade-in,
+.animate-slide-in-left,
+.animate-slide-in-right,
+.animate-scale-in {
+ opacity: 0;
+}
+
+/* Viewport-triggered animations */
+.animate-on-scroll {
+ opacity: 0;
+ transform: translateY(30px);
+ transition: opacity 0.8s ease-out, transform 0.8s ease-out;
+}
+
+.animate-on-scroll.animate-in-view {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.animate-on-scroll-left {
+ opacity: 0;
+ transform: translateX(-50px);
+ transition: opacity 0.8s ease-out, transform 0.8s ease-out;
+}
+
+.animate-on-scroll-left.animate-in-view {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.animate-on-scroll-right {
+ opacity: 0;
+ transform: translateX(50px);
+ transition: opacity 0.8s ease-out, transform 0.8s ease-out;
+}
+
+.animate-on-scroll-right.animate-in-view {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.animate-on-scroll-scale {
+ opacity: 0;
+ transform: scale(0.9);
+ transition: opacity 0.6s ease-out, transform 0.6s ease-out;
+}
+
+.animate-on-scroll-scale.animate-in-view {
+ opacity: 1;
+ transform: scale(1);
+}
+
+.animate-slide-up {
+ opacity: 0;
+ transform: translateY(100px);
+ transition: opacity 1s ease-out, transform 1s ease-out;
+}
+
+.animate-slide-up.animate-in-view {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.parallax-element {
+ will-change: transform;
+}
+
+/* Alternative: Simple CSS-based parallax using transform3d for better performance */
+@media (prefers-reduced-motion: no-preference) {
+ .parallax-slow {
+ transform: translateZ(0);
+ animation: parallaxFloat 20s ease-in-out infinite;
+ }
+}
+
+@keyframes parallaxFloat {
+ 0% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-30px);
+ }
+ 100% {
+ transform: translateY(0px);
+ }
+}
+
+/* Simple test animation */
+.test-animation {
+ animation: testPulse 2s ease-in-out infinite;
+}
+
+@keyframes testPulse {
+ 0%, 100% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.1);
+ }
+}
\ No newline at end of file
diff --git a/docs/src/styles/fonts.scss b/docs/src/styles/fonts.scss
index c7e7db47c0..4ea22523ef 100644
--- a/docs/src/styles/fonts.scss
+++ b/docs/src/styles/fonts.scss
@@ -44,3 +44,66 @@
font-weight: 300;
font-style: italic;
}
+
+/* Studio Feixen Sans - Ultralight */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Ultralight.otf')
+ format('opentype');
+ font-weight: 200;
+}
+
+/* Studio Feixen Sans - Light */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Light.otf')
+ format('opentype');
+ font-weight: 300;
+}
+
+/* Studio Feixen Sans - Book */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Book.otf')
+ format('opentype');
+ font-weight: 350;
+}
+
+/* Studio Feixen Sans - Regular */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Regular.otf')
+ format('opentype');
+ font-weight: 400;
+}
+
+/* Studio Feixen Sans - Medium */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Medium.otf')
+ format('opentype');
+ font-weight: 500;
+}
+
+/* Studio Feixen Sans - Semibold */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Semibold.otf')
+ format('opentype');
+ font-weight: 600;
+}
+
+/* Studio Feixen Sans - Bold */
+@font-face {
+ font-family: 'StudioFeixenSans';
+ font-display: swap;
+ src: url('../../public/assets/fonts/StudioFeixenSans-Bold.otf')
+ format('opentype');
+ font-weight: 700;
+}
diff --git a/docs/src/styles/main.scss b/docs/src/styles/main.scss
index 591b041029..8ac34ebb0f 100644
--- a/docs/src/styles/main.scss
+++ b/docs/src/styles/main.scss
@@ -6,3 +6,4 @@
@import './changelog.scss';
@import './fonts.scss';
@import './wall-of-love.scss';
+@import './animations.css';
diff --git a/docs/tailwind.config.ts b/docs/tailwind.config.ts
index 86f5f6c871..fcffa75fed 100644
--- a/docs/tailwind.config.ts
+++ b/docs/tailwind.config.ts
@@ -16,6 +16,17 @@ const config: Config = {
},
fontFamily: {
sans: [
+ 'StudioFeixenSans',
+ '-apple-system',
+ 'BlinkMacSystemFont',
+ 'Segoe UI',
+ 'Roboto',
+ 'Oxygen-Sans',
+ 'Ubuntu,Cantarell',
+ 'Helvetica',
+ 'sans-serif',
+ ],
+ inter: [
'Inter',
'-apple-system',
'BlinkMacSystemFont',
diff --git a/docs/theme.config.tsx b/docs/theme.config.tsx
index bad3a6beaa..8b71c4ccab 100644
--- a/docs/theme.config.tsx
+++ b/docs/theme.config.tsx
@@ -1,25 +1,26 @@
-import React, { Fragment } from "react";
-import { useConfig, DocsThemeConfig } from "nextra-theme-docs";
-import LogoMark from "@/components/LogoMark";
-import FooterMenu from "@/components/FooterMenu";
-import JSONLD from "@/components/JSONLD";
-import { useRouter } from "next/router";
-import Link from "next/link";
-import { LibraryBig, Blocks, BrainCircuit, Computer } from "lucide-react";
-import { AiOutlineGithub } from "react-icons/ai";
-import { BiLogoDiscordAlt } from "react-icons/bi";
-import { RiTwitterXFill } from "react-icons/ri";
+import React, { Fragment } from 'react'
+import { useConfig, DocsThemeConfig } from 'nextra-theme-docs'
+import LogoMark from '@/components/LogoMark'
+import FooterMenu from '@/components/FooterMenu'
+import JSONLD from '@/components/JSONLD'
+import { useRouter } from 'next/router'
+import Link from 'next/link'
+import { LibraryBig, Blocks, BrainCircuit, Computer } from 'lucide-react'
+import { AiOutlineGithub } from 'react-icons/ai'
+import { BiLogoDiscordAlt } from 'react-icons/bi'
+import { RiTwitterXFill } from 'react-icons/ri'
+import Navbar from '@/components/Navbar'
-const defaultUrl = "https://jan.ai";
-const defaultImage = "https://jan.ai/assets/images/general/og-image.png";
+const defaultUrl = 'https://jan.ai'
+const defaultImage = 'https://jan.ai/assets/images/general/og-image.png'
const structuredData = {
- "@context": "https://schema.org",
- "@type": "Organization",
- name: "Jan",
- url: `${defaultUrl}`,
- logo: `${defaultImage}`,
-};
+ '@context': 'https://schema.org',
+ '@type': 'Organization',
+ 'name': 'Jan',
+ 'url': `${defaultUrl}`,
+ 'logo': `${defaultImage}`,
+}
const config: DocsThemeConfig = {
logo: (
@@ -30,59 +31,47 @@ const config: DocsThemeConfig = {
),
- docsRepositoryBase: "https://github.com/menloresearch/jan/tree/dev/docs",
+ docsRepositoryBase: 'https://github.com/menloresearch/jan/tree/dev/docs',
feedback: {
- content: "Question? Give us feedback →",
- labels: "feedback",
+ content: 'Question? Give us feedback →',
+ labels: 'feedback',
},
editLink: {
- text: "Edit this page on GitHub →",
+ text: 'Edit this page on GitHub →',
},
useNextSeoProps() {
return {
- titleTemplate: "%s - Jan",
+ titleTemplate: '%s - Jan',
twitter: {
- cardType: "summary_large_image",
- site: "@jandotai",
+ cardType: 'summary_large_image',
+ site: '@jandotai',
},
openGraph: {
- type: "website",
+ type: 'website',
},
- };
+ }
},
navbar: {
- extraContent: (
-
{(() => {
const items = [
{
- title: "Jan Desktop & Mobile",
- path: "/docs/desktop",
+ title: 'Jan Desktop',
+ path: '/docs/desktop',
Icon: LibraryBig,
},
- { title: "Jan Server", path: "/docs/server", Icon: Computer },
- ];
+ { title: 'Jan Server', path: '/docs/server', Icon: Computer },
+ ]
return items.map((item) => {
- const active = asPath.startsWith(item.path);
+ const active = asPath.startsWith(item.path)
return active ? (
{item.title}
- );
- });
+ )
+ })
})()}
- );
+ )
}
- return title;
+ return title
},
defaultMenuCollapseLevel: 1,
toggleButton: true,
},
+ darkMode: false,
toc: {
backToTop: true,
},
head: function useHead() {
- const { title, frontMatter } = useConfig();
- const titleTemplate = (frontMatter?.title || title) + " - " + "Jan";
- const { asPath } = useRouter();
+ const { title, frontMatter } = useConfig()
+ const { asPath } = useRouter()
+ const titleTemplate =
+ (asPath.includes('/desktop')
+ ? 'Jan Desktop'
+ : asPath.includes('/server')
+ ? 'Jan Server'
+ : 'Jan') +
+ ' - ' +
+ (frontMatter?.title || title)
return (