Feat: Translate operator names and allow mailboxes to reference operator names #3221 (#9118)

### What problem does this PR solve?

Feat: Translate operator names and allow mailboxes to reference operator
names #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-07-30 16:16:47 +08:00
committed by GitHub
parent ffff5c2e8c
commit 840abd5239
9 changed files with 91 additions and 42 deletions

View File

@ -15,7 +15,9 @@ import { IModalProps } from '@/interfaces/common';
import { Operator } from '@/pages/agent/constant';
import { AgentInstanceContext, HandleContext } from '@/pages/agent/context';
import OperatorIcon from '@/pages/agent/operator-icon';
import { lowerFirst } from 'lodash';
import { PropsWithChildren, createContext, useContext } from 'react';
import { useTranslation } from 'react-i18next';
type OperatorItemProps = { operators: Operator[] };
@ -25,6 +27,7 @@ function OperatorItemList({ operators }: OperatorItemProps) {
const { addCanvasNode } = useContext(AgentInstanceContext);
const { nodeId, id, position } = useContext(HandleContext);
const hideModal = useContext(HideModalContext);
const { t } = useTranslation();
return (
<ul className="space-y-2">
@ -41,7 +44,7 @@ function OperatorItemList({ operators }: OperatorItemProps) {
onSelect={() => hideModal?.()}
>
<OperatorIcon name={x}></OperatorIcon>
{x}
{t(`flow.${lowerFirst(x)}`)}
</DropdownMenuItem>
);
})}

View File

@ -573,6 +573,12 @@ export const initialInvokeValues = {
proxy: '',
clean_html: false,
variables: [],
outputs: {
result: {
value: '',
type: 'string',
},
},
};
export const initialTemplateValues = {

View File

@ -1,21 +0,0 @@
.title {
flex-basis: 60px;
}
.formWrapper {
:global(.ant-form-item-label) {
font-weight: 600;
}
}
.operatorDescription {
font-size: 14px;
padding-top: 16px;
font-weight: normal;
}
.formDrawer {
:global(.ant-drawer-content-wrapper) {
transform: translateX(0) !important;
}
}

View File

@ -103,7 +103,11 @@ const FormSheet = ({
)}
<X onClick={hideModal} />
</div>
<span>{t(`${lowerFirst(operatorName)}Description`)}</span>
<span>
{t(
`${lowerFirst(operatorName === Operator.Tool ? clickedToolId : operatorName)}Description`,
)}
</span>
</section>
</SheetHeader>
<section className="pt-4 overflow-auto flex-1">

View File

@ -46,7 +46,7 @@ const Nodes: Array<Klass<LexicalNode>> = [
VariableNode,
];
type PromptContentProps = { showToolbar?: boolean };
type PromptContentProps = { showToolbar?: boolean; multiLine?: boolean };
type IProps = {
value?: string;
@ -54,7 +54,10 @@ type IProps = {
placeholder?: ReactNode;
} & PromptContentProps;
function PromptContent({ showToolbar = true }: PromptContentProps) {
function PromptContent({
showToolbar = true,
multiLine = true,
}: PromptContentProps) {
const [editor] = useLexicalComposerContext();
const [isBlur, setIsBlur] = useState(false);
const { t } = useTranslation();
@ -100,7 +103,9 @@ function PromptContent({ showToolbar = true }: PromptContentProps) {
</div>
)}
<ContentEditable
className="min-h-40 relative px-2 py-1 focus-visible:outline-none"
className={cn('relative px-2 py-1 focus-visible:outline-none', {
'min-h-40': multiLine,
})}
onBlur={handleBlur}
onFocus={handleFocus}
/>
@ -113,6 +118,7 @@ export function PromptEditor({
onChange,
placeholder,
showToolbar,
multiLine = true,
}: IProps) {
const { t } = useTranslation();
const initialConfig: InitialConfigType = {
@ -142,14 +148,22 @@ export function PromptEditor({
<LexicalComposer initialConfig={initialConfig}>
<RichTextPlugin
contentEditable={
<PromptContent showToolbar={showToolbar}></PromptContent>
<PromptContent
showToolbar={showToolbar}
multiLine={multiLine}
></PromptContent>
}
placeholder={
<div
className="absolute top-10 left-2 text-text-sub-title"
className={cn(
'absolute top-1 left-2 text-text-sub-title pointer-events-none',
{
'truncate w-[90%]': !multiLine,
},
)}
data-xxx
>
{placeholder || t('common.pleaseInput')}
{placeholder || t('common.promptPlaceholder')}
</div>
}
ErrorBoundary={LexicalErrorBoundary}

View File

@ -20,6 +20,7 @@ import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { PromptEditor } from '../components/prompt-editor';
interface InputFormFieldProps {
name: string;
@ -47,6 +48,29 @@ function InputFormField({ name, label, type }: InputFormFieldProps) {
);
}
function PromptFormField({ name, label }: InputFormFieldProps) {
const form = useFormContext();
return (
<FormField
control={form.control}
name={name}
render={({ field }) => (
<FormItem>
<FormLabel>{label}</FormLabel>
<FormControl>
<PromptEditor
{...field}
showToolbar={false}
multiLine={false}
></PromptEditor>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}
export function EmailFormWidgets() {
const { t } = useTranslate('flow');
@ -108,10 +132,22 @@ const EmailForm = ({ node }: INextOperatorForm) => {
<Form {...form}>
<FormWrapper>
<FormContainer>
<InputFormField name="to_email" label={t('toEmail')}></InputFormField>
<InputFormField name="cc_email" label={t('ccEmail')}></InputFormField>
<InputFormField name="content" label={t('content')}></InputFormField>
<InputFormField name="subject" label={t('subject')}></InputFormField>
<PromptFormField
name="to_email"
label={t('toEmail')}
></PromptFormField>
<PromptFormField
name="cc_email"
label={t('ccEmail')}
></PromptFormField>
<PromptFormField
name="content"
label={t('content')}
></PromptFormField>
<PromptFormField
name="subject"
label={t('subject')}
></PromptFormField>
<EmailFormWidgets></EmailFormWidgets>
</FormContainer>
</FormWrapper>

View File

@ -23,7 +23,9 @@ import { initialInvokeValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { FormSchema, FormSchemaType } from './schema';
import { useEditVariableRecord } from './use-edit-variable';
import { VariableDialog } from './variable-dialog';
@ -56,6 +58,8 @@ const TimeoutInput = ({ value, onChange }: TimeoutInputProps) => {
);
};
const outputList = buildOutputList(initialInvokeValues.outputs);
function InvokeForm({ node }: INextOperatorForm) {
const { t } = useTranslation();
const defaultValues = useFormValues(initialInvokeValues, node);
@ -212,6 +216,9 @@ function InvokeForm({ node }: INextOperatorForm) {
></VariableDialog>
)}
</FormWrapper>
<div className="p-5">
<Output list={outputList}></Output>
</div>
</Form>
);
}