The source picker in TargetingWiringEditor (and all executor editors) was
hardcoded to three tabs: DataPoint, Pin, and LogicGate. Users had no way to
wire any other node type as a source, and cross-server (peer) nodes were
invisible in the picker even though ClientNodeManager already holds all
connected peers’ nodes.
SourceList in NodeList.kt enumerated its type list as a hardcoded
listOf(KrillApp.DataPoint, KrillApp.Server.Pin, KrillApp.Executor.LogicGate)
and never looked beyond the local host. The P2P architecture was fully in place
in ClientNodeManager but the picker was never wired to use it.
Replaced the 3-tab SourceList with a two-step picker:
ClientNodeManager.nodes() — the live swarm
contents, not a hardcoded list; nodes with state == DELETING excluded;
sorted by KrillApp.title().ClientNodeManager already aggregates them), grouped by node.host. When
more than one host contributes nodes of the selected type, a host-name header
is shown per group (resolved from the host’s ServerMetaData.name). Each
candidate row uses NodeSummaryAndEditor(node, ViewMode.ROW) so the picker
stays visually consistent as per-type ROW composables evolve.existingSources: List<NodeIdentity> parameter to SourceList and
SourceListWithBackNavigation; wired it into TargetingWiringEditor so
already-wired nodes never appear as picker candidates.sourcePickerAvailableTypes and sourcePickerCandidates as
internal pure functions in NodeList.kt so the filtering + grouping logic
can be unit-tested without any Compose or network infrastructure.KrillApp subtypes in a picker warrants a comment
explaining why the subset is correct. If the comment cannot be written, derive
dynamically from the live swarm instead.sourcePickerCandidates is now tested for: DELETING exclusion, de-dupe
against existingSources, cross-host inclusion, and host-qualified identity
matching. Add tests here when extending the picker’s filter logic.groupBy { it.host } idiom is the canonical cross-server grouping
pattern; re-use it rather than inventing per-picker host logic.