fix: keep auto-layout hydration clean

This commit is contained in:
dhruv465 2026-05-31 21:49:14 +05:30
parent 3854502652
commit 242e2c2073
2 changed files with 65 additions and 12 deletions

View File

@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { import {
applyNodePositions,
defaultConfigFor, defaultConfigFor,
uniqueNodeKey, uniqueNodeKey,
} from "./flow-editor-state"; } from "./flow-editor-state";
@ -33,6 +34,48 @@ describe("uniqueNodeKey", () => {
}); });
}); });
describe("applyNodePositions", () => {
it("rounds and applies positions without changing unrelated nodes", () => {
const nodes: BuilderNode[] = [
{
node_key: "start",
node_type: "start",
config: {},
position_x: 0,
position_y: 0,
},
{
node_key: "message",
node_type: "send_message",
config: {},
position_x: 10,
position_y: 20,
},
];
expect(
applyNodePositions(nodes, {
start: { x: 10.4, y: 20.6 },
}),
).toEqual([
{
node_key: "start",
node_type: "start",
config: {},
position_x: 10,
position_y: 21,
},
{
node_key: "message",
node_type: "send_message",
config: {},
position_x: 10,
position_y: 20,
},
]);
});
});
describe("defaultConfigFor", () => { describe("defaultConfigFor", () => {
// The hook's addNode and the validator both depend on these defaults // The hook's addNode and the validator both depend on these defaults
// being self-consistent. A broken default would surface as a // being self-consistent. A broken default would surface as a

View File

@ -189,6 +189,22 @@ export function defaultConfigFor(type: NodeType): Record<string, unknown> {
} }
} }
export function applyNodePositions(
nodes: BuilderNode[],
positions: Record<string, { x: number; y: number }>,
): BuilderNode[] {
return nodes.map((n) => {
const next = positions[n.node_key];
return next
? {
...n,
position_x: Math.round(next.x),
position_y: Math.round(next.y),
}
: n;
});
}
// ============================================================ // ============================================================
// Context // Context
// ============================================================ // ============================================================
@ -443,21 +459,15 @@ export function FlowEditorProvider({
const updateNodePositions = useCallback( const updateNodePositions = useCallback(
(positions: Record<string, { x: number; y: number }>) => { (positions: Record<string, { x: number; y: number }>) => {
setState((s) => ({ // Initial Dagre layout hydration should not dirty the editor:
// opening a legacy all-zero flow must not enable Save or arm
// beforeunload before the user actually edits anything.
setStateRaw((s) => ({
...s, ...s,
nodes: s.nodes.map((n) => { nodes: applyNodePositions(s.nodes, positions),
const next = positions[n.node_key];
return next
? {
...n,
position_x: Math.round(next.x),
position_y: Math.round(next.y),
}
: n;
}),
})); }));
}, },
[setState], [],
); );
const addNode = useCallback( const addNode = useCallback(