Header removals wait for the code editor to be left: autosave saves with drop=false, so follow_header adds and renames from the header but keeps a port or setting whose parameter is gone, and leaving the editor (blur or closing the panel) saves once more with removals; source saves of a flow run one at a time

This commit is contained in:
2026-10-15 21:31:04 +02:00
parent 5ca90ffece
commit ce2ce4e3e7
10 changed files with 105 additions and 24 deletions
+27 -9
View File
@@ -1409,7 +1409,8 @@ function PanelBody({
expanded: boolean
onChange: (next: NodeDef_Input) => void
onRenameMessage: (previous: string, next: string) => void
onSaveSource: (code: string) => void
/** `drop` is false while the code is still being typed; see `settle`. */
onSaveSource: (code: string, drop: boolean) => void
onShared: () => void
onToggleExpand: () => void
}) {
@@ -1431,6 +1432,7 @@ function PanelBody({
// only overwrite the first.
const portMove = useRef<[string, string | null] | null>(null)
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
// Code typed since the editor was last left.
const pending = useRef<string | null>(null)
const save = useRef(onSaveSource)
save.current = onSaveSource
@@ -1441,7 +1443,22 @@ function PanelBody({
clearTimeout(timer.current)
timer.current = null
}
if (pending.current !== null) save.current(pending.current)
if (pending.current !== null) save.current(pending.current, false)
}
/**
* The editor was left, so the code is saved once more, now dropping the
* ports and settings its header no longer names. Saves while typing only
* add and rename, so a parameter deleted mid-edit keeps its port and
* message until then, and typing it back changes nothing.
*/
const settle = () => {
if (timer.current) {
clearTimeout(timer.current)
timer.current = null
}
if (pending.current !== null) save.current(pending.current, true)
pending.current = null
}
const editCode = (next: string) => {
@@ -1450,7 +1467,7 @@ function PanelBody({
if (timer.current) clearTimeout(timer.current)
timer.current = setTimeout(() => {
timer.current = null
save.current(next)
save.current(next, false)
}, 1000)
}
@@ -1520,13 +1537,13 @@ function PanelBody({
["mod+s"],
)
// Closing the panel must not lose the last keystrokes.
// Closing the panel leaves the editor too, and must not lose the last
// keystrokes.
useEffect(() => {
return () => {
if (timer.current) {
clearTimeout(timer.current)
if (pending.current !== null) save.current(pending.current)
}
if (timer.current) clearTimeout(timer.current)
if (pending.current !== null) save.current(pending.current, true)
pending.current = null
}
}, [])
@@ -1704,6 +1721,7 @@ function PanelBody({
<NodeEditor
value={code ?? source?.code ?? ""}
onChange={editCode}
onBlur={settle}
readOnly={Boolean(origin) || !mayEditCode}
/>
</Suspense>
@@ -1745,7 +1763,7 @@ export function NodePanel({
expanded: boolean
onChange: (next: NodeDef_Input) => void
onRenameMessage: (previous: string, next: string) => void
onSaveSource: (code: string) => void
onSaveSource: (code: string, drop: boolean) => void
onShared: () => void
onToggleExpand: () => void
onClose: () => void