From 548afe73be99a3d957bbee43f3bab1a19b55ae89 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 15:19:51 +0200 Subject: [PATCH 1/6] Wire document-format into graphene-cli --- Cargo.lock | 87 ++++++++++++----------- Cargo.toml | 4 +- node-graph/graph-craft/src/lib.rs | 1 + node-graph/graphene-cli/Cargo.toml | 2 + node-graph/graphene-cli/src/main.rs | 66 ++++++++--------- node-graph/libraries/resources/src/lib.rs | 2 + node-graph/preprocessor/src/lib.rs | 13 ++-- 7 files changed, 96 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0941d972f..dcc815672d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,9 +209,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" dependencies = [ "aws-lc-sys", "zeroize", @@ -219,9 +219,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -354,7 +354,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.6.4", + "objc2 0.6.3", ] [[package]] @@ -421,9 +421,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" @@ -582,7 +582,7 @@ dependencies = [ "clap", "libc", "libloading 0.9.0", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-foundation 0.3.2", "objc2-io-surface", "objc2-metal 0.3.2", @@ -1120,9 +1120,9 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deranged" -version = "0.5.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -1213,7 +1213,7 @@ dependencies = [ "bitflags 2.11.0", "block2 0.6.2", "libc", - "objc2 0.6.4", + "objc2 0.6.3", ] [[package]] @@ -1994,6 +1994,8 @@ version = "0.1.0" dependencies = [ "chrono", "clap", + "document-container", + "document-format", "fern", "futures", "graph-craft", @@ -2142,7 +2144,7 @@ dependencies = [ "interprocess", "lzma-rust2", "muda", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "open", @@ -2228,11 +2230,14 @@ dependencies = [ "bitflags 2.11.0", "color", "derivative", + "document-container", + "document-format", "dyn-any", "env_logger", "futures", "glam", "graph-craft", + "graph-storage", "graphene-hash", "graphene-std", "graphite-proc-macros", @@ -3282,7 +3287,7 @@ dependencies = [ "crossbeam-channel", "dpi", "keyboard-types", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -3451,9 +3456,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-derive" @@ -3525,9 +3530,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] @@ -3556,7 +3561,7 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.11.0", "block2 0.6.2", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -3582,7 +3587,7 @@ dependencies = [ "bitflags 2.11.0", "block2 0.6.2", "dispatch2", - "objc2 0.6.4", + "objc2 0.6.3", ] [[package]] @@ -3646,7 +3651,7 @@ dependencies = [ "bitflags 2.11.0", "block2 0.6.2", "libc", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", ] @@ -3658,7 +3663,7 @@ checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags 2.11.0", "libc", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -3684,7 +3689,7 @@ dependencies = [ "bitflags 2.11.0", "block2 0.6.2", "dispatch2", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-io-surface", @@ -3710,7 +3715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ "bitflags 2.11.0", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-metal 0.3.2", @@ -3723,7 +3728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", ] @@ -4500,7 +4505,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-quartz-core 0.3.2", @@ -4699,7 +4704,7 @@ dependencies = [ "js-sys", "libc", "log", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4843,9 +4848,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "aws-lc-rs", "log", @@ -4917,9 +4922,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "aws-lc-rs", "ring", @@ -5702,30 +5707,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -6784,7 +6789,7 @@ dependencies = [ "log", "naga", "ndk-sys", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-metal 0.3.2", @@ -7390,7 +7395,7 @@ dependencies = [ "block2 0.6.2", "dispatch2", "dpi", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", @@ -7409,7 +7414,7 @@ version = "0.30.12" source = "git+https://github.com/rust-windowing/winit.git#bd6fef1d80ba063cbe91e150b3fb343927cdc72b" dependencies = [ "memmap2 0.9.10", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "smol_str", "tracing", @@ -7457,7 +7462,7 @@ dependencies = [ "block2 0.6.2", "dispatch2", "dpi", - "objc2 0.6.4", + "objc2 0.6.3", "objc2-core-foundation", "objc2-foundation 0.3.2", "objc2-ui-kit", diff --git a/Cargo.toml b/Cargo.toml index de58ceffcc..6f1c3af456 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,8 @@ members = [ "desktop/platform/linux", "desktop/platform/mac", "desktop/platform/win", - "document/container", "document/graph-storage", + "document/container", "document/document-format", "editor", "frontend/wrapper", @@ -106,6 +106,7 @@ rustc-hash = "2.0" bytemuck = { version = "1.13", features = ["derive", "min_const_generics"] } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" +rmp-serde = "1.3" serde_bytes = "0.11" serde-wasm-bindgen = "0.6" reqwest = { version = "0.13", features = ["blocking", "json"] } @@ -172,7 +173,6 @@ color = "0.3" # Linebender ecosystem (END) rand = { version = "0.9", default-features = false, features = ["std_rng"] } rand_chacha = "0.9" -rmp-serde = "1.3" glam = { version = "0.32.1", default-features = false, features = [ "nostd-libm", "scalar-math", diff --git a/node-graph/graph-craft/src/lib.rs b/node-graph/graph-craft/src/lib.rs index 787b6789aa..de33be0e45 100644 --- a/node-graph/graph-craft/src/lib.rs +++ b/node-graph/graph-craft/src/lib.rs @@ -7,6 +7,7 @@ pub use core_types::{ProtoNodeIdentifier, Type, TypeDescriptor, concrete, descri pub mod application_io; pub mod document; +pub use document::{DocumentNode, NodeNetwork}; pub mod graphene_compiler; pub mod proto; #[cfg(feature = "loading")] diff --git a/node-graph/graphene-cli/Cargo.toml b/node-graph/graphene-cli/Cargo.toml index 05e3b78705..59bd39da0b 100644 --- a/node-graph/graphene-cli/Cargo.toml +++ b/node-graph/graphene-cli/Cargo.toml @@ -17,6 +17,8 @@ graphene-std = { workspace = true } interpreted-executor = { workspace = true } graph-craft = { workspace = true, features = ["loading"] } preprocessor = { workspace = true } +document-format = { workspace = true } +document-container = { workspace = true } # Workspace dependencies log = { workspace = true } diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs index 7c876c5d6a..47cf6ce3a7 100644 --- a/node-graph/graphene-cli/src/main.rs +++ b/node-graph/graphene-cli/src/main.rs @@ -1,15 +1,16 @@ mod export; use clap::{Args, Parser, Subcommand}; +use document_container::AnyContainer; +use document_container::backends::memory::MemoryBackend; +use document_format::{GddV1, GddV1Layout}; use fern::colors::{Color, ColoredLevelConfig}; use futures::executor::block_on; use graph_craft::application_io::EditorPreferences; -use graph_craft::application_io::resource::ResourceRegistry; use graph_craft::application_io::{PlatformApplicationIo, PlatformEditorApi}; use graph_craft::document::*; use graph_craft::graphene_compiler::Compiler; use graph_craft::proto::ProtoNetwork; -use graph_craft::util::load_network; use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender}; use interpreted_executor::dynamic_executor::DynamicExecutor; use interpreted_executor::util::wrap_network_in_scope; @@ -84,6 +85,11 @@ enum Command { duration: Option, }, ListNodeIdentifiers, + + /// Extract embedded legacy .graphite file from the new .gdd file + ExtractLegacyDoc { + document: PathBuf, + }, } #[derive(Debug, Args)] @@ -104,6 +110,7 @@ async fn main() -> Result<(), Box> { let document_path = match app.command { Command::Compile { ref document, .. } => document, Command::Export { ref document, .. } => document, + Command::ExtractLegacyDoc { ref document } => document, Command::ListNodeIdentifiers => { let mut nodes: Vec<_> = graphene_std::registry::NODE_METADATA.lock().unwrap().keys().cloned().collect(); nodes.sort_by_key(|x| x.as_str().to_string()); @@ -114,10 +121,24 @@ async fn main() -> Result<(), Box> { } }; - let document_string = std::fs::read_to_string(document_path).expect("Failed to read document"); + let archive = std::fs::read(document_path).expect("Failed to open file"); + let container = AnyContainer::Memory(MemoryBackend::new()); + let gdd = document_format::Gdd::open_from_archive(archive.as_ref(), container, GddV1Layout) + .await + .expect("Failed to open document"); + + if let Command::ExtractLegacyDoc { ref document } = app.command { + let legacy_doc = gdd.read_legacy_document().await.expect("gdd file did not contain legacy .graphite document"); + let mut new_path = document.to_path_buf(); + new_path.set_extension("graphite"); + std::fs::write(new_path.clone(), legacy_doc).expect("Failed to write write .graphite file"); + eprintln!("Saved file to {}", new_path.to_string_lossy()); + return Ok(()); + } log::info!("Creating GPU context"); - let application_io = block_on(PlatformApplicationIo::new()); + let mut application_io = block_on(PlatformApplicationIo::new()); + application_io.inject_resource_proxy(Box::new(gdd.resource_proxy())); // Convert application_io to Arc first let application_io_arc = Arc::new(application_io); @@ -137,8 +158,10 @@ async fn main() -> Result<(), Box> { node_graph_message_sender: Box::new(UpdateLogger {}), editor_preferences: Box::new(preferences), }); + let declarations = gdd.declarations(&gdd).await; + let (node_network, _metadata) = gdd.registry().to_runtime_with_metadata(&declarations)?; - let proto_graph = compile_graph(document_string, editor_api)?; + let proto_graph = compile_graph(node_network, editor_api, &gdd)?; match app.command { Command::Compile { print_proto, .. } => { @@ -218,34 +241,13 @@ fn init_logging(log_level: u8) { .unwrap(); } -// Migrations are done in the editor which is unfortunately not available here. -// TODO: remove this and share migrations between the editor and the CLI. -fn fix_nodes(network: &mut NodeNetwork) { - for node in network.nodes.values_mut() { - match &mut node.implementation { - // Recursively fix - DocumentNodeImplementation::Network(network) => fix_nodes(network), - // This replicates the migration from the editor linked: - // https://github.com/GraphiteEditor/Graphite/blob/d68f91ccca69e90e6d2df78d544d36cd1aaf348e/editor/src/messages/portfolio/portfolio_message_handler.rs#L535 - // Since the CLI doesn't have the document node definitions, a less robust method of just patching the inputs is used. - DocumentNodeImplementation::ProtoNode(proto_node_identifier) - if (proto_node_identifier.as_str().starts_with("graphene_core::ConstructLayerNode") || proto_node_identifier.as_str().starts_with("graphene_core::AddArtboardNode")) - && node.inputs.len() < 3 => - { - node.inputs.push(NodeInput::Reflection(DocumentNodeMetadata::DocumentNodePath)); - } - _ => {} - } - } -} -fn compile_graph(document_string: String, editor_api: Arc) -> Result> { - let mut network = load_network(&document_string); - fix_nodes(&mut network); - - let mut wrapped_network = wrap_network_in_scope(network, editor_api); - +fn compile_graph(mut network: NodeNetwork, editor_api: Arc, gdd: &GddV1) -> Result> { let preprocessor = preprocessor::Preprocessor::new(); - preprocessor.preprocess(&mut wrapped_network, &ResourceRegistry::default()).expect("Failed to expand network"); // TODO: actually load the resources from the document + preprocessor + .preprocess_with_resolver(&mut network, &|resource_id| gdd.registry().resources.get(&resource_id).and_then(|r| r.hash)) + .expect("Failed to expand network"); + + let wrapped_network = wrap_network_in_scope(network.clone(), editor_api); let compiler = Compiler {}; compiler.compile_single(wrapped_network).map_err(|x| x.into()) diff --git a/node-graph/libraries/resources/src/lib.rs b/node-graph/libraries/resources/src/lib.rs index 8c3e195b64..36070ae9e7 100644 --- a/node-graph/libraries/resources/src/lib.rs +++ b/node-graph/libraries/resources/src/lib.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::future::Future; use std::hash::Hash; use std::ops::Deref; +use std::path::PathBuf; use std::pin::Pin; use std::sync::Arc; @@ -291,6 +292,7 @@ pub type DataSources = Box<[DataSource]>; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum DataSource { Embedded, + FilePath(PathBuf), Url(url::Url), Font { family: String, style: Option }, } diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index 4017629ae3..c37d988634 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -7,6 +7,7 @@ use graph_craft::document::value::*; use graph_craft::document::*; use graph_craft::proto::RegistryValueSource; use graph_craft::{ProtoNodeIdentifier, concrete}; +use graphene_std::platform_application_io::ResourceHash; use graphene_std::registry::*; use graphene_std::*; use std::collections::{HashMap, HashSet}; @@ -20,8 +21,12 @@ pub struct Preprocessor { impl Preprocessor { pub fn preprocess(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> { + self.preprocess_with_resolver(network, &|resource_id| resources.hash(&resource_id)) + } + + pub fn preprocess_with_resolver(&self, network: &mut NodeNetwork, resolve_resource: &impl Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { self.insert_inject_scopes(network); - self.replace_resource_inputs(network, resources)?; + self.replace_resource_inputs(network, resolve_resource)?; self.expand_network(network); Ok(()) } @@ -41,13 +46,13 @@ impl Preprocessor { } /// Replace every `TaggedValue::Resource(hash)` input with a reference to a freshly inserted `resource` proto node. - fn replace_resource_inputs(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> { + fn replace_resource_inputs(&self, network: &mut NodeNetwork, resolve_resource: &impl Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { let mut hash_to_node_id: HashMap = HashMap::new(); let mut new_resource_nodes: Vec<(NodeId, DocumentNode)> = Vec::new(); for node in network.nodes.values_mut() { if let DocumentNodeImplementation::Network(nested) = &mut node.implementation { - self.replace_resource_inputs(nested, resources)?; + self.replace_resource_inputs(nested, resolve_resource)?; continue; } @@ -59,7 +64,7 @@ impl Preprocessor { let NodeInput::Value { tagged_value, .. } = input else { continue }; let TaggedValue::Resource(resource_id) = **tagged_value else { continue }; - let Some(hash) = resources.hash(&resource_id) else { + let Some(hash) = resolve_resource(resource_id) else { return Err(PreprocessorError::ResourceNotFound(resource_id)); }; From afa572c1173f5a36884f590c8bcb867a88b00441 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 15:41:04 +0200 Subject: [PATCH 2/6] Support loading both legacy and new graphite files in the cli --- Cargo.lock | 3 -- node-graph/graphene-cli/src/main.rs | 60 +++++++++++++++++------ node-graph/libraries/resources/src/lib.rs | 2 - 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcc815672d..f2650c16b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,14 +2230,11 @@ dependencies = [ "bitflags 2.11.0", "color", "derivative", - "document-container", - "document-format", "dyn-any", "env_logger", "futures", "glam", "graph-craft", - "graph-storage", "graphene-hash", "graphene-std", "graphite-proc-macros", diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs index 47cf6ce3a7..746df95dea 100644 --- a/node-graph/graphene-cli/src/main.rs +++ b/node-graph/graphene-cli/src/main.rs @@ -7,10 +7,12 @@ use document_format::{GddV1, GddV1Layout}; use fern::colors::{Color, ColoredLevelConfig}; use futures::executor::block_on; use graph_craft::application_io::EditorPreferences; +use graph_craft::application_io::resource::ResourceRegistry; use graph_craft::application_io::{PlatformApplicationIo, PlatformEditorApi}; use graph_craft::document::*; use graph_craft::graphene_compiler::Compiler; use graph_craft::proto::ProtoNetwork; +use graph_craft::util::load_network; use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender}; use interpreted_executor::dynamic_executor::DynamicExecutor; use interpreted_executor::util::wrap_network_in_scope; @@ -121,13 +123,23 @@ async fn main() -> Result<(), Box> { } }; - let archive = std::fs::read(document_path).expect("Failed to open file"); - let container = AnyContainer::Memory(MemoryBackend::new()); - let gdd = document_format::Gdd::open_from_archive(archive.as_ref(), container, GddV1Layout) - .await - .expect("Failed to open document"); + // Load the document by extension: `.gdd` opens the new archive format, anything else is treated as a + // legacy `.graphite` document. The legacy path has no `Gdd`, so resources fall back to the default registry. + let is_gdd = document_path.extension().is_some_and(|extension| extension.eq_ignore_ascii_case("gdd")); + + let gdd = if is_gdd { + let archive = std::fs::read(document_path).expect("Failed to open file"); + let container = AnyContainer::Memory(MemoryBackend::new()); + let gdd = document_format::Gdd::open_from_archive(archive.as_ref(), container, GddV1Layout) + .await + .expect("Failed to open document"); + Some(gdd) + } else { + None + }; if let Command::ExtractLegacyDoc { ref document } = app.command { + let gdd = gdd.as_ref().expect("ExtractLegacyDoc requires a .gdd document"); let legacy_doc = gdd.read_legacy_document().await.expect("gdd file did not contain legacy .graphite document"); let mut new_path = document.to_path_buf(); new_path.set_extension("graphite"); @@ -136,9 +148,24 @@ async fn main() -> Result<(), Box> { return Ok(()); } + // Build the runtime network: from the `.gdd` registry, or by loading a legacy `.graphite` document. + let node_network = match &gdd { + Some(gdd) => { + let declarations = gdd.declarations(gdd).await; + let (node_network, _metadata) = gdd.registry().to_runtime_with_metadata(&declarations)?; + node_network + } + None => { + let document_string = std::fs::read_to_string(document_path).expect("Failed to read document"); + load_network(&document_string) + } + }; + log::info!("Creating GPU context"); let mut application_io = block_on(PlatformApplicationIo::new()); - application_io.inject_resource_proxy(Box::new(gdd.resource_proxy())); + if let Some(gdd) = &gdd { + application_io.inject_resource_proxy(Box::new(gdd.resource_proxy())); + } // Convert application_io to Arc first let application_io_arc = Arc::new(application_io); @@ -158,10 +185,7 @@ async fn main() -> Result<(), Box> { node_graph_message_sender: Box::new(UpdateLogger {}), editor_preferences: Box::new(preferences), }); - let declarations = gdd.declarations(&gdd).await; - let (node_network, _metadata) = gdd.registry().to_runtime_with_metadata(&declarations)?; - - let proto_graph = compile_graph(node_network, editor_api, &gdd)?; + let proto_graph = compile_graph(node_network, editor_api, gdd.as_ref())?; match app.command { Command::Compile { print_proto, .. } => { @@ -241,13 +265,19 @@ fn init_logging(log_level: u8) { .unwrap(); } -fn compile_graph(mut network: NodeNetwork, editor_api: Arc, gdd: &GddV1) -> Result> { +fn compile_graph(mut network: NodeNetwork, editor_api: Arc, gdd: Option<&GddV1>) -> Result> { let preprocessor = preprocessor::Preprocessor::new(); - preprocessor - .preprocess_with_resolver(&mut network, &|resource_id| gdd.registry().resources.get(&resource_id).and_then(|r| r.hash)) - .expect("Failed to expand network"); - let wrapped_network = wrap_network_in_scope(network.clone(), editor_api); + // A `.gdd` resolves resource hashes from its registry; a legacy `.graphite` has no resource store, so it + // preprocesses against an empty registry (matching the pre-`.gdd` CLI behavior). + match gdd { + Some(gdd) => preprocessor + .preprocess_with_resolver(&mut network, &|resource_id| gdd.registry().resources.get(&resource_id).and_then(|r| r.hash)) + .expect("Failed to expand network"), + None => preprocessor.preprocess(&mut network, &ResourceRegistry::default()).expect("Failed to expand network"), + } + + let wrapped_network = wrap_network_in_scope(network, editor_api); let compiler = Compiler {}; compiler.compile_single(wrapped_network).map_err(|x| x.into()) diff --git a/node-graph/libraries/resources/src/lib.rs b/node-graph/libraries/resources/src/lib.rs index 36070ae9e7..8c3e195b64 100644 --- a/node-graph/libraries/resources/src/lib.rs +++ b/node-graph/libraries/resources/src/lib.rs @@ -4,7 +4,6 @@ use std::collections::HashMap; use std::future::Future; use std::hash::Hash; use std::ops::Deref; -use std::path::PathBuf; use std::pin::Pin; use std::sync::Arc; @@ -292,7 +291,6 @@ pub type DataSources = Box<[DataSource]>; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum DataSource { Embedded, - FilePath(PathBuf), Url(url::Url), Font { family: String, style: Option }, } From 216ccdbf02fef7c9dedc7e91494a950dbe397256 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 16:22:38 +0200 Subject: [PATCH 3/6] Address PR review: propagate CLI errors and use dyn resolver --- node-graph/graphene-cli/src/main.rs | 18 ++++++++++-------- node-graph/preprocessor/src/lib.rs | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs index 746df95dea..c931220542 100644 --- a/node-graph/graphene-cli/src/main.rs +++ b/node-graph/graphene-cli/src/main.rs @@ -128,22 +128,24 @@ async fn main() -> Result<(), Box> { let is_gdd = document_path.extension().is_some_and(|extension| extension.eq_ignore_ascii_case("gdd")); let gdd = if is_gdd { - let archive = std::fs::read(document_path).expect("Failed to open file"); + let archive = std::fs::read(document_path).map_err(|error| format!("Failed to read document {}: {error}", document_path.display()))?; let container = AnyContainer::Memory(MemoryBackend::new()); let gdd = document_format::Gdd::open_from_archive(archive.as_ref(), container, GddV1Layout) .await - .expect("Failed to open document"); + .map_err(|error| format!("Failed to open document: {error}"))?; Some(gdd) } else { None }; if let Command::ExtractLegacyDoc { ref document } = app.command { - let gdd = gdd.as_ref().expect("ExtractLegacyDoc requires a .gdd document"); - let legacy_doc = gdd.read_legacy_document().await.expect("gdd file did not contain legacy .graphite document"); - let mut new_path = document.to_path_buf(); + let Some(gdd) = &gdd else { return Err("ExtractLegacyDoc requires a .gdd document".into()) }; + let Some(legacy_doc) = gdd.read_legacy_document().await else { + return Err("gdd file did not contain a legacy .graphite document".into()); + }; + let mut new_path = document.clone(); new_path.set_extension("graphite"); - std::fs::write(new_path.clone(), legacy_doc).expect("Failed to write write .graphite file"); + std::fs::write(&new_path, legacy_doc).map_err(|error| format!("Failed to write .graphite file: {error}"))?; eprintln!("Saved file to {}", new_path.to_string_lossy()); return Ok(()); } @@ -156,13 +158,13 @@ async fn main() -> Result<(), Box> { node_network } None => { - let document_string = std::fs::read_to_string(document_path).expect("Failed to read document"); + let document_string = std::fs::read_to_string(document_path).map_err(|error| format!("Failed to read document {}: {error}", document_path.display()))?; load_network(&document_string) } }; log::info!("Creating GPU context"); - let mut application_io = block_on(PlatformApplicationIo::new()); + let mut application_io = PlatformApplicationIo::new().await; if let Some(gdd) = &gdd { application_io.inject_resource_proxy(Box::new(gdd.resource_proxy())); } diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index c37d988634..3a8ceddc02 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -24,7 +24,7 @@ impl Preprocessor { self.preprocess_with_resolver(network, &|resource_id| resources.hash(&resource_id)) } - pub fn preprocess_with_resolver(&self, network: &mut NodeNetwork, resolve_resource: &impl Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { + pub fn preprocess_with_resolver(&self, network: &mut NodeNetwork, resolve_resource: &dyn Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { self.insert_inject_scopes(network); self.replace_resource_inputs(network, resolve_resource)?; self.expand_network(network); @@ -46,7 +46,7 @@ impl Preprocessor { } /// Replace every `TaggedValue::Resource(hash)` input with a reference to a freshly inserted `resource` proto node. - fn replace_resource_inputs(&self, network: &mut NodeNetwork, resolve_resource: &impl Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { + fn replace_resource_inputs(&self, network: &mut NodeNetwork, resolve_resource: &dyn Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { let mut hash_to_node_id: HashMap = HashMap::new(); let mut new_resource_nodes: Vec<(NodeId, DocumentNode)> = Vec::new(); From dcaeaa4ca271441e0ad4155e51fe454d1195aafb Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 17:24:19 +0200 Subject: [PATCH 4/6] Fix CLI network wrap + preprocessor ordering --- node-graph/graphene-cli/Cargo.toml | 4 ++-- node-graph/graphene-cli/src/main.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node-graph/graphene-cli/Cargo.toml b/node-graph/graphene-cli/Cargo.toml index 59bd39da0b..5a7b82b9c3 100644 --- a/node-graph/graphene-cli/Cargo.toml +++ b/node-graph/graphene-cli/Cargo.toml @@ -17,8 +17,8 @@ graphene-std = { workspace = true } interpreted-executor = { workspace = true } graph-craft = { workspace = true, features = ["loading"] } preprocessor = { workspace = true } -document-format = { workspace = true } -document-container = { workspace = true } +document-format = { workspace = true, features = ["zip", "xz"] } +document-container = { workspace = true, features = ["zip", "xz"] } # Workspace dependencies log = { workspace = true } diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs index c931220542..dd0016ee3d 100644 --- a/node-graph/graphene-cli/src/main.rs +++ b/node-graph/graphene-cli/src/main.rs @@ -267,9 +267,11 @@ fn init_logging(log_level: u8) { .unwrap(); } -fn compile_graph(mut network: NodeNetwork, editor_api: Arc, gdd: Option<&GddV1>) -> Result> { +fn compile_graph(network: NodeNetwork, editor_api: Arc, gdd: Option<&GddV1>) -> Result> { let preprocessor = preprocessor::Preprocessor::new(); + let mut network = wrap_network_in_scope(network, editor_api); + // A `.gdd` resolves resource hashes from its registry; a legacy `.graphite` has no resource store, so it // preprocesses against an empty registry (matching the pre-`.gdd` CLI behavior). match gdd { @@ -279,10 +281,8 @@ fn compile_graph(mut network: NodeNetwork, editor_api: Arc, g None => preprocessor.preprocess(&mut network, &ResourceRegistry::default()).expect("Failed to expand network"), } - let wrapped_network = wrap_network_in_scope(network, editor_api); - let compiler = Compiler {}; - compiler.compile_single(wrapped_network).map_err(|x| x.into()) + compiler.compile_single(network).map_err(|x| x.into()) } fn create_executor(proto_network: ProtoNetwork) -> Result> { From 95818790a11332fe97450ce2a05c109946265902 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 17:41:53 +0200 Subject: [PATCH 5/6] Cleanup preprocessor --- editor/src/node_graph_executor/runtime.rs | 2 +- node-graph/graphene-cli/src/main.rs | 4 ++-- node-graph/interpreted-executor/benches/benchmark_util.rs | 3 +-- node-graph/preprocessor/src/lib.rs | 6 +----- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/editor/src/node_graph_executor/runtime.rs b/editor/src/node_graph_executor/runtime.rs index 6faf330a37..0140d0218d 100644 --- a/editor/src/node_graph_executor/runtime.rs +++ b/editor/src/node_graph_executor/runtime.rs @@ -348,7 +348,7 @@ impl NodeRuntime { async fn update_network(&mut self, graph: NodeNetwork) -> Result { let mut scoped_network = wrap_network_in_scope(graph, self.editor_api.clone()); - if let Err(e) = self.preprocessor.preprocess(&mut scoped_network, &self.resources) { + if let Err(e) = { self.preprocessor.preprocess(&mut scoped_network, &|resource_id| self.resources.hash(&resource_id)) } { return Err((ResolvedDocumentNodeTypesDelta::default(), e.to_string())); } diff --git a/node-graph/graphene-cli/src/main.rs b/node-graph/graphene-cli/src/main.rs index dd0016ee3d..673b0c3aa2 100644 --- a/node-graph/graphene-cli/src/main.rs +++ b/node-graph/graphene-cli/src/main.rs @@ -276,9 +276,9 @@ fn compile_graph(network: NodeNetwork, editor_api: Arc, gdd: // preprocesses against an empty registry (matching the pre-`.gdd` CLI behavior). match gdd { Some(gdd) => preprocessor - .preprocess_with_resolver(&mut network, &|resource_id| gdd.registry().resources.get(&resource_id).and_then(|r| r.hash)) + .preprocess(&mut network, &|resource_id| gdd.registry().resources.get(&resource_id).and_then(|r| r.hash)) .expect("Failed to expand network"), - None => preprocessor.preprocess(&mut network, &ResourceRegistry::default()).expect("Failed to expand network"), + None => { preprocessor.preprocess(&mut network, &|_| None) }.expect("Failed to expand network"), } let compiler = Compiler {}; diff --git a/node-graph/interpreted-executor/benches/benchmark_util.rs b/node-graph/interpreted-executor/benches/benchmark_util.rs index 08099719cf..54449ff13e 100644 --- a/node-graph/interpreted-executor/benches/benchmark_util.rs +++ b/node-graph/interpreted-executor/benches/benchmark_util.rs @@ -4,7 +4,6 @@ use futures::executor::block_on; use graph_craft::proto::ProtoNetwork; use graph_craft::util::{DEMO_ART, compile, load_from_name}; use graphene_std::application_io::EditorApi; -use graphene_std::application_io::resource::ResourceRegistry; use interpreted_executor::dynamic_executor::DynamicExecutor; use interpreted_executor::util::wrap_network_in_scope; @@ -13,7 +12,7 @@ pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) { let editor_api = std::sync::Arc::new(EditorApi::default()); let mut network = wrap_network_in_scope(network, editor_api); let preprocessor = preprocessor::Preprocessor::new(); - preprocessor.preprocess(&mut network, &ResourceRegistry::default()).unwrap(); + preprocessor.preprocess(&mut network, &|_| None).unwrap(); let proto_network = compile(network); let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap(); (executor, proto_network) diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index 3a8ceddc02..86cb11877a 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -20,11 +20,7 @@ pub struct Preprocessor { } impl Preprocessor { - pub fn preprocess(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> { - self.preprocess_with_resolver(network, &|resource_id| resources.hash(&resource_id)) - } - - pub fn preprocess_with_resolver(&self, network: &mut NodeNetwork, resolve_resource: &dyn Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { + pub fn preprocess(&self, network: &mut NodeNetwork, resolve_resource: &dyn Fn(ResourceId) -> Option) -> Result<(), PreprocessorError> { self.insert_inject_scopes(network); self.replace_resource_inputs(network, resolve_resource)?; self.expand_network(network); From da4ad4b7e55917532f0b9bef97d744f30986d2b2 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sun, 21 Jun 2026 17:48:37 +0200 Subject: [PATCH 6/6] Remove unused import --- editor/src/node_graph_executor/runtime.rs | 2 +- node-graph/preprocessor/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/node_graph_executor/runtime.rs b/editor/src/node_graph_executor/runtime.rs index 0140d0218d..d799b3817d 100644 --- a/editor/src/node_graph_executor/runtime.rs +++ b/editor/src/node_graph_executor/runtime.rs @@ -348,7 +348,7 @@ impl NodeRuntime { async fn update_network(&mut self, graph: NodeNetwork) -> Result { let mut scoped_network = wrap_network_in_scope(graph, self.editor_api.clone()); - if let Err(e) = { self.preprocessor.preprocess(&mut scoped_network, &|resource_id| self.resources.hash(&resource_id)) } { + if let Err(e) = self.preprocessor.preprocess(&mut scoped_network, &|resource_id| self.resources.hash(&resource_id)) { return Err((ResolvedDocumentNodeTypesDelta::default(), e.to_string())); } diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index 86cb11877a..4e543a7b7a 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -2,7 +2,7 @@ extern crate log; use graph_craft::Type; -use graph_craft::application_io::resource::{ResourceId, ResourceRegistry}; +use graph_craft::application_io::resource::ResourceId; use graph_craft::document::value::*; use graph_craft::document::*; use graph_craft::proto::RegistryValueSource;