feat(web): use bg replace img

This commit is contained in:
zhaoying
2026-04-01 12:03:56 +08:00
parent c9ca5df05c
commit d3cd66fc6e
30 changed files with 104 additions and 279 deletions

View File

@@ -137,7 +137,7 @@ const Runtime: FC<{ item: ChatItem; index: number;}> = ({
key: vo.node_id,
label: <div className={clsx("rb:flex rb:justify-between rb:items-center", getStatus(vo.status))}>
<div className="rb:flex rb:items-center rb:gap-1 rb:flex-1">
{vo.icon && <img src={vo.icon} className="rb:size-4" />}
{vo.icon && <div className={`rb:size-4 rb:bg-cover ${vo.icon}`} />}
<div className="rb:wrap-break-word rb:line-clamp-1">{vo.node_name}</div>
</div>
<span>

View File

@@ -45,11 +45,7 @@ const VariableComponent: React.FC<{ nodeKey: NodeKey; data: Suggestion }> = ({
{data.isContext ? (
<span style={{ fontSize: '12px', marginRight: '4px' }}>📄</span>
) : data.group !== 'CONVERSATION' ? (
<img
src={data.nodeData?.icon}
style={{ width: '12px', height: '12px', marginRight: '4px' }}
alt=""
/>
<div className={`rb:size-4 rb:mr-1 rb:bg-cover ${data.nodeData?.icon}`} />
) : null}
{!data.isContext && data.group !== 'CONVERSATION' && (
<>

View File

@@ -288,11 +288,7 @@ const AutocompletePlugin: FC<{ options: Suggestion[], enableJinja2?: boolean }>
return (
<div key={nodeId}>
<Flex align="center" gap={4} className="rb:px-3! rb:text-[12px] rb:py-1.25! rb:font-medium rb:text-[#5B6167]">
{nodeIcon && <img
src={nodeIcon}
className="rb:size-3"
alt=""
/>}
{nodeIcon && <div className={`rb:size-3 rb:bg-cover ${nodeIcon}`} />}
{nodeName}
</Flex>
{nodeOptions.map((option) => {

View File

@@ -49,7 +49,7 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
e.dataTransfer.setData('application/json', JSON.stringify(node));
}}
>
<img src={node.icon} className="rb:size-6 rb:cursor-pointer" />
<div className={`rb:size-6 rb:cursor-pointer rb:bg-cover ${node.icon}`} />
</div>
</Tooltip>
))
@@ -77,7 +77,7 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
e.dataTransfer.setData('application/json', JSON.stringify(node));
}}
>
<img src={node.icon} className="rb:size-6" />
<div className={`rb:size-6 rb:bg-cover ${node.icon}`} />
<span className="rb:font-medium rb:text-[12px] rb:leading-4">{t(`workflow.${node.type}`)}</span>
</Flex>
))}

View File

@@ -151,7 +151,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
e.currentTarget.style.background = 'white';
}}
>
<img src={nodeType.icon} className="rb:w-4 rb:h-4" />
<div className={`rb:size-4 rb:bg-cover ${nodeType.icon}`} />
<span style={{ fontSize: '14px' }}>{t(`workflow.${nodeType.type}`)}</span>
</div>
))}

View File

@@ -52,7 +52,7 @@ const ConditionNode: ReactShapeConfig['component'] = ({ node }) => {
})}>
<NodeTools node={node} />
<Flex align="center" gap={8} className="rb:flex-1">
<img src={data.icon} className="rb:size-6" />
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
</Flex>

View File

@@ -126,7 +126,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
})}>
<NodeTools node={node} />
<Flex align="center" gap={8} className="rb:flex-1">
<img src={data.icon} className="rb:size-6" />
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
</Flex>
<div className="rb:mt-3 rb:min-h-[calc(100%-36px)] rb:w-full rb:bg-[radial-gradient(circle,#939AB1_1px,#F0F3F8_1px)] rb:shadow-[0px_2px_4px_0px_rgba(23,23,25,0.03)] rb:rounded-[10px] rb:bg-size-[12px_12px]"></div>

View File

@@ -16,7 +16,7 @@ const NormalNode: ReactShapeConfig['component'] = ({ node }) => {
})}>
<NodeTools node={node} />
<Flex align="center" gap={8} className="rb:flex-1">
<img src={data.icon} className="rb:size-6" />
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
</Flex>

View File

