Improved plot view
This commit is contained in:
@@ -3,7 +3,7 @@ import { useState, useEffect, useRef } from 'preact/hooks';
|
||||
import SignalPicker, { SignalOption } from './SignalPicker';
|
||||
import LuaEditor from './LuaEditor';
|
||||
import type { SignalDef, SynthGraph, SynthGraphNode } from './lib/types';
|
||||
import { inferNodeTypes, SynthType } from './lib/synthTypes';
|
||||
import { inferNodeTypes, portAccept, typesCompatible, SynthType } from './lib/synthTypes';
|
||||
import { VersionTree, DiffViewer } from './VersionHistory';
|
||||
|
||||
interface DataSource { name: string; }
|
||||
@@ -542,6 +542,14 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
const fn = graph.nodes.find(n => n.id === from);
|
||||
const tn = graph.nodes.find(n => n.id === to);
|
||||
if (!fn || !tn || !hasOutput(fn.kind) || tn.kind === 'source') return;
|
||||
// Reject a definitely type-incompatible link (e.g. a scalar into an array
|
||||
// reduction). 'unknown'/'any' ports never block — runtime typing is final.
|
||||
const { types } = inferNodeTypes(compile(graph), sourceSynthType);
|
||||
const accept = tn.kind === 'op' ? portAccept(tn.op ?? '') : 'any';
|
||||
if (!typesCompatible(types.get(from) ?? 'unknown', accept)) {
|
||||
setError(`Incompatible connection: ${accept} port can't take a ${types.get(from)} value`);
|
||||
return;
|
||||
}
|
||||
// One wire per input port: replace any existing wire on that port.
|
||||
const wires = graph.wires.filter(w => !(w.to === to && w.toPort === toPort));
|
||||
if (wires.some(w => w.from === from && w.to === to && w.toPort === toPort)) return;
|
||||
@@ -754,6 +762,20 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
}
|
||||
}
|
||||
|
||||
// Port colour class. Output ports take the node's own inferred output type;
|
||||
// input ports take what the op accepts (gray when it accepts either type).
|
||||
function portTypeClass(t: SynthType | 'any'): string {
|
||||
if (t === 'array') return ' flow-port-type-array';
|
||||
if (t === 'scalar') return ' flow-port-type-scalar';
|
||||
return ' flow-port-type-any';
|
||||
}
|
||||
function inPortTypeClass(n: GNode): string {
|
||||
return portTypeClass(n.kind === 'op' ? portAccept(n.op ?? '') : 'any');
|
||||
}
|
||||
function outPortTypeClass(n: GNode): string {
|
||||
return portTypeClass(nodeTypes.get(n.id) ?? 'unknown');
|
||||
}
|
||||
|
||||
function nodeClass(n: GNode): string {
|
||||
const cat = n.kind === 'source' ? 'trigger' : n.kind === 'output' ? 'action' : 'flow';
|
||||
const err = nodeErrors.has(n.id) ? ' flow-node-error' : '';
|
||||
@@ -846,7 +868,7 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
const label = portLabel(node, port);
|
||||
return (
|
||||
<Fragment key={port}>
|
||||
<div class="flow-port flow-port-in" title={label || `Input ${port + 1}`}
|
||||
<div class={`flow-port flow-port-in${inPortTypeClass(node)}`} title={label || `Input ${port + 1}`}
|
||||
style={`top:${PORT_TOP + port * PORT_GAP - PORT_R}px; left:${-PORT_R}px;`}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onMouseUp={(e) => { e.stopPropagation(); finishWire(node, port); }} />
|
||||
@@ -858,7 +880,7 @@ export default function SyntheticGraphEditor({ name, create, panelId, onClose, o
|
||||
);
|
||||
})}
|
||||
{hasOutput(node.kind) && (
|
||||
<div class="flow-port flow-port-out" title="Output"
|
||||
<div class={`flow-port flow-port-out${outPortTypeClass(node)}`} title="Output"
|
||||
style={`top:${PORT_TOP - PORT_R}px; right:${-PORT_R}px;`}
|
||||
onMouseDown={(e) => startWire(e, node)} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user