✨enhancement: show app size on download dropdown (#5360)
* ✨enhancement: show app size on download dropdown
* Update docs/src/utils/format.ts
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* chor: update copy
---------
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
parent
8c507e5569
commit
85d32a4c70
@ -3,6 +3,7 @@ import { IconType } from 'react-icons/lib'
|
|||||||
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'
|
import { DownloadIcon } from 'lucide-react'
|
||||||
|
import { formatFileSize } from '@/utils/format'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lastRelease: any
|
lastRelease: any
|
||||||
@ -14,6 +15,7 @@ type SystemType = {
|
|||||||
logo: IconType
|
logo: IconType
|
||||||
fileFormat: string
|
fileFormat: string
|
||||||
href?: string
|
href?: string
|
||||||
|
size?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const systemsTemplate: SystemType[] = [
|
const systemsTemplate: SystemType[] = [
|
||||||
@ -84,9 +86,16 @@ export default function CardDownload({ lastRelease }: Props) {
|
|||||||
const downloadUrl = system.fileFormat
|
const downloadUrl = system.fileFormat
|
||||||
.replace('{appname}', appname)
|
.replace('{appname}', appname)
|
||||||
.replace('{tag}', tag)
|
.replace('{tag}', tag)
|
||||||
|
|
||||||
|
// Find the corresponding asset to get the file size
|
||||||
|
const asset = lastRelease.assets.find(
|
||||||
|
(asset: any) => asset.name === downloadUrl
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...system,
|
...system,
|
||||||
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
||||||
|
size: asset ? formatFileSize(asset.size) : undefined,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -118,6 +127,11 @@ export default function CardDownload({ lastRelease }: Props) {
|
|||||||
>
|
>
|
||||||
<span>{system.label}</span>
|
<span>{system.label}</span>
|
||||||
<DownloadIcon size={16} />
|
<DownloadIcon size={16} />
|
||||||
|
{system.size && (
|
||||||
|
<div className="text-sm text-black/60 dark:text-white/60">
|
||||||
|
{system.size}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { IconType } from 'react-icons/lib'
|
|||||||
import { IoChevronDownOutline } from 'react-icons/io5'
|
import { IoChevronDownOutline } from 'react-icons/io5'
|
||||||
import { useClickOutside } from '@/hooks/useClickOutside'
|
import { useClickOutside } from '@/hooks/useClickOutside'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
import { formatFileSize } from '@/utils/format'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lastRelease: any
|
lastRelease: any
|
||||||
@ -14,6 +15,7 @@ type SystemType = {
|
|||||||
logo: IconType
|
logo: IconType
|
||||||
fileFormat: string
|
fileFormat: string
|
||||||
href?: string
|
href?: string
|
||||||
|
size?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GpuInfo = {
|
type GpuInfo = {
|
||||||
@ -130,6 +132,7 @@ const DropdownDownload = ({ lastRelease }: Props) => {
|
|||||||
const updateDownloadLinks = async () => {
|
const updateDownloadLinks = async () => {
|
||||||
try {
|
try {
|
||||||
const firstAssetName = await lastRelease.assets[0]?.name
|
const firstAssetName = await lastRelease.assets[0]?.name
|
||||||
|
|
||||||
const appname = extractAppName(firstAssetName)
|
const appname = extractAppName(firstAssetName)
|
||||||
if (!appname) {
|
if (!appname) {
|
||||||
console.error(
|
console.error(
|
||||||
@ -147,9 +150,16 @@ const DropdownDownload = ({ lastRelease }: Props) => {
|
|||||||
const downloadUrl = system.fileFormat
|
const downloadUrl = system.fileFormat
|
||||||
.replace('{appname}', appname)
|
.replace('{appname}', appname)
|
||||||
.replace('{tag}', tag)
|
.replace('{tag}', tag)
|
||||||
|
|
||||||
|
// Find the corresponding asset to get the file size
|
||||||
|
const asset = lastRelease.assets.find(
|
||||||
|
(asset: any) => asset.name === downloadUrl
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...system,
|
...system,
|
||||||
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
||||||
|
size: asset ? formatFileSize(asset.size) : undefined,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setSystems(updatedSystems)
|
setSystems(updatedSystems)
|
||||||
@ -176,10 +186,15 @@ const DropdownDownload = ({ lastRelease }: Props) => {
|
|||||||
<div className="inline-flex flex-shrink-0 justify-center relative">
|
<div className="inline-flex flex-shrink-0 justify-center relative">
|
||||||
<a
|
<a
|
||||||
href={defaultSystem.href}
|
href={defaultSystem.href}
|
||||||
className="dark:border-r-0 dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white justify-center dark:border dark:border-neutral-800 flex-shrink-0 pl-4 pr-6 py-4 rounded-l-xl inline-flex items-center !rounded-r-none"
|
className="min-w-[300px] dark:border-r-0 dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white dark:border dark:border-neutral-800 flex-shrink-0 pl-4 pr-6 py-4 rounded-l-xl inline-flex items-center !rounded-r-none"
|
||||||
>
|
>
|
||||||
<defaultSystem.logo className="h-4 mr-2" />
|
<defaultSystem.logo className="h-4 mr-2" />
|
||||||
{defaultSystem.name}
|
<span>{defaultSystem.name}</span>
|
||||||
|
{defaultSystem.size && (
|
||||||
|
<span className="text-white/60 text-sm ml-2">
|
||||||
|
({defaultSystem.size})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
className="dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white justify-center dark:border border-l border-gray-500 dark:border-neutral-800 flex-shrink-0 p-4 px-3 rounded-r-xl"
|
className="dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white justify-center dark:border border-l border-gray-500 dark:border-neutral-800 flex-shrink-0 p-4 px-3 rounded-r-xl"
|
||||||
@ -192,18 +207,27 @@ const DropdownDownload = ({ lastRelease }: Props) => {
|
|||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div
|
||||||
className="absolute left-0 top-[64px] w-full dark:nx-bg-neutral-900 bg-black z-30 rounded-xl lg:w-[300px]"
|
className="absolute left-0 top-[64px] w-full dark:nx-bg-neutral-900 bg-black z-30 rounded-xl lg:w-[380px]"
|
||||||
ref={setRefDropdownContent}
|
ref={setRefDropdownContent}
|
||||||
>
|
>
|
||||||
{systems.map((system) => (
|
{systems.map((system) => (
|
||||||
<div key={system.name} className="py-1">
|
<div key={system.name} className="py-1">
|
||||||
<a
|
<a
|
||||||
href={system.href || ''}
|
href={system.href || ''}
|
||||||
className="flex px-4 py-3 items-center text-white hover:text-white hover:bg-white/10 dark:hover:bg-white/5"
|
className="flex px-4 py-3 items-center text-white hover:text-white hover:bg-white/10 dark:hover:bg-white/5 justify-between"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<system.logo className="w-3 mr-3 -mt-1 flex-shrink-0" />
|
<div className="flex items-center">
|
||||||
<span className="text-white font-medium">{system.name}</span>
|
<system.logo className="w-3 mr-3 -mt-1 flex-shrink-0" />
|
||||||
|
<span className="text-white font-medium flex-1">
|
||||||
|
{system.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{system.size && (
|
||||||
|
<span className="text-white/60 text-sm ml-2">
|
||||||
|
{system.size}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const features = [
|
|||||||
{
|
{
|
||||||
title: 'Chat with your files',
|
title: 'Chat with your files',
|
||||||
experimantal: true,
|
experimantal: true,
|
||||||
description: `Set up and run your own OpenAI-compatible API server using local models with just one click.`,
|
description: `Talk to PDFs, notes, and other documents directly to get summaries, answers, or insights.`,
|
||||||
image: {
|
image: {
|
||||||
light: '/assets/images/homepage/features05.png',
|
light: '/assets/images/homepage/features05.png',
|
||||||
dark: '/assets/images/homepage/features05dark.png',
|
dark: '/assets/images/homepage/features05dark.png',
|
||||||
|
|||||||
@ -1,8 +1,22 @@
|
|||||||
export function formatCompactNumber(count: number) {
|
export function formatCompactNumber(count: number) {
|
||||||
const formatter = Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 })
|
const formatter = Intl.NumberFormat('en', {
|
||||||
|
notation: 'compact',
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
})
|
||||||
return formatter.format(count)
|
return formatter.format(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatFileSize(bytes: number): string {
|
||||||
|
if (!bytes) return '0 B'
|
||||||
|
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||||
|
|
||||||
|
if (i === 0) return `${bytes} ${sizes[i]}`
|
||||||
|
|
||||||
|
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`
|
||||||
|
}
|
||||||
|
|
||||||
export const totalDownload = (release: []) => {
|
export const totalDownload = (release: []) => {
|
||||||
if (release instanceof Array) {
|
if (release instanceof Array) {
|
||||||
const count = release
|
const count = release
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user