Comprehensively update user manual and contributor guide, add Adam to core team

This commit is contained in:
Keavon Chambers
2025-01-08 03:52:11 -08:00
parent 9eb544df74
commit 93a60daa24
76 changed files with 931 additions and 685 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ css_external = ["https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&displ
Welcome, potential contributor! We're excited to have you join the Graphite project and it's our goal to make the process as smooth as possible. This guide will serve as your library of knowledge to help you get started contributing to the project. If you find any information missing or unclear, please let us know through Discord or submit a pull request to help document the process for future contributors.
The next page will cover how to compile the Graphite source code. But first, make sure you've joined our [Discord server](https://discord.graphite.rs) and assigned yourself the *"<span class="emoji">🤖</span> Interested in contributing code"* role from the *#welcome* channel. Done that? Alright, proceed!
The next page will cover how to compile the Graphite source code. But first, make sure you've joined our [Discord server](https://discord.graphite.rs) and assigned yourself the *"<span class="emoji">🤖</span> Interested in contributing code"* role from the `#🙂welcome` channel. Done that? Alright, proceed!
<p>
<img src="https://static.graphite.rs/content/volunteer/code-contributions.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a library of knowledge in a digital realm" />
@@ -29,13 +29,13 @@ The bar at the top of a _panel group_ which includes a clickable tab for each pa
### Active tab
The one tab in a _tab bar_ that is currently active. The user can click any inactive tab to make it become the active tab. The active tab shows the _panel content_ beneath it unless it is a _folded panel_.
### Folded panel
A shrunken _panel_ showing only the _tab bar_. A _panel_ consists of the _tab bar_ and _panel body_ except when the latter is folded away. The user may click the _active tab_ to fold and restore a panel, however a panel cannot be folded if there are no other unfolded panels in its column.
A shrunken _panel_ showing only the _tab bar_. A _panel_ consists of the _tab bar_ and _panel content_ except when the latter is folded away. The user may click the _active tab_ to fold and restore a panel, however a panel cannot be folded if there are no other unfolded panels in its column.
### Panel
### Panel body
### Options bar
The bar that spans horizontally across the top of a _panel_ (located under the _tab bar_) which displays options related to the _panel_.
### Panel content
### Control bar
The bar that spans horizontally across the top of a _panel_ (located under the _tab bar_) which displays controls related to the _panel_.
### Viewport
The area that takes up the main space in a _panel_ (located beneath the _options bar_) which displays the primary content of the _panel_.
The area that takes up the main space in a _panel_ (located beneath the _control bar_) which displays the primary content of the _panel_.
### Shelf
The bar that spans vertically along the left side of some _panels_ (located left of the _viewport_) which displays a catalog of available items, such as document editing _tools_ or common _nodes_.
### Tool
@@ -13,7 +13,7 @@ order = 1 # Page number after chapter intro
- Document title
- Window buttons
- Workspace
- Panel interface (tab, pin, options bar, left menu)
- Panel interface (tab, pin, control bar, left menu)
- Arrangement and docking
- Status bar
- Multiple windows
@@ -22,7 +22,7 @@ order = 1 # Page number after chapter intro
- Canvas and frames
- Rulers
- Tool menu
- Options bar
- Control bar
- Properties
- Blending
- Origin
@@ -0,0 +1,20 @@
+++
title = "Knowing your tooling"
[extra]
order = 2 # Page number after chapter intro
+++
## First time builds
Slower.
## Troubleshooting
Delete the `target`, `pkg`, `node_modules`, and `dist` directories.
## Slow builds
If you're seeing the terminal spend several seconds installing wasm-opt every recompilation, reinstall the exact version of `wasm-bindgen-cli` that matches the `wasm-bindgen` dependency in [`Cargo.toml`](https://github.com/GraphiteEditor/Graphite/blob/master/Cargo.toml).
## Rust Analyzer speed tips
@@ -5,13 +5,219 @@ page_template = "book.html"
[extra]
order = 2 # Chapter number
js = ["video-embed.js"]
js = ["youtube-embed.js"]
+++
The best introduction for getting up-to-speed with Graphite contribution comes from watching this webcast recording. Before asking questions in Discord, please watch the full video because it gives a comprehensive overview of most things you will need to know.
<div class="video-embed aspect-16x9">
<img data-video-embed="vUzIeg8frh4" src="https://static.graphite.rs/content/volunteer/guide/workshop-intro-to-coding-for-graphite-youtube.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Workshop: Intro to Coding for Graphite" />
</div>
The Graphite editor is built as a web app powered by Svelte and TypeScript in the frontend and Rust in the backend which is compiled to WebAssembly and run in the browser. The editor makes calls into Graphene, the node graph engine which manages and renders the documents.
<!-- ## Tech stack -->
<!-- - rustc: Compiler for node graph generics and custom nodes -->
<!-- - rust-gpu: Compiler backend to generate compute shaders from Rust source code -->
<!-- - wgpu: Portable graphics API for running compute shaders on desktop and web -->
<!-- - Tauri: lightweight desktop web UI shell while the backend runs natively (experimental) -->
<!-- - Vello: GPU-accelerated vector graphics renderer -->
<!-- - COSMIC Text: Text shaping and typesetting -->
<!-- - Wasmer or Wasmtime: Portable, sandboxed runtime for custom nodes -->
<!-- - Tokio: parallelized job execution in the node graph pipeline -->
<!-- - Xilem: High-performance native UI framework, to replace Tauri when ready -->
The Editor's frontend web code lives in `/frontend/src`. The backend Rust code is located in `/editor`. Graphene is found in `/node-graph`.
## Codebase structure
Graphite is built from several main software components. New developers may choose to specialize in one or more area without having to attain a working knowledge of the full codebase.
### Frontend
*Location: `/frontend/src`*
The frontend is the interface for Graphite which users see and interact with. It is built using web technologies with TypeScript and Svelte (HTML and SCSS). The frontend's philosophy is to be as lightweight and minimal as possible. It acts as the entry point for user input and then quickly hands off its work to the WebAssembly editor backend via its Wasm wrapper API. That API is written in Rust but has TypeScript bindings generated by the [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) tooling that is part of the Vite-based build chain. The frontend is built of many components that recursively form the window, panels, and widgets that make up the user interface.
### Editor
*Location: `/editor`*
The editor is the core of the Graphite application, and it's where all the business logic occurs for the tooling and user interaction. It is written in Rust and compiled to WebAssembly. At its heart is the message system described below. It is responsible for communicating with Graphene as well as handling the actual logic, state, tooling, and responsibilities of the interactive application.
### Graphene
*Location: `/node-graph`*
[Graphene](../graphene/) is the node graph engine which manages and renders the documents. It is itself a programming language, where Graphene programs are compiled while being edited live by the user, and where executing the program renders the document.
## Frontend/backend communication
Frontend-to-backend communication is achieved through a thin Rust translation layer in `/frontend/wasm/src/editor_api.rs` which wraps the editor backend's Rust-based message system API and provides the TypeScript-compatible API of callable functions. These wrapper functions are compiled by [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) into autogenerated TS functions that serve as an entry point from TS into the Wasm binary.
Backend-to-frontend communication happens by sending a queue of messages to the frontend message dispatcher. After the TS has called any wrapper API function to get into backend code execution, the editor's business logic runs and queues up `FrontendMessage`s (defined in `/editor/src/messages/frontend/frontend_message.rs`) which get mapped from Rust to TS-friendly data types in `/frontend/src/wasm-communication/messages.ts`. Various TS code subscribes to these messages by calling `subscribeJsMessage(MessageName, (messageData) => { /* callback code */ });`.
## The message system
The Graphite editor backend is organized into a hierarchy of subsystems, called *message handlers*, which talk to one another through message passing. Messages are pushed to the front or back of a queue and each one is processed sequentially by the backend's dispatcher.
The dispatcher lives at the root of the application hierarchy and it owns its message handlers. Thus, Rust's restrictions on mutable borrowing are satisfied because only the dispatcher mutably borrows its message handlers, one at a time, while each message is processed.
### Messages
Messages are enum variants that are dispatched to perform some intended activity within their respective message handlers. Here are two `DocumentMessage` definitions:
```rs
pub enum DocumentMessage {
...
// A message that carries one named data field
DeleteLayer {
id: NodeId,
}
// A message that carries no data
DeleteSelectedLayers,
...
}
```
As shown above, additional data fields can be included with each message. But as a special case denoted by the `#[child]` attribute, that data can also be a sub-message enum, which enables hierarchical nesting of message handler subsystems.
<br />
<details>
<summary>To view the hierarchical subsystem file structure: click here</summary>
<br />
<!--
Generated with:
cd editor/src/messages
tree -P '*_message.rs|*_message_handler.rs|*_tool.rs' --prune
Then the first line's "." was replaced with "messages"
-->
```
messages
├── broadcast
│   ├── broadcast_message.rs
│   └── broadcast_message_handler.rs
├── debug
│   ├── debug_message.rs
│   └── debug_message_handler.rs
├── dialog
│   ├── dialog_message.rs
│   ├── dialog_message_handler.rs
│   ├── export_dialog
│   │   ├── export_dialog_message.rs
│   │   └── export_dialog_message_handler.rs
│   ├── new_document_dialog
│   │   ├── new_document_dialog_message.rs
│   │   └── new_document_dialog_message_handler.rs
│   └── preferences_dialog
│   ├── preferences_dialog_message.rs
│   └── preferences_dialog_message_handler.rs
├── frontend
│   └── frontend_message.rs
├── globals
│   ├── globals_message.rs
│   └── globals_message_handler.rs
├── input_mapper
│   ├── input_mapper_message.rs
│   ├── input_mapper_message_handler.rs
│   └── key_mapping
│   ├── key_mapping_message.rs
│   └── key_mapping_message_handler.rs
├── input_preprocessor
│   ├── input_preprocessor_message.rs
│   └── input_preprocessor_message_handler.rs
├── layout
│   ├── layout_message.rs
│   └── layout_message_handler.rs
├── portfolio
│   ├── document
│   │   ├── document_message.rs
│   │   ├── document_message_handler.rs
│   │   ├── graph_operation
│   │   │   ├── graph_operation_message.rs
│   │   │   └── graph_operation_message_handler.rs
│   │   ├── navigation
│   │   │   ├── navigation_message.rs
│   │   │   └── navigation_message_handler.rs
│   │   ├── node_graph
│   │   │   ├── node_graph_message.rs
│   │   │   └── node_graph_message_handler.rs
│   │   ├── overlays
│   │   │   ├── overlays_message.rs
│   │   │   └── overlays_message_handler.rs
│   │   └── properties_panel
│   │   ├── properties_panel_message.rs
│   │   └── properties_panel_message_handler.rs
│   ├── menu_bar
│   │   ├── menu_bar_message.rs
│   │   └── menu_bar_message_handler.rs
│   ├── portfolio_message.rs
│   └── portfolio_message_handler.rs
├── preferences
│   ├── preferences_message.rs
│   └── preferences_message_handler.rs
├── tool
│   ├── tool_message.rs
│   ├── tool_message_handler.rs
│   ├── tool_messages
│   │   ├── artboard_tool.rs
│   │   ├── brush_tool.rs
│   │   ├── ellipse_tool.rs
│   │   ├── eyedropper_tool.rs
│   │   ├── fill_tool.rs
│   │   ├── freehand_tool.rs
│   │   ├── gradient_tool.rs
│   │   ├── imaginate_tool.rs
│   │   ├── line_tool.rs
│   │   ├── navigate_tool.rs
│   │   ├── path_tool.rs
│   │   ├── pen_tool.rs
│   │   ├── polygon_tool.rs
│   │   ├── rectangle_tool.rs
│   │   ├── select_tool.rs
│   │   ├── spline_tool.rs
│   │   └── text_tool.rs
│   └── transform_layer
│   ├── transform_layer_message.rs
│   └── transform_layer_message_handler.rs
└── workspace
├── workspace_message.rs
└── workspace_message_handler.rs
```
<br />
</details>
By convention, regular data must be written as struct-style named fields (shown above), while a sub-message enum must be written as a tuple/newtype-style field (shown below). The `DocumentMessage` enum of the previous example is defined as a child of `PortfolioMessage` which wraps it like this:
```rs
pub enum PortfolioMessage {
...
// A message that carries the `DocumentMessage` child enum as data
#[child]
Document(DocumentMessage),
...
}
```
Likewise, the `PortfolioMessage` enum is wrapped by the top-level `Message` enum. The dispatcher operates on the queue of these base-level `Message` types.
So for example, the `DeleteSelectedLayers` message mentioned previously will look like this as a `Message` data type:
```rs
Message::Portfolio(
PortfolioMessage::Document(
DocumentMessage::DeleteSelectedLayers
)
)
```
Writing out these nested message enum variants would be cumbersome, so that `#[child]` attribute shown earlier invokes a proc macro that automatically implements the `From` trait, letting you write this instead to get a `Message` data type:
```rs
DocumentMessage::DeleteSelectedLayers.into()
```
Most often, this is simplified even further because the `.into()` is called for you when pushing a message to the queue with `.add()` or `.add_front()`. So this becomes as simple as:
```rs
responses.add(DocumentMessage::DeleteSelectedLayers);
```
The `responses` message queue is composed of `Message` data types, and thanks to this system, child messages like `DocumentMessage::DeleteSelectedLayers` are automatically wrapped in their ancestor enum variants to become a `Message`, saving you from writing the verbose nested form.
@@ -1,84 +0,0 @@
+++
title = "Code structure"
[extra]
order = 1 # Page number after chapter intro
+++
## Tech stack
- rustc: Compiler for node graph generics and custom nodes
- rust-gpu: Compiler backend to generate compute shaders from Rust source code
- wgpu: Portable graphics API for running compute shaders on desktop and web
- Tauri: lightweight desktop web UI shell while the backend runs natively (experimental)
<!-- - Vello: GPU-accelerated vector graphics renderer -->
<!-- - COSMIC Text: Text shaping and typesetting -->
<!-- - Wasmer or Wasmtime: Portable, sandboxed runtime for custom nodes -->
<!-- - Tokio: parallelized job execution in the node graph pipeline -->
<!-- - Xilem: High-performance native UI framework, to replace Tauri when ready -->
## Frontend/backend communication
The Graphite editor frontend is the web code which displays the user interface. It passes user interactions to the backend. The Graphite editor backend handles all the day-to-day logic and responsibilities of a user-facing interactive application. Some duties include: user input, GUI state management, viewport tool behavior, layer management and selection, and handling of multiple document tabs.
Frontend (TS) -> backend (Rust/wasm) communication is achieved through a thin Rust translation layer in `/frontend/wasm/src/editor_api.rs` which wraps the Editor backend's complex Rust data type API and provides the TS with a simpler API of callable functions. These wrapper functions are compiled by wasm-bindgen into autogenerated TS functions that serve as an entry point into the wasm.
Backend (Rust) -> frontend (TS) communication happens by sending a queue of messages to the frontend message dispatcher. After the TS has called any wrapper API function to get into backend (Rust) code execution, the Editor's business logic runs and queues up `FrontendMessage`s (defined in `/editor/src/messages/frontend/frontend_message.rs`) which get mapped from Rust to TS-friendly data types in `/frontend/src/wasm-communication/messages.ts`. Various TS code subscribes to these messages by calling `subscribeJsMessage(MessageName, (messageData) => { /* callback code */ });`.
## The message system
The Graphite editor backend is organized into a hierarchy of systems, called *message handlers*, which talk to one another through message passing. Messages are pushed to the front or back of a queue and each one is processed sequentially by the backend's dispatcher. The dispatcher lives at the root of the application hierarchy and it owns its message handlers. Thus, Rust's restrictions on mutable borrowing are satisfied because only the dispatcher mutably borrows its message handlers, one at a time, while each message is processed.
### Messages
Messages are enum variants that are dispatched to perform some intended activity within their respective message handlers. Here are two `DocumentMessage` definitions:
```rs
pub enum DocumentMessage {
...
// A message that carries one named data field
DeleteLayer {
id: NodeId,
}
// A message that carries no data
DeleteSelectedLayers,
...
}
```
As shown above, additional data fields can be included with each message. But as a special case denoted by the `#[child]` attribute, that data can also be a sub-message, which enables us to nest message handler systems hierarchically. By convention, regular data must be written as struct-style named fields (shown above), while a sub-message must be written as an unnamed tuple/newtype-style field (shown below). The `DocumentMessage` enum of the previous example is defined as a child of `PortfolioMessage` which wraps it like this:
```rs
pub enum PortfolioMessage {
...
// A message that carries the `DocumentMessage` child enum as data
#[child]
Document(DocumentMessage),
...
}
```
Likewise, the `PortfolioMessage` enum is wrapped by the top-level `Message` enum. The dispatcher operates on the queue of these base-level `Message` types.
So for example, the `DeleteSelectedLayers` message mentioned previously will look like this as a `Message` data type:
```rs
Message::Portfolio(
PortfolioMessage::Document(
DocumentMessage::DeleteSelectedLayers
)
)
```
Writing out these nested message enum variants would be cumbersome, so that `#[child]` attribute shown earlier invokes a proc macro that automatically implements the `From` trait, letting you write this instead to get a `Message` data type:
```rs
DocumentMessage::DeleteSelectedLayers.into()
```
Most often, this is simplified even further because the `.into()` is called for you when pushing a message to the queue with `.add()` or `.add_front()`. So this becomes as simple as:
```rs
responses.add(DocumentMessage::DeleteSelectedLayers);
```
The `responses` message queue is composed of `Message` data types, and thanks to this system, child messages like `DocumentMessage::DeleteSelectedLayers` are automatically wrapped in their ancestor enum variants to become a `Message`, saving you from writing the verbose nested form.
@@ -1,47 +0,0 @@
+++
title = "Contributing guidelines"
[extra]
order = 3 # Page number after chapter intro
+++
## Code style
The Graphite project prizes code quality and accessibility to new contributors. Therefore, we ask you please make all efforts to contribute readable, well-documented code according to these best practices.
### Naming
Please use descriptive variable/function/symbol names and keep abbreviations to a minimum. Prefer to spell out full words most of the time, so `gen_doc_fmt` should be written out as `generate_document_format` instead.
This avoids the mental burden of expanding abbreviations into semantic meaning. Monitors are wide enough to display long variable/function names, so descriptive is better than cryptic. To streamline code review, it's recommended that you set up a spellcheck plugin in your editor. The project uses American English spelling conventions.
### Linting
Please ensure Clippy is enabled. This should be set up automatically in VS Code. Try to avoid committing code with lint warnings.
### Comments
For consistency, please try to write comments in *Sentence case* (starting with a capital letter). End with a period only if multiple sentences are used in the same comment. For doc comments (`///`), always write in full sentences (ending with a period).
Comments should be placed on a separate line, but exceptions are permitted where sensible. They should target the maximum line length of 200 characters (don't go over, and don't target a considerably lower number like 80 for line breaks).
### Imports
At the top of Rust files, please follow the convention of separating imports into three blocks, in this order:
1. Local (`use super::` and `use crate::`)
2. First-party crates (e.g. `use editor::`)
3. Third-party libraries (e.g. `use std::` or `use glam::`)
Combine related imports with common paths at the same depth. For example, the lines `use crate::A::B::C;`, `use crate::A::B::C::Foo;`, and `use crate::A::B::C::Bar;` should be combined into `use crate::A::B::C::{self, Foo, Bar};`. But do not combine imports at mixed path depths. For example, `use crate::A::{B::C::Foo, X::Hello};` should be split into two separate import lines. In simpler terms, avoid putting a `::` inside `{}`.
## Tests
It's great if you can write tests for your code, especially if it's a tricky stand-alone function. However at the moment, we are prioritizing rapid iteration and will usually accept code without associated unit tests. That stance will change in the near future as we begin focusing more on stability than iteration speed.
## Draft pull requests
Once you begin writing code, please open a pull request immediately and mark it as a **Draft**. Please push to this on a frequent basis, even if things don't compile or work fully yet. It's very helpful to have your work-in-progress code up on GitHub so the status of your feature is less of a mystery.
Open a new PR as a draft / convert an existing PR to a draft:
<img src="https://static.graphite.rs/content/volunteer/guide/draft-pr.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Screenhots showing GitHub's &quot;Create pull request (arrow) > Create draft pull request&quot; and &quot;Still in progress? Convert to draft&quot; buttons" />
@@ -0,0 +1,36 @@
+++
title = "Debugging tips"
[extra]
order = 4 # Page number after chapter intro
+++
The Wasm-based editor has some unique limitations about how you are able to debug it. This page offers tips and best practices to get the most out of your problem-solving efforts.
## Comparing with deployed builds
When tracking down a bug, first check if the issue you are noticing also exists in `master` or just your branch. Open up [dev.graphite.rs](https://dev.graphite.rs) which always deploys the lastest commit, compared to [editor.graphite.rs](https://editor.graphite.rs) which is manually deployed from time to time for the sake of stability.
Use *Help* > *About Graphite* in the editor to view any build's Git commit hash.
Beware of one potential pitfall: all deploys and build links are built with release optimizations enabled. This means some bugs (like crashes from bounds checks or debug assertions) may exist in `master` and would appear if run locally, but not in the deployed version.
## Printing to the console
Use the browser console (<kbd>F12</kbd>) to check for warnings and errors. Use the Rust macro `debug!("The number is {}", some_number);` to print to the browser console. These statements should be for temporary debugging. Remove them before your code is reviewed. Print-based debugging is necessary because breakpoints are not supported in WebAssembly.
Additional print statements are available that *should* be committed.
- `error!()` is for descriptive user-facing error messages arising from a bug
- `warn!()` is for non-critical problems that likely indicate a bug somewhere
- `trace!()` is for verbose logs of ordinary internal activity, hidden by default
To show `trace!()` logs, activate *Help* > *Debug: Print Trace Logs*.
## Message system logs
To also view logs of the messages dispatched by the message system, activate *Help* > *Debug: Print Messages* > *Only Names*. Or use *Full Contents* for a more verbose view containing the actual data being passed. This is an invaluable window into the activity of the message flow and works well together with `debug!()` printouts for tracking down message-related defects.
## Node/layer and document IDs
In debug mode, hover over a layer's name in the Layers panel, or a layer/node in the node graph, to view a tooltip with its ID. Likewise, document IDs may be read from their tab tooltips.
@@ -1,30 +0,0 @@
+++
title = "Debugging"
[extra]
order = 2 # Page number after chapter intro
+++
## Deployed builds
When tracking down a bug, first check if the issue you are noticing also exists in `master` or just your branch. Use [dev.graphite.rs](https://dev.graphite.rs) which should always deploy the lastest commit on `master`. By comparison, [editor.graphite.rs](https://editor.graphite.rs) is manually updated every few days or weeks to ensure stability. Use *Help* > *About Graphite* in the editor to view the build's [commit hash](https://github.com/GraphiteEditor/Graphite/commits/master).
## Printing to the console
Use the browser console (<kbd>F12</kbd>) to check for warnings and errors. Use the Rust macro `debug!("A debug message");` to print to the browser console. These statements should be for temporary debugging. Remove them before committing to `master`. Print-based debugging is necessary because breakpoints are not supported in WebAssembly.
Additional print statements are available that *should* be committed.
- `error!()` is for descriptive user-facing error messages arising from a bug
- `warn!()` is for non-critical problems that likely indicate a bug somewhere
- `trace!()` is for verbose logs of ordinary internal activity, hidden by default
To show `trace!()` logs, activate *Help* > *Debug: Print Trace Logs*.
## Message system logs
To also view logs of the messages dispatched by the message system, activate *Help* > *Debug: Print Messages* > *Only Names*. Or use *Full Contents* for more verbose insight with the actual data being passed. This is an invaluable window into the activity of the message flow and works well together with `debug!()` printouts for tracking down message-related issues.
## Node/layer and document IDs
In debug mode, hover over a layer's name in the Layers panel, or a layer/node in the node graph, to view a tooltip with its ID. Likewise, document IDs may be read by hovering over their tabs.
@@ -1,14 +0,0 @@
+++
title = "Editor and tooling"
[extra]
order = 1 # Page number after chapter intro
+++
We provide default configurations for VS Code users. When you open the project, watch for a prompt to install the project's suggested extensions. They will provide helpful web and Rust tooling. If you use a different IDE, you won't get default configurations for the project out of the box, so please remember to format your code and check CI for errors.
## Checking, linting, and formatting
While developing Rust code, `cargo check`, `cargo clippy`, and `cargo fmt` terminal commands may be run from the root directory. For web code, `npm run lint` and `npm run lint-no-fix` can be used from the `/frontend` directory to fix or view formatting issues.
If you don't use VS Code and its format-on-save feature, please remember to format before committing or consider [setting up a `pre-commit` hook](https://githooks.com/) to do that automatically. Disabling VS Code's *Auto Save* files feature is recommended to ensure you actually save (and thus format) file changes.
@@ -1,14 +0,0 @@
+++
title = "Getting help"
[extra]
order = 3 # Page number after chapter intro
+++
## Discord developer community
Join the [project's Discord server](https://discord.graphite.rs) then hop on the `#development` channel and ping @Keavon, @TrueDoctor, or @0Hypercube. The team would be delighted to help you get started by providing in-depth explanations of the code and programming assistance as you work. Please do not hesitate to reach out right away!
## Code documentation
Look out for `README.md` files in some folders of the codebase and doc comments at the top of some Rust files. The quantity of those files is limited right now, but documenting code is an excellent contribution if you wish to explain what you've learned for the sake of others, and improve your own understanding in the process.
@@ -1,12 +0,0 @@
+++
title = "Picking a task"
[extra]
order = 2 # Page number after chapter intro
+++
The [task board](https://github.com/orgs/GraphiteEditor/projects/1/views/1) provides a list of [available tasks](https://github.com/orgs/GraphiteEditor/projects/1/views/5), as well as a [beginner-friendly](https://github.com/orgs/GraphiteEditor/projects/1/views/6) subset. Issues partially or fully involving web development can also be seen [listed here](https://github.com/orgs/GraphiteEditor/projects/1/views/5?filterQuery=status%3AShort-Term%2CMedium-Term%2CLonger-Term+label%3AWeb+-label%3ARust) which may involve HTML/CSS/TypeScript/Svelte, although depending on the task, it may also involve Rust (which can be a good way to get gently introduced to the language if you come from a web background).
Writing new documentation by commenting existing code is another valuable way to contribute as you learn from reading code.
Feel free to pick whatever task interests you, then comment on the issue that you would like to start. After commenting, you can dig in right away, then we will assign the issue to you once you have a PR ready. (Always remembering to leave a comment is important, since GitHub doesn't allow assigning issues to people who haven't commented on them.)
@@ -4,11 +4,10 @@ template = "book.html"
page_template = "book.html"
[extra]
order = 3 # Chapter number
js = ["video-embed.js"]
order = 5 # Chapter number
+++
**Graphene** is the node graph engine that powers the Graphite editor.
Graphene is the node graph engine that powers the Graphite editor.
It's hard to describe in one sentence precisely what Graphene is, because it's a technology that serves several roles when viewed from different angles. But to get a feel for what it encompasses, here is a list of some of its purposes:
@@ -89,7 +88,7 @@ Since Graphene is fundamentally a programming language, throughout this document
| Graph execution | Program execution |
<!-- Our philosophy of building (bootstrapping) our own higher-level language features from the language itself -->
<!-- Primary inputs/outputs, secondary inputs/outputs, `.eval()`, recompiling when secondary input values are updated but not when primary input data is updated -->
<!-- Call arguments, construction arguments, `.eval()`, recompiling when construction argument values are updated but not when call argument data changes -->
<!-- Compose nodes and automatic/manual composition -->
<!-- Extract/inject nodes and metaprogramming -->
<!-- Cache nodes and stable node IDs -->
@@ -13,9 +13,9 @@ Any (sub)graph can import/export data from/to the outside world. For example, a
In the Graphite editor UI, here is an example graph of artwork that imports no data but exports its content to the canvas:
<img src="https://static.graphite.rs/content/index/gui-mockup-nodes__5.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Node graph UI mockup" data-carousel-image />
<img src="https://static.graphite.rs/content/index/gui-mockup-nodes__5.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Node graph UI mockup" />
The graph shown above represents the full artwork, meaning it's the root-level graph in its document. But there is nothing special about that graph compared to any subgraph. To avoid the confusion of calling it a graph or subgraph which comes with implications about user-facing concepts in the context of a document, we will use the less-ambiguous term **network** in the context of Graphene's internal concepts and code base.
The graph shown above represents the full artwork, meaning it's the root-level graph in its document. But there is nothing special about that graph compared to any subgraph. To avoid the confusion of calling it a graph or subgraph which comes with implications about user-facing concepts in the context of a document, we will use the less-ambiguous term **network** in the context of Graphene's internal concepts and codebase.
## Networks
@@ -1,5 +1,5 @@
+++
title = "Getting started"
title = "Project setup"
template = "book.html"
page_template = "book.html"
@@ -29,6 +29,7 @@ Regarding the last one: you'll likely get faster build times if you manually ins
<br />
<details>
<summary>Linux users: click here</summary>
<br />
On Linux, you likely need to install this set of additional packages which are required by Tauri, even if you're just building the web app:
@@ -65,11 +66,14 @@ From either the `/` (root) or `/frontend` directories, you can run the project b
npm start
```
This spins up the dev server at <http://localhost:8080> with a file watcher that performs hot reloading of the web page. You should be able to start the server, edit and save web and Rust code, and shut it down by double pressing <kbd>Ctrl</kbd><kbd>C</kbd>. You sometimes may need to reload the browser's page if hot reloading didn't behave right.
This spins up the dev server at <http://localhost:8080> with a file watcher that performs hot reloading of the web page. You should be able to start the server, edit and save web and Rust code, and shut it down by double pressing <kbd>Ctrl</kbd><kbd>C</kbd>. You sometimes may need to reload the browser's page if hot reloading didn't behave right— always refresh when Rust recompiles.
This method compiles Graphite code in debug mode which includes debug symbols for viewing function names in stack traces. But be aware, it runs slower and the Wasm binary is much larger. Having your browser's developer tools open will also significantly impact performance in both debug and release builds, so it's best to close that when not in use.
## Production builds
<br />
<details>
<summary>Production build instructions: click here</summary>
<br />
You'll rarely need to compile your own production builds because our CI/CD system takes care of deployments. However, you can compile a production build with full optimizations by first installing the additional `cargo-about` dev dependency:
@@ -84,3 +88,15 @@ npm run build
```
This produces the `/frontend/dist` directory containing the static site files that must be served by your own web server.
</details>
## Development tooling
We provide default configurations for VS Code users. When you open the project, watch for a prompt to install the project's [suggested extensions](https://github.com/GraphiteEditor/Graphite/blob/master/.vscode/extensions.json). They will provide helpful web and Rust tooling. If you use a different IDE, you won't get default configurations for the project out of the box, so please remember to format your code and check CI for errors.
### Checking, linting, and formatting
While developing Rust code, `cargo check`, `cargo clippy`, and `cargo fmt` terminal commands may be run from the root directory. For web code, formatting issues can be linted using `npm run lint` (to view) and `npm run lint-fix` (to fix) if run from the `/frontend` directory.
If you don't use VS Code and its format-on-save feature, please remember to format before committing or [set up a `pre-commit` hook](https://githooks.com/) to do that automatically. Disabling VS Code's *Auto Save* files feature is recommended to ensure you actually save (and thus format) file changes.
@@ -1,30 +0,0 @@
+++
title = "Projects"
template = "book.html"
page_template = "book.html"
[extra]
order = 4 # Chapter number
+++
Graphite is built from a number of separate projects, each with a distinct focus. New developers may choose to specialize in one or more area without having to attain a working knowledge of the entire codebase. This chapter details each project's purpose, what knowledge or background is best suited for getting involved with it, and how to begin making useful contributions. Opportunities also exist for contributing to specific self-contained sub-projects for students interested in completing a Google Summer of Code or other internship program, a university capstone project, or another similar endeavor.
## Frontend
The frontend is the interface for Graphite which users see and interact with. It is built using web technologies with TypeScript, Svelte, and SCSS. The frontend's philosophy is to be as lightweight and minimal as possible. It acts as the entry point for user input and quickly hands off its work to the WebAssembly editor backend via its Wasm wrapper API. That API is written in Rust but has TypeScript bindings generated by the wasm-bindgen tooling that is part of the Vite-based build chain. The frontend is built of many components that recursively form the window, panels, and widgets that make up the user interface.
## Editor
The editor is the core of the Graphite application, and it's where all the business logic occurs for the tooling and user interaction. It is written in Rust and compiled to WebAssembly, and at its heart is the message system [described here](../codebase-overview/code-structure#the-message-system).
## Graphene
Graphene is the node graph engine which manages and renders the documents. It is itself a programming language, where Graphene programs are compiled while being edited live by the user, and where executing the program renders the document.
## Libraries
Graphite maintains several Rust libraries that are published to crates.io for use by other developers, in addition to internal use by Graphite itself.
### Bezier-rs
Bezier-rs is a computational geometry library. It provides a rich selection of types and functions for working with Bézier segments and paths. There is also an [interactive demo](/libraries/bezier-rs/) gallery. This was initially developed as a successful [student project](./student-projects). Contribution opportunities include optimization, code cleanup and refactoring, and implementing new algorithms.
@@ -0,0 +1,16 @@
+++
title = "Starting a task"
template = "book.html"
page_template = "book.html"
[extra]
order = 3 # Chapter number
+++
There are two places to look for beginner-friendly development tasks. Usually, the best option is to select one of the many bite-sized task descriptions marked with a <span class="emoji">‼️</span> reaction in the `#✅code-todo-list` channel of the [Discord server](https://discord.graphite.rs). You may also browse the [task board](https://github.com/orgs/GraphiteEditor/projects/1/views/1), which lists [beginner issues](https://github.com/orgs/GraphiteEditor/projects/1/views/6) to pick from. The Discord option usually has the more approachable tasks compared to the GitHub issues, which tend to have more variability in complexity.
If you're unsure about which task to pick, feel free to ask in the `#📄development` channel.
You may right click a `#✅code-todo-list` task and select "Create Thread" to ask questions and discuss your development progress. If your work doesn't correspond to a specific listed task in that channel, you can also create a thread in `#🧵task-help` with a short, descriptive title ending with your issue or PR number.
If you're tackling a GitHub issue, please remember to comment in the issue with a link to your PR once you submit it. This is necessary because we will assign you to the issue after your PR has merged, but GitHub only allows assignments to those who have commented on the issue.
@@ -0,0 +1,61 @@
+++
title = "Code quality guidelines"
[extra]
order = 2 # Page number after chapter intro
+++
The Graphite project prizes code quality and accessibility to new contributors. Therefore, we ask you please make all efforts to contribute readable, well-documented code according to these best practices.
## Linting
Please ensure Clippy is enabled. This should be set up automatically in VS Code. Try to avoid committing code with lint warnings. You may execute `cargo clippy` anytime to confirm.
## Naming
Please use descriptive variable/function/symbol names and keep abbreviations to a minimum. Prefer spelling out full words most of the time, so `gen_doc_fmt` should be written out as `generate_document_format` instead.
This avoids the mental burden of expanding abbreviations into semantic meaning. Monitors are wide enough to display long variable/function names, so descriptive is better than cryptic.
Totally unambiguous, common shortened forms are acceptable such as "max" for "maximum", "eval" for "evaluate", and "info" for "information".
To avoid wasted effort in code review, it's recommended that you set up a spellcheck plugin, like [this extension](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) for VS Code. The project uses American English spelling conventions.
## Whole-number floats
Always use the style `42.` instead of `42.0` for whole-number floats to maintain consistency and brevity. For range syntax, either `0.0..42.` or `(0.)..42.` is acceptable.
## Comments
For consistency, please try to write comments in *Sentence case* (starting with a capital letter). End with a period only if multiple sentences are used in the same comment. For doc comments (`///`), always write in full sentences ending with a period. There should always be one space after the `//` or `///` comment markers, and `/* */` style comments shouldn't be used.
Avoid including commented-out code, unless you have a compelling reason to keep it around for future adaption, in your PRs that are open for code review.
Comments should usually be placed on a separate line above the code they are referring to, not at the end of the code line.
## Blank lines
Please make a habit of grouping together related lines of codes in blocks separated by blank lines. If you have dozens of lines comprising a single unbroken block of logic, you are likely not splitting it apart enough to aid readability. Find sensible places to partition the logic and insert blank lines between each. Roughly 10% of the code you write should ideally be blank lines, otherwise you are likely underutilizing them at the expense of readability.
## Imports
Our imports used to be a mess before we tamed the chaos with these rules.
At the top of Rust files, use the convention of separating imports into three blocks, ordered as:
1. Local (`use super::` and `use crate::`)
2. First-party crates (e.g. `use editor::` or `bezier_rs::`)
3. Third-party libraries (e.g. `use std::` or `use glam::`)
Combine related imports with common paths at the same depth. For example:
```rs
use crate::A::B::C;
use crate::A::B::C::Foo;
use crate::A::B::C::Bar;
// Should be combined into:
use crate::A::B::C::{self, Foo, Bar};
```
But do not combine imports at mixed path depths. For example, `use crate::A::{B::C::Foo, X::Hello};` should be split into two separate import lines— avoid having `::` inside `{}`.
@@ -0,0 +1,107 @@
+++
title = "Submitting a contribution"
[extra]
order = 3 # Page number after chapter intro
css_external = ["https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap"]
+++
Collaboration is a key part of real-world software engineering. Graphite follows some basic procedures to keep the process smooth and efficient. You will want to familiarize yourself with these guidelines to save yourself and Graphite maintainers time and confusion.
This assumes you understand enough about how Git works to utilize commits, branches, and multiple remotes. If you're new to Git, you will need to learn those topics on your own, but a good starting point is [this portion](https://youtu.be/vUzIeg8frh4?t=237) of the Graphite intro webcast which recommends installing the [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) extension for VS Code to visualize your Git history and branches.
## Git branch name
Before making your first commit, create a new branch with a name that describes what it's about. Aim for short but sufficiently descriptive. Kebab-case (using hyphens between words) is our usual convention. Don't include a prefix like `feature/` or `fix/` which just adds visual noise. An example like `fix-path-tool-selection-history` is fine, but almost too long.
Rename it if you already made a branch with a different name. Create a new branch if you've been committing to `master` or another existing branch. If your branch is specifically called `master`, it becomes harder to work with during code reviews.
After you push your branch to GitHub then open a PR, you won't be able to change its name. But please don't close a PR and open a new one just because the branch name isn't optimal. Just keep these tips in mind for the next time.
## Pull request
Once you have gotten your code far enough along that you are confident you'll be able to complete it, open a pull request (PR). You might also do this earlier if a maintainer requests to see your code in order to assist you.
Later on when you are building larger features, a PR should be opened once you have meaningful progress. That way, it can be kept safe on GitHub and other maintainers can check in to see your status so your work is less of a mystery.
Here's the important part: when you open a PR, it should be marked as a draft unless it is currently ready for review. The left image shows how to open a new PR as a draft, and the right image shows how to convert an existing PR to a draft.
<p><img src="https://static.graphite.rs/content/volunteer/guide/draft-pr.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Screenhots showing GitHub's &quot;Create pull request (arrow) > Create draft pull request&quot; and &quot;Still in progress? Convert to draft&quot; buttons" /></p>
<center><em>Open a new PR as a draft / convert an existing PR to a draft</em></center>
You should mark it as ready for review and ping a maintainer when you believe your code implements the needed functionality and doesn't introduce any new bugs or broken features.
## Title and description
Your PR title will become the commit message of your feature's commit in the project Git history. It should aim to concisely but descriptively summarize what your PR does. We use sentence case and imperative mood ("Fix X bug", "Add Y feature", "Make Z faster").
If you are working on a task from the `#✅code-todo-list` Discord channel, you should right-click the exact message, select "Copy Message Link", and paste that into your PR description.
If you are working on a task with a GitHub issue, please be certain to include that issue number in the description. GitHub requires the format "Closes #123", "Fixes #123", or "Resolves #123". If there are multiple issues, you have to fully repeat this trigger word for each one. If there is no issue, remove the pre-filled "Closes #" description text.
When the PR gets merged, any issue referenced by the trigger word will be automatically closed. That isn't desirable for [tracking issues](https://github.com/GraphiteEditor/Graphite/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+%22tracking+issue%22), so you should instead use "Part of #123" which isn't a trigger word. After the PR merges, please edit the description to change "Part of" to "Closes" so the tracking issue links to the PR without it having gotten closed.
As a bonus, it can be helpful for maintainers if you take a few minutes to write about what you changed and include relevant screenshots or video clips.
If you have concerns about a certain approach you took or if a certain part of your code is as clean as it could be, you can leave comments on lines of your own code from the "Files changed" tab after opening the PR.
## Comment on the issue
For any issue referenced in your PR (including tracking issues), we need you to leave a comment on issue. It doesn't matter what you write. You can just say "I opened PR #456" or something to that effect. This is only necessary because we will need to assign that issue to you upon merging your PR, but GitHub only allows assignments to those who have commented.
That way you get credit for your work and we can keep our closed issues cleanly organized. For consistency, a closed issue should have an assignee if it was resolved by a PR. Otherwise, only duplicate or invalid issues should be closed without an assignee.
We don't commonly assign issues while a PR is still in progress, only upon landing the PR. That's because PRs often get abandoned and we don't want an assignment blocking someone else from picking up the work.
## Code review etiquette
It is your responsibility to build the editor, thoroughly test your work, and employ common sense to avoid wasting a maintainer's time in needing to point out obvious flaws. It is not uncommon for inexperienced contributors to request review when their code entirely fails to implement the task at hand, or breaks surrounding functionality in a way that should have been immediately apparent. This doesn't leave a good impression and can frustrate maintainers.
If you don't actually understand what is intended with your feature/fix and why this is meaningful to a user of Graphite, spend time becoming that user and understanding the context. [Learning](/learn) at least the basics of using Graphite is important. Then ask questions in Discord if you're still confused about specific edge cases or the wording of the task.
It is also common for larger tasks to enter a round of review to confirm the direction is correct before you go back and polish the remaining details of the implementation. It's good to be in touch with the team to decide on when is the right time for this kind of preliminary review. It can save you effort reworking problems if you misunderstand the goals, or if the exact details of the requirements were never well-defined and you'll need to iterate on the design together with the team. Don't feel that every part of your PR needs to be 100% finished before requesting feedback, but also be clear so you aren't taking a maintainer away from other work to point out that you are obviously nowhere near done.
## Self-review
Before marking your PR as ready for review, you should do a self-review. That means reading over the diff of all your changes to ensure they are correct, complete, and lacking frivolous changes like unintended whitespace alterations, leftover debugging code, or commented-out lines. Read over it with a fine-toothed comb so maintainers don't have to nitpick as much. It is only fair that your first code reviewer should be yourself, so you catch the obvious flaws first.
## Passing CI
Upon pushing a commit to your PR's branch, CI will need to build and test your code. PRs from forks will have to wait until a maintainer approves the CI run. If you're uncertain, run `cargo test --all-features` on your machine or ask a maintainer to trigger CI for you.
You also have to pass `cargo fmt` and `cargo clippy` locally before your PR can be merged.
Your goal is for the check called "Editor: Dev & CI / build (pull_request)" to pass with a <span class="emoji">✅</span>. If it fails with a <span class="emoji">❌</span>, you will need to investigate. If you need access to the build logs, ask a maintainer to provide them. Occasionally, other checks may fail, but you likely won't be responsible for fixing those and they can be ignored.
## Keeping your work up-to-date
Be sure to start your work from the latest commit on the `master` branch by pulling (`git pull`) with `master` checked out when you begin coding.
As time goes on and `master` accumulates new commits, your branch will become outdated. It has to be synced up with `master` before your PR can be merged. Sometimes there will be conflicts that you need to resolve, which you can find learning resources for online. Enabling Git's three-way diff conflict style with `git config --global merge.conflictstyle diff3` can make this process easier.
When your branch can be updated with `master` without conflicts, you can click the "Update branch" button below the CI status. If you click the dropdown button beside it, you can choose instead to update with a rebase. If this can be done without conflicts, this is preferred because it maintains a clean, linear history for your branch.
<p><img src="https://static.graphite.rs/content/volunteer/guide/update-branch-with-rebase.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Screenhots showing GitHub's &quot;Update with rebase&quot; button" /></p>
Be sure to pull the rebased, or updated-with-a-merge-commit, branch after you or a maintainer updates it (or pushes other commits to it) to ensure you are working on the latest code.
## Review process
Assuming you have done what's explained above, a maintainer will aim to review your PR within a few days if possible. Feel free to send reminders because PRs can get overlooked.
As a rule of thumb, at this stage you are about 50% done with your work. The other 50% of your time will be spent responding to feedback and making (sometimes significant) changes.
There are two parts to the review process, QA and code review, which occur separately:
- Quality assurance (QA): A build of your code will be opened and tested to ensure it implements the requested functionality and doesn't introduce regressions. This is not a substitute for your own testing, but it is a necessary line of defense against overlooked issues. This is usually performed by Keavon, the founder and product designer, whose eye for detail keeps the app polished and consistent. Maintainers (and only maintainers) have the ability to invoke CI by commenting "!build" on your PR which will produce a build link. That is a unique link hosting a build of your PR's current code.
- Code review: The code will be checked for flawed approaches, pitfalls, confusing logic, [style guide](../code-quality-guidelines) adherence, sufficient comments and tests, and general quality. A review may be left through GitHub or your PR may have commits added to it. Feel free to read the diffs of those commits to understand what was changed so you can learn from that feedback. Direct commits are often faster than leaving dozens of comments. These can range from nitpicks to larger improvements. Our process is to collaborate on PRs as a team to write the best code possible, meaning your PR won't always be exclusively written by you.
When changes are requested, the maintainer will usually mark the PR as a draft again while awaiting your updates. It is your responsibility to mark it as ready for review again once you've addressed the feedback.
- If a PR is a draft, the ball is in your court to move it forward.
- If it's marked as ready for review, it means there is nothing more for you to do until the maintainer has time to review it.
After any number of back-and-forth cycles, a maintainer (usually Keavon who often gives the final say) will merge your PR. All your commits will be squashed into a single new commit on the `master` branch. This keeps the Git history linear and easy to follow.
Congratulations on landing your successful contribution! Ping `@Keavon` on Discord to be given the "Code Contributor" role.
@@ -1,21 +1,23 @@
+++
title = "Student projects"
template = "book.html"
page_template = "book.html"
[extra]
order = 1 # Page number after chapter intro
order = 4 # Chapter number
+++
Graphite offers a number of opportunities for students to contribute by building a self-contained project as part of a structured format. These projects are designed to be completed over several months and are ideal for Google Summer of Code or similar internship programs, solo or group university capstone projects, and other arrangements. Each project has a distinct focus and is a great way to make a meaningful contribution to open source over the length of the program while receiving mentorship and guidance from the Graphite team.
Student projects require adherence to a set schedule with regular check-ins, milestones, and evaluations. The structured setting is designed to provide a supportive environment for students to learn and grow as developers while gaining real-world industry experience from collaborating on a sizable software product and remaining accountable to stakeholders. It's our goal to make sure you succeed!
Use this [contributor guide](../..) to start out with the code. Then when you're ready, reach out through [Discord](https://discord.graphite.rs) and use the `#🎓student-projects` channel to discuss and work towards proposing a project with the Graphite core team.
Use this [contributor guide](..) to start out with the code. Then when you're ready, reach out through [Discord](https://discord.graphite.rs) and use the `#🎓student-projects` channel to discuss and work towards proposing a project with the Graphite core team.
## Google Summer of Code
GSoC is a program offering students a [stipend](https://developers.google.com/open-source/gsoc/help/student-stipends) for successful completion of an internship-style experience with an open source organization. Read about [how it works](https://summerofcode.withgoogle.com/how-it-works/).
Graphite [participated](https://summerofcode.withgoogle.com/programs/2024/organizations/graphite) in GSoC 2024 and we anticipate doing so again in 2025. Getting involved early is a great way to have a head start and stand out next summer.
Graphite [participated in GSoC 2024](https://summerofcode.withgoogle.com/programs/2024/organizations/graphite) and we anticipate doing so again in 2025 if our organization's application is accepted. Getting involved early is a great way to have a head start and stand out in your application.
<!-- The proposal formulation period is open now until the April 2 deadline (see the full [timeline](https://developers.google.com/open-source/gsoc/timeline)). -->
### Writing a proposal
@@ -166,14 +168,13 @@ As is the case with all projects, please discuss this with us on Discord to fles
In addition to the detailed projects above, here are some loose ideas that may be expanded into full project descriptions before the 2025 GSoC application period opens:
- Sophisticated text layout and advanced typography features
- PDF import/export? (Scope and viability depend on the state of available libraries)
- PDF import/export? (scope and viability depends on the state of available libraries)
- Traditional brush engine
- [Procedural brush engine](https://github.com/Keavon/Brush-Nodes)
- Color management for HDR/WCG (requires good understanding of color science)
- Image processing algorithms for photography
- Snapping system overhaul
- Photo processing graphics algorithms
- [Node equivalence rewriting](https://github.com/GraphiteEditor/Graphite/issues/2021)
- Snapping system overhaul
## Successful past projects
@@ -196,7 +197,7 @@ Unlike other node editors that are centered around manual graph editing, where u
While general graph layout algorithms are complex and struggle to produce good results in other node editors, Graphite's graph topology is more constrained and predictable, which makes it possible to design a layout system that can produce good results. Nodes tend to be organized into rows, and layers into columns. This turns the problem into more of a constraint-based, axis-aligned packing problem.
### 2024: Rendering Performance infrastructure improvements
### 2024: Rendering performance infrastructure improvements
*Graphite performance is bottlenecked by limitations in the new node graph rendering architecture that needs improvements.*
@@ -223,7 +224,7 @@ Student: Elbert Ronnie
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1771)
- [Rawkit library](https://crates.io/crates/rawkit)
**Outcomes:** A Rust library that implements raw photo decoding functionality to native Rust. A clean, well-structured code base and API. At a minimum, demonstrate the successful end-to-end decoding, debayering, and color space handling of Sony ARW format photos in Graphite. Publish the library to crates.io.
**Outcomes:** A Rust library that implements raw photo decoding functionality to native Rust. A clean, well-structured codebase and API. At a minimum, demonstrate the successful end-to-end decoding, debayering, and color space handling of Sony ARW format photos in Graphite. Publish the library to crates.io.
**Background:** For Graphite to work as a photo editing app, it needs to import raw photos. These contain compressed sensor imagery and metadata in a variety of formats. Sony ARW is the first target and additional camera brands are stretch goals. Graphite needs a library written in pure Rust with a suitable (non-GPL) license, which does not currently exist in the ecosystem, so we need to create one ourselves.