import { useCallback, useEffect, useState } from 'react' import { BaseExtension, Compatibility, InstallationState, abortDownload, } from '@janhq/core' import { Button, Progress, Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger, } from '@janhq/uikit' import { InfoCircledIcon } from '@radix-ui/react-icons' import { useAtomValue } from 'jotai' import { Marked, Renderer } from 'marked' import { extensionManager } from '@/extension' import { installingExtensionAtom } from '@/helpers/atoms/Extension.atom' type Props = { item: BaseExtension } const ExtensionItem: React.FC = ({ item }) => { const [compatibility, setCompatibility] = useState( undefined ) const [installState, setInstallState] = useState('NotRequired') const installingExtensions = useAtomValue(installingExtensionAtom) const isInstalling = installingExtensions.some( (e) => e.extensionId === item.name ) const progress = isInstalling ? installingExtensions.find((e) => e.extensionId === item.name) ?.percentage ?? -1 : -1 useEffect(() => { const getExtensionInstallationState = async () => { const extension = extensionManager.getByName(item.name) if (!extension) return if (typeof extension?.installationState === 'function') { const installState = await extension.installationState() setInstallState(installState) } } getExtensionInstallationState() }, [item.name, isInstalling]) useEffect(() => { const extension = extensionManager.getByName(item.name) if (!extension) return setCompatibility(extension.compatibility()) }, [setCompatibility, item.name]) const onInstallClick = useCallback(async () => { const extension = extensionManager.getByName(item.name) if (!extension) return await extension.install() }, [item.name]) const onCancelInstallingClick = () => { const extension = installingExtensions.find( (e) => e.extensionId === item.name ) if (extension?.localPath) { abortDownload(extension.localPath) } } const description = marked.parse(item.description ?? '', { async: false }) return (
Additional Dependencies
{ // eslint-disable-next-line @typescript-eslint/naming-convention
}
) } type InstallStateProps = { installProgress: number compatibility?: Compatibility installState: InstallationState onInstallClick: () => void onCancelClick: () => void } const InstallStateIndicator: React.FC = ({ installProgress, compatibility, installState, onInstallClick, onCancelClick, }) => { if (installProgress !== -1) { const progress = installProgress * 100 return (
{progress.toFixed(0)}%
) } switch (installState) { case 'Installed': return (
Installed
) case 'NotCompatible': return (
Incompatible{' '} {compatibility && !compatibility['platform']?.includes(PLATFORM) ? ( Only available on{' '} {compatibility?.platform ?.map((e: string) => e === 'win32' ? 'Windows' : e === 'linux' ? 'Linux' : 'MacOS' ) .join(', ')} ) : ( Your GPUs are not compatible with this extension )}
) case 'NotInstalled': return ( ) default: return
} } const marked: Marked = new Marked({ renderer: { link: (href, title, text) => { return Renderer.prototype.link ?.apply(this, [href, title, text]) .replace('