diff --git a/frontend/src/components/FinalMaskForm.tsx b/frontend/src/components/FinalMaskForm.tsx index 6f4c86e2..bd2840b6 100644 --- a/frontend/src/components/FinalMaskForm.tsx +++ b/frontend/src/components/FinalMaskForm.tsx @@ -156,7 +156,6 @@ function TcpMaskItem({ onRemove: () => void; }) { const path = [...base, 'tcp', index]; - const type = Form.useWatch([...path, 'type'], form) as string | undefined; return (
@@ -176,47 +175,60 @@ function TcpMaskItem({ /> - {type === 'fragment' && ( - <> - - - - - - - - - - - )} - - {type === 'sudoku' && ( - <> - - - - - - - - - - - - )} - - {type === 'header-custom' && ( - - )} + + (prev as Record)[String(path[0])] !== (curr as Record)[String(path[0])] + } + > + {({ getFieldValue }) => { + const type = getFieldValue([...path, 'type']) as string | undefined; + if (type === 'fragment') { + return ( + <> + + + + + + + + + + + ); + } + if (type === 'sudoku') { + return ( + <> + + + + + + + + + + + + ); + } + if (type === 'header-custom') { + return ; + } + return null; + }} +
); } diff --git a/frontend/src/pages/inbounds/InboundFormModal.tsx b/frontend/src/pages/inbounds/InboundFormModal.tsx index 60f09c60..4765b2d1 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/InboundFormModal.tsx @@ -93,33 +93,40 @@ const { Text } = Typography; function AdvancedSliceEditor({ form, path, + wrapKey, minHeight, maxHeight, }: { form: FormInstance; path: NamePath; + // When set, the editor wraps the inner value with `{ [wrapKey]: ... }` so + // the JSON the user sees matches the wire shape's slice envelope (e.g. + // `{ "settings": { ... } }`). Edits unwrap the outer key before writing + // back to the form. Mirrors the legacy modal's wrappedConfigValue. + wrapKey?: string; minHeight?: string; maxHeight?: string; }) { - // The editor keeps a local text buffer so partial / invalid JSON typing - // doesn't clobber the form. lastEmitRef tracks the serialized form value - // at the moment we last accepted a write — if useWatch later fires with - // a different value than that, the form was changed from elsewhere - // (Stream tab toggle, sibling JSON tab edit), and we re-sync. + const serialize = (value: unknown): string => { + const inner = value ?? {}; + return JSON.stringify(wrapKey ? { [wrapKey]: inner } : inner, null, 2); + }; + const watched = Form.useWatch(path, form); const lastEmitRef = useRef(''); const [text, setText] = useState(() => { - const initial = JSON.stringify(form.getFieldValue(path) ?? {}, null, 2); + const initial = serialize(form.getFieldValue(path)); lastEmitRef.current = initial; return initial; }); useEffect(() => { - const formStr = JSON.stringify(watched ?? {}, null, 2); + const formStr = serialize(watched); if (formStr === lastEmitRef.current) return; setText(formStr); lastEmitRef.current = formStr; - }, [watched]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [watched, wrapKey]); return ( )[wrapKey] ?? {} + : parsed; + form.setFieldValue(path, toWrite); + lastEmitRef.current = JSON.stringify(wrapKey ? { [wrapKey]: toWrite } : toWrite, null, 2); } catch { // invalid JSON; keep buffer, don't push to form } @@ -2621,6 +2631,7 @@ export default function InboundFormModal({ @@ -2640,6 +2651,7 @@ export default function InboundFormModal({ @@ -2659,6 +2671,7 @@ export default function InboundFormModal({