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 { useEffect } from 'react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { TextNode, $createTextNode } from 'lexical'; import { TextNode, $createTextNode, $getSelection, $isRangeSelection } from 'lexical';
const Jinja2HighlightPlugin = () => { const Jinja2HighlightPlugin = () => {
const [editor] = useLexicalComposerContext(); const [editor] = useLexicalComposerContext();
@@ -18,6 +18,16 @@ const Jinja2HighlightPlugin = () => {
const parent = textNode.getParent(); const parent = textNode.getParent();
if (!parent) return; 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); const tokens = tokenizeJinja2(text);
// Skip if no meaningful tokenization (only one text token) // Skip if no meaningful tokenization (only one text token)
@@ -85,6 +95,19 @@ const Jinja2HighlightPlugin = () => {
for (let i = 1; i < newNodes.length; i++) { for (let i = 1; i < newNodes.length; i++) {
newNodes[i - 1].insertAfter(newNodes[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]); }, [editor]);

View File

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