From a871bcf5c80f4dfe48b57f026a28ab879a1cfaf0 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 02:04:51 -0700 Subject: [PATCH] Valid types --- .../document/document_message_handler.rs | 2 +- .../node_graph/node_graph_message_handler.rs | 11 ++-- .../document/node_graph/node_properties.rs | 4 +- .../document/node_graph/utility_types.rs | 5 +- .../utility_types/network_interface.rs | 47 ++++------------ .../network_interface/resolved_types.rs | 53 +++++++++++-------- frontend/src/components/views/Graph.svelte | 26 +++++---- .../src/dynamic_executor.rs | 11 ++-- 8 files changed, 78 insertions(+), 81 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index b8b0c6fcd3..d7f2257b68 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -17,7 +17,7 @@ use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; -use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate, OutputConnector}; +use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::nodes::RawBuffer; use crate::messages::portfolio::utility_types::PanelType; use crate::messages::portfolio::utility_types::PersistentData; diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 5e9bde5708..e86870f736 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -10,13 +10,13 @@ use crate::messages::portfolio::document::node_graph::utility_types::{ContextMen use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::network_interface::{ - self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource, + self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, }; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; -use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_clip_mode}; +use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed; use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; @@ -1206,14 +1206,14 @@ impl<'a> MessageHandler> for NodeG return; } + let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string(); + // Get the output types from the network interface let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { warn!("No network_metadata"); return; }; - let compatible_type = network_interface.output_type(&output_connector.unwrap(), selection_network_path).add_node_string(); - let appear_right_of_mouse = if ipp.mouse.position.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. }; let appear_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. }; let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x; @@ -1648,6 +1648,7 @@ impl<'a> MessageHandler> for NodeG has_left_input_wire, }); responses.add(NodeGraphMessage::SendSelectedNodes); + responses.add(NodeGraphMessage::SendWires); self.update_node_graph_hints(responses); } } @@ -2100,7 +2101,7 @@ impl NodeGraphMessageHandler { .popover_layout({ // Showing only compatible types let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) { - (true, false, Some(layer)) => network_interface.output_type(&OutputConnector::node(node_id, 0), &[]).add_node_string(), + (true, false, Some(layer)) => network_interface.output_type(&OutputConnector::node(layer.to_node(), 1), &[]).add_node_string(), _ => None, }; diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 7b5166c434..cbc38954bf 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -8,9 +8,9 @@ use crate::messages::prelude::*; use choice::enum_choice; use dyn_any::DynAny; use glam::{DAffine2, DVec2}; -use graph_craft::Type; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput}; +use graph_craft::{Type, concrete}; use graphene_std::NodeInputDecleration; use graphene_std::animation::RealTimeMode; use graphene_std::extract_xy::XY; @@ -1996,7 +1996,7 @@ pub struct ParameterWidgetsInfo<'a> { impl<'a> ParameterWidgetsInfo<'a> { pub fn new(node_id: NodeId, index: usize, blank_assist: bool, context: &'a mut NodePropertiesContext) -> ParameterWidgetsInfo<'a> { let (name, description) = context.network_interface.displayed_input_name_and_description(&node_id, index, context.selection_network_path); - let input_type = FrontendGraphDataType::from_type(&context.network_interface.input_type(&InputConnector::node(node_id, index), context.selection_network_path)); + let input_type = FrontendGraphDataType::displayed_type(&context.network_interface.input_type(&InputConnector::node(node_id, index), context.selection_network_path)); let document_node = context.network_interface.document_node(&node_id, context.selection_network_path); ParameterWidgetsInfo { diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index 08bfca30e3..5449046e3a 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,10 +1,11 @@ -use crate::messages::portfolio::document::utility_types::network_interface::TypeSource; use glam::IVec2; use graph_craft::document::NodeId; use graph_craft::document::value::TaggedValue; use graphene_std::Type; use std::borrow::Cow; +use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource; + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] pub enum FrontendGraphDataType { #[default] @@ -42,7 +43,7 @@ impl FrontendGraphDataType { } } - pub fn displayed_type(type_source: TypeSource) -> Self { + pub fn displayed_type(type_source: &TypeSource) -> Self { match type_source.compiled_nested_type() { Some(nested_type) => Self::from_type(&nested_type), None => Self::General, diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 07441df8c3..7df2eb2eb2 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1,5 +1,3 @@ -mod deserialization; - use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations}; use super::misc::PTZ; use super::nodes::SelectedNodes; @@ -13,25 +11,23 @@ use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode; use deserialization::deserialize_node_persistent_metadata; use glam::{DAffine2, DVec2, IVec2}; +use graph_craft::Type; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{DocumentNode, DocumentNodeImplementation, InlineRust, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork}; -use graph_craft::{ProtoNodeIdentifier, Type, concrete}; +use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork}; use graphene_std::ContextDependencies; use graphene_std::math::quad::Quad; use graphene_std::subpath::Subpath; use graphene_std::transform::Footprint; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::{PointId, Vector, VectorModificationType}; -use interpreted_executor::node_registry::NODE_REGISTRY; use kurbo::BezPath; use serde_json::{Value, json}; use std::collections::{HashMap, HashSet, VecDeque}; -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::hash::Hash; +use std::ops::Deref; mod deserialization; -mod resolved_types; -use deserialization::deserialize_node_persistent_metadata; -use std::ops::Deref; +pub mod resolved_types; /// All network modifications should be done through this API, so the fields cannot be public. However, all fields within this struct can be public since it it not possible to have a public mutable reference. #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] @@ -670,7 +666,7 @@ impl NodeNetworkInterface { let valid_types = match self.valid_input_types(&input_connector, network_path) { Ok(input_types) => input_types.iter().map(|ty| ty.to_string()).collect(), Err(e) => { - log::error!("Error getting valid types for input {input_connector}: {e}"); + log::error!("Error getting valid types for input {input_connector:?}: {e}"); Vec::new() } }; @@ -719,7 +715,7 @@ impl NodeNetworkInterface { } else if let Some(import_type_name) = output_type.compiled_nested_type_name() { import_type_name } else { - format!("Import index {}", export_index) + format!("Import index {}", *import_index) }; (import_name, description) @@ -1061,7 +1057,10 @@ impl NodeNetworkInterface { log::error!("Could not get downstream_connectors in primary_output_connected_to_layer"); return false; }; - let downstream_nodes = downstream_connectors.iter().filter_map(|connector| connector.node_id()).collect::>(); + let downstream_nodes = downstream_connectors + .iter() + .filter_map(|connector| if connector.input_index() == 0 { connector.node_id() } else { None }) + .collect::>(); downstream_nodes.iter().any(|node_id| self.is_layer(node_id, network_path)) } @@ -1444,30 +1443,6 @@ impl NodeNetworkInterface { } } -/// Gets the type for a random protonode implementation (used if there is no type from the compiled network) -fn random_protonode_implementation(protonode: &graph_craft::ProtoNodeIdentifier) -> Option<&graphene_std::NodeIOTypes> { - let mut protonode = protonode.clone(); - // TODO: Remove - if let Some((path, _generics)) = protonode.name.split_once('<') { - protonode = path.to_string().to_string().into(); - } - let Some(node_io_hashmap) = NODE_REGISTRY.get(&protonode) else { - log::error!("Could not get hashmap for proto node: {protonode:?}"); - return None; - }; - - let node_types = node_io_hashmap.keys().min_by_key(|node_io_types| { - let mut hasher = DefaultHasher::new(); - node_io_types.hash(&mut hasher); - hasher.finish() - }); - - if node_types.is_none() { - log::error!("Could not get node_types from hashmap"); - }; - node_types -} - // Private mutable getters for use within the network interface impl NodeNetworkInterface { fn network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs index 52a10b4bdf..1e099c7ca1 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs @@ -1,8 +1,14 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; -use graph_craft::Type; +use graph_craft::{ + ProtoNodeIdentifier, Type, concrete, + document::{DocumentNodeImplementation, InlineRust, NodeInput, value::TaggedValue}, +}; use graphene_std::uuid::NodeId; -use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta; +use interpreted_executor::{ + dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta}, + node_registry::NODE_REGISTRY, +}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector}; @@ -12,13 +18,6 @@ pub struct ResolvedDocumentNodeTypes { pub types: HashMap, NodeTypes>, } -#[derive(Debug, Default)] -pub struct NodeTypes { - // TODO: This is currently unused. Only the output is used - pub inputs: Vec, - pub output: Type, -} - impl ResolvedDocumentNodeTypes { pub fn update(&mut self, delta: ResolvedDocumentNodeTypesDelta) { for (path, node_type) in delta.add { @@ -56,20 +55,20 @@ impl TypeSource { pub fn compiled_nested_type(&self) -> Option<&Type> { match self { - TypeSource::Compiled(compiled_type) => Some(compiled_type.compiled_nested_type()), - TypeSource::TaggedValue(value_type) => Some(value_type.compiled_nested_type()), + TypeSource::Compiled(compiled_type) => Some(compiled_type.nested_type()), + TypeSource::TaggedValue(value_type) => Some(value_type.nested_type()), _ => None, } } // If Some, the type should be displayed in the imports/exports, if None it should be replaced with "import/export index _" - pub fn compiled_nested_type_name(self) -> Option { - self.into_compiled_nested_type().map(|ty| ty.to_string()) + pub fn compiled_nested_type_name(&self) -> Option { + self.compiled_nested_type().map(|ty| ty.to_string()) } // Used when searching for nodes in the add Node popup - pub fn add_node_string(self) -> Option { - self.into_compiled_nested_type().map(|ty| format!("type:{}", ty.to_string())) + pub fn add_node_string(&self) -> Option { + self.compiled_nested_type().map(|ty| format!("type:{}", ty.to_string())) } // The type to display in the tooltip @@ -99,8 +98,15 @@ impl NodeNetworkInterface { // If we are trying to get the input type of an unknown node, check if it has a reference to its definition and use that input type if let InputConnector::Node { node_id, input_index } = input_connector { if let Some(definition) = self.get_node_definition(node_id, network_path) { - if let Some(value) = definition.node_template.document_node.inputs.get(*input_index).cloned().and_then(|input| input.as_value()) { - return TypeSource::DocumentNodeDefinition(value.ty()); + if let Some(ty) = definition + .node_template + .document_node + .inputs + .get(*input_index) + .cloned() + .and_then(|input| input.as_value().map(|value| value.ty())) + { + return TypeSource::DocumentNodeDefinition(ty); } } } @@ -174,13 +180,14 @@ impl NodeNetworkInterface { let valid_types = implementations .iter() .filter_map(|(node_io, _)| { - if !valid_output_types.contains(&node_io.return_value) { + if !valid_output_types.iter().any(|output_type| output_type.nested_type() == node_io.return_value.nested_type()) { return None; } + let valid_inputs = (0..node_io.inputs.len()).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| { let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path); match input_type.into_compiled_nested_type() { - Some(input_type) => node_io.inputs.get(iterator_index) == Some(&input_type), + Some(input_type) => node_io.inputs.get(iterator_index).map(|input_type| input_type.nested_type()) == Some(&input_type), None => true, } }); @@ -205,7 +212,7 @@ impl NodeNetworkInterface { let Some(implementations) = NODE_REGISTRY.get(&ProtoNodeIdentifier::new(render_node)) else { return Err(format!("Protonode {render_node:?} not found in registry")); }; - Ok(implementations.iter().map(|(types, _)| types.inputs[1]).collect()) + Ok(implementations.iter().map(|(types, _)| types.inputs[1].clone()).collect()) } } } @@ -293,7 +300,7 @@ impl NodeNetworkInterface { return None; }; match implementation { - DocumentNodeImplementation::Network(node_network) => { + DocumentNodeImplementation::Network(_) => { let Some(outward_wires) = self.outward_wires(&network_path) else { log::error!("Could not get outward wires in random_downstream_protonode_from_connector"); return None; @@ -305,7 +312,7 @@ impl NodeNetworkInterface { let Some(first_input) = inputs_from_import.first().cloned() else { return None; }; - self.random_downstream_type_from_connector(&first_input, &[network_path, &[node_id]].concat()) + self.random_downstream_type_from_connector(&first_input, &[network_path, &[*node_id]].concat()) } DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 7ac2d4f162..9c8d2f8b58 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -176,6 +176,14 @@ return `M-2,-2 L${nodeWidth + 2},-2 L${nodeWidth + 2},${nodeHeight + 2} L-2,${nodeHeight + 2}z ${rectangles.join(" ")}`; } + function inputTooltip(value: FrontendGraphInput): string { + return dataTypeTooltip(value) + "\n\n" + inputConnectedToText(value) + "\n\n" + validTypesText(value); + } + + function outputTooltip(value: FrontendGraphOutput): string { + return dataTypeTooltip(value) + "\n\n" + outputConnectedToText(value); + } + function dataTypeTooltip(value: FrontendGraphInput | FrontendGraphOutput): string { return `Data Type: ${value.resolvedType}`; } @@ -317,7 +325,7 @@ style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24} style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index} > - {`${dataTypeTooltip(frontendOutput)}\n\n${outputConnectedToText(frontendOutput)}`} + {outputTooltip(frontendOutput)} {#if frontendOutput.connectedTo.length > 0} {:else} @@ -389,7 +397,7 @@ style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 8) / 24} style:--offset-top={($nodeGraph.updateImportsExports.exportPosition.y - 8) / 24 + index} > - {`${dataTypeTooltip(frontendInput)}\n\n${inputConnectedToText(frontendInput)}`} + {inputTooltip(frontendInput)} {#if frontendInput.connectedTo !== "nothing"} {:else} @@ -528,7 +536,7 @@ style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryOutput)}\n\n${outputConnectedToText(node.primaryOutput)}`} + {outputTooltip(node.primaryOutput)} {#if node.primaryOutput.connectedTo.length > 0} {#if node.primaryOutputConnectedToLayer} @@ -550,7 +558,7 @@ style:--data-color-dim={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()}-dim)`} > {#if node.primaryInput} - {`${dataTypeTooltip(node.primaryInput)}\n\n${validTypesText(node.primaryInput)}\n\n${inputConnectedToText(node.primaryInput)}`} + {inputTooltip(node.primaryInput)} {/if} {#if node.primaryInput?.connectedTo !== "nothing"} @@ -574,7 +582,7 @@ style:--data-color={`var(--color-data-${stackDataInput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${stackDataInput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(stackDataInput)}\n\n${validTypesText(stackDataInput)}\n\n${inputConnectedToText(stackDataInput)}`} + {inputTooltip(stackDataInput)} {#if stackDataInput.connectedTo !== undefined} {:else} @@ -692,7 +700,7 @@ style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryInput)}\n\n${validTypesText(node.primaryInput)}\n\n${inputConnectedToText(node.primaryInput)}`} + {inputTooltip(node.primaryInput)} {#if node.primaryInput.connectedTo !== undefined} {:else} @@ -711,7 +719,7 @@ style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(secondary)}\n\n${validTypesText(secondary)}\n\n${inputConnectedToText(secondary)}`} + {inputTooltip(secondary)} {#if secondary.connectedTo !== undefined} {:else} @@ -733,7 +741,7 @@ style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryOutput)}\n\n${outputConnectedToText(node.primaryOutput)}`} + {outputTooltip(node.primaryOutput)} {#if node.primaryOutput.connectedTo !== undefined} {:else} @@ -751,7 +759,7 @@ style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(secondary)}\n\n${outputConnectedToText(secondary)}`} + {outputTooltip(secondary)} {#if secondary.connectedTo !== undefined} {:else} diff --git a/node-graph/interpreted-executor/src/dynamic_executor.rs b/node-graph/interpreted-executor/src/dynamic_executor.rs index d97db7a77a..53f23fa50f 100644 --- a/node-graph/interpreted-executor/src/dynamic_executor.rs +++ b/node-graph/interpreted-executor/src/dynamic_executor.rs @@ -35,12 +35,19 @@ impl Default for DynamicExecutor { type Path = Box<[NodeId]>; -#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ResolvedDocumentNodeTypesDelta { pub add: Vec<(Path, NodeTypes)>, pub remove: Vec, } +#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeTypes { + // This is currently unused. Only the output is used + pub inputs: Vec, + pub output: Type, +} + impl DynamicExecutor { pub async fn new(proto_network: ProtoNetwork) -> Result { let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY); @@ -100,8 +107,6 @@ impl DynamicExecutor { pub fn document_node_types<'a>(&'a self, nodes: impl Iterator + 'a) -> impl Iterator + 'a { nodes.flat_map(|id| self.tree.source_map().get(&id).map(|(_, b)| (id, b.clone()))) - // TODO: https://github.com/GraphiteEditor/Graphite/issues/1767 - // TODO: Non exposed inputs are not added to the inputs_source_map, so they are not included in the resolved_document_node_types. The type is still available in the typing_context. This only affects the UI-only "Import" node. } }