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:
Faisal Amir 2025-06-19 12:04:16 +07:00 committed by GitHub
parent 8c507e5569
commit 85d32a4c70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 8 deletions

View File

@ -3,6 +3,7 @@ import { IconType } from 'react-icons/lib'
import { FaWindows, FaApple, FaLinux } from 'react-icons/fa'
import { twMerge } from 'tailwind-merge'
import { DownloadIcon } from 'lucide-react'
import { formatFileSize } from '@/utils/format'
type Props = {
lastRelease: any
@ -14,6 +15,7 @@ type SystemType = {
logo: IconType
fileFormat: string
href?: string
size?: string
}
const systemsTemplate: SystemType[] = [
@ -84,9 +86,16 @@ export default function CardDownload({ lastRelease }: Props) {
const downloadUrl = system.fileFormat
.replace('{appname}', appname)
.replace('{tag}', tag)
// Find the corresponding asset to get the file size
const asset = lastRelease.assets.find(
(asset: any) => asset.name === downloadUrl
)
return {
...system,
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>
<DownloadIcon size={16} />
{system.size && (
<div className="text-sm text-black/60 dark:text-white/60">
{system.size}
</div>
)}
</a>
</div>
))}

View File

@ -4,6 +4,7 @@ import { IconType } from 'react-icons/lib'
import { IoChevronDownOutline } from 'react-icons/io5'
import { useClickOutside } from '@/hooks/useClickOutside'
import { twMerge } from 'tailwind-merge'
import { formatFileSize } from '@/utils/format'
type Props = {
lastRelease: any
@ -14,6 +15,7 @@ type SystemType = {
logo: IconType
fileFormat: string
href?: string
size?: string
}
type GpuInfo = {
@ -130,6 +132,7 @@ const DropdownDownload = ({ lastRelease }: Props) => {
const updateDownloadLinks = async () => {
try {
const firstAssetName = await lastRelease.assets[0]?.name
const appname = extractAppName(firstAssetName)
if (!appname) {
console.error(
@ -147,9 +150,16 @@ const DropdownDownload = ({ lastRelease }: Props) => {
const downloadUrl = system.fileFormat
.replace('{appname}', appname)
.replace('{tag}', tag)
// Find the corresponding asset to get the file size
const asset = lastRelease.assets.find(
(asset: any) => asset.name === downloadUrl
)
return {
...system,
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
size: asset ? formatFileSize(asset.size) : undefined,
}
})
setSystems(updatedSystems)
@ -176,10 +186,15 @@ const DropdownDownload = ({ lastRelease }: Props) => {
<div className="inline-flex flex-shrink-0 justify-center relative">
<a
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.name}
<span>{defaultSystem.name}</span>
{defaultSystem.size && (
<span className="text-white/60 text-sm ml-2">
({defaultSystem.size})
</span>
)}
</a>
<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"
@ -192,18 +207,27 @@ const DropdownDownload = ({ lastRelease }: Props) => {
</button>
{open && (
<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}
>
{systems.map((system) => (
<div key={system.name} className="py-1">
<a
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)}
>
<system.logo className="w-3 mr-3 -mt-1 flex-shrink-0" />
<span className="text-white font-medium">{system.name}</span>
<div className="flex items-center">
<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>
</div>
))}

View File

@ -44,7 +44,7 @@ const features = [
{
title: 'Chat with your files',
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: {
light: '/assets/images/homepage/features05.png',
dark: '/assets/images/homepage/features05dark.png',

View File

@ -1,8 +1,22 @@
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)
}
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: []) => {
if (release instanceof Array) {
const count = release