Draw every flow as one graph, merged on what it talks to

A node type can now say which outside thing its parameters point at, and
nodes sharing one — a broker topic, a URL, a bucket — are drawn as a single
neuron on a new /brain canvas. That makes the wiring which runs between
flows through a broker visible for the first time; no single flow's canvas
can show it. The key is read off stored parameters, so a credential
reference never reaches an id.

Layout is a d3 force simulation settled once and then frozen, lit by the
socket the editor already listens to: a neuron pulses when any node behind
it publishes, and its connections light as values pass.

Fixes the message pulse while here: interpolating the stroke against the
edge's `color-mix()` resting colour went through oklab and left the gamut,
which turned every pulse on both canvases fluorescent yellow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MeiWk3Yq12n2pTvnQWYvt
This commit is contained in:
2026-08-16 22:59:31 +02:00
co-authored by Claude Fable 5
parent af3ba51571
commit 4bcd38354b
25 changed files with 790 additions and 4 deletions
+86
View File
@@ -192,6 +192,92 @@ export const Body_oauth_tokenSchema = {
title: 'Body_oauth-token'
} as const;
export const BrainEdgeSchema = {
properties: {
source: {
type: 'string',
title: 'Source'
},
target: {
type: 'string',
title: 'Target'
},
messages: {
items: {
type: 'string'
},
type: 'array',
title: 'Messages'
}
},
type: 'object',
required: ['source', 'target'],
title: 'BrainEdge',
description: 'Messages carrying values from one neuron to another.'
} as const;
export const BrainGraphSchema = {
properties: {
nodes: {
items: {
'$ref': '#/components/schemas/BrainNode'
},
type: 'array',
title: 'Nodes'
},
edges: {
items: {
'$ref': '#/components/schemas/BrainEdge'
},
type: 'array',
title: 'Edges'
}
},
type: 'object',
title: 'BrainGraph',
description: 'Every published flow at once, merged on what its nodes talk to.'
} as const;
export const BrainNodeSchema = {
properties: {
id: {
type: 'string',
title: 'Id'
},
label: {
type: 'string',
title: 'Label'
},
kind: {
type: 'string',
title: 'Kind'
},
members: {
items: {
type: 'string'
},
type: 'array',
title: 'Members'
},
flows: {
items: {
type: 'string'
},
type: 'array',
title: 'Flows'
}
},
type: 'object',
required: ['id', 'label', 'kind'],
title: 'BrainNode',
description: `One neuron: a thing the engine talks to, or a node that only computes.
Nodes of the same type pointing at the same outside thing — one broker
topic, one URL, one bucket — are a single entry here, whichever flows they
sit in. \`\`members\`\` are the \`\`flow.node_id\`\` names behind it, which is also
what the live events are keyed by.`
} as const;
export const ChannelSchema = {
properties: {
name: {