feat: dark theme icon support (#28858)
This commit is contained in:
@@ -29,6 +29,7 @@ class SimpleModelProviderEntity(BaseModel):
|
|||||||
provider: str
|
provider: str
|
||||||
label: I18nObject
|
label: I18nObject
|
||||||
icon_small: I18nObject | None = None
|
icon_small: I18nObject | None = None
|
||||||
|
icon_small_dark: I18nObject | None = None
|
||||||
icon_large: I18nObject | None = None
|
icon_large: I18nObject | None = None
|
||||||
supported_model_types: list[ModelType]
|
supported_model_types: list[ModelType]
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ class SimpleModelProviderEntity(BaseModel):
|
|||||||
provider=provider_entity.provider,
|
provider=provider_entity.provider,
|
||||||
label=provider_entity.label,
|
label=provider_entity.label,
|
||||||
icon_small=provider_entity.icon_small,
|
icon_small=provider_entity.icon_small,
|
||||||
|
icon_small_dark=provider_entity.icon_small_dark,
|
||||||
icon_large=provider_entity.icon_large,
|
icon_large=provider_entity.icon_large,
|
||||||
supported_model_types=provider_entity.supported_model_types,
|
supported_model_types=provider_entity.supported_model_types,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class SimpleProviderEntity(BaseModel):
|
|||||||
provider: str
|
provider: str
|
||||||
label: I18nObject
|
label: I18nObject
|
||||||
icon_small: I18nObject | None = None
|
icon_small: I18nObject | None = None
|
||||||
|
icon_small_dark: I18nObject | None = None
|
||||||
icon_large: I18nObject | None = None
|
icon_large: I18nObject | None = None
|
||||||
supported_model_types: Sequence[ModelType]
|
supported_model_types: Sequence[ModelType]
|
||||||
models: list[AIModelEntity] = []
|
models: list[AIModelEntity] = []
|
||||||
@@ -124,7 +125,6 @@ class ProviderEntity(BaseModel):
|
|||||||
icon_small: I18nObject | None = None
|
icon_small: I18nObject | None = None
|
||||||
icon_large: I18nObject | None = None
|
icon_large: I18nObject | None = None
|
||||||
icon_small_dark: I18nObject | None = None
|
icon_small_dark: I18nObject | None = None
|
||||||
icon_large_dark: I18nObject | None = None
|
|
||||||
background: str | None = None
|
background: str | None = None
|
||||||
help: ProviderHelpEntity | None = None
|
help: ProviderHelpEntity | None = None
|
||||||
supported_model_types: Sequence[ModelType]
|
supported_model_types: Sequence[ModelType]
|
||||||
|
|||||||
@@ -300,6 +300,14 @@ class ModelProviderFactory:
|
|||||||
file_name = provider_schema.icon_small.zh_Hans
|
file_name = provider_schema.icon_small.zh_Hans
|
||||||
else:
|
else:
|
||||||
file_name = provider_schema.icon_small.en_US
|
file_name = provider_schema.icon_small.en_US
|
||||||
|
elif icon_type.lower() == "icon_small_dark":
|
||||||
|
if not provider_schema.icon_small_dark:
|
||||||
|
raise ValueError(f"Provider {provider} does not have small dark icon.")
|
||||||
|
|
||||||
|
if lang.lower() == "zh_hans":
|
||||||
|
file_name = provider_schema.icon_small_dark.zh_Hans
|
||||||
|
else:
|
||||||
|
file_name = provider_schema.icon_small_dark.en_US
|
||||||
else:
|
else:
|
||||||
if not provider_schema.icon_large:
|
if not provider_schema.icon_large:
|
||||||
raise ValueError(f"Provider {provider} does not have large icon.")
|
raise ValueError(f"Provider {provider} does not have large icon.")
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class ProviderResponse(BaseModel):
|
|||||||
label: I18nObject
|
label: I18nObject
|
||||||
description: I18nObject | None = None
|
description: I18nObject | None = None
|
||||||
icon_small: I18nObject | None = None
|
icon_small: I18nObject | None = None
|
||||||
|
icon_small_dark: I18nObject | None = None
|
||||||
icon_large: I18nObject | None = None
|
icon_large: I18nObject | None = None
|
||||||
background: str | None = None
|
background: str | None = None
|
||||||
help: ProviderHelpEntity | None = None
|
help: ProviderHelpEntity | None = None
|
||||||
@@ -92,6 +93,11 @@ class ProviderResponse(BaseModel):
|
|||||||
self.icon_small = I18nObject(
|
self.icon_small = I18nObject(
|
||||||
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
||||||
)
|
)
|
||||||
|
if self.icon_small_dark is not None:
|
||||||
|
self.icon_small_dark = I18nObject(
|
||||||
|
en_US=f"{url_prefix}/icon_small_dark/en_US",
|
||||||
|
zh_Hans=f"{url_prefix}/icon_small_dark/zh_Hans",
|
||||||
|
)
|
||||||
|
|
||||||
if self.icon_large is not None:
|
if self.icon_large is not None:
|
||||||
self.icon_large = I18nObject(
|
self.icon_large = I18nObject(
|
||||||
@@ -109,6 +115,7 @@ class ProviderWithModelsResponse(BaseModel):
|
|||||||
provider: str
|
provider: str
|
||||||
label: I18nObject
|
label: I18nObject
|
||||||
icon_small: I18nObject | None = None
|
icon_small: I18nObject | None = None
|
||||||
|
icon_small_dark: I18nObject | None = None
|
||||||
icon_large: I18nObject | None = None
|
icon_large: I18nObject | None = None
|
||||||
status: CustomConfigurationStatus
|
status: CustomConfigurationStatus
|
||||||
models: list[ProviderModelWithStatusEntity]
|
models: list[ProviderModelWithStatusEntity]
|
||||||
@@ -123,6 +130,11 @@ class ProviderWithModelsResponse(BaseModel):
|
|||||||
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.icon_small_dark is not None:
|
||||||
|
self.icon_small_dark = I18nObject(
|
||||||
|
en_US=f"{url_prefix}/icon_small_dark/en_US", zh_Hans=f"{url_prefix}/icon_small_dark/zh_Hans"
|
||||||
|
)
|
||||||
|
|
||||||
if self.icon_large is not None:
|
if self.icon_large is not None:
|
||||||
self.icon_large = I18nObject(
|
self.icon_large = I18nObject(
|
||||||
en_US=f"{url_prefix}/icon_large/en_US", zh_Hans=f"{url_prefix}/icon_large/zh_Hans"
|
en_US=f"{url_prefix}/icon_large/en_US", zh_Hans=f"{url_prefix}/icon_large/zh_Hans"
|
||||||
@@ -147,6 +159,11 @@ class SimpleProviderEntityResponse(SimpleProviderEntity):
|
|||||||
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
en_US=f"{url_prefix}/icon_small/en_US", zh_Hans=f"{url_prefix}/icon_small/zh_Hans"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.icon_small_dark is not None:
|
||||||
|
self.icon_small_dark = I18nObject(
|
||||||
|
en_US=f"{url_prefix}/icon_small_dark/en_US", zh_Hans=f"{url_prefix}/icon_small_dark/zh_Hans"
|
||||||
|
)
|
||||||
|
|
||||||
if self.icon_large is not None:
|
if self.icon_large is not None:
|
||||||
self.icon_large = I18nObject(
|
self.icon_large = I18nObject(
|
||||||
en_US=f"{url_prefix}/icon_large/en_US", zh_Hans=f"{url_prefix}/icon_large/zh_Hans"
|
en_US=f"{url_prefix}/icon_large/en_US", zh_Hans=f"{url_prefix}/icon_large/zh_Hans"
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ class ModelProviderService:
|
|||||||
label=provider_configuration.provider.label,
|
label=provider_configuration.provider.label,
|
||||||
description=provider_configuration.provider.description,
|
description=provider_configuration.provider.description,
|
||||||
icon_small=provider_configuration.provider.icon_small,
|
icon_small=provider_configuration.provider.icon_small,
|
||||||
|
icon_small_dark=provider_configuration.provider.icon_small_dark,
|
||||||
icon_large=provider_configuration.provider.icon_large,
|
icon_large=provider_configuration.provider.icon_large,
|
||||||
background=provider_configuration.provider.background,
|
background=provider_configuration.provider.background,
|
||||||
help=provider_configuration.provider.help,
|
help=provider_configuration.provider.help,
|
||||||
@@ -402,6 +403,7 @@ class ModelProviderService:
|
|||||||
provider=provider,
|
provider=provider,
|
||||||
label=first_model.provider.label,
|
label=first_model.provider.label,
|
||||||
icon_small=first_model.provider.icon_small,
|
icon_small=first_model.provider.icon_small,
|
||||||
|
icon_small_dark=first_model.provider.icon_small_dark,
|
||||||
icon_large=first_model.provider.icon_large,
|
icon_large=first_model.provider.icon_large,
|
||||||
status=CustomConfigurationStatus.ACTIVE,
|
status=CustomConfigurationStatus.ACTIVE,
|
||||||
models=[
|
models=[
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ class TestModelProviderService:
|
|||||||
mock_provider_entity.label = {"en_US": "OpenAI", "zh_Hans": "OpenAI"}
|
mock_provider_entity.label = {"en_US": "OpenAI", "zh_Hans": "OpenAI"}
|
||||||
mock_provider_entity.description = {"en_US": "OpenAI provider", "zh_Hans": "OpenAI 提供商"}
|
mock_provider_entity.description = {"en_US": "OpenAI provider", "zh_Hans": "OpenAI 提供商"}
|
||||||
mock_provider_entity.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
mock_provider_entity.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
||||||
|
mock_provider_entity.icon_small_dark = None
|
||||||
mock_provider_entity.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
mock_provider_entity.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
||||||
mock_provider_entity.background = "#FF6B6B"
|
mock_provider_entity.background = "#FF6B6B"
|
||||||
mock_provider_entity.help = None
|
mock_provider_entity.help = None
|
||||||
@@ -300,6 +301,7 @@ class TestModelProviderService:
|
|||||||
mock_provider_entity_llm.label = {"en_US": "OpenAI", "zh_Hans": "OpenAI"}
|
mock_provider_entity_llm.label = {"en_US": "OpenAI", "zh_Hans": "OpenAI"}
|
||||||
mock_provider_entity_llm.description = {"en_US": "OpenAI provider", "zh_Hans": "OpenAI 提供商"}
|
mock_provider_entity_llm.description = {"en_US": "OpenAI provider", "zh_Hans": "OpenAI 提供商"}
|
||||||
mock_provider_entity_llm.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
mock_provider_entity_llm.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
||||||
|
mock_provider_entity_llm.icon_small_dark = None
|
||||||
mock_provider_entity_llm.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
mock_provider_entity_llm.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
||||||
mock_provider_entity_llm.background = "#FF6B6B"
|
mock_provider_entity_llm.background = "#FF6B6B"
|
||||||
mock_provider_entity_llm.help = None
|
mock_provider_entity_llm.help = None
|
||||||
@@ -313,6 +315,7 @@ class TestModelProviderService:
|
|||||||
mock_provider_entity_embedding.label = {"en_US": "Cohere", "zh_Hans": "Cohere"}
|
mock_provider_entity_embedding.label = {"en_US": "Cohere", "zh_Hans": "Cohere"}
|
||||||
mock_provider_entity_embedding.description = {"en_US": "Cohere provider", "zh_Hans": "Cohere 提供商"}
|
mock_provider_entity_embedding.description = {"en_US": "Cohere provider", "zh_Hans": "Cohere 提供商"}
|
||||||
mock_provider_entity_embedding.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
mock_provider_entity_embedding.icon_small = {"en_US": "icon_small.png", "zh_Hans": "icon_small.png"}
|
||||||
|
mock_provider_entity_embedding.icon_small_dark = None
|
||||||
mock_provider_entity_embedding.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
mock_provider_entity_embedding.icon_large = {"en_US": "icon_large.png", "zh_Hans": "icon_large.png"}
|
||||||
mock_provider_entity_embedding.background = "#4ECDC4"
|
mock_provider_entity_embedding.background = "#4ECDC4"
|
||||||
mock_provider_entity_embedding.help = None
|
mock_provider_entity_embedding.help = None
|
||||||
@@ -1023,6 +1026,7 @@ class TestModelProviderService:
|
|||||||
provider="openai",
|
provider="openai",
|
||||||
label={"en_US": "OpenAI", "zh_Hans": "OpenAI"},
|
label={"en_US": "OpenAI", "zh_Hans": "OpenAI"},
|
||||||
icon_small={"en_US": "icon_small.png", "zh_Hans": "icon_small.png"},
|
icon_small={"en_US": "icon_small.png", "zh_Hans": "icon_small.png"},
|
||||||
|
icon_small_dark=None,
|
||||||
icon_large={"en_US": "icon_large.png", "zh_Hans": "icon_large.png"},
|
icon_large={"en_US": "icon_large.png", "zh_Hans": "icon_large.png"},
|
||||||
),
|
),
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
@@ -1040,6 +1044,7 @@ class TestModelProviderService:
|
|||||||
provider="openai",
|
provider="openai",
|
||||||
label={"en_US": "OpenAI", "zh_Hans": "OpenAI"},
|
label={"en_US": "OpenAI", "zh_Hans": "OpenAI"},
|
||||||
icon_small={"en_US": "icon_small.png", "zh_Hans": "icon_small.png"},
|
icon_small={"en_US": "icon_small.png", "zh_Hans": "icon_small.png"},
|
||||||
|
icon_small_dark=None,
|
||||||
icon_large={"en_US": "icon_large.png", "zh_Hans": "icon_large.png"},
|
icon_large={"en_US": "icon_large.png", "zh_Hans": "icon_large.png"},
|
||||||
),
|
),
|
||||||
model="gpt-4",
|
model="gpt-4",
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ export type ModelProvider = {
|
|||||||
url: TypeWithI18N
|
url: TypeWithI18N
|
||||||
}
|
}
|
||||||
icon_small: TypeWithI18N
|
icon_small: TypeWithI18N
|
||||||
|
icon_small_dark?: TypeWithI18N
|
||||||
icon_large: TypeWithI18N
|
icon_large: TypeWithI18N
|
||||||
background?: string
|
background?: string
|
||||||
supported_model_types: ModelTypeEnum[]
|
supported_model_types: ModelTypeEnum[]
|
||||||
@@ -255,6 +256,7 @@ export type Model = {
|
|||||||
provider: string
|
provider: string
|
||||||
icon_large: TypeWithI18N
|
icon_large: TypeWithI18N
|
||||||
icon_small: TypeWithI18N
|
icon_small: TypeWithI18N
|
||||||
|
icon_small_dark?: TypeWithI18N
|
||||||
label: TypeWithI18N
|
label: TypeWithI18N
|
||||||
models: ModelItem[]
|
models: ModelItem[]
|
||||||
status: ModelStatusEnum
|
status: ModelStatusEnum
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import type {
|
|||||||
import { useLanguage } from '../hooks'
|
import { useLanguage } from '../hooks'
|
||||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||||
import { OpenaiBlue, OpenaiTeal, OpenaiViolet, OpenaiYellow } from '@/app/components/base/icons/src/public/llm'
|
import { OpenaiBlue, OpenaiTeal, OpenaiViolet, OpenaiYellow } from '@/app/components/base/icons/src/public/llm'
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
import { renderI18nObject } from '@/i18n-config'
|
import { renderI18nObject } from '@/i18n-config'
|
||||||
|
import { Theme } from '@/types/app'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
|
||||||
type ModelIconProps = {
|
type ModelIconProps = {
|
||||||
provider?: Model | ModelProvider
|
provider?: Model | ModelProvider
|
||||||
@@ -23,6 +25,7 @@ const ModelIcon: FC<ModelIconProps> = ({
|
|||||||
iconClassName,
|
iconClassName,
|
||||||
isDeprecated = false,
|
isDeprecated = false,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { theme } = useTheme()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.startsWith('o'))
|
if (provider?.provider && ['openai', 'langgenius/openai/openai'].includes(provider.provider) && modelName?.startsWith('o'))
|
||||||
return <div className='flex items-center justify-center'><OpenaiYellow className={cn('h-5 w-5', className)} /></div>
|
return <div className='flex items-center justify-center'><OpenaiYellow className={cn('h-5 w-5', className)} /></div>
|
||||||
@@ -36,7 +39,16 @@ const ModelIcon: FC<ModelIconProps> = ({
|
|||||||
if (provider?.icon_small) {
|
if (provider?.icon_small) {
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex h-5 w-5 items-center justify-center', isDeprecated && 'opacity-50', className)}>
|
<div className={cn('flex h-5 w-5 items-center justify-center', isDeprecated && 'opacity-50', className)}>
|
||||||
<img alt='model-icon' src={renderI18nObject(provider.icon_small, language)} className={iconClassName} />
|
<img
|
||||||
|
alt='model-icon'
|
||||||
|
src={renderI18nObject(
|
||||||
|
theme === Theme.dark && provider.icon_small_dark
|
||||||
|
? provider.icon_small_dark
|
||||||
|
: provider.icon_small,
|
||||||
|
language,
|
||||||
|
)}
|
||||||
|
className={iconClassName}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ const ProviderIcon: FC<ProviderIconProps> = ({
|
|||||||
<div className={cn('inline-flex items-center gap-2', className)}>
|
<div className={cn('inline-flex items-center gap-2', className)}>
|
||||||
<img
|
<img
|
||||||
alt='provider-icon'
|
alt='provider-icon'
|
||||||
src={renderI18nObject(provider.icon_small, language)}
|
src={renderI18nObject(
|
||||||
|
theme === Theme.dark && provider.icon_small_dark
|
||||||
|
? provider.icon_small_dark
|
||||||
|
: provider.icon_small,
|
||||||
|
language,
|
||||||
|
)}
|
||||||
className='h-6 w-6'
|
className='h-6 w-6'
|
||||||
/>
|
/>
|
||||||
<div className='system-md-semibold text-text-primary'>
|
<div className='system-md-semibold text-text-primary'>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { getLanguage } from '@/i18n-config/language'
|
|||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { RiAlertFill } from '@remixicon/react'
|
import { RiAlertFill } from '@remixicon/react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
import { Theme } from '@/types/app'
|
||||||
import Partner from '../base/badges/partner'
|
import Partner from '../base/badges/partner'
|
||||||
import Verified from '../base/badges/verified'
|
import Verified from '../base/badges/verified'
|
||||||
import Icon from '../card/base/card-icon'
|
import Icon from '../card/base/card-icon'
|
||||||
@@ -50,7 +52,9 @@ const Card = ({
|
|||||||
const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
|
const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
|
||||||
const { t } = useMixedTranslation(localeFromProps)
|
const { t } = useMixedTranslation(localeFromProps)
|
||||||
const { categoriesMap } = useCategories(t, true)
|
const { categoriesMap } = useCategories(t, true)
|
||||||
const { category, type, name, org, label, brief, icon, verified, badges = [] } = payload
|
const { category, type, name, org, label, brief, icon, icon_dark, verified, badges = [] } = payload
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const iconSrc = theme === Theme.dark && icon_dark ? icon_dark : icon
|
||||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||||
obj ? renderI18nObject(obj, locale) : ''
|
obj ? renderI18nObject(obj, locale) : ''
|
||||||
const isPartner = badges.includes('partner')
|
const isPartner = badges.includes('partner')
|
||||||
@@ -71,7 +75,7 @@ const Card = ({
|
|||||||
{!hideCornerMark && <CornerMark text={categoriesMap[type === 'bundle' ? type : category]?.label} />}
|
{!hideCornerMark && <CornerMark text={categoriesMap[type === 'bundle' ? type : category]?.label} />}
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Icon src={icon} installed={installed} installFailed={installFailed} />
|
<Icon src={iconSrc} installed={installed} installFailed={installFailed} />
|
||||||
<div className="ml-3 w-0 grow">
|
<div className="ml-3 w-0 grow">
|
||||||
<div className="flex h-5 items-center">
|
<div className="flex h-5 items-center">
|
||||||
<Title title={getLocalizedText(label)} />
|
<Title title={getLocalizedText(label)} />
|
||||||
|
|||||||
@@ -64,10 +64,12 @@ const InstallFromLocalPackage: React.FC<InstallFromLocalPackageProps> = ({
|
|||||||
uniqueIdentifier,
|
uniqueIdentifier,
|
||||||
} = result
|
} = result
|
||||||
const icon = await getIconUrl(manifest!.icon)
|
const icon = await getIconUrl(manifest!.icon)
|
||||||
|
const iconDark = manifest.icon_dark ? await getIconUrl(manifest.icon_dark) : undefined
|
||||||
setUniqueIdentifier(uniqueIdentifier)
|
setUniqueIdentifier(uniqueIdentifier)
|
||||||
setManifest({
|
setManifest({
|
||||||
...manifest,
|
...manifest,
|
||||||
icon,
|
icon,
|
||||||
|
icon_dark: iconDark,
|
||||||
})
|
})
|
||||||
setStep(InstallStep.readyToInstall)
|
setStep(InstallStep.readyToInstall)
|
||||||
}, [getIconUrl])
|
}, [getIconUrl])
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaratio
|
|||||||
brief: pluginManifest.description,
|
brief: pluginManifest.description,
|
||||||
description: pluginManifest.description,
|
description: pluginManifest.description,
|
||||||
icon: pluginManifest.icon,
|
icon: pluginManifest.icon,
|
||||||
|
icon_dark: pluginManifest.icon_dark,
|
||||||
verified: pluginManifest.verified,
|
verified: pluginManifest.verified,
|
||||||
introduction: '',
|
introduction: '',
|
||||||
repository: '',
|
repository: '',
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import {
|
|||||||
RiHardDrive3Line,
|
RiHardDrive3Line,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { useTheme } from 'next-themes'
|
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
import Verified from '../base/badges/verified'
|
import Verified from '../base/badges/verified'
|
||||||
import { AutoUpdateLine } from '../../base/icons/src/vender/system'
|
import { AutoUpdateLine } from '../../base/icons/src/vender/system'
|
||||||
import DeprecationNotice from '../base/deprecation-notice'
|
import DeprecationNotice from '../base/deprecation-notice'
|
||||||
@@ -86,7 +86,7 @@ const DetailHeader = ({
|
|||||||
alternative_plugin_id,
|
alternative_plugin_id,
|
||||||
} = detail
|
} = detail
|
||||||
|
|
||||||
const { author, category, name, label, description, icon, verified, tool } = detail.declaration || detail
|
const { author, category, name, label, description, icon, icon_dark, verified, tool } = detail.declaration || detail
|
||||||
const isTool = category === PluginCategoryEnum.tool
|
const isTool = category === PluginCategoryEnum.tool
|
||||||
const providerBriefInfo = tool?.identity
|
const providerBriefInfo = tool?.identity
|
||||||
const providerKey = `${plugin_id}/${providerBriefInfo?.name}`
|
const providerKey = `${plugin_id}/${providerBriefInfo?.name}`
|
||||||
@@ -109,6 +109,11 @@ const DetailHeader = ({
|
|||||||
return false
|
return false
|
||||||
}, [isFromMarketplace, latest_version, version])
|
}, [isFromMarketplace, latest_version, version])
|
||||||
|
|
||||||
|
const iconFileName = theme === 'dark' && icon_dark ? icon_dark : icon
|
||||||
|
const iconSrc = iconFileName
|
||||||
|
? (iconFileName.startsWith('http') ? iconFileName : `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${iconFileName}`)
|
||||||
|
: ''
|
||||||
|
|
||||||
const detailUrl = useMemo(() => {
|
const detailUrl = useMemo(() => {
|
||||||
if (isFromGitHub)
|
if (isFromGitHub)
|
||||||
return `https://github.com/${meta!.repo}`
|
return `https://github.com/${meta!.repo}`
|
||||||
@@ -214,7 +219,7 @@ const DetailHeader = ({
|
|||||||
<div className={cn('shrink-0 border-b border-divider-subtle bg-components-panel-bg p-4 pb-3', isReadmeView && 'border-b-0 bg-transparent p-0')}>
|
<div className={cn('shrink-0 border-b border-divider-subtle bg-components-panel-bg p-4 pb-3', isReadmeView && 'border-b-0 bg-transparent p-0')}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className={cn('overflow-hidden rounded-xl border border-components-panel-border-subtle', isReadmeView && 'bg-components-panel-bg')}>
|
<div className={cn('overflow-hidden rounded-xl border border-components-panel-border-subtle', isReadmeView && 'bg-components-panel-bg')}>
|
||||||
<Icon src={icon.startsWith('http') ? icon : `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${icon}`} />
|
<Icon src={iconSrc} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 w-0 grow">
|
<div className="ml-3 w-0 grow">
|
||||||
<div className="flex h-5 items-center">
|
<div className="flex h-5 items-center">
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ import {
|
|||||||
RiHardDrive3Line,
|
RiHardDrive3Line,
|
||||||
RiLoginCircleLine,
|
RiLoginCircleLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useTheme } from 'next-themes'
|
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { gte } from 'semver'
|
import { gte } from 'semver'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
import Verified from '../base/badges/verified'
|
import Verified from '../base/badges/verified'
|
||||||
import Badge from '../../base/badge'
|
import Badge from '../../base/badge'
|
||||||
import { Github } from '../../base/icons/src/public/common'
|
import { Github } from '../../base/icons/src/public/common'
|
||||||
@@ -58,7 +58,7 @@ const PluginItem: FC<Props> = ({
|
|||||||
status,
|
status,
|
||||||
deprecated_reason,
|
deprecated_reason,
|
||||||
} = plugin
|
} = plugin
|
||||||
const { category, author, name, label, description, icon, verified, meta: declarationMeta } = plugin.declaration
|
const { category, author, name, label, description, icon, icon_dark, verified, meta: declarationMeta } = plugin.declaration
|
||||||
|
|
||||||
const orgName = useMemo(() => {
|
const orgName = useMemo(() => {
|
||||||
return [PluginSource.github, PluginSource.marketplace].includes(source) ? author : ''
|
return [PluginSource.github, PluginSource.marketplace].includes(source) ? author : ''
|
||||||
@@ -84,6 +84,10 @@ const PluginItem: FC<Props> = ({
|
|||||||
const title = getValueFromI18nObject(label)
|
const title = getValueFromI18nObject(label)
|
||||||
const descriptionText = getValueFromI18nObject(description)
|
const descriptionText = getValueFromI18nObject(description)
|
||||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||||
|
const iconFileName = theme === 'dark' && icon_dark ? icon_dark : icon
|
||||||
|
const iconSrc = iconFileName
|
||||||
|
? (iconFileName.startsWith('http') ? iconFileName : `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${iconFileName}`)
|
||||||
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -105,7 +109,7 @@ const PluginItem: FC<Props> = ({
|
|||||||
<div className='flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border-[1px] border-components-panel-border-subtle'>
|
<div className='flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border-[1px] border-components-panel-border-subtle'>
|
||||||
<img
|
<img
|
||||||
className='h-full w-full'
|
className='h-full w-full'
|
||||||
src={`${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${icon}`}
|
src={iconSrc}
|
||||||
alt={`plugin-${plugin_unique_identifier}-logo`}
|
alt={`plugin-${plugin_unique_identifier}-logo`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export type PluginDeclaration = {
|
|||||||
version: string
|
version: string
|
||||||
author: string
|
author: string
|
||||||
icon: string
|
icon: string
|
||||||
|
icon_dark?: string
|
||||||
name: string
|
name: string
|
||||||
category: PluginCategoryEnum
|
category: PluginCategoryEnum
|
||||||
label: Record<Locale, string>
|
label: Record<Locale, string>
|
||||||
@@ -248,7 +249,7 @@ export type PluginInfoFromMarketPlace = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Plugin = {
|
export type Plugin = {
|
||||||
type: 'plugin' | 'bundle' | 'model' | 'extension' | 'tool' | 'agent_strategy'
|
type: 'plugin' | 'bundle' | 'model' | 'extension' | 'tool' | 'agent_strategy' | 'datasource' | 'trigger'
|
||||||
org: string
|
org: string
|
||||||
author?: string
|
author?: string
|
||||||
name: string
|
name: string
|
||||||
@@ -257,6 +258,7 @@ export type Plugin = {
|
|||||||
latest_version: string
|
latest_version: string
|
||||||
latest_package_identifier: string
|
latest_package_identifier: string
|
||||||
icon: string
|
icon: string
|
||||||
|
icon_dark?: string
|
||||||
verified: boolean
|
verified: boolean
|
||||||
label: Record<Locale, string>
|
label: Record<Locale, string>
|
||||||
brief: Record<Locale, string>
|
brief: Record<Locale, string>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export type Collection = {
|
|||||||
author: string
|
author: string
|
||||||
description: TypeWithI18N
|
description: TypeWithI18N
|
||||||
icon: string | Emoji
|
icon: string | Emoji
|
||||||
|
icon_dark?: string | Emoji
|
||||||
label: TypeWithI18N
|
label: TypeWithI18N
|
||||||
type: CollectionType | string
|
type: CollectionType | string
|
||||||
team_credentials: Record<string, any>
|
team_credentials: Record<string, any>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import type { ToolWithProvider } from '../../types'
|
import type { ToolWithProvider } from '../../types'
|
||||||
import { BlockEnum } from '../../types'
|
import { BlockEnum } from '../../types'
|
||||||
import type { ToolDefaultValue } from '../types'
|
import type { ToolDefaultValue } from '../types'
|
||||||
@@ -10,9 +10,13 @@ import { useGetLanguage } from '@/context/i18n'
|
|||||||
import BlockIcon from '../../block-icon'
|
import BlockIcon from '../../block-icon'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
import { Theme } from '@/types/app'
|
||||||
import { basePath } from '@/utils/var'
|
import { basePath } from '@/utils/var'
|
||||||
|
|
||||||
const normalizeProviderIcon = (icon: ToolWithProvider['icon']) => {
|
const normalizeProviderIcon = (icon?: ToolWithProvider['icon']) => {
|
||||||
|
if (!icon)
|
||||||
|
return icon
|
||||||
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
|
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
|
||||||
return `${basePath}${icon}`
|
return `${basePath}${icon}`
|
||||||
return icon
|
return icon
|
||||||
@@ -36,6 +40,20 @@ const ToolItem: FC<Props> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const language = useGetLanguage()
|
const language = useGetLanguage()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const normalizedIcon = useMemo<ToolWithProvider['icon']>(() => {
|
||||||
|
return normalizeProviderIcon(provider.icon) ?? provider.icon
|
||||||
|
}, [provider.icon])
|
||||||
|
const normalizedIconDark = useMemo(() => {
|
||||||
|
if (!provider.icon_dark)
|
||||||
|
return undefined
|
||||||
|
return normalizeProviderIcon(provider.icon_dark) ?? provider.icon_dark
|
||||||
|
}, [provider.icon_dark])
|
||||||
|
const providerIcon = useMemo(() => {
|
||||||
|
if (theme === Theme.dark && normalizedIconDark)
|
||||||
|
return normalizedIconDark
|
||||||
|
return normalizedIcon
|
||||||
|
}, [theme, normalizedIcon, normalizedIconDark])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -49,7 +67,7 @@ const ToolItem: FC<Props> = ({
|
|||||||
size='md'
|
size='md'
|
||||||
className='mb-2'
|
className='mb-2'
|
||||||
type={BlockEnum.Tool}
|
type={BlockEnum.Tool}
|
||||||
toolIcon={provider.icon}
|
toolIcon={providerIcon}
|
||||||
/>
|
/>
|
||||||
<div className='mb-1 text-sm leading-5 text-text-primary'>{payload.label[language]}</div>
|
<div className='mb-1 text-sm leading-5 text-text-primary'>{payload.label[language]}</div>
|
||||||
<div className='text-xs leading-[18px] text-text-secondary'>{payload.description[language]}</div>
|
<div className='text-xs leading-[18px] text-text-secondary'>{payload.description[language]}</div>
|
||||||
@@ -73,7 +91,8 @@ const ToolItem: FC<Props> = ({
|
|||||||
provider_name: provider.name,
|
provider_name: provider.name,
|
||||||
plugin_id: provider.plugin_id,
|
plugin_id: provider.plugin_id,
|
||||||
plugin_unique_identifier: provider.plugin_unique_identifier,
|
plugin_unique_identifier: provider.plugin_unique_identifier,
|
||||||
provider_icon: normalizeProviderIcon(provider.icon),
|
provider_icon: normalizedIcon,
|
||||||
|
provider_icon_dark: normalizedIconDark,
|
||||||
tool_name: payload.name,
|
tool_name: payload.name,
|
||||||
tool_label: payload.label[language],
|
tool_label: payload.label[language],
|
||||||
tool_description: payload.description[language],
|
tool_description: payload.description[language],
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ import ActionItem from './action-item'
|
|||||||
import BlockIcon from '../../block-icon'
|
import BlockIcon from '../../block-icon'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useHover } from 'ahooks'
|
import { useHover } from 'ahooks'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
import { Theme } from '@/types/app'
|
||||||
import McpToolNotSupportTooltip from '../../nodes/_base/components/mcp-tool-not-support-tooltip'
|
import McpToolNotSupportTooltip from '../../nodes/_base/components/mcp-tool-not-support-tooltip'
|
||||||
import { Mcp } from '@/app/components/base/icons/src/vender/other'
|
import { Mcp } from '@/app/components/base/icons/src/vender/other'
|
||||||
import { basePath } from '@/utils/var'
|
import { basePath } from '@/utils/var'
|
||||||
|
|
||||||
const normalizeProviderIcon = (icon: ToolWithProvider['icon']) => {
|
const normalizeProviderIcon = (icon?: ToolWithProvider['icon']) => {
|
||||||
|
if (!icon)
|
||||||
|
return icon
|
||||||
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
|
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
|
||||||
return `${basePath}${icon}`
|
return `${basePath}${icon}`
|
||||||
return icon
|
return icon
|
||||||
@@ -59,6 +63,20 @@ const Tool: FC<Props> = ({
|
|||||||
const isHovering = useHover(ref)
|
const isHovering = useHover(ref)
|
||||||
const isMCPTool = payload.type === CollectionType.mcp
|
const isMCPTool = payload.type === CollectionType.mcp
|
||||||
const isShowCanNotChooseMCPTip = !canChooseMCPTool && isMCPTool
|
const isShowCanNotChooseMCPTip = !canChooseMCPTool && isMCPTool
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const normalizedIcon = useMemo<ToolWithProvider['icon']>(() => {
|
||||||
|
return normalizeProviderIcon(payload.icon) ?? payload.icon
|
||||||
|
}, [payload.icon])
|
||||||
|
const normalizedIconDark = useMemo(() => {
|
||||||
|
if (!payload.icon_dark)
|
||||||
|
return undefined
|
||||||
|
return normalizeProviderIcon(payload.icon_dark) ?? payload.icon_dark
|
||||||
|
}, [payload.icon_dark])
|
||||||
|
const providerIcon = useMemo<ToolWithProvider['icon']>(() => {
|
||||||
|
if (theme === Theme.dark && normalizedIconDark)
|
||||||
|
return normalizedIconDark
|
||||||
|
return normalizedIcon
|
||||||
|
}, [theme, normalizedIcon, normalizedIconDark])
|
||||||
const getIsDisabled = useCallback((tool: ToolType) => {
|
const getIsDisabled = useCallback((tool: ToolType) => {
|
||||||
if (!selectedTools || !selectedTools.length) return false
|
if (!selectedTools || !selectedTools.length) return false
|
||||||
return selectedTools.some(selectedTool => (selectedTool.provider_name === payload.name || selectedTool.provider_name === payload.id) && selectedTool.tool_name === tool.name)
|
return selectedTools.some(selectedTool => (selectedTool.provider_name === payload.name || selectedTool.provider_name === payload.id) && selectedTool.tool_name === tool.name)
|
||||||
@@ -95,7 +113,8 @@ const Tool: FC<Props> = ({
|
|||||||
provider_name: payload.name,
|
provider_name: payload.name,
|
||||||
plugin_id: payload.plugin_id,
|
plugin_id: payload.plugin_id,
|
||||||
plugin_unique_identifier: payload.plugin_unique_identifier,
|
plugin_unique_identifier: payload.plugin_unique_identifier,
|
||||||
provider_icon: normalizeProviderIcon(payload.icon),
|
provider_icon: normalizedIcon,
|
||||||
|
provider_icon_dark: normalizedIconDark,
|
||||||
tool_name: tool.name,
|
tool_name: tool.name,
|
||||||
tool_label: tool.label[language],
|
tool_label: tool.label[language],
|
||||||
tool_description: tool.description[language],
|
tool_description: tool.description[language],
|
||||||
@@ -177,7 +196,8 @@ const Tool: FC<Props> = ({
|
|||||||
provider_name: payload.name,
|
provider_name: payload.name,
|
||||||
plugin_id: payload.plugin_id,
|
plugin_id: payload.plugin_id,
|
||||||
plugin_unique_identifier: payload.plugin_unique_identifier,
|
plugin_unique_identifier: payload.plugin_unique_identifier,
|
||||||
provider_icon: normalizeProviderIcon(payload.icon),
|
provider_icon: normalizedIcon,
|
||||||
|
provider_icon_dark: normalizedIconDark,
|
||||||
tool_name: tool.name,
|
tool_name: tool.name,
|
||||||
tool_label: tool.label[language],
|
tool_label: tool.label[language],
|
||||||
tool_description: tool.description[language],
|
tool_description: tool.description[language],
|
||||||
@@ -192,7 +212,7 @@ const Tool: FC<Props> = ({
|
|||||||
<BlockIcon
|
<BlockIcon
|
||||||
className='shrink-0'
|
className='shrink-0'
|
||||||
type={BlockEnum.Tool}
|
type={BlockEnum.Tool}
|
||||||
toolIcon={payload.icon}
|
toolIcon={providerIcon}
|
||||||
/>
|
/>
|
||||||
<div className='ml-2 flex w-0 grow items-center text-sm text-text-primary'>
|
<div className='ml-2 flex w-0 grow items-center text-sm text-text-primary'>
|
||||||
<span className='max-w-[250px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span>
|
<span className='max-w-[250px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span>
|
||||||
|
|||||||
@@ -10,6 +10,17 @@ import BlockIcon from '@/app/components/workflow/block-icon'
|
|||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import type { TriggerDefaultValue, TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
|
import type { TriggerDefaultValue, TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
|
||||||
import TriggerPluginActionItem from './action-item'
|
import TriggerPluginActionItem from './action-item'
|
||||||
|
import { Theme } from '@/types/app'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
import { basePath } from '@/utils/var'
|
||||||
|
|
||||||
|
const normalizeProviderIcon = (icon?: TriggerWithProvider['icon']) => {
|
||||||
|
if (!icon)
|
||||||
|
return icon
|
||||||
|
if (typeof icon === 'string' && basePath && icon.startsWith('/') && !icon.startsWith(`${basePath}/`))
|
||||||
|
return `${basePath}${icon}`
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
@@ -26,6 +37,7 @@ const TriggerPluginItem: FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const language = useGetLanguage()
|
const language = useGetLanguage()
|
||||||
|
const { theme } = useTheme()
|
||||||
const notShowProvider = payload.type === CollectionType.workflow
|
const notShowProvider = payload.type === CollectionType.workflow
|
||||||
const actions = payload.events
|
const actions = payload.events
|
||||||
const hasAction = !notShowProvider
|
const hasAction = !notShowProvider
|
||||||
@@ -55,6 +67,23 @@ const TriggerPluginItem: FC<Props> = ({
|
|||||||
|
|
||||||
return payload.author || ''
|
return payload.author || ''
|
||||||
}, [payload.author, payload.type, t])
|
}, [payload.author, payload.type, t])
|
||||||
|
const normalizedIcon = useMemo<TriggerWithProvider['icon']>(() => {
|
||||||
|
return normalizeProviderIcon(payload.icon) ?? payload.icon
|
||||||
|
}, [payload.icon])
|
||||||
|
const normalizedIconDark = useMemo(() => {
|
||||||
|
if (!payload.icon_dark)
|
||||||
|
return undefined
|
||||||
|
return normalizeProviderIcon(payload.icon_dark) ?? payload.icon_dark
|
||||||
|
}, [payload.icon_dark])
|
||||||
|
const providerIcon = useMemo<TriggerWithProvider['icon']>(() => {
|
||||||
|
if (theme === Theme.dark && normalizedIconDark)
|
||||||
|
return normalizedIconDark
|
||||||
|
return normalizedIcon
|
||||||
|
}, [normalizedIcon, normalizedIconDark, theme])
|
||||||
|
const providerWithResolvedIcon = useMemo(() => ({
|
||||||
|
...payload,
|
||||||
|
icon: providerIcon,
|
||||||
|
}), [payload, providerIcon])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -99,7 +128,7 @@ const TriggerPluginItem: FC<Props> = ({
|
|||||||
<BlockIcon
|
<BlockIcon
|
||||||
className='shrink-0'
|
className='shrink-0'
|
||||||
type={BlockEnum.TriggerPlugin}
|
type={BlockEnum.TriggerPlugin}
|
||||||
toolIcon={payload.icon}
|
toolIcon={providerIcon}
|
||||||
/>
|
/>
|
||||||
<div className='ml-2 flex min-w-0 flex-1 items-center text-sm text-text-primary'>
|
<div className='ml-2 flex min-w-0 flex-1 items-center text-sm text-text-primary'>
|
||||||
<span className='max-w-[200px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span>
|
<span className='max-w-[200px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span>
|
||||||
@@ -118,7 +147,7 @@ const TriggerPluginItem: FC<Props> = ({
|
|||||||
actions.map(action => (
|
actions.map(action => (
|
||||||
<TriggerPluginActionItem
|
<TriggerPluginActionItem
|
||||||
key={action.name}
|
key={action.name}
|
||||||
provider={payload}
|
provider={providerWithResolvedIcon}
|
||||||
payload={action}
|
payload={action}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export type ToolDefaultValue = PluginCommonDefaultValue & {
|
|||||||
meta?: PluginMeta
|
meta?: PluginMeta
|
||||||
plugin_id?: string
|
plugin_id?: string
|
||||||
provider_icon?: Collection['icon']
|
provider_icon?: Collection['icon']
|
||||||
|
provider_icon_dark?: Collection['icon']
|
||||||
plugin_unique_identifier?: string
|
plugin_unique_identifier?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
|
|||||||
import type { ToolNodeType } from '../nodes/tool/types'
|
import type { ToolNodeType } from '../nodes/tool/types'
|
||||||
import type { DataSourceNodeType } from '../nodes/data-source/types'
|
import type { DataSourceNodeType } from '../nodes/data-source/types'
|
||||||
import type { TriggerWithProvider } from '../block-selector/types'
|
import type { TriggerWithProvider } from '../block-selector/types'
|
||||||
|
import useTheme from '@/hooks/use-theme'
|
||||||
|
|
||||||
const isTriggerPluginNode = (data: Node['data']): data is PluginTriggerNodeType => data.type === BlockEnum.TriggerPlugin
|
const isTriggerPluginNode = (data: Node['data']): data is PluginTriggerNodeType => data.type === BlockEnum.TriggerPlugin
|
||||||
|
|
||||||
@@ -22,17 +23,30 @@ const isToolNode = (data: Node['data']): data is ToolNodeType => data.type === B
|
|||||||
|
|
||||||
const isDataSourceNode = (data: Node['data']): data is DataSourceNodeType => data.type === BlockEnum.DataSource
|
const isDataSourceNode = (data: Node['data']): data is DataSourceNodeType => data.type === BlockEnum.DataSource
|
||||||
|
|
||||||
|
type IconValue = ToolWithProvider['icon']
|
||||||
|
|
||||||
|
const resolveIconByTheme = (
|
||||||
|
currentTheme: string | undefined,
|
||||||
|
icon?: IconValue,
|
||||||
|
iconDark?: IconValue,
|
||||||
|
) => {
|
||||||
|
if (currentTheme === 'dark' && iconDark)
|
||||||
|
return iconDark
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
const findTriggerPluginIcon = (
|
const findTriggerPluginIcon = (
|
||||||
identifiers: (string | undefined)[],
|
identifiers: (string | undefined)[],
|
||||||
triggers: TriggerWithProvider[] | undefined,
|
triggers: TriggerWithProvider[] | undefined,
|
||||||
|
currentTheme?: string,
|
||||||
) => {
|
) => {
|
||||||
const targetTriggers = triggers || []
|
const targetTriggers = triggers || []
|
||||||
for (const identifier of identifiers) {
|
for (const identifier of identifiers) {
|
||||||
if (!identifier)
|
if (!identifier)
|
||||||
continue
|
continue
|
||||||
const matched = targetTriggers.find(trigger => trigger.id === identifier || canFindTool(trigger.id, identifier))
|
const matched = targetTriggers.find(trigger => trigger.id === identifier || canFindTool(trigger.id, identifier))
|
||||||
if (matched?.icon)
|
if (matched)
|
||||||
return matched.icon
|
return resolveIconByTheme(currentTheme, matched.icon, matched.icon_dark)
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -44,6 +58,7 @@ export const useToolIcon = (data?: Node['data']) => {
|
|||||||
const { data: mcpTools } = useAllMCPTools()
|
const { data: mcpTools } = useAllMCPTools()
|
||||||
const dataSourceList = useStore(s => s.dataSourceList)
|
const dataSourceList = useStore(s => s.dataSourceList)
|
||||||
const { data: triggerPlugins } = useAllTriggerPlugins()
|
const { data: triggerPlugins } = useAllTriggerPlugins()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const toolIcon = useMemo(() => {
|
const toolIcon = useMemo(() => {
|
||||||
if (!data)
|
if (!data)
|
||||||
@@ -57,6 +72,7 @@ export const useToolIcon = (data?: Node['data']) => {
|
|||||||
data.provider_name,
|
data.provider_name,
|
||||||
],
|
],
|
||||||
triggerPlugins,
|
triggerPlugins,
|
||||||
|
theme,
|
||||||
)
|
)
|
||||||
if (icon)
|
if (icon)
|
||||||
return icon
|
return icon
|
||||||
@@ -100,12 +116,16 @@ export const useToolIcon = (data?: Node['data']) => {
|
|||||||
return true
|
return true
|
||||||
return data.provider_name === toolWithProvider.name
|
return data.provider_name === toolWithProvider.name
|
||||||
})
|
})
|
||||||
if (matched?.icon)
|
if (matched) {
|
||||||
return matched.icon
|
const icon = resolveIconByTheme(theme, matched.icon, matched.icon_dark)
|
||||||
|
if (icon)
|
||||||
|
return icon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.provider_icon)
|
const fallbackIcon = resolveIconByTheme(theme, data.provider_icon, data.provider_icon_dark)
|
||||||
return data.provider_icon
|
if (fallbackIcon)
|
||||||
|
return fallbackIcon
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -114,7 +134,7 @@ export const useToolIcon = (data?: Node['data']) => {
|
|||||||
return dataSourceList?.find(toolWithProvider => toolWithProvider.plugin_id === data.plugin_id)?.icon || ''
|
return dataSourceList?.find(toolWithProvider => toolWithProvider.plugin_id === data.plugin_id)?.icon || ''
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
}, [data, dataSourceList, buildInTools, customTools, workflowTools, mcpTools, triggerPlugins])
|
}, [data, dataSourceList, buildInTools, customTools, workflowTools, mcpTools, triggerPlugins, theme])
|
||||||
|
|
||||||
return toolIcon
|
return toolIcon
|
||||||
}
|
}
|
||||||
@@ -126,6 +146,7 @@ export const useGetToolIcon = () => {
|
|||||||
const { data: mcpTools } = useAllMCPTools()
|
const { data: mcpTools } = useAllMCPTools()
|
||||||
const { data: triggerPlugins } = useAllTriggerPlugins()
|
const { data: triggerPlugins } = useAllTriggerPlugins()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const getToolIcon = useCallback((data: Node['data']) => {
|
const getToolIcon = useCallback((data: Node['data']) => {
|
||||||
const {
|
const {
|
||||||
@@ -144,6 +165,7 @@ export const useGetToolIcon = () => {
|
|||||||
data.provider_name,
|
data.provider_name,
|
||||||
],
|
],
|
||||||
triggerPlugins,
|
triggerPlugins,
|
||||||
|
theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,12 +204,16 @@ export const useGetToolIcon = () => {
|
|||||||
return true
|
return true
|
||||||
return data.provider_name === toolWithProvider.name
|
return data.provider_name === toolWithProvider.name
|
||||||
})
|
})
|
||||||
if (matched?.icon)
|
if (matched) {
|
||||||
return matched.icon
|
const icon = resolveIconByTheme(theme, matched.icon, matched.icon_dark)
|
||||||
|
if (icon)
|
||||||
|
return icon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.provider_icon)
|
const fallbackIcon = resolveIconByTheme(theme, data.provider_icon, data.provider_icon_dark)
|
||||||
return data.provider_icon
|
if (fallbackIcon)
|
||||||
|
return fallbackIcon
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -196,7 +222,7 @@ export const useGetToolIcon = () => {
|
|||||||
return dataSourceList?.find(toolWithProvider => toolWithProvider.plugin_id === data.plugin_id)?.icon
|
return dataSourceList?.find(toolWithProvider => toolWithProvider.plugin_id === data.plugin_id)?.icon
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}, [workflowStore, triggerPlugins, buildInTools, customTools, workflowTools, mcpTools])
|
}, [workflowStore, triggerPlugins, buildInTools, customTools, workflowTools, mcpTools, theme])
|
||||||
|
|
||||||
return getToolIcon
|
return getToolIcon
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ export type ToolNodeType = CommonNodeType & {
|
|||||||
params?: Record<string, any>
|
params?: Record<string, any>
|
||||||
plugin_id?: string
|
plugin_id?: string
|
||||||
provider_icon?: Collection['icon']
|
provider_icon?: Collection['icon']
|
||||||
|
provider_icon_dark?: Collection['icon_dark']
|
||||||
plugin_unique_identifier?: string
|
plugin_unique_identifier?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const convertToTriggerWithProvider = (provider: TriggerProviderApiEntity): Trigg
|
|||||||
author: provider.author,
|
author: provider.author,
|
||||||
description: provider.description,
|
description: provider.description,
|
||||||
icon: provider.icon || '',
|
icon: provider.icon || '',
|
||||||
|
icon_dark: provider.icon_dark || '',
|
||||||
label: provider.label,
|
label: provider.label,
|
||||||
type: CollectionType.trigger,
|
type: CollectionType.trigger,
|
||||||
team_credentials: {},
|
team_credentials: {},
|
||||||
|
|||||||
Reference in New Issue
Block a user