diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts
index e78964d5..59a303f1 100644
--- a/web/src/i18n/en.ts
+++ b/web/src/i18n/en.ts
@@ -2552,6 +2552,9 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
'list-operator.input_list': 'Input list',
},
checkListHasErrors: 'Please resolve all issues in the checklist before publishing',
+ variableSelect: {
+ empty: 'No variables available',
+ },
},
emotionEngine: {
emotionEngineConfig: 'Emotion Engine Configuration',
diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts
index d206a1c6..1c3791d4 100644
--- a/web/src/i18n/zh.ts
+++ b/web/src/i18n/zh.ts
@@ -2516,6 +2516,9 @@ export const zh = {
'list-operator.input_list': '输入变量',
},
checkListHasErrors: '发布前确认检查清单中所有问题均已解决',
+ variableSelect: {
+ empty: '暂无变量',
+ },
},
emotionEngine: {
emotionEngineConfig: '情感引擎配置',
diff --git a/web/src/views/Workflow/components/Editor/nodes/VariableNode.tsx b/web/src/views/Workflow/components/Editor/nodes/VariableNode.tsx
index 5688342c..72e73220 100644
--- a/web/src/views/Workflow/components/Editor/nodes/VariableNode.tsx
+++ b/web/src/views/Workflow/components/Editor/nodes/VariableNode.tsx
@@ -48,17 +48,13 @@ const VariableComponent: React.FC<{ nodeKey: NodeKey; data: Suggestion }> = ({
return (
- {data.isContext ? (
- 📄
- ) : data.group !== 'CONVERSATION' && !data.value.includes('conv') ? (
-
- ) : }
+ {!data.isContext && data.group !== 'CONVERSATION' && !data.value.includes('conv')
+ ?
+ : null
+ }
{!data.isContext && data.group !== 'CONVERSATION' && (
<>
{!data.value.includes('conv') && <>
@@ -73,7 +69,7 @@ const VariableComponent: React.FC<{ nodeKey: NodeKey; data: Suggestion }> = ({
)}
>
)}
- {data.label}
+ {data.label}
);
};
diff --git a/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx b/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx
index f9537032..9f718826 100644
--- a/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx
+++ b/web/src/views/Workflow/components/Editor/plugin/AutocompletePlugin.tsx
@@ -2,12 +2,13 @@
* @Author: ZhaoYing
* @Date: 2025-12-23 16:22:51
* @Last Modified by: ZhaoYing
- * @Last Modified time: 2026-04-07 16:51:04
+ * @Last Modified time: 2026-04-13 11:12:18
*/
import { useEffect, useLayoutEffect, useState, useRef, type FC } from 'react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_HIGH, KEY_ENTER_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND } from 'lexical';
import { Space, Flex } from 'antd';
+import clsx from 'clsx';
import { INSERT_VARIABLE_COMMAND, CLOSE_AUTOCOMPLETE_COMMAND } from '../commands';
import type { NodeProperties } from '../../../types'
@@ -284,23 +285,23 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => {
ref={popupRef}
data-autocomplete-popup="true"
onMouseDown={(e) => e.preventDefault()}
- className="rb:fixed rb:z-1000 rb:bg-white rb:rounded-xl rb:shadow-[0px_2px_12px_0px_rgba(23,23,25,0.12)]"
+ className="rb:min-w-70 rb:max-h-57.5 rb:overflow-y-auto rb:fixed rb:z-1000 rb:bg-white rb:rounded-lg rb:border-[0.5px] rb:border-[#EBEBEB] rb:shadow-[0px_2px_6px_0px_rgba(0,0,0,0.1)] rb:py-3 rb:px-2"
style={{
top: popupPosition.top,
left: popupPosition.left,
}}
>
-
-
- {Object.entries(groupedSuggestions).map(([nodeId, nodeOptions]) => {
- const nodeName = nodeOptions[0]?.nodeData?.name || nodeId;
- const nodeIcon = nodeOptions[0]?.nodeData?.icon;
- return (
-
- {nodeName !== 'undefined' &&
- {nodeIcon && }
+
+ {Object.entries(groupedSuggestions).map(([nodeId, nodeOptions]) => {
+ const nodeName = nodeOptions[0]?.nodeData?.name || nodeId;
+ return (
+
+ {nodeName !== 'undefined' &&
+
{nodeName}
- }
+
+ }
+
{nodeOptions.map((option) => {
const globalIndex = flatOptions.indexOf(option);
const isExpanded = expandedParent?.key === option.key;
@@ -310,14 +311,13 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => {
key={option.key}
ref={(el) => { if (el) itemRefs.current.set(option.key, el); }}
data-selected={selectedIndex === globalIndex}
- className="rb:pl-6! rb:pr-3! rb:py-2!"
+ className={clsx("rb:px-2! rb:py-0.75! rb:rounded-sm rb:leading-4.5 rb:text-[#5B6167] rb:hover:bg-[#F6F6F6]", {
+ 'rb:bg-[#F6F6F6]': selectedIndex === globalIndex || isExpanded,
+ 'rb:cursor-not-allowed rb:opacity-65': option.disabled,
+ 'rb:cursor-pointer': !option.disabled,
+ })}
align="center"
justify="space-between"
- style={{
- cursor: option.disabled ? 'not-allowed' : 'pointer',
- background: (selectedIndex === globalIndex || isExpanded) ? '#f0f8ff' : 'white',
- opacity: option.disabled ? 0.5 : 1,
- }}
onClick={() => {
if (option.disabled) return;
insertMention(option);
@@ -337,26 +337,27 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => {
}
}}
>
- {option.label &&
- {option.isContext ? '📄' : `{x}`}
- {option.label}
- }
-
- {option.dataType && {option.dataType}}
- {hasChildren && ›}
+ {option.label &&
+
+ {`{x}`} {option.label}
+
+ }
+
+ {option.dataType && {option.dataType}}
+ {hasChildren && }
);
})}
-
- );
- })}
-
-
+
+
+ );
+ })}
+
{/* Child variables panel - floats to the left */}
{expandedParent?.children?.length && (
= ({ options }) => {
}}
onMouseEnter={() => setExpandedParent(expandedParent)}
>
- {/* Header */}
-
-
+
+
{expandedParent.nodeData.name}.{expandedParent.label}
{expandedParent.dataType}
@@ -377,19 +377,18 @@ const AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) => {
!child.disabled && insertMention(child)}
onMouseEnter={() => setSelectedIndex(childIndex)}
>
- {child.label}
- {child.dataType && {child.dataType}}
+ {child.label}
+ {child.dataType && {child.dataType}}
);
})}
diff --git a/web/src/views/Workflow/components/Properties/VariableSelect.tsx b/web/src/views/Workflow/components/Properties/VariableSelect.tsx
index b28d7b4f..c0207cb5 100644
--- a/web/src/views/Workflow/components/Properties/VariableSelect.tsx
+++ b/web/src/views/Workflow/components/Properties/VariableSelect.tsx
@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:40:13
* @Last Modified by: ZhaoYing
- * @Last Modified time: 2026-04-08 10:48:21
+ * @Last Modified time: 2026-04-13 11:25:40
*/
import { useState, useRef, useEffect, useLayoutEffect, type FC } from 'react'
import { createPortal } from 'react-dom'
@@ -190,20 +190,30 @@ const VariableSelect: FC
= ({
{/* Trigger */}
setOpen(o => !o)}
>
{multiple ? (
selectedValues.length > 0 ? (
-
+
{selectedValues.map(v => {
const s = suggestionMap.get(v);
if (!s) return null;
@@ -214,11 +224,11 @@ const VariableSelect: FC = ({
return (
- {!isConv && nd?.icon && }
+ {!isConv && nd?.icon && }
{!isConv && nd?.name && {nd.name}{sep}}
-
+
{parent ? <>{parent.label}{sep}{s.label}> : s.label}
= ({
);
})}
-
+
) : (
- {placeholder}
+ {placeholder}
)
) : selectedSuggestion ? (
-
- {!isConversation && nodeData?.icon && }
- {!isConversation && nodeData?.name && {nodeData.name}}
- {!isConversation && nodeData?.name && {sep}}
-
+
+ {!isConversation && nodeData?.icon && }
+ {!isConversation && nodeData?.name && {nodeData.name}}
+ {!isConversation && nodeData?.name && {sep}}
+
{parentOfSelected ? <>{parentOfSelected.label}{sep}{selectedSuggestion.label}> : selectedSuggestion.label}
@@ -266,18 +278,19 @@ const VariableSelect: FC = ({
{open && createPortal(
-
- {Object.entries(filteredGroups).map(([nodeId, suggestions]) => {
+
+ {Object.entries(filteredGroups).map(([nodeId, suggestions], index) => {
const nd = suggestions[0].nodeData;
return (
-
-
- {nd.icon && }
+
+
{nd.name}
-
+
{suggestions.map(s => {
const isSelected = multiple
? selectedValues.includes(`{{${s.value}}}`)
@@ -288,11 +301,9 @@ const VariableSelect: FC
= ({
{ if (el) itemRefs.current.set(s.key, el); }}
- className={clsx("rb:pl-6! rb:pr-3! rb:py-1.25! rb:rounded-lg!", {
- 'rb:bg-[#e6f4ff]': isSelected || isExpanded,
- 'rb:bg-white rb:hover:bg-[#F6F6F6]!': !(isSelected || isExpanded),
- 'rb:opacity-60': s.disabled,
- 'rb:cursor-not-allowed': s.disabled,
+ className={clsx("rb:px-2! rb:py-0.75! rb:rounded-sm rb:leading-4.5 rb:text-[#5B6167] rb:hover:bg-[#F6F6F6]", {
+ 'rb:bg-[#F6F6F6]': isSelected || isExpanded,
+ 'rb:cursor-not-allowed rb:opacity-65': s.disabled,
'rb:cursor-pointer': !s.disabled,
})}
align="center"
@@ -314,17 +325,16 @@ const VariableSelect: FC = ({
}
}}
>
-
+
{multiple && (
-
+
)}
- {`{x}`}
- {s.label}
-
-
- {s.dataType && {s.dataType}}
+ {`{x}`} {s.label}
+
- {hasChildren && }
+
+ {s.dataType && {s.dataType}}
+ {hasChildren && }
);
@@ -334,7 +344,7 @@ const VariableSelect: FC = ({
})}
{Object.keys(filteredGroups).length === 0 && (
- {t('workflow.variableSelect.empty', '暂无变量')}
+ {t('workflow.variableSelect.empty')}
)}
@@ -346,18 +356,13 @@ const VariableSelect: FC = ({
{open && expandedParent?.children?.length && createPortal(
setExpandedParent(expandedParent)}
>
-
!expandedParent.disabled && handleSelect(expandedParent)}
- >
+
-
- {expandedParent.nodeData.name}.{expandedParent.label}
-
+ {expandedParent.nodeData.name}.{expandedParent.label}
{expandedParent.dataType}
@@ -365,32 +370,27 @@ const VariableSelect: FC
= ({
const isSelected = multiple
? selectedValues.includes(`{{${child.value}}}`)
: `{{${child.value}}}` === value;
- const hasGrandChildren = !!child.children?.length;
return (
!child.disabled && handleSelect(child)}
>
-
+
{multiple && (
)}
- {child.label}
-
-
- {child.dataType && {child.dataType}}
- {hasGrandChildren && ›}
+ {child.label}
+
+ {child.dataType && {child.dataType}}
+
);
})}
diff --git a/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts b/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts
index 3c4ea6f7..14dcced2 100644
--- a/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts
+++ b/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts
@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-01-19 17:00:26
* @Last Modified by: ZhaoYing
- * @Last Modified time: 2026-04-08 10:12:27
+ * @Last Modified time: 2026-04-13 10:44:17
*/
/**
* useVariableList Hook
@@ -414,7 +414,7 @@ export const useVariableList = (
const pd = parentLoop.getData();
const pid = pd.id;
if (pd.type === 'loop') {
- (pd.cycle_vars || []).forEach((cv: any) => addVariable(list, keys, `${pid}_cycle_${cv.name}`, cv.name, cv.type || 'String', `${pid}.${cv.name}`, pd));
+ (pd.cycle_vars || []).forEach((cv: any) => addVariable(list, keys, `${pid}_cycle_${cv.name}`, cv.name, cv.type || 'string', `${pid}.${cv.name}`, pd));
} else if (pd.type === 'iteration' && pd.config.input.defaultValue) {
let itemType = 'object';
const iv = list.find(v => `{{${v.value}}}` === pd.config.input.defaultValue);
diff --git a/web/src/views/Workflow/components/Properties/index.tsx b/web/src/views/Workflow/components/Properties/index.tsx
index b5bc2d2e..f826edd9 100644
--- a/web/src/views/Workflow/components/Properties/index.tsx
+++ b/web/src/views/Workflow/components/Properties/index.tsx
@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:39:59
* @Last Modified by: ZhaoYing
- * @Last Modified time: 2026-04-10 17:24:19
+ * @Last Modified time: 2026-04-13 10:44:19
*/
import { type FC, useEffect, useState, useMemo } from "react";
import clsx from 'clsx'
@@ -266,7 +266,7 @@ const Properties: FC = ({
key,
label: cycleVar.name,
type: 'variable',
- dataType: cycleVar.type || 'String',
+ dataType: cycleVar.type || 'string',
value: `${parentNodeId}.${cycleVar.name}`,
nodeData: parentData,
});
@@ -643,7 +643,7 @@ const Properties: FC = ({
key: contextKey,
label: 'context',
type: 'variable',
- dataType: 'String',
+ dataType: 'string',
value: `context`,
nodeData: selectedNode.getData(),
isContext: true,
@@ -791,7 +791,7 @@ const Properties: FC = ({
key: `${selectedNode.id}_cycle_${cycleVar.name}`,
label: cycleVar.name,
type: 'variable',
- dataType: cycleVar.type || 'String',
+ dataType: cycleVar.type || 'string',
value: `${selectedNode.getData().id}.${cycleVar.name}`,
nodeData: selectedNode.getData(),
}));