Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins

This commit is contained in:
AkaraChen 2025-01-07 14:03:53 +08:00
commit b39acd0bad
4 changed files with 101 additions and 9 deletions

View File

@ -0,0 +1,44 @@
@keyframes realistic-blink {
0% { fill: #37ff37; opacity: 0.4; }
15% { fill: #37ff37; opacity: 0.9; }
25% { fill: #37ff37; opacity: 0.3; }
38% { fill: #ff4444; opacity: 0.8; }
42% { fill: #ff4444; opacity: 0.3; }
58% { fill: #37ff37; opacity: 0.9; }
65% { fill: #37ff37; opacity: 0.4; }
79% { fill: #ff4444; opacity: 0.8; }
84% { fill: #ff4444; opacity: 0.3; }
92% { fill: #37ff37; opacity: 0.8; }
100% { fill: #37ff37; opacity: 0.4; }
}
@keyframes drop {
0% {
transform: translateY(-4px);
opacity: 0;
}
5% {
transform: translateY(-4px);
opacity: 1;
}
65% {
transform: translateY(2px);
opacity: 1;
}
80% {
transform: translateY(2px);
opacity: 0;
}
100% {
transform: translateY(2px);
opacity: 0;
}
}
#downloadingIconLight {
animation: realistic-blink 3s infinite ease-in-out;
}
#downloadingIconArrow {
animation: drop 1.2s cubic-bezier(0.4, 0, 1, 1) infinite;
}

View File

@ -0,0 +1,17 @@
import s from './downloading-icon.module.css'
const DownloadingIcon = () => {
return (
<div className="inline-flex text-components-button-secondary-text">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="install-icon">
<g id="install-line">
<path d="M8 2V4H5L4.999 14H18.999L19 4H16V2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H8ZM18.999 16H4.999L5 20H19L18.999 16Z" fill="currentColor"/>
<path id={s.downloadingIconLight} d="M17 19V17H15V19H17Z"/>
<path id={s.downloadingIconArrow} d="M13 2V7H16L12 11L8 7H11V2H13Z" fill="currentColor"/>
</g>
</svg>
</div>
)
}
export default DownloadingIcon

View File

@ -5,6 +5,9 @@ import Link from 'next/link'
import classNames from '@/utils/classnames'
import { Group } from '@/app/components/base/icons/src/vender/other'
import { useSelectedLayoutSegment } from 'next/navigation'
import DownloadingIcon from './downloading-icon'
import { usePluginTaskStatus } from '@/app/components/plugins/plugin-page/plugin-tasks/hooks'
import Indicator from '@/app/components/header/indicator'
type PluginsNavProps = {
className?: string
@ -16,17 +19,43 @@ const PluginsNav = ({
const { t } = useTranslation()
const selectedSegment = useSelectedLayoutSegment()
const activated = selectedSegment === 'plugins'
const {
isInstalling,
isInstallingWithError,
isFailed,
} = usePluginTaskStatus()
return (
<Link href="/plugins" className={classNames(
className, 'group',
)}>
<div className={`flex flex-row h-8 p-1.5 gap-0.5 items-center justify-center
rounded-xl system-sm-medium-uppercase ${activated
? 'border border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text'
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'}`}>
<div
className={classNames(
'relative flex flex-row h-8 p-1.5 gap-0.5 border border-transparent items-center justify-center rounded-xl system-sm-medium-uppercase',
activated && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text',
!activated && 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
(isInstallingWithError || isFailed) && !activated && 'border-components-panel-border-subtle',
)}
>
{
(isFailed || isInstallingWithError) && !activated && (
<Indicator
color='red'
className='absolute top-[-1px] left-[-1px]'
/>
)
}
<div className='flex mr-0.5 w-5 h-5 justify-center items-center'>
<Group className='w-4 h-4' />
{
(!(isInstalling || isInstallingWithError) || activated) && (
<Group className='w-4 h-4' />
)
}
{
(isInstalling || isInstallingWithError) && !activated && (
<DownloadingIcon />
)
}
</div>
<span className='px-0.5'>{t('common.menus.plugins')}</span>
</div>

View File

@ -64,14 +64,16 @@ export const usePluginTaskStatus = () => {
const timerRef = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
if (isSuccess && opacity > 0) {
if (isSuccess) {
if (timerRef.current) {
clearTimeout(timerRef.current)
timerRef.current = null
}
timerRef.current = setTimeout(() => {
setOpacity(v => v - 0.1)
}, 200)
if (opacity > 0) {
timerRef.current = setTimeout(() => {
setOpacity(v => v - 0.1)
}, 200)
}
}
if (!isSuccess)