import type { FC } from 'react' import { memo, useCallback, useMemo, useSyncExternalStore, } from 'react' import { useStore as useAppStore } from '@/app/components/app/store' import AppIcon from '@/app/components/base/app-icon' import { useFeaturesStore } from '@/app/components/base/features/hooks' import { Folder as FolderLine } from '@/app/components/base/icons/src/vender/line/files' import { Agent, Answer, Assigner, Code, Datasource, DocsExtractor, End, Home, Http, HumanInLoop, IfElse, Iteration, KnowledgeBase, KnowledgeRetrieval, ListFilter, Llm, Loop, LoopEnd, ParameterExtractor, QuestionClassifier, Schedule, TemplatingTransform, VariableX, WebhookLine, WindowCursor, } from '@/app/components/base/icons/src/vender/workflow' import { STORAGE_KEYS } from '@/config/storage-keys' import { cn } from '@/utils/classnames' import { storage } from '@/utils/storage' import { BlockEnum } from './types' type BlockIconProps = { type: BlockEnum size?: string className?: string toolIcon?: string | { content: string, background: string } } const ICON_CONTAINER_CLASSNAME_SIZE_MAP: Record = { xs: 'w-4 h-4 rounded-[5px] shadow-xs', sm: 'w-5 h-5 rounded-md shadow-xs', md: 'w-6 h-6 rounded-lg shadow-md', } const DEFAULT_ICON_MAP: Record> = { [BlockEnum.Start]: Home, [BlockEnum.LLM]: Llm, [BlockEnum.Code]: Code, [BlockEnum.Command]: WindowCursor, [BlockEnum.FileUpload]: DocsExtractor, [BlockEnum.End]: End, [BlockEnum.IfElse]: IfElse, [BlockEnum.HttpRequest]: Http, [BlockEnum.Answer]: Answer, [BlockEnum.KnowledgeRetrieval]: KnowledgeRetrieval, [BlockEnum.QuestionClassifier]: QuestionClassifier, [BlockEnum.TemplateTransform]: TemplatingTransform, [BlockEnum.VariableAssigner]: VariableX, [BlockEnum.VariableAggregator]: VariableX, [BlockEnum.Group]: FolderLine, [BlockEnum.Assigner]: Assigner, [BlockEnum.Tool]: VariableX, [BlockEnum.IterationStart]: VariableX, [BlockEnum.Iteration]: Iteration, [BlockEnum.LoopStart]: VariableX, [BlockEnum.Loop]: Loop, [BlockEnum.LoopEnd]: LoopEnd, [BlockEnum.ParameterExtractor]: ParameterExtractor, [BlockEnum.DocExtractor]: DocsExtractor, [BlockEnum.ListFilter]: ListFilter, [BlockEnum.Agent]: Agent, [BlockEnum.KnowledgeBase]: KnowledgeBase, [BlockEnum.DataSource]: Datasource, [BlockEnum.DataSourceEmpty]: () => null, [BlockEnum.TriggerSchedule]: Schedule, [BlockEnum.TriggerWebhook]: WebhookLine, [BlockEnum.TriggerPlugin]: VariableX, [BlockEnum.HumanInput]: HumanInLoop, } const getIcon = (type: BlockEnum, className: string) => { const DefaultIcon = DEFAULT_ICON_MAP[type] if (!DefaultIcon) return null return } const ICON_CONTAINER_BG_COLOR_MAP: Record = { [BlockEnum.Start]: 'bg-util-colors-blue-brand-blue-brand-500', [BlockEnum.LLM]: 'bg-util-colors-indigo-indigo-500', [BlockEnum.Code]: 'bg-util-colors-blue-blue-500', [BlockEnum.Command]: 'bg-util-colors-blue-blue-500', [BlockEnum.FileUpload]: 'bg-util-colors-green-green-500', [BlockEnum.End]: 'bg-util-colors-warning-warning-500', [BlockEnum.IfElse]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.Iteration]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.Loop]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.LoopEnd]: 'bg-util-colors-warning-warning-500', [BlockEnum.HttpRequest]: 'bg-util-colors-violet-violet-500', [BlockEnum.Answer]: 'bg-util-colors-warning-warning-500', [BlockEnum.KnowledgeRetrieval]: 'bg-util-colors-green-green-500', [BlockEnum.QuestionClassifier]: 'bg-util-colors-green-green-500', [BlockEnum.TemplateTransform]: 'bg-util-colors-blue-blue-500', [BlockEnum.VariableAssigner]: 'bg-util-colors-blue-blue-500', [BlockEnum.VariableAggregator]: 'bg-util-colors-blue-blue-500', [BlockEnum.Tool]: 'bg-util-colors-blue-blue-500', [BlockEnum.Group]: 'bg-util-colors-blue-blue-500', [BlockEnum.Assigner]: 'bg-util-colors-blue-blue-500', [BlockEnum.ParameterExtractor]: 'bg-util-colors-blue-blue-500', [BlockEnum.DocExtractor]: 'bg-util-colors-green-green-500', [BlockEnum.ListFilter]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.Agent]: 'bg-util-colors-indigo-indigo-500', [BlockEnum.HumanInput]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.KnowledgeBase]: 'bg-util-colors-warning-warning-500', [BlockEnum.DataSource]: 'bg-components-icon-bg-midnight-solid', [BlockEnum.TriggerSchedule]: 'bg-util-colors-violet-violet-500', [BlockEnum.TriggerWebhook]: 'bg-util-colors-blue-blue-500', [BlockEnum.TriggerPlugin]: 'bg-util-colors-blue-blue-500', } const useDisplayBlockType = (type: BlockEnum) => { const appDetail = useAppStore(s => s.appDetail) const featuresStore = useFeaturesStore() const subscribe = useCallback((listener: () => void) => { if (!featuresStore) return () => {} return featuresStore.subscribe(listener) }, [featuresStore]) const getSnapshot = useCallback(() => { if (!featuresStore) return false return featuresStore.getState().features.sandbox?.enabled ?? false }, [featuresStore]) const isSandboxFeatureEnabled = useSyncExternalStore(subscribe, getSnapshot, () => false) const isSandboxRuntime = appDetail?.runtime_type === 'sandboxed' const isSandboxSelection = useMemo(() => { if (!appDetail?.id) return false return storage.getBoolean(`${STORAGE_KEYS.LOCAL.WORKFLOW.SANDBOX_RUNTIME_PREFIX}${appDetail.id}`) === true }, [appDetail?.id]) const isSandboxed = isSandboxRuntime || isSandboxFeatureEnabled || isSandboxSelection return isSandboxed && type === BlockEnum.LLM ? BlockEnum.Agent : type } const BlockIcon: FC = ({ type, size = 'sm', className, toolIcon, }) => { const displayType = useDisplayBlockType(type) const isToolOrDataSourceOrTriggerPlugin = displayType === BlockEnum.Tool || displayType === BlockEnum.DataSource || displayType === BlockEnum.TriggerPlugin const showDefaultIcon = !isToolOrDataSourceOrTriggerPlugin || !toolIcon return (
{ showDefaultIcon && ( getIcon(displayType, (displayType === BlockEnum.TriggerSchedule || displayType === BlockEnum.TriggerWebhook) ? (size === 'xs' ? 'w-4 h-4' : 'w-4.5 h-4.5') : (size === 'xs' ? 'w-3 h-3' : 'w-3.5 h-3.5')) ) } { !showDefaultIcon && ( <> { typeof toolIcon === 'string' ? (
) : ( ) } ) }
) } export const VarBlockIcon: FC = ({ type, className, }) => { const displayType = useDisplayBlockType(type) return ( <> {getIcon(displayType, `w-3 h-3 ${className}`)} ) } export default memo(BlockIcon)