mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 07:38:11 +08:00
Revamp the Graphite website (#1265)
Revamp the website with more content
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
+++
|
||||
title = "Codebase overview"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 2 # Chapter number
|
||||
+++
|
||||
|
||||
<div class="video-embed aspect-16x9">
|
||||
<iframe width="1280" height="720" src="https://www.youtube.com/embed/vUzIeg8frh4" title="Workshop: Intro to Coding for Graphite" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
The Graphite editor is built as a web app powered by Svelte in the frontend and Rust in the backend which is compiled to WebAssembly (wasm) and run in the browser.
|
||||
|
||||
The Editor's frontend web code lives in `/frontend/src` and the backend Rust code lives in `/editor`. The web-based frontend is intended to be semi-temporary and eventually replaceable with a pure-Rust GUI frontend. Therefore, all backend code should be unaware of JavaScript or web concepts and all Editor application logic should be written in Rust not JS.
|
||||
|
||||
## Frontend/backend communication
|
||||
|
||||
Frontend (JS) -> 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 JS with a simpler API of callable functions. These wrapper functions are compiled by wasm-bindgen into autogenerated JS functions that serve as an entry point into the wasm.
|
||||
|
||||
Backend (Rust) -> frontend (JS) communication happens by sending a queue of messages to the frontend message dispatcher. After the JS calls 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 JS-friendly data types in `/frontend/src/wasm-communication/messages.ts`. Various JS code subscribes to these messages by calling `subscribeJsMessage(MessageName, (messageData) => { /* callback code */ });`.
|
||||
|
||||
## The Editor backend and Legacy Document modules
|
||||
|
||||
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.
|
||||
|
||||
The actual document (the artwork data and layers included in a saved `.graphite` file) is part of another core module located in `/document-legacy`. The (soon-to-be-replaced) Legacy Document codebase manages a user's document. Once it is replaced, the new Document module (that will be located in `/document`) will store a document's node graph and change history. While it's OK for the Editor to read data from—or make immutable function calls upon—the user's document controlled by the Legacy Document module, it should never be directly mutated. Instead, messages (called Operations) should be sent to the document to request changes occur. The Legacy Document code is designed to be used by the Editor or by third-party Rust or C/C++ code directly so a careful separation of concerns between the Editor and Legacy Document modules should be considered.
|
||||
|
||||
## The message bus
|
||||
|
||||
Every part of the Graphite stack works based on the concept of message passing. Messages are pushed to the front or back of a queue and each one is processed by the module's dispatcher in the order encountered. Only the dispatcher owns a mutable reference to update its module's state.
|
||||
|
||||
### Additional technical details
|
||||
|
||||
A message is an enum variant of a certain message sub-type like `FrontendMessage`, `ToolMessage`, `PortfolioMessage`, or `DocumentMessage`. Two example messages:
|
||||
```rs
|
||||
// Carries no data
|
||||
DocumentMessage::DeleteSelectedLayers
|
||||
|
||||
// Carries a layer path and a string as data
|
||||
DocumentMessage::RenameLayer(Vec<LayerId>, String)
|
||||
```
|
||||
|
||||
Message sub-types hierarchically wrap other message sub-types; for example, `DocumentMessage` is wrapped by `PortfolioMessage` via:
|
||||
```rs
|
||||
// Carries the child message as data
|
||||
PortfolioMessage::Document(DocumentMessage)
|
||||
```
|
||||
and `EllipseMessage` is wrapped by `ToolMessage` via:
|
||||
```rs
|
||||
// Carries the child message as data
|
||||
ToolMessage::Ellipse(EllipseMessage)
|
||||
```
|
||||
Every message sub-type is wrapped by the top-level `Message`, so the previous example is actually:
|
||||
```rs
|
||||
Message::Tool(ToolMessage::Ellipse(EllipseMessage))
|
||||
```
|
||||
|
||||
Because this is cumbersome, we have a proc macro `#[child]` that automatically implements the `From` trait on message sub-types and lets you write:
|
||||
```rs
|
||||
DocumentMessage::DeleteSelectedLayers.into()
|
||||
```
|
||||
instead of:
|
||||
```rs
|
||||
Message(PortfolioMessage::Document(DocumentMessage::DeleteSelectedLayers))
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
+++
|
||||
title = "Contributing guidelines"
|
||||
|
||||
[extra]
|
||||
order = 2 # 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 serde::`)
|
||||
|
||||
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:
|
||||
|
||||

|
||||
@@ -0,0 +1,30 @@
|
||||
+++
|
||||
title = "Debugging"
|
||||
|
||||
[extra]
|
||||
order = 1 # 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 bus 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.
|
||||
|
||||
## Layer paths and document IDs
|
||||
|
||||
In debug mode, hover over a layer's name in the Layer Tree panel to view a tooltip with its `u64` path. Likewise, document IDs may be read by hovering over their tabs.
|
||||
@@ -0,0 +1,16 @@
|
||||
+++
|
||||
title = "Tech stack"
|
||||
|
||||
[extra]
|
||||
order = 3 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
- 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 -->
|
||||
Reference in New Issue
Block a user