From 96f52e3ca27276c79f39b0554b6a236bfe13725b Mon Sep 17 00:00:00 2001 From: 0xSage Date: Mon, 13 Nov 2023 14:40:34 +0800 Subject: [PATCH 1/8] docs: add nitro docus --- docs/.gitignore | 20 + docs/README.md | 45 + docs/babel.config.js | 3 + docs/blog/2023-11-05-hello-world.md | 12 + docs/blog/authors.yml | 6 + docs/docs/api/overview.md | 3 + docs/docs/docs/overview.md | 6 + docs/docs/guides/overview.md | 6 + docs/docs/guides/troubleshooting.md | 3 + docs/docusaurus.config.js | 158 + docs/openapi/OpenAPISpec.json | 74 + docs/package.json | 57 + docs/sidebars.js | 52 + docs/src/components/Announcement/index.js | 78 + docs/src/components/Elements/dropdown.js | 169 + docs/src/components/Footer/index.js | 124 + docs/src/css/custom_toc.css | 5 + docs/src/hooks/useAppRelease.js | 31 + docs/src/hooks/useAppStars.js | 30 + docs/src/js/custom_toc.js | 9 + docs/src/pages/index.js | 144 + docs/src/styles/base.scss | 45 + docs/src/styles/components.scss | 18 + docs/src/styles/main.scss | 8 + docs/src/styles/tweaks.scss | 100 + docs/src/styles/typography.scss | 42 + docs/src/theme/Layout/Provider/index.js | 21 + docs/src/theme/Layout/index.js | 53 + docs/static/.nojekyll | 0 docs/static/img/2x4090-workstation.png | Bin 0 -> 1112001 bytes docs/static/img/apple-logo-white.png | Bin 0 -> 2153 bytes docs/static/img/bg-hero-dark.svg | 167 + docs/static/img/bg-hero-light.svg | 136 + .../img/desktop-explore-models-dark.png | Bin 0 -> 444737 bytes .../img/desktop-explore-models-light.png | Bin 0 -> 445084 bytes docs/static/img/desktop-explore-models.png | Bin 0 -> 132177 bytes docs/static/img/desktop-llm-chat-dark.png | Bin 0 -> 251517 bytes docs/static/img/desktop-llm-chat-light.png | Bin 0 -> 250096 bytes docs/static/img/desktop-llm-chat.png | Bin 0 -> 634077 bytes .../img/desktop-model-settings-dark.png | Bin 0 -> 190713 bytes .../img/desktop-model-settings-light.png | Bin 0 -> 197154 bytes docs/static/img/desktop-model-settings.png | Bin 0 -> 262954 bytes docs/static/img/favicon.ico | Bin 0 -> 15406 bytes docs/static/img/github-readme-banner.png | Bin 0 -> 187861 bytes docs/static/img/hcmc-launch-party.png | Bin 0 -> 2501629 bytes docs/static/img/jan-social-card.png | Bin 0 -> 193350 bytes docs/static/img/linux-logo-white.png | Bin 0 -> 12490 bytes docs/static/img/logo.png | Bin 0 -> 5146 bytes docs/static/img/logo.svg | 12 + docs/static/img/nvidia-llm-day-header.png | Bin 0 -> 915764 bytes docs/static/img/nvidia-llm-day.png | Bin 0 -> 1390926 bytes docs/static/img/windows-logo-white.png | Bin 0 -> 2426 bytes docs/tailwind.config.js | 29 + docs/yarn.lock | 11571 ++++++++++++++++ 54 files changed, 13237 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/README.md create mode 100644 docs/babel.config.js create mode 100644 docs/blog/2023-11-05-hello-world.md create mode 100644 docs/blog/authors.yml create mode 100644 docs/docs/api/overview.md create mode 100644 docs/docs/docs/overview.md create mode 100644 docs/docs/guides/overview.md create mode 100644 docs/docs/guides/troubleshooting.md create mode 100644 docs/docusaurus.config.js create mode 100644 docs/openapi/OpenAPISpec.json create mode 100644 docs/package.json create mode 100644 docs/sidebars.js create mode 100644 docs/src/components/Announcement/index.js create mode 100644 docs/src/components/Elements/dropdown.js create mode 100644 docs/src/components/Footer/index.js create mode 100644 docs/src/css/custom_toc.css create mode 100644 docs/src/hooks/useAppRelease.js create mode 100644 docs/src/hooks/useAppStars.js create mode 100644 docs/src/js/custom_toc.js create mode 100644 docs/src/pages/index.js create mode 100644 docs/src/styles/base.scss create mode 100644 docs/src/styles/components.scss create mode 100644 docs/src/styles/main.scss create mode 100644 docs/src/styles/tweaks.scss create mode 100644 docs/src/styles/typography.scss create mode 100644 docs/src/theme/Layout/Provider/index.js create mode 100644 docs/src/theme/Layout/index.js create mode 100644 docs/static/.nojekyll create mode 100644 docs/static/img/2x4090-workstation.png create mode 100644 docs/static/img/apple-logo-white.png create mode 100644 docs/static/img/bg-hero-dark.svg create mode 100644 docs/static/img/bg-hero-light.svg create mode 100644 docs/static/img/desktop-explore-models-dark.png create mode 100644 docs/static/img/desktop-explore-models-light.png create mode 100644 docs/static/img/desktop-explore-models.png create mode 100644 docs/static/img/desktop-llm-chat-dark.png create mode 100644 docs/static/img/desktop-llm-chat-light.png create mode 100644 docs/static/img/desktop-llm-chat.png create mode 100644 docs/static/img/desktop-model-settings-dark.png create mode 100644 docs/static/img/desktop-model-settings-light.png create mode 100644 docs/static/img/desktop-model-settings.png create mode 100644 docs/static/img/favicon.ico create mode 100644 docs/static/img/github-readme-banner.png create mode 100644 docs/static/img/hcmc-launch-party.png create mode 100644 docs/static/img/jan-social-card.png create mode 100644 docs/static/img/linux-logo-white.png create mode 100644 docs/static/img/logo.png create mode 100644 docs/static/img/logo.svg create mode 100644 docs/static/img/nvidia-llm-day-header.png create mode 100644 docs/static/img/nvidia-llm-day.png create mode 100644 docs/static/img/windows-logo-white.png create mode 100644 docs/tailwind.config.js create mode 100644 docs/yarn.lock diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..b2d6de306 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..f516942cb --- /dev/null +++ b/docs/README.md @@ -0,0 +1,45 @@ +# Website + +This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +Using SSH: + +``` +$ USE_SSH=true yarn deploy +``` + +Not using SSH: + +``` +$ GIT_USER= yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. + +### Additional Plugins +- @docusaurus/theme-live-codeblock +- [Redocusaurus](https://redocusaurus.vercel.app/): manually upload swagger files at `/openapi/OpenAPISpec.json` \ No newline at end of file diff --git a/docs/babel.config.js b/docs/babel.config.js new file mode 100644 index 000000000..e00595dae --- /dev/null +++ b/docs/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/docs/blog/2023-11-05-hello-world.md b/docs/blog/2023-11-05-hello-world.md new file mode 100644 index 000000000..ad080e320 --- /dev/null +++ b/docs/blog/2023-11-05-hello-world.md @@ -0,0 +1,12 @@ +--- +title: Hello World +description: This is my first post on Docusaurus. +slug: hello-world +authors: + - name: Daniel +tags: [hello, jan] +image: https://i.imgur.com/mErPwqL.png +hide_table_of_contents: false +--- + +Hello World! \ No newline at end of file diff --git a/docs/blog/authors.yml b/docs/blog/authors.yml new file mode 100644 index 000000000..f30d4610d --- /dev/null +++ b/docs/blog/authors.yml @@ -0,0 +1,6 @@ +dan-jan: + name: Daniel Onggunhao + title: Co-Founder + url: https://github.com/dan-jan + image_url: https://avatars.githubusercontent.com/u/101145494?v=4 + email: daniel@jan.ai \ No newline at end of file diff --git a/docs/docs/api/overview.md b/docs/docs/api/overview.md new file mode 100644 index 000000000..7137deb37 --- /dev/null +++ b/docs/docs/api/overview.md @@ -0,0 +1,3 @@ +--- +title: Overview +--- diff --git a/docs/docs/docs/overview.md b/docs/docs/docs/overview.md new file mode 100644 index 000000000..af7fc5e93 --- /dev/null +++ b/docs/docs/docs/overview.md @@ -0,0 +1,6 @@ +--- +title: Introduction +slug: /docs +--- + +Nitro diff --git a/docs/docs/guides/overview.md b/docs/docs/guides/overview.md new file mode 100644 index 000000000..8e7f03312 --- /dev/null +++ b/docs/docs/guides/overview.md @@ -0,0 +1,6 @@ +--- +title: Overview +slug: /guides +--- + +About Nitro diff --git a/docs/docs/guides/troubleshooting.md b/docs/docs/guides/troubleshooting.md new file mode 100644 index 000000000..f7010e49f --- /dev/null +++ b/docs/docs/guides/troubleshooting.md @@ -0,0 +1,3 @@ +--- +title: Troubleshooting Nitro +--- diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js new file mode 100644 index 000000000..3f5f244f7 --- /dev/null +++ b/docs/docusaurus.config.js @@ -0,0 +1,158 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require("prism-react-renderer/themes/github"); +const darkCodeTheme = require("prism-react-renderer/themes/dracula"); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: "Nitro", + tagline: "Run your own AI", + favicon: "img/favicon.ico", + + // Set the production url of your site here + url: "https://nitro.jan.ai", + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: "/", + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: "janhq", // Usually your GitHub org/user name. + projectName: "jan", // Usually your repo name. + + onBrokenLinks: "warn", + onBrokenMarkdownLinks: "warn", + + // Even if you don't use internalization, you can use this field to set useful + // metadata like html lang. For example, if your site is Chinese, you may want + // to replace "en" with "zh-Hans". + i18n: { + defaultLocale: "en", + locales: ["en"], + }, + + // Plugins we added + plugins: [ + "docusaurus-plugin-sass", + async function myPlugin(context, options) { + return { + name: "docusaurus-tailwindcss", + configurePostCss(postcssOptions) { + // Appends TailwindCSS and AutoPrefixer. + postcssOptions.plugins.push(require("tailwindcss")); + postcssOptions.plugins.push(require("autoprefixer")); + return postcssOptions; + }, + }; + }, + ], + + // Only for react live + themes: ["@docusaurus/theme-live-codeblock"], + + // The classic preset will relay each option entry to the respective sub plugin/theme. + presets: [ + [ + "classic", + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + // Will be passed to @docusaurus/plugin-content-docs (false to disable) + docs: { + routeBasePath: "/", + sidebarPath: require.resolve("./sidebars.js"), + editUrl: "https://github.com/janhq/jan/tree/main/docs", + showLastUpdateAuthor: true, + showLastUpdateTime: true, + }, + // Will be passed to @docusaurus/plugin-content-sitemap (false to disable) + sitemap: { + changefreq: "daily", + priority: 1.0, + ignorePatterns: ["/tags/**"], + filename: "sitemap.xml", + }, + // Will be passed to @docusaurus/plugin-content-blog (false to disable) + blog: { + blogSidebarTitle: "All Posts", + blogSidebarCount: "ALL", + }, + // Will be passed to @docusaurus/theme-classic. + theme: { + customCss: require.resolve("./src/styles/main.scss"), + }, + // Will be passed to @docusaurus/plugin-content-pages (false to disable) + // pages: {}, + }), + ], + // Redoc preset + [ + "redocusaurus", + { + specs: [ + { + spec: "openapi/OpenAPISpec.json", // can be local file, url, or parsed json object + route: "/api/", + }, + ], + theme: { + primaryColor: "#1a73e8", + primaryColorDark: "#1a73e8", + // redocOptions: { hideDownloadButton: false }, + }, + }, + ], + ], + + // Docs: https://docusaurus.io/docs/api/themes/configuration + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + image: "img/jan-social-card.png", + // Only for react live + liveCodeBlock: { + playgroundPosition: "bottom", + }, + navbar: { + title: "Nitro", + logo: { + alt: "Jan Logo", + src: "img/logo.svg", + }, + items: [ + // Navbar left + { + type: "docSidebar", + sidebarId: "guidesSidebar", + position: "left", + label: "User Guide", + }, + { + type: "docSidebar", + sidebarId: "docsSidebar", + position: "left", + label: "Documentation", + }, + { + type: "docSidebar", + sidebarId: "apiSidebar", + position: "left", + label: "API Reference", + }, + // Navbar right + ], + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + additionalLanguages: ["python"], + }, + colorMode: { + defaultMode: "dark", + disableSwitch: false, + respectPrefersColorScheme: false, + }, + }), +}; + +module.exports = config; diff --git a/docs/openapi/OpenAPISpec.json b/docs/openapi/OpenAPISpec.json new file mode 100644 index 000000000..9c18ec9d1 --- /dev/null +++ b/docs/openapi/OpenAPISpec.json @@ -0,0 +1,74 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Jan.ai api reference documentation.", + "title": "Rest Endpoints", + "version": "" + }, + "paths": { + "/api/rest/myquery": { + "get": { + "summary": "MyQuery", + "description": "***\nThe GraphQl query for this endpoint is:\n``` graphql\nquery MyQuery {\n collections {\n id\n name\n slug\n }\n}\n```", + "parameters": [ + { + "description": "Your x-hasura-admin-secret will be used for authentication of the API request.", + "in": "header", + "name": "x-hasura-admin-secret", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "collections": { + "items": { + "description": "columns and relationships of \"collections\"", + "nullable": false, + "properties": { + "id": { + "$ref": "#/components/schemas/uuid!" + }, + "name": { + "nullable": false, + "title": "String", + "type": "string" + }, + "slug": { + "nullable": false, + "title": "String", + "type": "string" + } + }, + "title": "collections", + "type": "object" + }, + "nullable": false, + "type": "array" + } + } + } + } + }, + "description": "Responses for GET /api/rest/myquery" + } + } + } + } + }, + "components": { + "schemas": { + "uuid!": { + "nullable": false, + "pattern": "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}", + "title": "uuid", + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..002b47321 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,57 @@ +{ + "name": "docs", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start --port 3001", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "^2.4.3", + "@docusaurus/preset-classic": "^2.4.3", + "@docusaurus/theme-live-codeblock": "^2.4.3", + "@docusaurus/theme-mermaid": "^3.0.0", + "@headlessui/react": "^1.7.17", + "@heroicons/react": "^2.0.18", + "@mdx-js/react": "^1.6.22", + "autoprefixer": "^10.4.16", + "axios": "^1.5.1", + "clsx": "^1.2.1", + "docusaurus-plugin-sass": "^0.2.5", + "js-yaml": "^4.1.0", + "postcss": "^8.4.30", + "prism-react-renderer": "^1.3.5", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-icons": "^4.11.0", + "redocusaurus": "^1.6.3", + "sass": "^1.69.3", + "tailwindcss": "^3.3.3" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "2.4.1", + "tailwindcss-animate": "^1.0.7" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "engines": { + "node": ">=16.14" + } +} diff --git a/docs/sidebars.js b/docs/sidebars.js new file mode 100644 index 000000000..668ca8bf6 --- /dev/null +++ b/docs/sidebars.js @@ -0,0 +1,52 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars are explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + guidesSidebar: [ + "guides/overview", + // { + // type: "category", + // label: "SomeLabel", + // collapsible: true, + // collapsed: false, + // items: [ + // { + // type: "autogenerated", + // dirName: "guides/folder", + // }, + // ], + // }, + "guides/troubleshooting", + ], + + docsSidebar: ["docs/overview"], + + apiSidebar: [ + "api/overview", + { + type: "category", + label: "Endpoints", + collapsible: true, + collapsed: false, + items: [ + { + type: "autogenerated", + dirName: "api", + }, + ], + }, + ], +}; + +module.exports = sidebars; diff --git a/docs/src/components/Announcement/index.js b/docs/src/components/Announcement/index.js new file mode 100644 index 000000000..922be62f7 --- /dev/null +++ b/docs/src/components/Announcement/index.js @@ -0,0 +1,78 @@ +import React from "react"; + +import { useAppStars } from "@site/src/hooks/useAppStars"; +import { useAppRelease } from "@site/src/hooks/useAppRelease"; + +import { AiOutlineGithub, AiOutlineTwitter } from "react-icons/ai"; +import { BiLogoDiscordAlt } from "react-icons/bi"; + +const socials = [ + { + icon: , + href: "https://twitter.com/janhq_", + }, + { + icon: , + href: "https://discord.com/invite/FTk2MvZwJH", + }, + { + icon: , + href: "https://github.com/janhq/jan", + }, +]; + +export default function AnnoncementBanner() { + const { stargazers } = useAppStars(); + const { release } = useAppRelease(); + + return ( + + ); +} diff --git a/docs/src/components/Elements/dropdown.js b/docs/src/components/Elements/dropdown.js new file mode 100644 index 000000000..443547132 --- /dev/null +++ b/docs/src/components/Elements/dropdown.js @@ -0,0 +1,169 @@ +import React, { useState, useEffect } from "react"; +import { Fragment } from "react"; +import { Menu, Transition } from "@headlessui/react"; +import { ChevronDownIcon } from "@heroicons/react/20/solid"; +import axios from "axios"; + +const systemsTemplate = [ + { + name: "Download for Mac (M1/M2)", + logo: require("@site/static/img/apple-logo-white.png").default, + fileFormat: "{appname}-mac-arm64-{tag}.dmg", + }, + { + name: "Download for Mac (Intel)", + logo: require("@site/static/img/apple-logo-white.png").default, + fileFormat: "{appname}-mac-x64-{tag}.dmg", + }, + { + name: "Download for Windows", + logo: require("@site/static/img/windows-logo-white.png").default, + fileFormat: "{appname}-win-x64-{tag}.exe", + }, + { + name: "Download for Linux", + logo: require("@site/static/img/linux-logo-white.png").default, + fileFormat: "{appname}-linux-amd64-{tag}.deb", + }, +]; + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +export default function Dropdown() { + const [systems, setSystems] = useState(systemsTemplate); + const [defaultSystem, setDefaultSystem] = useState(systems[0]); + + const getLatestReleaseInfo = async (repoOwner, repoName) => { + const url = `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`; + try { + const response = await axios.get(url); + return response.data; + } catch (error) { + console.error(error); + return null; + } + }; + + const extractAppName = (fileName) => { + // Extract appname using a regex that matches the provided file formats + const regex = /^(.*?)-(?:mac|win|linux)-(?:arm64|x64|amd64)-.*$/; + const match = fileName.match(regex); + return match ? match[1] : null; + }; + + const changeDefaultSystem = (systems) => { + const userAgent = navigator.userAgent; + if (userAgent.includes("Windows")) { + // windows user + setDefaultSystem(systems[2]); + } else if (userAgent.includes("Linux")) { + // linux user + setDefaultSystem(systems[3]); + } else if (userAgent.includes("Mac OS") && userAgent.includes("Intel")) { + // mac intel user + setDefaultSystem(systems[1]); + } else { + // mac user and also default + setDefaultSystem(systems[0]); + } + }; + useEffect(() => { + const updateDownloadLinks = async () => { + try { + const releaseInfo = await getLatestReleaseInfo("janhq", "jan"); + + // Extract appname from the first asset name + const firstAssetName = releaseInfo.assets[0].name; + const appname = extractAppName(firstAssetName); + + if (!appname) { + console.error( + "Failed to extract appname from file name:", + firstAssetName + ); + changeDefaultSystem(systems); + return; + } + + // Remove 'v' at the start of the tag_name + const tag = releaseInfo.tag_name.startsWith("v") + ? releaseInfo.tag_name.substring(1) + : releaseInfo.tag_name; + + const updatedSystems = systems.map((system) => { + const downloadUrl = system.fileFormat + .replace("{appname}", appname) + .replace("{tag}", tag); + return { + ...system, + href: `https://github.com/janhq/jan/releases/download/${releaseInfo.tag_name}/${downloadUrl}`, + }; + }); + + setSystems(updatedSystems); + changeDefaultSystem(updatedSystems); + } catch (error) { + console.error("Failed to update download links:", error); + } + }; + + updateDownloadLinks(); + }, []); + + return ( +
+ + Logo + {defaultSystem.name} + + + + Open OS options + + + +
+ {systems.map((system) => ( + setDefaultSystem(system)} + > + {({ active }) => ( + + Logo + {system.name} + + )} + + ))} +
+
+
+
+
+ ); +} diff --git a/docs/src/components/Footer/index.js b/docs/src/components/Footer/index.js new file mode 100644 index 000000000..d6bfca608 --- /dev/null +++ b/docs/src/components/Footer/index.js @@ -0,0 +1,124 @@ +import React from "react"; + +const menus = [ + { + name: "Resources", + child: [ + { + menu: "Home", + path: "/", + }, + { + menu: "Platform", + path: "/platform", + }, + { + menu: "Solutions", + path: "/solutions", + }, + ], + }, + { + name: "For Developers", + child: [ + { + menu: "Documentation (WIP)", + path: "/docs", + }, + { + menu: "Hardware (WIP)", + path: "/hardware", + }, + { + menu: "API (WIP)", + path: "/api", + }, + { + menu: "Changelog", + path: "https://github.com/janhq/jan/releases", + external: true, + }, + ], + }, + { + name: "Community", + child: [ + { + menu: "Github", + path: "https://github.com/janhq/jan", + external: true, + }, + { + menu: "Discord", + path: "https://discord.gg/FTk2MvZwJH", + external: true, + }, + { + menu: "Twitter", + path: "https://twitter.com/janhq_", + external: true, + }, + ], + }, + { + name: "Company", + child: [ + { + menu: "About", + path: "/about", + }, + { + menu: "Careers", + path: "https://janai.bamboohr.com/careers", + external: true, + }, + ], + }, +]; + +const getCurrentYear = new Date().getFullYear(); + +export default function Footer() { + return ( +
+
+
+
+
Jan
+

+ Run Large Language Models locally on Windows, Mac and Linux. + Available on Desktop and Cloud-Native. +

+
+ {menus.map((menu, i) => { + return ( +
+
{menu.name}
+ +
+ ); + })} +
+
+
+ + ©{getCurrentYear} Jan AI Pte Ltd. + +
+
+ ); +} diff --git a/docs/src/css/custom_toc.css b/docs/src/css/custom_toc.css new file mode 100644 index 000000000..caa5d1982 --- /dev/null +++ b/docs/src/css/custom_toc.css @@ -0,0 +1,5 @@ +.custom-toc-title { + font-weight: bold; + margin-bottom: 16px; + margin-top: -20px; + } \ No newline at end of file diff --git a/docs/src/hooks/useAppRelease.js b/docs/src/hooks/useAppRelease.js new file mode 100644 index 000000000..2a02ea00a --- /dev/null +++ b/docs/src/hooks/useAppRelease.js @@ -0,0 +1,31 @@ +import React, { useEffect, useState } from "react"; + +import axios from "axios"; + +import { isAxiosError } from "axios"; + +export const useAppRelease = () => { + const [release, setRelease] = useState({ + tagVersion: "", + }); + + useEffect(() => { + const updateStargazers = async () => { + try { + const { data } = await axios.get( + "https://api.github.com/repos/janhq/jan/releases/latest" + ); + setRelease({ + tagVersion: data.tag_name, + }); + } catch (error) { + if (isAxiosError(error)) { + console.error("Failed to get stargazers:", error); + } + } + }; + updateStargazers(); + }, []); + + return { release }; +}; diff --git a/docs/src/hooks/useAppStars.js b/docs/src/hooks/useAppStars.js new file mode 100644 index 000000000..671e78b1a --- /dev/null +++ b/docs/src/hooks/useAppStars.js @@ -0,0 +1,30 @@ +import React, { useEffect, useState } from "react"; + +import axios from "axios"; +import { isAxiosError } from "axios"; + +export const useAppStars = () => { + const [stargazers, setStargazers] = useState({ + count: 0, + }); + + useEffect(() => { + const updateStargazers = async () => { + try { + const { data } = await axios.get( + "https://api.github.com/repos/janhq/jan" + ); + setStargazers({ + count: data.stargazers_count, + }); + } catch (error) { + if (isAxiosError(error)) { + console.error("Failed to get stargazers:", error); + } + } + }; + updateStargazers(); + }, []); + + return { stargazers }; +}; diff --git a/docs/src/js/custom_toc.js b/docs/src/js/custom_toc.js new file mode 100644 index 000000000..7cda4e86a --- /dev/null +++ b/docs/src/js/custom_toc.js @@ -0,0 +1,9 @@ +document.addEventListener('DOMContentLoaded', function () { + const toc = document.querySelector('.table-of-contents'); + if (toc) { + const title = document.createElement('div'); + title.className = 'custom-toc-title'; + title.innerText = 'On this page'; + toc.insertBefore(title, toc.firstChild); + } +}); \ No newline at end of file diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js new file mode 100644 index 000000000..b4a994635 --- /dev/null +++ b/docs/src/pages/index.js @@ -0,0 +1,144 @@ +import React from "react"; +import Dropdown from "@site/src/components/Elements/dropdown"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; + +import useBaseUrl from "@docusaurus/useBaseUrl"; +import Layout from "@theme/Layout"; +import AnnoncementBanner from "@site/src/components/Announcement"; +import { + CloudArrowUpIcon, + CursorArrowRaysIcon, + ShieldCheckIcon, + CpuChipIcon, + ClipboardDocumentIcon, + CubeTransparentIcon, + ComputerDesktopIcon, + FolderPlusIcon, +} from "@heroicons/react/24/outline"; + +import ThemedImage from "@theme/ThemedImage"; + +const features = [ + { + name: "Personal AI that runs on your computer", + desc: "Jan runs directly on your local machine, offering privacy, convenience and customizability.", + icon: ComputerDesktopIcon, + }, + { + name: "Private and offline, your data never leaves your machine", + desc: "Your conversations and data are with an AI that runs on your computer, where only you have access.", + icon: ShieldCheckIcon, + }, + { + name: "No subscription fees, the AI runs on your computer", + desc: "Say goodbye to monthly subscriptions or usage-based APIs. Jan runs 100% free on your own hardware.", + icon: CubeTransparentIcon, + }, + { + name: "Extendable via App and Plugin framework", + desc: "Jan has a versatile app and plugin framework, allowing you to customize it to your needs.", + icon: FolderPlusIcon, + }, +]; + +export default function Home() { + const { siteConfig } = useDocusaurusContext(); + return ( + <> + + +
+
+ +
+
+
+ {/* TODO: Add upcoming events here */} + {/* */} + +

+ Nitro +

+

+ todo +

+ +
+ + +
+
+ +
+ {/*
*/} +
+ +
+
+
+
+
+ +
+

AI that you control

+

+ Nitro is a ... +

+
+ {features.map((feat, i) => { + return ( +
+
+ ); + })} +
+
+
+
+ + ); +} diff --git a/docs/src/styles/base.scss b/docs/src/styles/base.scss new file mode 100644 index 000000000..fb6388922 --- /dev/null +++ b/docs/src/styles/base.scss @@ -0,0 +1,45 @@ +@layer base { + html { + @apply scroll-smooth; + } + html[data-theme="light"] { + --ifm-background-color: white; + --ifm-color-primary: #2563eb; /* New Primary Blue */ + --ifm-color-primary-dark: #204fcf; /* Darker Blue */ + --ifm-color-primary-darker: #1b45b7; /* Even Darker Blue */ + --ifm-color-primary-darkest: #163c9d; /* Darkest Blue */ + --ifm-color-primary-light: #2974ff; /* Light Blue */ + --ifm-color-primary-lighter: #3280ff; /* Lighter Blue */ + --ifm-color-primary-lightest: #3a8bff; /* Lightest Blue */ + --ifm-code-font-size: 95%; + --ifm-navbar-link-hover-color: inherit; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + } + html[data-theme="dark"] { + --ifm-background-color: black; + --ifm-color-primary: #ffffff; /* New Primary Blue */ + --ifm-color-primary-dark: #204fcf; /* Darker Blue */ + --ifm-color-primary-darker: #1b45b7; /* Even Darker Blue */ + --ifm-color-primary-darkest: #163c9d; /* Darkest Blue */ + --ifm-color-primary-light: #2974ff; /* Light Blue */ + --ifm-color-primary-lighter: #3280ff; /* Lighter Blue */ + --ifm-color-primary-lightest: #3a8bff; /* Lightest Blue */ + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + } + body { + @apply text-sm; + @apply antialiased; + @apply bg-white dark:bg-black; + @apply text-gray-700 dark:text-gray-400; + } + img { + pointer-events: none; + } + + a { + &:hover { + color: inherit; + text-decoration: none; + } + } +} diff --git a/docs/src/styles/components.scss b/docs/src/styles/components.scss new file mode 100644 index 000000000..3583c258c --- /dev/null +++ b/docs/src/styles/components.scss @@ -0,0 +1,18 @@ +@layer components { + .el-blur-hero { + height: 200px; + background: linear-gradient( + 180deg, + hsl(296, 100%, 67%) 0%, + hsl(253, 100%, 57%) 100% + ); + z-index: 2; + border-bottom-left-radius: 100%; + border-bottom-right-radius: 100%; + border-top-left-radius: 100%; + border-top-right-radius: 100%; + filter: blur(100px); + -webkit-filter: blur(100px); + opacity: 0.5; + } +} diff --git a/docs/src/styles/main.scss b/docs/src/styles/main.scss new file mode 100644 index 000000000..ea6e4f723 --- /dev/null +++ b/docs/src/styles/main.scss @@ -0,0 +1,8 @@ +@import "tailwindcss/base"; +@import "tailwindcss/components"; +@import "tailwindcss/utilities"; + +@import "./typography.scss"; +@import "./tweaks.scss"; +@import "./base.scss"; +@import "./components.scss"; diff --git a/docs/src/styles/tweaks.scss b/docs/src/styles/tweaks.scss new file mode 100644 index 000000000..14ec842a9 --- /dev/null +++ b/docs/src/styles/tweaks.scss @@ -0,0 +1,100 @@ +.redocusaurus { + div[data-section-id] { + min-height: auto; + } +} + +[class*="docItemCol_"] { + @apply p-4 lg:px-16 lg:py-4; +} + +[class*="breadcrumbsContainer_"] { + margin-bottom: 40px !important; +} + +.theme-doc-footer { + margin-top: 40px; + margin-bottom: 20px; +} + +.menu.thin-scrollbar { + padding: 32px 10px; +} + +[class*="iconExternalLink_"] { + display: none; +} + +.navbar { + @apply px-0 lg:h-16 border-b border-gray-200 dark:border-gray-800 bg-white/50 backdrop-blur-lg dark:bg-black/50 shadow-none; + + .navbar__toggle { + width: 24px; + } + + .navbar-sidebar__backdrop { + height: 100dvh; + } + + .navbar-sidebar { + height: 100dvh; + .navbar-sidebar__close { + width: 14px; + } + } + + .navbar__title { + @apply text-lg; + } + .navbar__brand { + margin-right: 24px; + &:hover { + color: inherit; + } + } + .navbar__inner { + @apply container; + } +} + +.breadcrumbs__item { + position: relative; + &:first-child { + .breadcrumbs__link { + vertical-align: middle; + margin-top: -2px; + } + } +} + +[class*="docMainContainer_"], +[class*="docSidebarContainer_"] { + @apply bg-gray-50 dark:bg-gray-950/95; +} + +.navbar-sidebar { + @apply bg-gray-50 dark:bg-gray-900; +} + +.theme-doc-sidebar-container { + @apply border-gray-200 dark:border-gray-800; +} + +.theme-doc-markdown { + a { + @apply text-blue-600 dark:text-blue-400; + } + ul { + list-style: revert; + } + ol { + list-style: decimal; + } + ul, + ol { + padding-left: 16px; + li { + @apply leading-loose; + } + } +} diff --git a/docs/src/styles/typography.scss b/docs/src/styles/typography.scss new file mode 100644 index 000000000..42953ac2b --- /dev/null +++ b/docs/src/styles/typography.scss @@ -0,0 +1,42 @@ +h1, +.h1 { + line-height: 48px; + font-size: 40px; + @apply font-bold text-black dark:text-white; +} +h2, +.h2 { + font-size: 32px; + @apply font-bold text-black dark:text-white; + line-height: 40px; +} +h3, +.h3 { + font-size: 28px; + @apply font-bold text-black dark:text-white; + line-height: 40px; +} +h4, +.h4 { + font-size: 24px; + @apply font-bold text-black dark:text-white; + line-height: 32px; +} +h5, +.h5 { + font-size: 20px; + @apply font-bold text-black dark:text-white; + line-height: 28px; +} +h6, +.h6 { + font-size: 16px; + @apply font-bold text-black dark:text-white; + line-height: 24px; +} +p { + line-height: 24px; +} +.paragraph { + line-height: 24px; +} diff --git a/docs/src/theme/Layout/Provider/index.js b/docs/src/theme/Layout/Provider/index.js new file mode 100644 index 000000000..1940672e2 --- /dev/null +++ b/docs/src/theme/Layout/Provider/index.js @@ -0,0 +1,21 @@ +import React from "react"; +import { composeProviders } from "@docusaurus/theme-common"; +import { + ColorModeProvider, + AnnouncementBarProvider, + DocsPreferredVersionContextProvider, + ScrollControllerProvider, + NavbarProvider, + PluginHtmlClassNameProvider, +} from "@docusaurus/theme-common/internal"; +const Provider = composeProviders([ + ColorModeProvider, + AnnouncementBarProvider, + ScrollControllerProvider, + DocsPreferredVersionContextProvider, + PluginHtmlClassNameProvider, + NavbarProvider, +]); +export default function LayoutProvider({ children }) { + return {children}; +} diff --git a/docs/src/theme/Layout/index.js b/docs/src/theme/Layout/index.js new file mode 100644 index 000000000..22c4cc131 --- /dev/null +++ b/docs/src/theme/Layout/index.js @@ -0,0 +1,53 @@ +import React from "react"; +import clsx from "clsx"; +import ErrorBoundary from "@docusaurus/ErrorBoundary"; +import { + PageMetadata, + SkipToContentFallbackId, + ThemeClassNames, +} from "@docusaurus/theme-common"; +import { useKeyboardNavigation } from "@docusaurus/theme-common/internal"; +import SkipToContent from "@theme/SkipToContent"; +import AnnouncementBar from "@theme/AnnouncementBar"; +import Navbar from "@theme/Navbar"; +import Footer from "@site/src/components/Footer"; +import LayoutProvider from "@theme/Layout/Provider"; +import ErrorPageContent from "@theme/ErrorPageContent"; +import styles from "./styles.module.scss"; +export default function Layout(props) { + const { + children, + noFooter, + wrapperClassName, + // Not really layout-related, but kept for convenience/retro-compatibility + title, + description, + } = props; + useKeyboardNavigation(); + return ( + + + + + + + + + +
+ }> + {children} + +
+ + {!noFooter &&