Finalize responsive new web

This commit is contained in:
Faisal Amir 2023-11-16 11:46:39 +07:00
parent 0501787a08
commit 5f20a34e39
17 changed files with 278 additions and 59 deletions

View File

@ -0,0 +1,127 @@
import React, { useState, useEffect } from "react";
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 DownloadLink() {
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 = async (systems) => {
const userAgent = navigator.userAgent;
const arc = await navigator?.userAgentData?.getHighEntropyValues([
"architecture",
]);
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") &&
arc &&
arc.architecture === "arm"
) {
setDefaultSystem(systems[0]);
} else {
setDefaultSystem(systems[1]);
}
};
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 (
<div className="mt-2">
<a href={defaultSystem.href}>
<span className="text-blue-600 font-bold">Download Jan</span>
</a>
</div>
);
}

View File

@ -76,6 +76,7 @@ export default function Dropdown() {
setDefaultSystem(systems[1]);
}
};
useEffect(() => {
const updateDownloadLinks = async () => {
try {

View File

@ -5,38 +5,12 @@ 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 { AiOutlineGithub, AiOutlineTwitter } from "react-icons/ai";
import { AiOutlineGithub } from "react-icons/ai";
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.",
},
{
name: "Extendable via App and Plugin framework",
desc: "Jan has a versatile app and plugin framework, allowing you to customize it to your needs.",
},
{
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.",
},
{
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.",
},
];
import DownloadLink from "@site/src/components/Elements/downloadLink";
export default function Home() {
const { siteConfig } = useDocusaurusContext();
@ -45,19 +19,18 @@ export default function Home() {
<AnnoncementBanner />
<Layout
title={`${siteConfig.tagline}`}
description="Jan runs Large Language Models locally on Windows, Mac and Linux.
Available on Desktop and Cloud-Native."
description="Jan runs Large Language Models locally on Windows, Mac and Linux. Available on Desktop and Cloud-Native."
>
<main className="bg-gray-50 dark:bg-gray-950/95 relative">
<div className="relative">
{/* <ThemedImage
<ThemedImage
alt="App screenshot"
sources={{
light: useBaseUrl("/img/bg-hero-light.svg"),
dark: useBaseUrl("/img/bg-hero-dark.svg"),
}}
className="absolute w-full h-full opacity-10 dark:opacity-20 top-0 object-cover blur-3xl"
/> */}
/>
<div className="container py-16">
<div className="grid grid-cols-1 items-center gap-4">
<div className="relative z-10 text-center ">
@ -72,7 +45,6 @@ export default function Home() {
</p>
</a>
</div> */}
<h1 className="bg-gradient-to-r dark:from-white from-black to-gray-500 dark:to-gray-400 bg-clip-text text-4xl lg:text-6xl font-bold leading-tight text-transparent dark:text-transparent lg:leading-tight">
Own your AI
</h1>
@ -108,7 +80,6 @@ export default function Home() {
</div>
<div className="text-center relative ">
{/* <div className="el-blur-hero absolute -left-40 w-full top-1/2 -translate-y-1/2" /> */}
<div className="p-3 border dark:border-gray-500 border-gray-400 inline-block rounded-lg">
<ThemedImage
alt="App screenshot"
@ -124,36 +95,142 @@ export default function Home() {
</div>
</div>
</div>
{/* <div className="container mt-10 mb-20 text-center">
<div className="container mt-10 mb-20 text-center">
<h2>AI that you control</h2>
<p className="text-base mt-2 w-full lg:w-2/5 mx-auto leading-relaxed">
Private. Local. Infinitely Customizable.
</p>
<div className="grid text-left lg:grid-cols-2 mt-16 gap-16">
{features.map((feat, i) => {
return (
<div
className="flex gap-x-4 p-8 rounded-3xl border bg-gray-100 border-gray-100 dark:border-[#202231] dark:bg-[#111217]"
key={i}
>
<div>
<h5>{feat.name}</h5>
<p className="mt-2">{feat.desc}</p>
<div className="grid text-left lg:grid-cols-2 mt-16 gap-4">
<div className="card relative min-h-[380px] lg:min-h-[460px]">
<img
src="/img/card-element.png"
alt="Element"
className="absolute w-full bottom-0 left-0"
/>
<div class="p-8 relative z-40">
<h5>Personal AI that runs on your computer</h5>
<p className="mt-2">
Jan runs directly on your local machine, offering privacy,
convenience and customizability.
</p>
<ThemedImage
alt="Group Chat"
sources={{
light: useBaseUrl("/img/group-chat-light.png"),
dark: useBaseUrl("/img/group-chat-dark.png"),
}}
className="mt-10"
/>
</div>
</div>
);
})}
<div className="card relative min-h-[380px] lg:min-h-[460px]">
<div className="p-8">
<h5>Extendable via App and Plugin framework</h5>
<p className="mt-2">
Jan has a versatile app and plugin framework, allowing you
to customize it to your needs.
</p>
</div>
</div> */}
<div class="container">
<div class="flex">
<ThemedImage
alt="Framework"
sources={{
light: useBaseUrl("/img/card-framework-light.png"),
dark: useBaseUrl("/img/card-framework-dark.png"),
}}
className="w-11/12 ml-auto mt-auto"
/>
</div>
<div className="card relative min-h-[380px] lg:min-h-[460px]">
<div className="p-8">
<h5>
Private and offline, your data never leaves your machine
</h5>
<p className="mt-2">
Your conversations and data are with an AI that runs on your
computer, where only you have access.
</p>
</div>
<ThemedImage
alt="Group Chat"
sources={{
light: useBaseUrl("/img/card-nitro-light.png"),
dark: useBaseUrl("/img/card-nitro-dark.png"),
}}
className="w-3/4 mx-auto mt-auto"
/>
</div>
<div className="card relative min-h-[380px] lg:min-h-[460px]">
<div className="p-8">
<h5>No subscription fees, the AI runs on your computer</h5>
<p className="mt-2">
Say goodbye to monthly subscriptions or usage-based APIs.
Jan runs 100% free on your own hardware.
</p>
</div>
<ThemedImage
alt="Group Chat"
sources={{
light: useBaseUrl("/img/card-free-light.png"),
dark: useBaseUrl("/img/card-free-dark.png"),
}}
className="w-full mt-auto mx-auto"
/>
</div>
</div>
</div>
<div class="container lg:px-20 py-20 text-center lg:text-left">
<div class="flex flex-col lg:flex-row space-y-20 lg:space-y-0">
<div>
<h1 className="text-7xl">Your AI, forever.</h1>
<p>Apps come and go, but your AI and data should last. </p>
<p className="text-2xl mt-2">
Apps come and go, but your AI and data should last.{" "}
</p>
<div class="w-full lg:w-3/4 mt-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-10 lg:gap-24">
<div>
<img
src="/img/ic-park-solid-unlock.svg"
alt="Icon - Lock"
className="w-8 mb-4 mx-auto lg:mx-0"
/>
<p>
Jan uses open, standard and non-proprietary files stored
locally on your device.
</p>
</div>
<div>
<img
src="img/ic-baseline-control-camera.svg"
alt="Icon - Camera"
className="w-8 mb-4 mx-auto lg:mx-0"
/>
<p>
You have total control over your AI, which means you can
use Jan offline and switch to another app easily if you
want.
</p>
</div>
</div>
</div>
<div class="container py-20 text-center">
</div>
<div className="w-full lg:w-80 text-center">
<ThemedImage
alt="App screenshot"
sources={{
light: useBaseUrl("/img/jan-icon-light.png"),
dark: useBaseUrl("/img/jan-icon-dark.png"),
}}
className="w-40 lg:w-full mx-auto"
/>
<p className="mt-1 font-bold">100% free on your own hardware</p>
<DownloadLink />
</div>
</div>
</div>
<div class="container pb-20 pt-10 text-center">
<h2>
We are open-source. <br /> Join Jan community.
</h2>

View File

@ -14,7 +14,7 @@
0px 1px 2px 0px #525154 inset;
}
.card {
@apply p-8 rounded-3xl border bg-gray-100 border-gray-100 dark:border-[#202231] dark:bg-[#111217];
@apply rounded-3xl border bg-gray-100 border-gray-100 dark:border-[#202231] dark:bg-[#111217];
&-link {
display: inline-flex;

BIN
docs/static/img/card-element.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/static/img/card-framework-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/static/img/card-framework-light.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
docs/static/img/card-free-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
docs/static/img/card-free-light.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
docs/static/img/card-nitro-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
docs/static/img/card-nitro-light.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/static/img/group-chat-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
docs/static/img/group-chat-light.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -0,0 +1,4 @@
<svg width="36" height="37" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.31 8.41986L20.655 11.0599L18 8.41986L15.345 11.0599L12.69 8.41986L18 3.10986L23.31 8.41986ZM27.69 23.4199L25.05 20.7649L27.69 18.1099L25.05 15.4549L27.69 12.7999L33 18.1099L27.69 23.4199ZM12.69 27.7999L15.345 25.1599L18 27.7999L20.655 25.1599L23.31 27.7999L18 33.1099L12.69 27.7999ZM8.31 12.7999L10.95 15.4549L8.31 18.1099L10.95 20.7649L8.31 23.4199L3 18.1099L8.31 12.7999Z" fill="#3B82F6"/>
<path d="M18 22.6099C20.4853 22.6099 22.5 20.5951 22.5 18.1099C22.5 15.6246 20.4853 13.6099 18 13.6099C15.5147 13.6099 13.5 15.6246 13.5 18.1099C13.5 20.5951 15.5147 22.6099 18 22.6099Z" fill="#3B82F6"/>
</svg>

After

Width:  |  Height:  |  Size: 712 B

View File

@ -0,0 +1,10 @@
<svg width="36" height="37" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_206_5233" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="3" y="1" width="30" height="34">
<path d="M29.25 16.6458H6.75C5.92157 16.6458 5.25 17.3173 5.25 18.1458V31.6458C5.25 32.4742 5.92157 33.1458 6.75 33.1458H29.25C30.0784 33.1458 30.75 32.4742 30.75 31.6458V18.1458C30.75 17.3173 30.0784 16.6458 29.25 16.6458Z" fill="white" stroke="white" stroke-width="3" stroke-linejoin="round"/>
<path d="M10.5 16.6097V10.6135C10.4963 6.76224 13.4423 3.53499 17.3145 3.14799C21.1868 2.76099 24.7253 5.34024 25.5 9.11424" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 22.6099V27.1099" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</mask>
<g mask="url(#mask0_206_5233)">
<path d="M0 0.109863H36V36.1099H0V0.109863Z" fill="#3B82F6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 940 B

BIN
docs/static/img/jan-icon-dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/static/img/jan-icon-light.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB