feat: download page (#2409)

feat: download page
This commit is contained in:
Henry 2024-03-19 12:55:10 +09:00 committed by GitHub
commit e61985c8fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 346 additions and 253 deletions

View File

@ -334,6 +334,11 @@ const config = {
position: 'left', position: 'left',
label: 'Ecosystem', label: 'Ecosystem',
}, },
{
to: 'download',
position: 'left',
label: 'Download',
},
// { // {
// type: "docSidebar", // type: "docSidebar",
// sidebarId: "pricingSidebar", // sidebarId: "pricingSidebar",

View File

@ -37,6 +37,7 @@
"postcss": "^8.4.30", "postcss": "^8.4.30",
"posthog-docusaurus": "^2.0.0", "posthog-docusaurus": "^2.0.0",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"lucide-react": "^0.291.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.47.0", "react-hook-form": "^7.47.0",

View File

@ -15,77 +15,76 @@
const sidebars = { const sidebars = {
aboutSidebar: [ aboutSidebar: [
{ {
type: "category", type: 'category',
label: "What is Jan?", label: 'What is Jan?',
link: { type: "doc", id: "about/about" }, link: { type: 'doc', id: 'about/about' },
items: [ items: [
//"about/roadmap", //"about/roadmap",
"community/community", 'community/community',
], ],
}, },
{ {
type: "category", type: 'category',
label: "Who we are", label: 'Who we are',
link: { type: "doc", id: "team/team" }, link: { type: 'doc', id: 'team/team' },
items: ["team/join-us", "team/contributor-program"], items: ['team/join-us', 'team/contributor-program'],
}, },
"wall-of-love", 'wall-of-love',
{ {
type: "category", type: 'category',
label: "How We Work", label: 'How We Work',
link: { type: "doc", id: "how-we-work" }, link: { type: 'doc', id: 'how-we-work' },
items: [ items: [
"how-we-work/strategy/strategy", 'how-we-work/strategy/strategy',
"how-we-work/project-management/project-management", 'how-we-work/project-management/project-management',
{ {
type: "category", type: 'category',
label: "Engineering", label: 'Engineering',
link: { type: "doc", id: "how-we-work/engineering/engineering" }, link: { type: 'doc', id: 'how-we-work/engineering/engineering' },
items: [ items: [
"how-we-work/engineering/ci-cd", 'how-we-work/engineering/ci-cd',
"how-we-work/engineering/qa", 'how-we-work/engineering/qa',
], ],
}, },
"how-we-work/product-design/product-design", 'how-we-work/product-design/product-design',
"how-we-work/analytics/analytics", 'how-we-work/analytics/analytics',
"how-we-work/website-docs/website-docs", 'how-we-work/website-docs/website-docs',
], ],
}, },
"acknowledgements", 'acknowledgements',
{ {
type: "category", type: 'category',
label: "FAQ", label: 'FAQ',
link: { type: "doc", id: "about/faq" }, link: { type: 'doc', id: 'about/faq' },
items: items: [],
[],
}, },
], ],
productSidebar: [ productSidebar: [
{ {
type: "category", type: 'category',
label: "Platforms", label: 'Platforms',
collapsible: false, collapsible: false,
items: [ items: [
"platforms/desktop", 'platforms/desktop',
"server-suite/home-server", 'server-suite/home-server',
// "server-suite/enterprise", // "server-suite/enterprise",
// "platforms/mobile", // "platforms/mobile",
// "platforms/hub", // "platforms/hub",
], ],
}, },
{ {
type: "category", type: 'category',
collapsible: true, collapsible: true,
collapsed: false, collapsed: false,
label: "Features", label: 'Features',
link: { type: "doc", id: "features/features" }, link: { type: 'doc', id: 'features/features' },
items: [ items: [
"features/local", 'features/local',
"features/remote", 'features/remote',
"features/api-server", 'features/api-server',
"features/extensions-framework", 'features/extensions-framework',
"features/agents-framework", 'features/agents-framework',
"features/data-security", 'features/data-security',
], ],
}, },
// NOTE: Jan Server Suite will be torn out into it's own section in the future // NOTE: Jan Server Suite will be torn out into it's own section in the future
@ -103,56 +102,56 @@ const sidebars = {
], ],
solutionSidebar: [ solutionSidebar: [
{ {
type: "category", type: 'category',
label: "Use Cases", label: 'Use Cases',
collapsed: true, collapsed: true,
collapsible: true, collapsible: true,
items: ["solutions/ai-pc", "solutions/chatgpt-alternative"], items: ['solutions/ai-pc', 'solutions/chatgpt-alternative'],
}, },
{ {
type: "category", type: 'category',
label: "Sectors", label: 'Sectors',
collapsed: true, collapsed: true,
collapsible: true, collapsible: true,
items: [ items: [
"solutions/finance", 'solutions/finance',
"solutions/healthcare", 'solutions/healthcare',
"solutions/legal", 'solutions/legal',
"solutions/government", 'solutions/government',
], ],
}, },
{ {
type: "category", type: 'category',
label: "Organization Type", label: 'Organization Type',
collapsed: true, collapsed: true,
collapsible: true, collapsible: true,
items: [ items: [
"solutions/developers", 'solutions/developers',
"solutions/consultants", 'solutions/consultants',
"solutions/startups", 'solutions/startups',
"solutions/enterprises", 'solutions/enterprises',
], ],
}, },
], ],
pricingSidebar: ["pricing/pricing"], pricingSidebar: ['pricing/pricing'],
ecosystemSidebar: [ ecosystemSidebar: [
"ecosystem/ecosystem", 'ecosystem/ecosystem',
{ {
type: "category", type: 'category',
label: "Partners", label: 'Partners',
link: { type: "doc", id: "partners/partners" }, link: { type: 'doc', id: 'partners/partners' },
collapsible: true, collapsible: true,
items: ["partners/become-a-partner"], items: ['partners/become-a-partner'],
}, },
{ {
type: "category", type: 'category',
label: "Integrations", label: 'Integrations',
link: { type: "doc", id: "integrations" }, link: { type: 'doc', id: 'integrations' },
items: [ items: [
{ {
type: "autogenerated", type: 'autogenerated',
dirName: "integrations", dirName: 'integrations',
}, },
], ],
}, },
@ -165,159 +164,154 @@ const sidebars = {
// ], // ],
guidesSidebar: [ guidesSidebar: [
{ {
type: "category", type: 'category',
label: "Get Started", label: 'Get Started',
collapsible: false, collapsible: false,
className: "head_Menu", className: 'head_Menu',
items: [ items: [
"guides/quickstart", 'guides/quickstart',
"guides/install", 'guides/install',
"guides/start-server", 'guides/start-server',
"guides/models-list" 'guides/models-list',
] ],
}, },
{ {
type: "category", type: 'category',
label: "Guides", label: 'Guides',
collapsible: false, collapsible: false,
className: "head_Menu", className: 'head_Menu',
items: [ items: ['guides/best-practices', 'guides/thread'],
"guides/best-practices",
"guides/thread",
]
}, },
{ {
type: "category", type: 'category',
label: "Advanced Features", label: 'Advanced Features',
collapsible: false, collapsible: false,
className: "head_Menu", className: 'head_Menu',
items: [ items: [
{ {
type: "category", type: 'category',
label: "Advanced Settings", label: 'Advanced Settings',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/advanced-settings/advanced-settings", id: 'guides/advanced-settings/advanced-settings',
}, },
items: [ items: ['guides/advanced-settings/http-proxy'],
"guides/advanced-settings/http-proxy",
]
}, },
{ {
type: "category", type: 'category',
label: "Advanced Model Setup", label: 'Advanced Model Setup',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/models/README", id: 'guides/models/README',
}, },
items: [ items: [
"guides/models/customize-engine", 'guides/models/customize-engine',
"guides/models/import-models", 'guides/models/import-models',
"guides/models/integrate-remote", 'guides/models/integrate-remote',
] ],
}, },
{ {
type: "category", type: 'category',
label: "Inference Providers", label: 'Inference Providers',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/providers/README", id: 'guides/providers/README',
}, },
items: [ items: [
"guides/providers/llama-cpp", 'guides/providers/llama-cpp',
"guides/providers/tensorrt-llm", 'guides/providers/tensorrt-llm',
] ],
}, },
{ {
type: "category", type: 'category',
label: "Extensions", label: 'Extensions',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/extensions/README", id: 'guides/extensions/README',
}, },
items: [ items: [
"guides/extensions/import-ext", 'guides/extensions/import-ext',
"guides/extensions/setup-ext", 'guides/extensions/setup-ext',
] ],
}, },
{ {
type: "category", type: 'category',
label: "Integrations", label: 'Integrations',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/integration/README", id: 'guides/integration/README',
}, },
items: [ items: [
"guides/integration/azure", 'guides/integration/azure',
"guides/integration/discord", 'guides/integration/discord',
"guides/integration/groq", 'guides/integration/groq',
"guides/integration/lmstudio", 'guides/integration/lmstudio',
"guides/integration/mistral", 'guides/integration/mistral',
"guides/integration/ollama", 'guides/integration/ollama',
"guides/integration/openinterpreter", 'guides/integration/openinterpreter',
"guides/integration/openrouter", 'guides/integration/openrouter',
"guides/integration/raycast", 'guides/integration/raycast',
"guides/integration/vscode", 'guides/integration/vscode',
] ],
}, },
] ],
}, },
{ {
type: "category", type: 'category',
label: "Troubleshooting", label: 'Troubleshooting',
collapsible: false, collapsible: false,
className: "head_Menu", className: 'head_Menu',
items: [ items: [
{ {
type: "category", type: 'category',
label: "Error Codes", label: 'Error Codes',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/error-codes/README", id: 'guides/error-codes/README',
}, },
items: [ items: [
"guides/error-codes/how-to-get-error-logs", 'guides/error-codes/how-to-get-error-logs',
"guides/error-codes/permission-denied", 'guides/error-codes/permission-denied',
"guides/error-codes/something-amiss", 'guides/error-codes/something-amiss',
"guides/error-codes/undefined-issue", 'guides/error-codes/undefined-issue',
"guides/error-codes/unexpected-token", 'guides/error-codes/unexpected-token',
] ],
}, },
{ {
type: "category", type: 'category',
label: "Common Error", label: 'Common Error',
className: "head_SubMenu", className: 'head_SubMenu',
link: { link: {
type: 'doc', type: 'doc',
id: "guides/common-error/README", id: 'guides/common-error/README',
}, },
items: [ items: [
"guides/common-error/broken-build", 'guides/common-error/broken-build',
"guides/common-error/not-using-gpu", 'guides/common-error/not-using-gpu',
] ],
}, },
"guides/faq" 'guides/faq',
] ],
}, },
], ],
developerSidebar: [ developerSidebar: [
{ {
type: "autogenerated", type: 'autogenerated',
dirName: "developer", dirName: 'developer',
}, },
], ],
releasesSidebar: [ releasesSidebar: [
{ {
type: "autogenerated", type: 'autogenerated',
dirName: "releases", dirName: 'releases',
}, },
] ],
}; }
module.exports = sidebars; module.exports = sidebars

View File

@ -1,135 +1,169 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from 'react'
import axios from "axios"; import axios from 'axios'
import { FaWindows, FaApple, FaLinux } from "react-icons/fa"; import { FaWindows, FaApple, FaLinux } from 'react-icons/fa'
import { twMerge } from "tailwind-merge"; import { twMerge } from 'tailwind-merge'
import { DownloadIcon } from 'lucide-react'
const systemsTemplate = [ const systemsTemplate = [
{ {
name: "Mac M1, M2, M3", name: 'Mac M1, M2, M3',
label: 'Apple Silicon',
logo: FaApple, logo: FaApple,
fileFormat: "{appname}-mac-arm64-{tag}.dmg", fileFormat: '{appname}-mac-arm64-{tag}.dmg',
comingSoon: false,
}, },
{ {
name: "Mac (Intel)", name: 'Mac (Intel)',
label: 'Apple Intel',
logo: FaApple, logo: FaApple,
fileFormat: "{appname}-mac-x64-{tag}.dmg", fileFormat: '{appname}-mac-x64-{tag}.dmg',
comingSoon: false,
}, },
{ {
name: "Windows", name: 'Windows',
label: 'Standard (64-bit)',
logo: FaWindows, logo: FaWindows,
fileFormat: "{appname}-win-x64-{tag}.exe", fileFormat: '{appname}-win-x64-{tag}.exe',
}, },
{ {
name: "Linux (AppImage)", name: 'Linux (AppImage)',
label: 'AppImage',
logo: FaLinux, logo: FaLinux,
fileFormat: "{appname}-linux-x86_64-{tag}.AppImage", fileFormat: '{appname}-linux-x86_64-{tag}.AppImage',
}, },
{ {
name: "Linux (deb)", name: 'Linux (deb)',
label: 'Deb',
logo: FaLinux, logo: FaLinux,
fileFormat: "{appname}-linux-amd64-{tag}.deb", fileFormat: '{appname}-linux-amd64-{tag}.deb',
}, },
]; ]
const groupTemnplate = [
{ label: 'MacOS', name: 'mac', logo: FaApple },
{ label: 'Windows', name: 'windows', logo: FaWindows },
{ label: 'Linux', name: 'linux', logo: FaLinux },
]
export default function DownloadApp() { export default function DownloadApp() {
const [systems, setSystems] = useState(systemsTemplate); const [systems, setSystems] = useState(systemsTemplate)
const getLatestReleaseInfo = async (repoOwner, repoName) => { const getLatestReleaseInfo = async (repoOwner, repoName) => {
const url = `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`; const url = `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`
try { try {
const response = await axios.get(url); const response = await axios.get(url)
return response.data; return response.data
} catch (error) { } catch (error) {
console.error(error); console.error(error)
return null; return null
}
} }
};
const extractAppName = (fileName) => { const extractAppName = (fileName) => {
// Extract appname using a regex that matches the provided file formats // Extract appname using a regex that matches the provided file formats
const regex = /^(.*?)-(?:mac|win|linux)-(?:arm64|x64|amd64|x86_64)-.*$/; const regex = /^(.*?)-(?:mac|win|linux)-(?:arm64|x64|amd64|x86_64)-.*$/
const match = fileName.match(regex); const match = fileName.match(regex)
return match ? match[1] : null; return match ? match[1] : null
}; }
useEffect(() => { useEffect(() => {
const updateDownloadLinks = async () => { const updateDownloadLinks = async () => {
try { try {
const releaseInfo = await getLatestReleaseInfo("janhq", "jan"); const releaseInfo = await getLatestReleaseInfo('janhq', 'jan')
// Extract appname from the first asset name // Extract appname from the first asset name
const firstAssetName = releaseInfo.assets[0].name; const firstAssetName = releaseInfo.assets[0].name
const appname = extractAppName(firstAssetName); const appname = extractAppName(firstAssetName)
if (!appname) { if (!appname) {
console.error( console.error(
"Failed to extract appname from file name:", 'Failed to extract appname from file name:',
firstAssetName firstAssetName
); )
return; return
} }
// Remove 'v' at the start of the tag_name // Remove 'v' at the start of the tag_name
const tag = releaseInfo.tag_name.startsWith("v") const tag = releaseInfo.tag_name.startsWith('v')
? releaseInfo.tag_name.substring(1) ? releaseInfo.tag_name.substring(1)
: releaseInfo.tag_name; : releaseInfo.tag_name
const updatedSystems = systems.map((system) => { const updatedSystems = systems.map((system) => {
const downloadUrl = system.fileFormat const downloadUrl = system.fileFormat
.replace("{appname}", appname) .replace('{appname}', appname)
.replace("{tag}", tag); .replace('{tag}', tag)
return { return {
...system, ...system,
href: `https://github.com/janhq/jan/releases/download/${releaseInfo.tag_name}/${downloadUrl}`, href: `https://github.com/janhq/jan/releases/download/${releaseInfo.tag_name}/${downloadUrl}`,
};
});
setSystems(updatedSystems);
} catch (error) {
console.error("Failed to update download links:", error);
} }
}; })
updateDownloadLinks(); setSystems(updatedSystems)
}, []); } catch (error) {
console.error('Failed to update download links:', error)
}
}
updateDownloadLinks()
}, [])
const renderDownloadLink = (group) => {
return (
<>
{systems
.filter((x) => x.name.toLowerCase().includes(group))
.map((system, i) => (
<div
key={i}
className="border-b border-[#F0F0F0] dark:border-gray-800 last:border-none pb-2 pt-2"
>
<a
href={system.href || ''}
className={twMerge(
'inline-flex text-lg my-2 font-semibold cursor-pointer justify-center items-center space-x-2] text-blue-500 hover:text-blue-500 gap-2',
system.comingSoon && 'pointer-events-none'
)}
>
<span className="text-sm">{system.label}</span>
<DownloadIcon size={16} />
</a>
</div>
))}
</>
)
}
return ( return (
<div> <div>
<div className="flex flex-col lg:flex-row items-center justify-center gap-4 mb-4"> <div className="flex flex-col items-center justify-center gap-4 mb-4">
<span className="text-zinc-500 text-lg font-medium inline-block"> <h6 className="text-2xl font-medium">Detailed platforms</h6>
Download for PC <p className="leading-relaxed text-black/60 dark:text-white/60">
</span>
<div className="bg-yellow-50 text-yellow-700 space-x-2 px-4 py-2 border border-yellow-400 rounded-lg text-base">
<span>🚧</span>
<span className="font-semibold">Warning:</span>
<span className="font-medium">
Jan is in the process of being built. Expect bugs! Jan is in the process of being built. Expect bugs!
</span> </p>
</div> </div>
</div> <div className="w-full lg:w-3/5 mx-auto px-4">
<div className="mx-auto text-center"> <div className="grid grid-cols-1 lg:grid-cols-3 py-10 gap-8">
{systems.map((system, i) => ( {groupTemnplate.map((item, i) => {
<a return (
<div
className="border border-[#F0F0F0] dark:border-gray-800 rounded-xl text-center"
key={i} key={i}
href={system.href || ""}
className={twMerge(
"btn-shadow inline-flex m-2 px-4 rounded-lg text-lg font-semibold cursor-pointer justify-center items-center space-x-2 border border-zinc-200 dark:border-gray-700 text-black dark:text-white bg-zinc-50 min-w-[150px] dark:bg-[#18181B] h-[36px]",
system.comingSoon && "pointer-events-none"
)}
> >
<system.logo /> <div className="text-center">
<span className="text-sm">{system.name}</span> <div className="flex gap-2 p-4 border-b border-[#F0F0F0] dark:border-gray-800 items-center justify-center">
{system.comingSoon && ( <div className="text-2xl">
<span className="bg-zinc-200 py-0.5 px-2 inline-block ml-2 rounded-md text-xs h-[20px] dark:text-black"> <item.logo />
Coming Soon </div>
</span> <h6>{item.label}</h6>
)} </div>
</a> <div className="mx-auto text-center py-2">
))} {renderDownloadLink(item.name)}
</div> </div>
</div> </div>
); </div>
)
})}
</div>
</div>
</div>
)
} }

View File

@ -36,6 +36,10 @@ const menus = [
{ {
name: 'Product', name: 'Product',
child: [ child: [
{
menu: 'Download',
path: '/download',
},
{ {
menu: 'Documentation', menu: 'Documentation',
path: '/developer', path: '/developer',

View File

@ -0,0 +1,62 @@
import React, { useState, useEffect } from 'react'
import DownloadApp from '@site/src/containers/DownloadApp'
import ThemedImage from '@theme/ThemedImage'
import Layout from '@theme/Layout'
import Banner from '@site/src/containers/Banner'
import useBaseUrl from '@docusaurus/useBaseUrl'
export default function Download() {
return (
<>
<Banner />
<Layout
title="Download"
description="Jan turns your computer into an AI machine by running LLMs locally on your computer. It's a privacy-focus, local-first, open-source solution."
>
<main>
{/* Hero */}
<div className="text-center px-4 py-20">
<h1 className="text-6xl lg:text-7xl !font-normal leading-tight lg:leading-tight mt-2 font-serif">
Download Jan for your desktop
</h1>
<p className="text-2xl -mt-1 leading-relaxed text-black/60 dark:text-white/60">
Turn your computer into an AI machine
</p>
<div className="my-16">
<ThemedImage
className="w-28 mx-auto h-auto"
alt="App screenshots"
sources={{
light: useBaseUrl('/img/homepage/mac-system-black.svg'),
dark: useBaseUrl('/img/homepage/mac-system-white.svg'),
}}
/>
</div>
<div className="mb-14">
<a
href="https://jan.ai/guides/install/"
target="_blank"
className="text-blue-500 hover:text-blue-500 pr-4 border-r border-black/40 dark:border-white/40 mr-4 inline-block"
>
Installation Guide
</a>
<a
href="https://jan.ai/changelog/"
target="_blank"
className="text-blue-500 hover:text-blue-500"
>
Changelog
</a>
</div>
<DownloadApp />
</div>
</main>
</Layout>
</>
)
}

View File

@ -87,12 +87,6 @@ export default function Home() {
const { stargazers } = useAppStars() const { stargazers } = useAppStars()
const { data } = useDiscordWidget() const { data } = useDiscordWidget()
const handleAnchorLink = () => {
document
.getElementById('download-section')
.scrollIntoView({ behavior: 'smooth' })
}
const userAgent = isBrowser && navigator.userAgent const userAgent = isBrowser && navigator.userAgent
const isBrowserChrome = isBrowser && userAgent.includes('Chrome') const isBrowserChrome = isBrowser && userAgent.includes('Chrome')
@ -167,12 +161,12 @@ export default function Home() {
</p> </p>
<div className="mt-10"> <div className="mt-10">
{!isBrowserChrome ? ( {!isBrowserChrome ? (
<div <a
onClick={() => handleAnchorLink()} href="/download"
className="inline-flex px-4 py-3 rounded-lg text-lg font-semibold cursor-pointer justify-center items-center space-x-2 dark:bg-white dark:text-black bg-black text-white dark:hover:text-black hover:text-white scroll-smooth" className="inline-flex px-4 py-3 rounded-lg text-lg font-semibold cursor-pointer justify-center items-center space-x-2 dark:bg-white dark:text-black bg-black text-white dark:hover:text-black hover:text-white scroll-smooth"
> >
<span>Download Jan for PC</span> <span>Download Jan for PC</span>
</div> </a>
) : ( ) : (
<Dropdown /> <Dropdown />
)} )}
@ -811,12 +805,12 @@ export default function Home() {
</div> </div>
<div className="mt-10 w-full lg:w-1/2 mx-auto lg:mr-auto lg:text-right"> <div className="mt-10 w-full lg:w-1/2 mx-auto lg:mr-auto lg:text-right">
{!isBrowserChrome ? ( {!isBrowserChrome ? (
<div <a
onClick={() => handleAnchorLink()} href="/download"
className="inline-flex px-4 py-3 rounded-lg text-lg font-semibold cursor-pointer justify-center items-center space-x-2 dark:bg-white dark:text-black bg-black text-white dark:hover:text-black hover:text-white scroll-smooth" className="inline-flex px-4 py-3 rounded-lg text-lg font-semibold cursor-pointer justify-center items-center space-x-2 dark:bg-white dark:text-black bg-black text-white dark:hover:text-black hover:text-white scroll-smooth"
> >
<span>Download Jan for PC</span> <span>Download Jan for PC</span>
</div> </a>
) : ( ) : (
<Dropdown /> <Dropdown />
)} )}
@ -826,12 +820,6 @@ export default function Home() {
</div> </div>
</div> </div>
</div> </div>
{!isBrowserChrome && (
<div className="my-10" id="download-section">
<DownloadApp />
</div>
)}
</main> </main>
</Layout> </Layout>
</> </>

View File

@ -7097,6 +7097,11 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484"
integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==
lucide-react@^0.291.0:
version "0.291.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.291.0.tgz#d6db8f5117a68c8bbc6ed51d800879fa6a471978"
integrity sha512-79jHlT9Je2PXSvXIBGDkItCK7In2O9iKnnSJ/bJxvIBOFaX2Ex0xEcC4fRS/g0F2uQGFejjmn2qWhwdc5wicMQ==
lunr@^2.3.9: lunr@^2.3.9:
version "2.3.9" version "2.3.9"
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"