fix(editor): Delete/Backspace key removes selected node + orphan edges

React Flow's built-in keyboard handling was silently no-op'd because
deleteKeyCode wasn't set (v12 defaults it to null when the prop is absent
in the strict TS types we consume). Set it to ['Delete', 'Backspace'] and
add an onNodesDelete handler that filters edges whose endpoints just
vanished — applyNodeChanges only touches nodes, so without this the graph
kept dangling edges.

Also fixes a tsc regression the same touch caught in ConnectionsPage.test
where a request.json() cast leaked 'never' back into a later access.
This commit is contained in:
Claude
2026-08-28 19:51:37 +00:00
parent cd5827cca7
commit 8c2d630af5
2 changed files with 13 additions and 2 deletions

View File

@@ -4,6 +4,8 @@ import {
useReactFlow, type Connection, type Edge, type Node, type NodeChange, type EdgeChange,
applyNodeChanges, applyEdgeChanges,
} from "@xyflow/react";
const DELETE_KEYS = ["Delete", "Backspace"];
import { nodeTypes as vcNodeTypes } from "./customNode";
import type { VcNodeData } from "./graphAdapter";
@@ -51,6 +53,14 @@ function Inner({
onEdgesChange={handleEdges}
onConnect={onConnect}
onSelectionChange={({ nodes: sel }) => onSelectNode(sel[0]?.id ?? null)}
onNodesDelete={(deleted) => {
// React Flow removes the nodes via applyNodeChanges above; it does
// NOT auto-remove edges whose endpoints just vanished. Filter here
// so the graph stays valid.
const gone = new Set(deleted.map((n) => n.id));
onEdgesChange(edges.filter((e) => !gone.has(e.source) && !gone.has(e.target)));
}}
deleteKeyCode={DELETE_KEYS}
nodeTypes={vcNodeTypes}
fitView
>

View File

@@ -38,9 +38,10 @@ describe("ConnectionsPage", () => {
{ id: "c1", providerId: "nats", name: "local", config: { url: "nats://old" } },
] as never)),
http.put("/api/connections/c1", async ({ request }) => {
latestPut = await request.json() as never;
const body = await request.json() as { name?: string; config?: { url?: string; pass?: string } };
latestPut = body;
return HttpResponse.json({
id: "c1", providerId: "nats", name: latestPut!.name, config: { url: latestPut!.config?.url },
id: "c1", providerId: "nats", name: body.name, config: { url: body.config?.url },
} as never);
}),
);