diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 799fa74631..72184b5b96 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -573,7 +573,7 @@ impl MessageHandler> for DocumentMes self.node_graph_handler.context_menu = None; responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None }); } - // Exit one level up if inside a nested network + // Go back up one level in the breadcrumb path if we're in a subgraph else if !self.breadcrumb_network_path.is_empty() { responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 }); } diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index a826225f16..0fb342d900 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -1226,6 +1226,30 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_ } } + // The "Image" wrapper network was replaced with the `image` proto node directly. Convert old `Network("Image")` instances to the proto node, forwarding the embedded image data so the `image` pass in `migrate_node` below turns it into a resource. + // Pre-pass for the same reason as the Brush and Transform migrations above: replacing the outer Image's network impl orphans its child paths. + let image_layers: Vec<(NodeId, Vec)> = document + .network_interface + .document_network() + .recursive_nodes() + .filter_map(|(node_id, _, path)| (document.network_interface.reference(node_id, &path) == Some(DefinitionIdentifier::Network("Image".into()))).then_some((*node_id, path))) + .collect(); + for (node_id, network_path) in &image_layers { + let _ = document.network_interface.outward_wires(network_path); + let new_reference = DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER); + let Some(definition) = resolve_document_node_type(&new_reference) else { continue }; + let mut node_template = definition.default_node_template(); + document.network_interface.replace_implementation(node_id, network_path, &mut node_template); + let Some(old_inputs) = document.network_interface.replace_inputs(node_id, network_path, &mut node_template) else { + continue; + }; + + // Forward the embedded image data into input 0, where the `migrate_node` `image` pass finds it and converts it to a resource. + if let Some(image_input) = old_inputs.into_iter().find(|input| matches!(input.as_value(), Some(TaggedValue::ImageData(_)))) { + document.network_interface.set_input(&InputConnector::node(*node_id, 0), image_input, network_path); + } + } + // Apply upgrades to each unmodified node. let nodes = document .network_interface diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index ab137a24c9..69c92db887 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -623,7 +623,11 @@ pub fn deserialize_tagged_value_with_legacy_migration<'de, D: serde::Deserialize "Graphic" | "GraphicGroup" | "Group" => return Ok(MemoHash::new(TaggedValue::TypeDefault(descriptor!(List)))), "Artboard" | "ArtboardGroup" => return Ok(MemoHash::new(TaggedValue::TypeDefault(descriptor!(List)))), "Raster" | "ImageFrame" | "RasterData" | "Image" => { - let first_element = content.as_object().and_then(|c| c.get("element")).and_then(|e| e.as_array()).and_then(|arr| arr.first()); + let first_element = content + .as_object() + .and_then(|c| c.get("element").or_else(|| c.get("instance")).or_else(|| c.get("instances"))) + .and_then(|e| e.as_array()) + .and_then(|arr| arr.first()); if let Some(image_value) = first_element { let image: Image = serde_json::from_value(image_value.clone()).map_err(serde::de::Error::custom)?; return Ok(MemoHash::new(TaggedValue::ImageData(image)));