mirror of
https://github.com/langgenius/dify.git
synced 2026-11-12 06:11:12 +08:00
feat(file-scan): show each scanner vendor's own logo
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d88eac2fe
commit
1879bcf975
8 files changed
+74
-15
No files matched your search
@@ -0,0 +1,26 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vite-plus/test'
|
||||
import { ProviderTypeIcon } from '../provider-type-icon'
|
||||
|
||||
describe('ProviderTypeIcon', () => {
|
||||
it.each([
|
||||
['clamav', '/images/file-scan/clamav.png'],
|
||||
['cloudmersive', '/images/file-scan/cloudmersive.png'],
|
||||
['scanii', '/images/file-scan/scanii.svg'],
|
||||
['metadefender', '/images/file-scan/metadefender.png'],
|
||||
] as const)('shows the %s brand logo', (type, path) => {
|
||||
const { container } = render(<ProviderTypeIcon type={type} />)
|
||||
|
||||
const logo = container.querySelector('img')
|
||||
expect(logo).toHaveAttribute('src', expect.stringContaining(path))
|
||||
// Decorative: the type name is always shown next to it.
|
||||
expect(logo).toHaveAttribute('alt', '')
|
||||
})
|
||||
|
||||
it('keeps a generic icon for a custom endpoint, which has no brand', () => {
|
||||
const { container } = render(<ProviderTypeIcon type="api" />)
|
||||
|
||||
expect(container.querySelector('img')).not.toBeInTheDocument()
|
||||
expect(container.querySelector('.i-ri-plug-line')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { FileScanProviderType } from './provider-types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { basePath } from '@/utils/var'
|
||||
|
||||
// Each vendor's own mark, taken from its official site, so a scanner is recognizable at a glance.
|
||||
const BRAND_LOGOS: Partial<Record<FileScanProviderType, string>> = {
|
||||
clamav: 'clamav.png',
|
||||
cloudmersive: 'cloudmersive.png',
|
||||
scanii: 'scanii.svg',
|
||||
metadefender: 'metadefender.png',
|
||||
}
|
||||
|
||||
type ProviderTypeIconProps = {
|
||||
type: FileScanProviderType
|
||||
className?: string
|
||||
}
|
||||
|
||||
/** The brand logo of a scanner type; a custom endpoint has no brand and keeps a generic icon. */
|
||||
export function ProviderTypeIcon({ type, className }: ProviderTypeIconProps) {
|
||||
const logo = BRAND_LOGOS[type]
|
||||
if (!logo) {
|
||||
return (
|
||||
<span aria-hidden className={cn('i-ri-plug-line size-4 text-text-secondary', className)} />
|
||||
)
|
||||
}
|
||||
return (
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden
|
||||
src={`${basePath}/images/file-scan/${logo}`}
|
||||
className={cn('size-5 shrink-0 rounded-sm object-contain', className)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { ApiBasedExtensionSelector } from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { getErrorMessage } from '@/utils/error-message'
|
||||
import { ProviderTypeIcon } from './provider-type-icon'
|
||||
import {
|
||||
FILE_SCAN_PROVIDER_TYPES,
|
||||
fileScanProviderSendsFileContents,
|
||||
@@ -56,20 +57,6 @@ type FileScanProviderDialogProps = {
|
||||
|
||||
const stringSetting = (value: unknown) => (typeof value === 'string' ? value : '')
|
||||
|
||||
function ProviderTypeIcon({ type }: { type: FileScanProviderType }) {
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'size-4 text-text-secondary',
|
||||
type === 'clamav' && 'i-ri-server-line',
|
||||
type === 'api' && 'i-ri-plug-line',
|
||||
type !== 'clamav' && type !== 'api' && 'i-ri-cloud-line',
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function FileScanProviderDialog({
|
||||
onOpenChange,
|
||||
onSaved,
|
||||
|
||||
@@ -25,7 +25,8 @@ import { isCurrentWorkspaceManagerAtom } from '@/context/workspace-state'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { getErrorMessage } from '@/utils/error-message'
|
||||
import { useFileScanProviderTypeLabel } from './provider-types'
|
||||
import { ProviderTypeIcon } from './provider-type-icon'
|
||||
import { isFileScanProviderType, useFileScanProviderTypeLabel } from './provider-types'
|
||||
import { FileScanProviderDialog } from './scanner-dialog'
|
||||
|
||||
type DialogState = { mode: 'create' } | { mode: 'edit'; provider: FileScanProviderResponse } | null
|
||||
@@ -64,6 +65,11 @@ function ScannerItem({
|
||||
return (
|
||||
<li className="rounded-xl border-[0.5px] border-components-card-border bg-components-card-bg px-4 py-3 shadow-xs">
|
||||
<div className="flex items-center gap-3">
|
||||
{isFileScanProviderType(provider.provider_type) && (
|
||||
<span className="flex size-8 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular bg-background-default-dodge">
|
||||
<ProviderTypeIcon type={provider.provider_type} />
|
||||
</span>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span id={nameId} className="truncate system-sm-semibold text-text-primary">
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="99" height="99" rx="15.5" fill="#272343" stroke="black"/>
|
||||
<path
|
||||
d="M50.9255 83.28C41.4535 83.28 34.2855 81.5307 29.4215 78.032C24.5575 74.448 21.6988 69.7547 20.8455 63.952L37.1015 59.856C37.6135 62.3307 39.0215 64.336 41.3255 65.872C43.6295 67.3227 46.7868 68.048 50.7975 68.048C57.1975 68.048 60.3975 66.5547 60.3975 63.568C60.3975 62.032 59.4588 60.88 57.5815 60.112C55.7895 59.2587 52.8455 58.448 48.7495 57.68C41.0695 56.3147 35.0962 54.1387 30.8295 51.152C26.6482 48.1653 24.5575 43.6 24.5575 37.456C24.5575 33.2747 25.7522 29.5627 28.1415 26.32C30.5308 23.0773 33.8162 20.5173 37.9975 18.64C42.1788 16.7627 47.0002 15.824 52.4615 15.824C59.5442 15.824 65.3895 17.488 69.9975 20.816C74.6908 24.144 77.4642 28.24 78.3175 33.104L62.3175 37.456C61.7202 35.5787 60.5255 34.0427 58.7335 32.848C57.0268 31.6533 54.6802 31.056 51.6935 31.056C48.8775 31.056 46.7868 31.5253 45.4215 32.464C44.1415 33.4027 43.5015 34.512 43.5015 35.792C43.5015 37.328 44.3975 38.5653 46.1895 39.504C47.9815 40.3573 51.1388 41.2533 55.6615 42.192C60.2695 43.0453 64.3228 44.24 67.8215 45.776C71.4055 47.2267 74.1788 49.2747 76.1415 51.92C78.1895 54.48 79.2135 57.9787 79.2135 62.416C79.2135 66.5973 77.9762 70.2667 75.5015 73.424C73.1122 76.5813 69.7842 79.0133 65.5175 80.72C61.2508 82.4267 56.3868 83.28 50.9255 83.28Z"
|
||||
fill="#FFFFFE"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
Reference in new issue
Block a user