### Run Development Build Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Execute this command in the project directory to start the development server. It checks dependencies, installs missing ones, and hot-reloads the web app on file changes. ```sh cargo run ``` -------------------------------- ### Get Help with Build Commands Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Run this command to see a list of available build commands and options for the project. ```sh cargo run help ``` -------------------------------- ### Transform Node Example with ModifyFootprint Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/rfcs/fine-grained-context-caching.md This example demonstrates a transform node that modifies the footprint context. It illustrates how the node can extract, modify, and inject the footprint, but only requires it if downstream nodes actually need it. ```rust // Transform node example - modifies footprint but doesn't need it unless downstream requires it #[node_macro::node(category("Transform"))] fn transform( ctx: impl Ctx + ModifyFootprint, input: Vector, transform: Transform2D, ) -> Vector { // This node can extract the footprint, modify it, and inject the result // But if no downstream node needs the footprint, this node doesn't need it either let modified_footprint = ctx.footprint().transform(transform); // ... transform logic ... } ``` -------------------------------- ### Opacity Node Definition Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Example of defining a custom document node type for the 'Opacity' operation, specifying its name, category, implementation, inputs, and outputs. ```rust DocumentNodeDefinition { name: "Opacity", category: "Image Adjustments", implementation: DocumentNodeImplementation::proto("graphene_core::raster::OpacityNode"), inputs: vec![ DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true), DocumentInputType::value("Factor", TaggedValue::F32(100.), false), ], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: node_properties::multiply_opacity, ..Default::default() }, ``` -------------------------------- ### Run Release Build Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Use this command to build the project with release optimizations, which can be useful for testing performance or in environments with slow network connections. ```sh cargo run release ``` -------------------------------- ### Testing a Manual Node Implementation Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Unit test for the `OpacityNode`. Demonstrates how to instantiate a node with a concrete input (e.g., `CopiedNode`) and evaluate it. ```rust #[test] fn test_opacity_node() { let opacity_node = OpacityNode { opacity_multiplier: crate::value::CopiedNode(10_f64), // set opacity to 10% }; assert_eq!(opacity_node.eval(Color::WHITE), Color::from_rgbaf32_unchecked(1., 1., 1., 0.1)); } ``` -------------------------------- ### Explore Crate Dependencies Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/_index.md Run this command to visualize the crate dependency graph of the Graphite codebase. This helps understand the project's modular structure. ```sh cargo run explore deps ``` -------------------------------- ### Accessing the Build Bisect Tool Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/debugging-tips.md Quickly access the interactive bisect tool from your command line. This tool helps binary search through recent commits to find when a regression or feature was introduced. ```sh cargo run explore bisect ``` -------------------------------- ### Node Creation using the `node` Macro Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Use the `node` macro to simplify node creation. It automatically generates the necessary struct, trait implementations, and registry entries. Supports default values and property constraints. ```rust #[node_macro::node(category("Raster: Adjustments"))] fn opacity(_input: (), #[default(424242)] color: Color, #[soft_min(0.1)] opacity_multiplier: f64) -> Color { let opacity_multiplier = opacity_multiplier as f32 / 100.; Color::from_rgbaf32_unchecked(color.r(), color.g(), color.b(), color.a() * opacity_multiplier) } ``` -------------------------------- ### Debugging with log::debug! Macro Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Demonstrates how to use the `log::debug!()` macro for in-node debugging, allowing you to inspect variable values during execution. ```rust log::debug!("The opacity is {opacity_multiplier}"); ``` -------------------------------- ### Check Web Code Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Commands to check for errors, lint code quality, and format web code using npm scripts. Run these from the \/frontend directory. ```sh npm run check ``` ```sh npm run fix ``` -------------------------------- ### Define OpacityNode Constructor Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md This function is run when converting the `ProtoNode` struct into the desired struct. It constructs the `OpacityNode` and wraps it for per-pixel image processing. ```rust ( // Matches against the string defined in the document node. ProtoNodeIdentifier::new("graphene_core::raster::OpacityNode"), // This function is run when converting the `ProtoNode` struct into the desired struct. |args| { Box::pin(async move { // Creates an instance of the struct that defines the node. let node = construct_node!(args, graphene_core::raster::OpacityNode<_>, [f64]).await; // Create a new map image node, that calls the `node` for each pixel. let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); // Wraps this in a type erased future `Box + 'n>>` - this allows it to work with async. let map_node = graphene_std::any::FutureWrapperNode::new(map_node); // The `DynAnyNode` downcasts its input from a `Box` i.e. dynamically typed, to the desired statically typed input value. It then runs the wrapped node and converts the result back into a dynamically typed `Box`. let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); // Nodes are stored as type erased, which means they are `Box`. This allows us to create dynamic graphs, using dynamic dispatch so we do not have to know all node combinations at compile time. any.into_type_erased() }) }, // Defines the call argument, return value, and inputs. NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![fn_type!((), f64)]), ) ``` -------------------------------- ### Explore Editor Subsystem Hierarchy Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/editor-structure.md Command to quickly explore the editor's subsystem hierarchy. This command helps visualize the structure of subsystems, their state, and interactions. ```sh # Access this quickly in the future: car go run explore editor ``` -------------------------------- ### Automatic Message Wrapping with `.add()` Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/editor-structure.md Demonstrates the simplest way to add a message to the queue, where `.add()` automatically handles the `.into()` conversion and enum wrapping. ```rust responses.add(DocumentMessage::DeleteSelectedLayers); ``` -------------------------------- ### Register General Nodes with register_node! Macro Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Use this macro for registering general nodes that do not specifically need to run per pixel, simplifying their definition. ```rust register_node!(graphene_core::transform_nodes::SetTransformNode<_>, input: Vector, params: [DAffine2]) ``` -------------------------------- ### Clone Graphite Repository Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Use this command to clone the Graphite project from GitHub to your local machine. ```sh git clone https://github.com/GraphiteEditor/Graphite.git ``` -------------------------------- ### Decode and Process Sony ARW Image with Rawkit Source: https://github.com/graphiteeditor/graphite/blob/master/libraries/rawkit/README.md This snippet demonstrates how to open a Sony ARW file, decode its raw pixel data and metadata, optionally modify metadata like white balance and transform, and then process it into an 8-bit RGB image. Ensure you have the necessary file I/O and rawkit imports. ```rust use rawkit::RawImage; use rawkit::tiff::values::Transform; // Open a file for reading let file = BufReader::new(File::open("example.arw")?); // Decode the file to extract the raw pixels and its associated metadata let mut raw_image = RawImage::decode(file); // All the raw pixel data and metadata is stored within `raw_image` println!("Initial Bayer pixel values: {:?}", raw_image.data[:10]); println!("Image size: {} x {}", raw_image.width, raw_image.height); println!("CFA Pattern: {:?}", raw_image.cfa_pattern); println!("Camera Model: {:?}", raw_image.camera_model); println!("White balance: {:?}", raw_image.white_balance); // The metadata could also be edited if the extracted metadata needs to be customized raw_image.white_balance = Some([2609, 1024, 1024, 1220]); // For RGGB camera raw_image.transform = Transform::Rotate90; // Process the raw image into an RGB image let image = raw_image.process_8bit(); // The final image data will be stored within `image` println!("Initial RGB pixel values: {:?}", image.data[:10]); println!("Image size: {} x {}", image.width, image.height); ``` -------------------------------- ### Permanent Logging Levels in Rust Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/debugging-tips.md Utilize `log::error!`, `log::warn!`, and `log::trace!` for different levels of logging that should be committed. `error!` is for user-facing errors, `warn!` for non-critical issues, and `trace!` for verbose internal activity. ```rust log::error!("Descriptive user-facing error messages"); ``` ```rust log::warn!("Non-critical problems that likely indicate a bug"); ``` ```rust log::trace!("Verbose logs of ordinary internal activity"); ``` -------------------------------- ### Simplify OpacityNode Definition with raster_node! Macro Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md This macro simplifies the definition of opacity nodes that operate per pixel, abstracting away some of the boilerplate code. ```rust raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]) ``` -------------------------------- ### Define Portfolio Message with Child Document Message Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/codebase-overview/editor-structure.md Defines a `PortfolioMessage` that wraps `DocumentMessage` as a child, enabling hierarchical message nesting. ```rust pub enum PortfolioMessage { ... // A message that carries the `DocumentMessage` child enum as data #[child] Document(DocumentMessage), ... } ``` -------------------------------- ### Loading Indicator Styles Source: https://github.com/graphiteeditor/graphite/blob/master/frontend/index.html CSS for a spinning loading indicator and fallback for reduced motion. Ensure JavaScript is enabled for full functionality. ```css body { height: 100%; margin: 0; } body::before { content: ""; position: absolute; inset: 0; background: #222; } body::after { content: ""; display: block; position: absolute; left: 50%; top: 50%; width: 60px; height: 60px; border-radius: 50%; border: 4px solid #eee; border-color: #eee transparent #eee transparent; animation: spinning-loading-indicator 1s linear infinite; } @media (prefers-reduced-motion) { body::after { border: none; animation: none; content: "Loading…"; font-family: Arial, sans-serif; font-size: 24px; } } @keyframes spinning-loading-indicator { 0% { transform: translate(-30px, -30px) rotate(0deg); } 100% { transform: translate(-30px, -30px) rotate(360deg); } } ``` -------------------------------- ### Fetch GitHub Stars Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/about.md Fetches the number of GitHub stars for the Graphite repository and displays it in a formatted string. ```javascript (async () => { const response = await fetch("https://api.github.com/repos/graphiteeditor/graphite?per_page=1"); const json = await response.json(); const stars = parseInt(json.stargazers_count); if (!stars) return; document.querySelector("[data-github-stars]").innerText = `${Math.round(stars / 100) / 10}k ⭐`; })(); ``` -------------------------------- ### Check Rust Code Source: https://github.com/graphiteeditor/graphite/blob/master/website/content/volunteer/guide/project-setup/_index.md Commands to check, lint, and format Rust code directly from the terminal. ```sh cargo check ``` ```sh cargo clippy ``` ```sh cargo fmt ``` -------------------------------- ### Multiply Opacity Node Properties Source: https://github.com/graphiteeditor/graphite/blob/master/node-graph/README.md Defines the properties panel for the 'multiply_opacity' node, creating a 'Factor' number input widget with specified range and units. ```rust pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let factor = number_widget(document_node, node_id, 1, "Factor", NumberInput::default().min(0.).max(100.).unit("%"), true); vec![LayoutGroup::Row { widgets: factor }] } ```