@@ -5,7 +5,7 @@
* @Last Modified time: 2026-03-30 15:14:02
*/
import { useEffect, useState } from 'react';
import { Popover } from 'antd';
import { Flex, Popover } from 'antd';
import { useTranslation } from 'react-i18next';
import { nodeLibrary, graphNodeLibrary, edgeAttrs, nodeWidth } from '../constant';
@@ -286,21 +286,16 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
};
const content = (
<div style={{ maxHeight: '300px', overflowY: 'auto', minWidth: `${nodeWidth}px` }}>
{nodeLibrary.map((category, categoryIndex) => {
<Flex vertical gap={16} className="rb:max-h-[300px] rb:overflow-y-auto rb:p-3" style={{ minWidth: `${nodeWidth}px` }}>
{nodeLibrary.map((category) => {
const sourceNodeData = sourceNode?.getData();
const isChildOfLoop = sourceNodeData?.cycle && graph?.getNodes().find((n: any) => n.getData()?.id === sourceNodeData.cycle && n.getData()?.type === 'loop');
const isChildOfIteration = sourceNodeData?.cycle && graph?.getNodes().find((n: any) => n.getData()?.id === sourceNodeData.cycle && n.getData()?.type === 'iteration');
let filteredNodes;
if (isChildOfLoop) {
// Use same filtering as AddNode for child nodes of loop, but allow break
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'cycle-start', 'iteration'].includes(nodeType.type));
} else if (isChildOfIteration) {
// Filter out loop and iteration nodes for children of iteration nodes, but allow break
if (isChildOfLoop || isChildOfIteration) {
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'cycle-start', 'iteration'].includes(nodeType.type));
} else {
// Original filtering for non-loop child nodes
filteredNodes = category.nodes.filter(nodeType =>
nodeType.type !== 'start' && nodeType.type !== 'cycle-start' && nodeType.type !== 'break'
);
@@ -310,36 +305,27 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
return (
<div key={category.category}>
{categoryIndex > 0 && <div style={{ height: '1px', background: '#f0f0f0', margin: '4px 0' }} />}
<div style={{ padding: '4px 12px', fontSize: '12px', color: '#999', fontWeight: 'bold' }}>
<div className="rb:font-semibold rb:mb-2 rb:text-[12px] rb:leading-4.5 rb:pl-1">
{t(`workflow.${category.category}`)}
</div>
{filteredNodes.map((nodeType) => (
<div
key={nodeType.type}
style={{
padding: '8px 12px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
onClick={() => handleNodeSelect(nodeType)}
onMouseEnter={(e) => {
e.currentTarget.style.background = '#f0f8ff';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'white';
}}
>
<img src={nodeType.icon} className="rb:w-4 rb:h-4" />
<span style={{ fontSize: '14px' }}>{t(`workflow.${nodeType.type}`)}</span>
</div>
))}
<Flex gap={6} vertical>
{filteredNodes.map((nodeType) => (
<Flex
key={nodeType.type}
align="center"
gap={8}
className="rb:rounded-xl rb:p-2! rb:border rb:border-[#EBEBEB] rb:cursor-pointer rb:hover:border rb:hover:border-[#171719]!"
onClick={() => handleNodeSelect(nodeType)}
>
<div className={`rb:size-6 rb:bg-cover ${nodeType.icon}`} />
<span className="rb:font-medium rb:text-[12px] rb:leading-4">{t(`workflow.${nodeType.type}`)}</span>
</Flex>
))}
</Flex>
</div>
);
})}
</div>
</Flex>
);
if (!tempElement) return null;

View File

@@ -73,11 +73,7 @@ const VariableSelect: FC<VariableSelectProps> = ({
>
{filterOption.nodeData?.icon && filterOption.nodeData?.name && (
<>
<img
src={filterOption.nodeData.icon}
style={{ width: '12px', height: '12px', marginRight: '4px' }}
alt=""
/>
<div className={`rb:size-3 rb:mr-1 rb:bg-cover ${filterOption.nodeData.icon}`} />
{filterOption.nodeData.name}
<span className="rb:text-[#DFE4ED] rb:mx-0.5">/</span>
</>
@@ -111,11 +107,7 @@ const VariableSelect: FC<VariableSelectProps> = ({
*/
const groupedOptions = Object.entries(groupedSuggestions).map(([_nodeId, suggestions]) => ({
label: <Flex align="center" gap={4}>
{suggestions[0].nodeData.icon && <img
src={suggestions[0].nodeData.icon}
className="rb:size-3"
alt=""
/>}
{suggestions[0].nodeData.icon && <div className={`rb:size-3 ${suggestions[0].nodeData.icon}`} />}
{suggestions[0].nodeData.name}
</Flex>,
options: suggestions.map(s => ({

View File

@@ -474,7 +474,7 @@ const Properties: FC<PropertiesProps> = ({
label: t(`workflow.${category.category}`),
options: category.nodes.filter(item => !['cycle-start', 'break'].includes(item.type)).map(node => ({
label: <div className="rb:flex rb:items-center rb:gap-2 rb:flex-1">
<img src={node.icon} className="rb:size-3.5" />
<div className={`rb:size-3.5 rb:bg-cover ${node.icon}`} />
<div className="rb:wrap-break-word rb:line-clamp-1">{t(`workflow.${node.type}`)}</div>
</div>,
value: node.type