Skip to content

Commit 179d109

Browse files
committed
fix: graph upsert on re-execute, node name labels, optional port outlines, remove const inputs
1. platform_create_function_graph: SELECT existing graph by (store_id, name) before INSERT — prevents duplicate key on re-execute 2. Node name label (e.g. 'string1', 'send-email1') shown above each node on the canvas in both compact and rich rendering modes 3. Optional input ports render as outlines (hollow circle with colored stroke) vs required ports which are solid filled. Subtle visual cue. 4. Const nodes (const/string, const/number, const/boolean) no longer get a spurious 'payload' input — platformFnToDefinition returns empty inputs array when none are defined instead of the fallback.
1 parent 004447c commit 179d109

4 files changed

Lines changed: 76 additions & 35 deletions

File tree

packages/fbp-graph-editor/src/components/GraphNode.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
152152
onMouseDown={handleMouseDown}
153153
style={{ cursor: isDragging ? 'grabbing' : 'grab' }}
154154
>
155+
{/* Node name label above */}
156+
<text
157+
x={width / 2}
158+
y={-8}
159+
textAnchor="middle"
160+
fill="#52525b"
161+
fontSize={10}
162+
fontFamily="system-ui, sans-serif"
163+
>
164+
{node.name}
165+
</text>
155166
{/* Shadow */}
156167
<rect
157168
x={3} y={3}
@@ -238,13 +249,14 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
238249
const isValidDrop = state.connecting.active && state.connecting.isOutput && state.connecting.sourceNode !== node.name;
239250
const isHov = hoveredPort?.name === port.name && !hoveredPort?.isOutput;
240251
const highlight = isValidDrop && isHov;
252+
const isOptional = 'optional' in port && port.optional;
241253
return (
242254
<g key={`input-${port.name}`}>
243255
<circle
244256
cx={0} cy={portY}
245257
r={PORT_RADIUS}
246-
fill={highlight ? '#60a5fa' : '#3b82f6'}
247-
stroke="#18181b"
258+
fill={highlight ? '#60a5fa' : isOptional ? '#18181b' : '#3b82f6'}
259+
stroke={isOptional ? '#3b82f6' : '#18181b'}
248260
strokeWidth={2}
249261
style={{ cursor: 'crosshair' }}
250262
onMouseDown={(e) => handlePortMouseDown(e, port.name, false, y + portY, x)}
@@ -255,7 +267,7 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
255267
<text
256268
x={12} y={portY}
257269
dominantBaseline="middle"
258-
fill="#a1a1aa"
270+
fill={isOptional ? '#64748b' : '#a1a1aa'}
259271
fontSize={11}
260272
fontFamily="system-ui, sans-serif"
261273
>
@@ -312,6 +324,17 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
312324
onMouseDown={handleMouseDown}
313325
style={{ cursor: isDragging ? 'grabbing' : 'grab' }}
314326
>
327+
{/* Node name label above */}
328+
<text
329+
x={NODE_WIDTH / 2}
330+
y={-8}
331+
textAnchor="middle"
332+
fill="#52525b"
333+
fontSize={10}
334+
fontFamily="system-ui, sans-serif"
335+
>
336+
{node.name}
337+
</text>
315338
{/* Shadow */}
316339
<rect
317340
x={3} y={3}
@@ -394,14 +417,16 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
394417
const isValidDropTarget = state.connecting.active && state.connecting.isOutput && state.connecting.sourceNode !== node.name;
395418
const isHovered = hoveredPort?.name === port.name && hoveredPort?.isOutput === false;
396419
const highlight = isValidDropTarget && isHovered;
420+
const isOptional = 'optional' in port && port.optional;
421+
const portColor = getPortColor(port.type);
397422
return (
398423
<g key={`input-${port.name}`} transform={`translate(0, ${NODE_HEADER_HEIGHT + i * PORT_HEIGHT})`}>
399424
<circle
400425
cx={0}
401426
cy={PORT_HEIGHT / 2}
402427
r={PORT_RADIUS}
403-
fill={highlight ? '#60a5fa' : getPortColor(port.type)}
404-
stroke="#18181b"
428+
fill={highlight ? '#60a5fa' : isOptional ? '#18181b' : portColor}
429+
stroke={isOptional ? portColor : '#18181b'}
405430
strokeWidth={2}
406431
style={{ cursor: 'crosshair' }}
407432
onMouseDown={(e) => handlePortMouseDown(e, port.name, false, y + NODE_HEADER_HEIGHT + i * PORT_HEIGHT + PORT_HEIGHT / 2, x)}
@@ -413,7 +438,7 @@ export function GraphNode({ node, onStartConnect, onEndConnect }: GraphNodeProps
413438
x={12}
414439
y={PORT_HEIGHT / 2}
415440
dominantBaseline="middle"
416-
fill="#a1a1aa"
441+
fill={isOptional ? '#64748b' : '#a1a1aa'}
417442
fontSize={11}
418443
fontFamily="system-ui, sans-serif"
419444
>

pgpm/constructive-compute/deploy/schemas/constructive_compute_public/procedures/platform_create_function_graph/procedure.sql

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,27 @@ BEGIN
3333
RETURNING id INTO v_store_id;
3434
PERFORM "constructive_platform_function_graph_public".init_empty_repo(platform_create_function_graph.database_id, v_store_id);
3535
END IF;
36-
INSERT INTO "constructive_compute_public".platform_function_graphs (
37-
database_id,
38-
store_id,
39-
name,
40-
description,
41-
context,
42-
entity_id,
43-
created_by,
44-
definitions_commit_id
45-
)
46-
VALUES
47-
(platform_create_function_graph.database_id, v_store_id, platform_create_function_graph.name, platform_create_function_graph.description, platform_create_function_graph.context, platform_create_function_graph.entity_id, platform_create_function_graph.created_by, platform_create_function_graph.definitions_commit_id)
48-
RETURNING id INTO v_graph_id;
36+
SELECT g.id
37+
FROM "constructive_compute_public".platform_function_graphs AS g
38+
WHERE
39+
g.store_id = v_store_id
40+
AND g.name = platform_create_function_graph.name
41+
INTO v_graph_id;
42+
IF v_graph_id IS NULL THEN
43+
INSERT INTO "constructive_compute_public".platform_function_graphs (
44+
database_id,
45+
store_id,
46+
name,
47+
description,
48+
context,
49+
entity_id,
50+
created_by,
51+
definitions_commit_id
52+
)
53+
VALUES
54+
(platform_create_function_graph.database_id, v_store_id, platform_create_function_graph.name, platform_create_function_graph.description, platform_create_function_graph.context, platform_create_function_graph.entity_id, platform_create_function_graph.created_by, platform_create_function_graph.definitions_commit_id)
55+
RETURNING id INTO v_graph_id;
56+
END IF;
4957
RETURN v_graph_id;
5058
END;
5159
$_PGFN_$ LANGUAGE plpgsql VOLATILE SECURITY INVOKER;

pgpm/constructive-compute/sql/constructive-compute--0.0.1.sql

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -858,19 +858,27 @@ BEGIN
858858
RETURNING id INTO v_store_id;
859859
PERFORM "constructive_platform_function_graph_public".init_empty_repo(platform_create_function_graph.database_id, v_store_id);
860860
END IF;
861-
INSERT INTO "constructive_compute_public".platform_function_graphs (
862-
database_id,
863-
store_id,
864-
name,
865-
description,
866-
context,
867-
entity_id,
868-
created_by,
869-
definitions_commit_id
870-
)
871-
VALUES
872-
(platform_create_function_graph.database_id, v_store_id, platform_create_function_graph.name, platform_create_function_graph.description, platform_create_function_graph.context, platform_create_function_graph.entity_id, platform_create_function_graph.created_by, platform_create_function_graph.definitions_commit_id)
873-
RETURNING id INTO v_graph_id;
861+
SELECT g.id
862+
FROM "constructive_compute_public".platform_function_graphs AS g
863+
WHERE
864+
g.store_id = v_store_id
865+
AND g.name = platform_create_function_graph.name
866+
INTO v_graph_id;
867+
IF v_graph_id IS NULL THEN
868+
INSERT INTO "constructive_compute_public".platform_function_graphs (
869+
database_id,
870+
store_id,
871+
name,
872+
description,
873+
context,
874+
entity_id,
875+
created_by,
876+
definitions_commit_id
877+
)
878+
VALUES
879+
(platform_create_function_graph.database_id, v_store_id, platform_create_function_graph.name, platform_create_function_graph.description, platform_create_function_graph.context, platform_create_function_graph.entity_id, platform_create_function_graph.created_by, platform_create_function_graph.definitions_commit_id)
880+
RETURNING id INTO v_graph_id;
881+
END IF;
874882
RETURN v_graph_id;
875883
END;
876884
$EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY INVOKER;

www/src/components/FlowsPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ interface StoreEntry {
7878

7979

8080
function platformFnToDefinition(fn: FunctionNode): NodeDefinition {
81-
const inputs = (fn.inputs && fn.inputs.length > 0)
82-
? fn.inputs.map(p => ({ name: p.name, type: p.type, description: p.description }))
83-
: [{ name: 'payload', type: 'json' }];
81+
const inputs = fn.inputs
82+
? fn.inputs.map(p => ({ name: p.name, type: p.type, description: p.description, optional: p.optional }))
83+
: [];
8484
const outputs = (fn.outputs && fn.outputs.length > 0)
8585
? fn.outputs.map(p => ({ name: p.name, type: p.type, description: p.description }))
8686
: [{ name: 'result', type: 'json' }];

0 commit comments

Comments
 (0)