Merge pull request #175 from SuanmoSuanyangTechnology/feature/ui_zy

fix(web): JinjaRender's form bugfix
This commit is contained in:
yingzhao
2026-01-22 15:18:54 +08:00
committed by GitHub
2 changed files with 55 additions and 41 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { TextNode, $createTextNode } from 'lexical';
import { TextNode, $createTextNode, $getSelection, $isRangeSelection } from 'lexical';
const Jinja2HighlightPlugin = () => {
const [editor] = useLexicalComposerContext();
@@ -18,6 +18,16 @@ const Jinja2HighlightPlugin = () => {
const parent = textNode.getParent();
if (!parent) return;
// Preserve selection
const selection = $getSelection();
let selectionOffset = null;
if ($isRangeSelection(selection)) {
const anchor = selection.anchor;
if (anchor.getNode() === textNode) {
selectionOffset = anchor.offset;
}
}
const tokens = tokenizeJinja2(text);
// Skip if no meaningful tokenization (only one text token)
@@ -85,6 +95,19 @@ const Jinja2HighlightPlugin = () => {
for (let i = 1; i < newNodes.length; i++) {
newNodes[i - 1].insertAfter(newNodes[i]);
}
// Restore selection
if (selectionOffset !== null && $isRangeSelection(selection)) {
let currentOffset = 0;
for (const node of newNodes) {
const nodeLength = node.getTextContent().length;
if (currentOffset + nodeLength >= selectionOffset) {
node.select(selectionOffset - currentOffset, selectionOffset - currentOffset);
break;
}
currentOffset += nodeLength;
}
}
}
});
}, [editor]);

View File

@@ -31,13 +31,11 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
const form = Form.useFormInstance()
const values = Form.useWatch([], form) || {}
console.log('JinjaRender values', values)
const prevMappingNamesRef = useRef<string[]>([])
const prevTemplateVarsRef = useRef<string[]>([])
const syncTimeoutRef = useRef<number | null>(null)
const isSyncingRef = useRef(false)
const lastSyncSourceRef = useRef<'mapping' | 'template' | null>(null)
const editorKeyRef = useRef(0)
// Reset refs when node changes
useEffect(() => {
@@ -68,46 +66,39 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
if (JSON.stringify(prevNames) === JSON.stringify(currentMappingNames)) return
if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current)
const activeElement = document.activeElement as HTMLElement
let updatedTemplate = String(form.getFieldValue('template') || '')
syncTimeoutRef.current = setTimeout(() => {
let updatedTemplate = String(form.getFieldValue('template') || '')
prevNames.forEach((oldName, index) => {
const newName = currentMappingNames[index]
if (newName && oldName !== newName) {
updatedTemplate = updatedTemplate.replace(
new RegExp(`{{\\s*${oldName}\\s*}}`, 'g'),
`{{${newName}}}`
)
}
})
if (updatedTemplate !== form.getFieldValue('template')) {
isSyncingRef.current = true
lastSyncSourceRef.current = 'mapping'
prevTemplateVarsRef.current = extractTemplateVars(updatedTemplate)
prevMappingNamesRef.current = currentMappingNames
form.setFieldValue('template', updatedTemplate)
requestAnimationFrame(() => {
activeElement?.focus?.()
setTimeout(() => {
isSyncingRef.current = false
lastSyncSourceRef.current = null
}, 50)
})
} else {
prevMappingNamesRef.current = currentMappingNames
prevNames.forEach((oldName, index) => {
const newName = currentMappingNames[index]
if (newName && oldName !== newName) {
updatedTemplate = updatedTemplate.replace(
new RegExp(`{{\\s*${oldName}\\s*}}`, 'g'),
`{{${newName}}}`
)
}
}, 0)
})
if (updatedTemplate !== form.getFieldValue('template')) {
isSyncingRef.current = true
lastSyncSourceRef.current = 'mapping'
prevTemplateVarsRef.current = extractTemplateVars(updatedTemplate)
prevMappingNamesRef.current = currentMappingNames
form.setFieldValue('template', updatedTemplate)
editorKeyRef.current++
setTimeout(() => {
isSyncingRef.current = false
lastSyncSourceRef.current = null
}, 0)
} else {
prevMappingNamesRef.current = currentMappingNames
}
}, [values?.mapping, selectedNode?.data?.type, form])
// Sync mapping when template variables change
useEffect(() => {
console.log('values?.template', values?.template)
if (
isSyncingRef.current ||
lastSyncSourceRef.current === 'template' ||
@@ -155,11 +146,10 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
}
})
// Remove unused mappings and duplicates
// Remove duplicates only
const seenNames = new Set<string>()
const finalMapping = updatedMapping.filter(item => {
const isUsed = templateVars.some(v => item.name === v || item.value === `{{${v}}}`)
if (!isUsed || !item.name || seenNames.has(item.name)) return false
if (!item.name || seenNames.has(item.name)) return false
seenNames.add(item.name)
return true
})
@@ -190,6 +180,7 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
<Form.Item name="template">
<MessageEditor
key={editorKeyRef.current}
title={t('workflow.config.jinja-render.template')}
isArray={false}
parentName="template"