feat: update icon alert warning and copies tooltip model dropdown (#3021)

* feat: update icon alert warning and copies tooltip model dropdown

* chore: update loader when user click download model dropdown
This commit is contained in:
Faisal Amir 2024-06-11 18:46:12 +07:00 committed by GitHub
parent aecc645a1a
commit cf8f401dab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 165 additions and 43 deletions

View File

@ -0,0 +1,50 @@
import React from 'react'
interface ProgressCircleProps {
percentage: number
size?: number
strokeWidth?: number
}
const ProgressCircle: React.FC<ProgressCircleProps> = ({
percentage,
size = 100,
strokeWidth = 14,
}) => {
const radius = (size - strokeWidth) / 2
const circumference = 2 * Math.PI * radius
const offset = circumference - (percentage / 100) * circumference
return (
<svg
className="ml-0.5 h-4 w-4 rotate-[-90deg] transform text-[hsla(var(--primary-bg))]"
height={size}
width={size}
xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 ${size} ${size}`}
>
<circle
className="opacity-25"
cx={size / 2}
cy={size / 2}
r={radius}
stroke="currentColor"
strokeWidth={strokeWidth}
fill="none"
></circle>
<circle
className="transition-stroke-dashoffset duration-300"
cx={size / 2}
cy={size / 2}
r={radius}
stroke="currentColor"
strokeWidth={strokeWidth}
fill="none"
strokeDasharray={circumference}
strokeDashoffset={offset}
></circle>
</svg>
)
}
export default ProgressCircle

View File

@ -8,16 +8,19 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { ChevronDownIcon, DownloadCloudIcon, XIcon } from 'lucide-react' import { ChevronDownIcon, DownloadCloudIcon, XIcon } from 'lucide-react'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import ProgressCircle from '@/containers/Loader/ProgressCircle'
import ModelLabel from '@/containers/ModelLabel' import ModelLabel from '@/containers/ModelLabel'
import SetupRemoteModel from '@/containers/SetupRemoteModel' import SetupRemoteModel from '@/containers/SetupRemoteModel'
import useDownloadModel from '@/hooks/useDownloadModel' import useDownloadModel from '@/hooks/useDownloadModel'
import { modelDownloadStateAtom } from '@/hooks/useDownloadState'
import useRecommendedModel from '@/hooks/useRecommendedModel' import useRecommendedModel from '@/hooks/useRecommendedModel'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { toGibibytes } from '@/utils/converter' import { formatDownloadPercentage, toGibibytes } from '@/utils/converter'
import { extensionManager } from '@/extension' import { extensionManager } from '@/extension'
@ -64,6 +67,7 @@ const ModelDropdown = ({
const [dropdownOptions, setDropdownOptions] = useState<HTMLDivElement | null>( const [dropdownOptions, setDropdownOptions] = useState<HTMLDivElement | null>(
null null
) )
const downloadStates = useAtomValue(modelDownloadStateAtom)
const setThreadModelParams = useSetAtom(setThreadModelParamsAtom) const setThreadModelParams = useSetAtom(setThreadModelParamsAtom)
const { updateModelParameter } = useUpdateModelParameters() const { updateModelParameter } = useUpdateModelParameters()
@ -351,12 +355,29 @@ const ModelDropdown = ({
<span className="font-medium"> <span className="font-medium">
{toGibibytes(model.metadata.size)} {toGibibytes(model.metadata.size)}
</span> </span>
{!isDownloading && ( {!isDownloading ? (
<DownloadCloudIcon <DownloadCloudIcon
size={18} size={18}
className="cursor-pointer text-[hsla(var(--app-link))]" className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)} onClick={() => downloadModel(model)}
/> />
) : (
Object.values(downloadStates)
.filter((x) => x.modelId === model.id)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(
item?.percent,
{
hidePercentage: true,
}
) as number
}
size={100}
/>
))
)} )}
</div> </div>
</li> </li>
@ -397,12 +418,29 @@ const ModelDropdown = ({
<span className="font-medium"> <span className="font-medium">
{toGibibytes(model.metadata.size)} {toGibibytes(model.metadata.size)}
</span> </span>
{!isDownloading && ( {!isDownloading ? (
<DownloadCloudIcon <DownloadCloudIcon
size={18} size={18}
className="cursor-pointer text-[hsla(var(--app-link))]" className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)} onClick={() => downloadModel(model)}
/> />
) : (
Object.values(downloadStates)
.filter((x) => x.modelId === model.id)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(
item?.percent,
{
hidePercentage: true,
}
) as number
}
size={100}
/>
))
)} )}
</div> </div>
</li> </li>

View File

@ -1,32 +1,49 @@
import { memo } from 'react' import { Fragment, memo } from 'react'
import { Badge, Tooltip } from '@janhq/joi' import { Badge, Tooltip } from '@janhq/joi'
import { InfoIcon } from 'lucide-react' import { AlertTriangleIcon, InfoIcon } from 'lucide-react'
import { twMerge } from 'tailwind-merge'
type Props = { type Props = {
compact?: boolean compact?: boolean
unit: string unit: string
} }
const tooltipContent = `Your device doesn't have enough RAM to run this model. Consider upgrading your RAM or using a device with more memory capacity.`
const NotEnoughMemoryLabel = ({ unit, compact }: Props) => ( const NotEnoughMemoryLabel = ({ unit, compact }: Props) => (
<Badge <>
theme="destructive" {compact ? (
variant="soft" <div className="flex h-5 w-5 items-center">
className={twMerge(compact && 'h-5 w-5 p-1')} <Tooltip
> trigger={
{!compact && <span className="line-clamp-1">Not enough {unit}</span>} <AlertTriangleIcon
<Tooltip size={14}
trigger={ className="cursor-pointer text-[hsla(var(--destructive-bg))]"
compact ? ( />
<div className="h-2 w-2 cursor-pointer rounded-full bg-[hsla(var(--destructive-bg))]" /> }
) : ( content={
<InfoIcon size={14} className="ml-2 flex-shrink-0 cursor-pointer" /> <Fragment>
) <b>Not enough RAM:</b> <span>{tooltipContent}</span>
} </Fragment>
content="This tag signals insufficient RAM for optimal model performance. It's dynamic and may change with your system's RAM availability." }
/> />
</Badge> </div>
) : (
<Badge theme="destructive" variant="soft">
<span className="line-clamp-1">Not enough {unit}</span>
<Tooltip
trigger={
<InfoIcon size={14} className="ml-2 flex-shrink-0 cursor-pointer" />
}
content={
<Fragment>
<b>Not enough RAM:</b> <span>{tooltipContent}</span>
</Fragment>
}
/>
</Badge>
)}
</>
) )
export default memo(NotEnoughMemoryLabel) export default memo(NotEnoughMemoryLabel)

View File

@ -1,32 +1,49 @@
import { memo } from 'react' import { Fragment, memo } from 'react'
import { Badge, Tooltip } from '@janhq/joi' import { Badge, Tooltip } from '@janhq/joi'
import { InfoIcon } from 'lucide-react' import { AlertTriangleIcon, InfoIcon } from 'lucide-react'
import { twMerge } from 'tailwind-merge'
type Props = { type Props = {
compact?: boolean compact?: boolean
} }
const tooltipContent = `Your device may be running low on available RAM, which can affect the speed of this model. Try closing any unnecessary applications to free up system memory.`
const SlowOnYourDeviceLabel = ({ compact }: Props) => ( const SlowOnYourDeviceLabel = ({ compact }: Props) => (
<Badge <>
theme="warning" {compact ? (
variant="soft" <div className="flex h-5 w-5 items-center">
className={twMerge(compact && 'h-5 w-5 p-1')} <Tooltip
> trigger={
{!compact && <span className="line-clamp-1">Slow on your device</span>} <AlertTriangleIcon
<Tooltip size={14}
trigger={ className="cursor-pointer text-[hsla(var(--warning-bg))]"
compact ? ( />
<div className="h-2 w-2 cursor-pointer rounded-full bg-[hsla(var(--warning-bg))] p-0" /> }
) : ( content={
<InfoIcon size={14} className="ml-2 flex-shrink-0 cursor-pointer" /> <Fragment>
) <b>Slow on your device:</b> <span>{tooltipContent}</span>
} </Fragment>
content="This tag indicates that your current RAM performance may affect model speed. It can change based on other active apps. To improve, consider closing unnecessary applications to free up RAM." }
/> />
</Badge> </div>
) : (
<Badge theme="warning" variant="soft">
<span className="line-clamp-1">Slow on your device</span>
<Tooltip
trigger={
<InfoIcon size={14} className="ml-2 flex-shrink-0 cursor-pointer" />
}
content={
<Fragment>
<b>Slow on your device:</b> <span>{tooltipContent}</span>
</Fragment>
}
/>
</Badge>
)}
</>
) )
export default memo(SlowOnYourDeviceLabel) export default memo(SlowOnYourDeviceLabel)