diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index c3324a16ff..6fa77698ab 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -2,7 +2,7 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument}; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::node_graph::utility_types::{ - BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, Transform, + BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphError, Transform }; use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer}; use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate}; @@ -289,6 +289,9 @@ pub enum FrontendMessage { UpdateNodeGraphNodes { nodes: Vec, }, + UpdateNodeGraphError { + error: Option, + }, UpdateVisibleNodes { nodes: Vec, }, 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 f64dd130a9..2d2e7f15ab 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 @@ -6,7 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::document_message_handler::navigation_controls; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext; -use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType}; +use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, NodeGraphError}; 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::{ @@ -793,10 +793,9 @@ impl<'a> MessageHandler> for NodeG DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x }; - let context_menu_coordinates = ((node_graph_point.x + node_graph_shift.x) as i32, (node_graph_point.y + node_graph_shift.y) as i32); - + let context_menu_coordinates = node_graph_point + node_graph_shift; self.context_menu = Some(ContextMenuInformation { - context_menu_coordinates, + context_menu_coordinates: context_menu_coordinates.into(), context_menu_data, }); @@ -1218,9 +1217,10 @@ impl<'a> MessageHandler> for NodeG 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; let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string(); + let context_menu_coordinates = point + node_graph_shift; self.context_menu = Some(ContextMenuInformation { - context_menu_coordinates: ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32), + context_menu_coordinates: context_menu_coordinates.into(), context_menu_data: ContextMenuData::CreateNode { compatible_type }, }); @@ -1640,6 +1640,8 @@ impl<'a> MessageHandler> for NodeG responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes }); responses.add(NodeGraphMessage::UpdateVisibleNodes); + let error = self.node_graph_error(network_interface, breadcrumb_network_path); + responses.add(FrontendMessage::UpdateNodeGraphError { error }); let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path); responses.add(NodeGraphMessage::UpdateImportsExports); @@ -2511,8 +2513,6 @@ impl NodeGraphMessageHandler { }; let mut nodes = Vec::new(); for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::>() { - let node_id_path = [breadcrumb_network_path, &[node_id]].concat(); - let primary_input_connector = InputConnector::node(node_id, 0); let primary_input = if network_interface @@ -2554,20 +2554,6 @@ impl NodeGraphMessageHandler { let locked = network_interface.is_locked(&node_id, breadcrumb_network_path); - let errors = network_interface - .resolved_types - .node_graph_errors - .iter() - .find(|error| error.node_path == node_id_path) - .map(|error| format!("{:?}", error.error.clone())) - .or_else(|| { - if network_interface.resolved_types.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) { - Some("Node graph type error within this node".to_string()) - } else { - None - } - }); - nodes.push(FrontendNode { id: node_id, is_layer: network_interface @@ -2586,7 +2572,6 @@ impl NodeGraphMessageHandler { previewed, visible, locked, - errors, }); } @@ -2608,6 +2593,28 @@ impl NodeGraphMessageHandler { Some(subgraph_names) } + fn node_graph_error(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Option { + let error = network_interface + .resolved_types + .node_graph_errors + .iter() + .filter(|error| error.node_path.starts_with(breadcrumb_network_path) && error.node_path.len() > breadcrumb_network_path.len()) + .next()?; + let error_node = error.node_path[breadcrumb_network_path.len()]; + let mut position = network_interface.position(&error_node, breadcrumb_network_path)?; + // Convert to graph space + position *= 24; + if network_interface.is_layer(&error_node, breadcrumb_network_path) { + position += IVec2::new(12, -12) + } + let error = if error.node_path.len() == breadcrumb_network_path.len() + 1 { + format!("{:?}", error.error) + } else { + "Node graph type error within this node".to_string() + }; + Some(NodeGraphError { position: position.into(), error }) + } + fn update_layer_panel(network_interface: &NodeNetworkInterface, selection_network_path: &[NodeId], collapsed: &CollapsedLayers, layers_panel_open: bool, responses: &mut VecDeque) { if !layers_panel_open { return; 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 7b490f26d1..66817bd743 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,4 +1,4 @@ -use glam::IVec2; +use glam::{DVec2, IVec2}; use graph_craft::document::NodeId; use graph_craft::document::value::TaggedValue; use graphene_std::Type; @@ -98,7 +98,6 @@ pub struct FrontendNode { pub visible: bool, pub locked: bool, pub previewed: bool, - pub errors: Option, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] @@ -172,7 +171,7 @@ pub enum ContextMenuData { pub struct ContextMenuInformation { // Stores whether the context menu is open and its position in graph coordinates #[serde(rename = "contextMenuCoordinates")] - pub context_menu_coordinates: (i32, i32), + pub context_menu_coordinates: FrontendXY, #[serde(rename = "contextMenuData")] pub context_menu_data: ContextMenuData, } @@ -202,3 +201,28 @@ pub enum Direction { Left, Right, } + +#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct NodeGraphError { + pub position: FrontendXY, + pub error: String, +} + +/// Stores node graph coordinates which are then transformed in svelte based on the node graph transform +#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendXY { + pub x: i32, + pub y: i32, +} + +impl From for FrontendXY { + fn from(v: DVec2) -> Self { + FrontendXY { x: v.x as i32, y: v.y as i32 } + } +} + +impl From for FrontendXY { + fn from(v: IVec2) -> Self { + FrontendXY { x: v.x as i32, y: v.y as i32 } + } +} diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 7ac2d4f162..7d10115e7e 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -180,10 +180,10 @@ return `Data Type: ${value.resolvedType}`; } - function validTypesText(value: FrontendGraphInput): string { - const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; - return `Valid Types:\n${validTypes}`; - } + // function validTypesText(value: FrontendGraphInput): string { + // const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; + // return `Valid Types:\n${validTypes}`; + // } function outputConnectedToText(output: FrontendGraphOutput): string { if (output.connectedTo.length === 0) return "Connected to nothing"; @@ -257,6 +257,27 @@ {/if} + {#if $nodeGraph.error} +
+ {$nodeGraph.error.error} + {$nodeGraph.error.error} +
+ {/if} + {#if $nodeGraph.clickTargets}
@@ -492,7 +513,6 @@ {@const layerAreaWidth = $nodeGraph.layerWidths.get(node.id) || 8} {@const layerChainWidth = $nodeGraph.chainWidths.get(node.id) || 0} {@const hasLeftInputWire = $nodeGraph.hasLeftInputWire.get(node.id) || false} - {@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
- {#if node.errors} - {node.errors} - {node.errors} - {/if}
{#if $nodeGraph.thumbnails.has(node.id)} {@html $nodeGraph.thumbnails.get(node.id)} @@ -528,7 +543,6 @@ 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)}`} {#if node.primaryOutput.connectedTo.length > 0} {#if node.primaryOutputConnectedToLayer} @@ -549,9 +563,6 @@ style:--data-color={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()})`} 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)}`} - {/if} {#if node.primaryInput?.connectedTo !== "nothing"} {#if node.primaryInputConnectedToLayer} @@ -574,7 +585,6 @@ 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)}`} {#if stackDataInput.connectedTo !== undefined} {:else} @@ -587,7 +597,7 @@ {node.displayName}
-
+
({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)} {@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)} {@const clipPathId = String(Math.random()).substring(2)} - {@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
- {#if node.errors} - {node.errors} - {node.errors} - {/if}
@@ -673,7 +677,7 @@
{#each exposedInputsOutputs as [input, output]}
- + {input !== undefined ? input.name : output.name}
@@ -692,7 +696,6 @@ 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)}`} {#if node.primaryInput.connectedTo !== undefined} {:else} @@ -711,7 +714,6 @@ 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)}`} {#if secondary.connectedTo !== undefined} {:else} @@ -733,7 +735,6 @@ 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)}`} {#if node.primaryOutput.connectedTo !== undefined} {:else} @@ -751,7 +752,6 @@ 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)}`} {#if secondary.connectedTo !== undefined} {:else} @@ -776,7 +776,6 @@
- {#if $nodeGraph.box}
{ if (data.obj.contextMenuInformation === undefined) return undefined; - const contextMenuCoordinates = { x: data.obj.contextMenuInformation.contextMenuCoordinates[0], y: data.obj.contextMenuInformation.contextMenuCoordinates[1] }; + const contextMenuCoordinates = data.obj.contextMenuInformation.contextMenuCoordinates; let contextMenuData = data.obj.contextMenuInformation.contextMenuData; if (contextMenuData.ToggleLayer !== undefined) { contextMenuData = { nodeId: contextMenuData.ToggleLayer.nodeId, currentlyIsNode: contextMenuData.ToggleLayer.currentlyIsNode }; @@ -94,6 +94,15 @@ export class UpdateNodeGraphNodes extends JsMessage { readonly nodes!: FrontendNode[]; } +export class UpdateNodeGraphError extends JsMessage { + readonly error!: NodeGraphError | undefined; +} + +export class NodeGraphError { + readonly position!: XY; + readonly error!: string; +} + export class UpdateVisibleNodes extends JsMessage { readonly nodes!: bigint[]; } @@ -1691,6 +1700,7 @@ export const messageMakers: Record = { UpdateMouseCursor, UpdateNodeGraphControlBarLayout, UpdateNodeGraphNodes, + UpdateNodeGraphError, UpdateNodeGraphSelection, UpdateNodeGraphTransform, UpdateNodeGraphWires, diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts index fd6c7c5724..cdb28f47e3 100644 --- a/frontend/src/state-providers/node-graph.ts +++ b/frontend/src/state-providers/node-graph.ts @@ -1,6 +1,7 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/editor"; +import type { NodeGraphError } from "@graphite/messages"; import { type Box, type FrontendClickTargets, @@ -25,6 +26,7 @@ import { UpdateNodeGraphTransform, UpdateNodeThumbnail, UpdateWirePathInProgress, + UpdateNodeGraphError, } from "@graphite/messages"; export function createNodeGraphState(editor: Editor) { @@ -32,6 +34,7 @@ export function createNodeGraphState(editor: Editor) { box: undefined as Box | undefined, clickTargets: undefined as FrontendClickTargets | undefined, contextMenuInformation: undefined as ContextMenuInformation | undefined, + error: undefined as NodeGraphError | undefined, layerWidths: new Map(), chainWidths: new Map(), hasLeftInputWire: new Map(), @@ -118,6 +121,12 @@ export function createNodeGraphState(editor: Editor) { return state; }); }); + editor.subscriptions.subscribeJsMessage(UpdateNodeGraphError, (updateNodeGraphError) => { + update((state) => { + state.error = updateNodeGraphError.error; + return state; + }); + }); editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => { update((state) => { state.visibleNodes = new Set(updateVisibleNodes.nodes);