Skip to content
Merged

Docs #97

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/docs-artifact.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Docs artifact

# Publishes the documentation artifact for a release: the hand-written manual
# under `docs/manual/` plus `metadata.json`, the projection of every registry
# Darkly has. Both go in one tarball under one `version`, so a consumer can
# never pair prose from one build with metadata from another.
#
# The consumer is pull-based and is not named here on purpose — this repo is
# public and knows nothing about who reads the artifact. Whoever wants it
# resolves this asset off the release and unpacks it.
#
# `docs/manual/` is **pure markdown**. It must not reference components from any
# consumer, or the public repo acquires an invisible dependency on private code.
# Anything needing a component is a generated page on the consumer side.
#
# No GPU and no `render-docs` here: `export-docs` builds from `&'static`
# registration data alone. Rendered previews are a separate, later artifact.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: >-
Existing release tag to attach the artifact to. Leave blank to build
and upload for inspection without touching any release.
required: false

jobs:
docs-artifact:
name: export-docs + manual → release asset
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# Full checkout (with .git) so build.rs's `git describe --tags` resolves —
# that string is what stamps `version`, and a shallow checkout breaks it.
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install fontconfig (native font enumeration via fontique)
run: |
sudo apt-get update
sudo apt-get install -y libfontconfig1-dev
- uses: dtolnay/rust-toolchain@stable

- name: Export registry metadata
run: cargo run -p darkly --bin export-docs -- --out out/metadata.json

# An icon name is not self-describing, and `local:` icons exist nowhere but
# this repo — so the artifact carries resolved SVG rather than asking a
# consumer to own an icon toolchain. Needs the frontend's @iconify deps.
- uses: actions/setup-node@v7
with:
node-version: 22
- name: npm ci
working-directory: frontend
run: npm ci
- name: Resolve icons
run: node frontend/scripts/export-doc-icons.mjs --metadata out/metadata.json --out out/icons.json

- name: Stage the manual
run: cp -r docs/manual out/manual

# Which release this ends up on: the pushed tag, or the one a manual run
# names. A dispatch with no tag names nothing and stops after the upload
# below — a real dry run of the export that touches no release.
- name: Resolve target release
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
echo "TARGET=$GITHUB_REF_NAME" >> "$GITHUB_ENV"
else
echo "TARGET=$INPUT_TAG" >> "$GITHUB_ENV"
fi

- name: Pack
run: |
NAME="darkly-docs-${TARGET:-${GITHUB_REF_NAME//\//-}}.tar.gz"
echo "ARTIFACT=$NAME" >> "$GITHUB_ENV"
tar czf "$NAME" -C out .
tar tzf "$NAME"

- name: Upload for inspection
uses: actions/upload-artifact@v4
with:
name: docs-artifact
path: ${{ env.ARTIFACT }}

# The release object for a tag is produced outside this repo, so this job
# may run before or after it exists — create-if-missing plus --clobber
# makes either order work, and makes a re-run idempotent.
- name: Attach to the release
if: env.TARGET != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release view "$TARGET" >/dev/null 2>&1 \
|| gh release create "$TARGET" --generate-notes
gh release upload "$TARGET" "$ARTIFACT" --clobber
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ target
Graphite
/krita
/gimp
/gegl
.claude
frontend/wasm/pkg
frontend/dist
6 changes: 2 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ flowchart LR
```
crates/darkly/src/
document/ Authoritative model (layer tree, canvas, ...)
layer_kinds/ ★ group, raster, void
modifiers/ ★ mask, selection
layer_kinds/ ★ group, raster, vector, void, filter
filters/ ★ mask, selection
engine/ DarklyEngine — session + per-domain dispatch
(painting, rendering, load/save, export,
floating, flatten, merge, undo_dispatch, …)
Expand Down Expand Up @@ -71,8 +71,6 @@ crates/darkly/src/
nodegraph/ Generic node-graph (graph, compiler, layout)
frontend/wasm/ WASM bridge (wasm-bindgen) — single API surface
frontend/src/ Svelte UI
shared/styles/ @darkly/styles — tokens + themes (UI + website)
website/ Astro + Starlight site (splash, docs, /demo/)
```

### Coordinate Systems
Expand Down
23 changes: 4 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Darkly is a Photoshop alternative where painters are first-class citizens. It ha

**Try the demo [here](https://demo.darkly.art).**

Documentation [here](https://darkly.art/docs/).

### Darkly pledges to:

- 🛐 Honor human imagination
Expand All @@ -35,24 +37,7 @@ https://github.com/user-attachments/assets/1fc0632d-5846-4c64-bac8-e39b0794b8b5

![brush-engine-screenshot](https://github.com/user-attachments/assets/67f8826e-a5b5-4cbe-83e1-3e29246c293c)

Darkly features a unified node-based brush system. Every brush type -- clone, liquify, watercolor, etc. -- all live in a single engine. This enables infinite customizability, mixing and matching of brush features, and on-the-fly creation of custom brushes.

### Familiar Hotkeys

<img src="https://github.com/user-attachments/assets/63544586-f006-4616-b378-97dd54e321d3" width="400"/>

On first launch, Darkly will ask you which editor preset you want. Currently we support GIMP, Krita, and Photoshop. I come from Krita, so that one's gotten the most TLC. But we want everyone to feel at home no matter which editor they come from. If you find any gaps, please let us know!

### Hotkey Cheatsheet

Full documentation is on the way; however, Darkly is mostly self-documenting, meaning if you can't find something, you can quickly search with `CTRL+F` and immediately see its hotkey, description, etc. - without leaving the app.

<img src="https://github.com/user-attachments/assets/370043b6-24a7-4a73-8816-ed58ce9108c4" width="500"/>

If you like using hotkeys, we also have a cheat sheet just for you. You can print it or put it on a second screen.

<img src="https://github.com/user-attachments/assets/2bb1737b-169b-4ca2-9687-2c54fbc07a6b" width="500"/>

Darkly's unique brushes live inside a node-based system. This enables infinite customizability, mixing and matching of brush features, and on-the-fly creation of custom brushes.

## Dark Arts

Expand Down Expand Up @@ -246,7 +231,7 @@ See the [crate README](crates/darkly/README.md) for a runnable example, and the

## Contribution

We love hackers as much as artists. Contributions are welcome! Please see [AGENTS.md](./AGENTS.md) for details.
We love hackers as much as we love artists. Contributions are welcome! Please see [AGENTS.md](./AGENTS.md) for details on how to contribute and rules of thumb for the repo.

### Use of AI

Expand Down
10 changes: 10 additions & 0 deletions crates/darkly/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ pollster = "1.0"
naga = { version = "29.0", features = ["wgsl-in"] }
syn = { version = "3", features = ["visit", "full"] }

# The hyphenated name does not match the file stem, so cargo cannot infer the
# path from it. No `required-features`: the exporter needs no GPU.
[[bin]]
name = "export-docs"
path = "src/bin/export_docs.rs"

[[bin]]
name = "render_docs"
required-features = ["testing"]

[[bin]]
name = "stroke_replay_bench"
required-features = ["testing"]
Expand Down
1 change: 1 addition & 0 deletions crates/darkly/brushes/airbrush.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Airbrush
category: Basic
description: A fully soft disc that builds color up gradually; hold it in one place and the tone deepens.
nodes:
pen_input:
type: pen_input
Expand Down
1 change: 1 addition & 0 deletions crates/darkly/brushes/blur.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Blur
category: Effects
description: Softens whatever is already on the layer instead of laying down color.
nodes:
pen_input:
type: pen_input
Expand Down
1 change: 1 addition & 0 deletions crates/darkly/brushes/calligraphy.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Calligraphy
category: Basic
description: A broad elliptical nib held at a fixed angle, so strokes thicken and thin with direction.
nodes:
pen_input:
type: pen_input
Expand Down
1 change: 1 addition & 0 deletions crates/darkly/brushes/charcoal.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Charcoal
category: Dry Media
description: A grainy stick that catches on the paper's tooth, laying color down heavily where you press and skipping where you don't.
nodes:
brush_settings:
type: brush_settings
Expand Down
1 change: 1 addition & 0 deletions crates/darkly/brushes/clone.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Clone
category: Misc
description: Paints with pixels sampled from elsewhere on the canvas rather than with the current color.
nodes:
pen_input:
type: pen_input
Expand Down
19 changes: 14 additions & 5 deletions crates/darkly/brushes/hair.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: Hair
category: Dry Media
description: A bundle of fine strands that breaks each dab into separate hairs, for fur, grass and dry-bristle texture.
nodes:
add:
type: add
Expand All @@ -18,10 +19,10 @@ nodes:
curve:
- - 0.0
- 1.0
- - 0.682973
- 0.5305826
- - 0.48145637
- 0.795719
- - 1.0
- 0.0
- 0.6756355
divide:
type: divide
levels:
Expand All @@ -34,10 +35,18 @@ nodes:
type: multiply
inputs:
a: 0.5
ranges:
a:
- -1.0
- 1.0
multiply_3:
type: multiply
inputs:
b: 0.085
b: 0.1217829
ranges:
b:
- 0.03296951
- 0.21059628
noise:
type: noise
inputs:
Expand All @@ -54,7 +63,7 @@ nodes:
subtract:
type: subtract
inputs:
a: 0.12
a: 0.11999999731779099
connections:
- add.result -> levels.input
- brush_settings.size -> divide.b
Expand Down
46 changes: 18 additions & 28 deletions crates/darkly/brushes/ink_pen.yaml
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
name: Ink Pen
category: Basic
description: A crisp-edged nib with a slow pressure ramp, for confident line work that holds its weight.
nodes:
pen_input:
type: pen_input
brush_settings:
type: brush_settings
inputs:
stabilize: 0.6
paint_color:
type: paint_color
circle:
type: circle
inputs:
softness: 0.1
stamp:
type: stamp
paint:
type: paint
curve:
type: curve
inputs:
curve:
- - 0.0
- 0.0
- - 0.4
- 0.7
- - 1.0
- 1.0
paint_color:
type: paint_color
pen_input:
type: pen_input
stamp:
type: stamp
connections:
- 'pen_input.position -> paint.position'
- 'pen_input.pressure -> paint.flow'
- 'pen_input.pressure -> curve.input'
- 'paint_color.color -> stamp.color'
- 'circle.mask -> stamp.tip'
- 'stamp.dab -> paint.rgba'
- 'curve.output -> paint.size'
- circle.mask -> stamp.tip
- paint_color.color -> stamp.color
- pen_input.position -> paint.position
- pen_input.pressure -> paint.flow
- pen_input.pressure -> paint.size
- stamp.dab -> paint.rgba
exposed_ports:
"brush_settings.stabilize": {}
"brush_settings.size": {}
"circle.softness": {}
"paint.flow": {}
"paint.opacity": {}
brush_settings.stabilize: {}
brush_settings.size: {}
circle.softness: {}
paint.flow: {}
paint.opacity: {}
5 changes: 3 additions & 2 deletions crates/darkly/brushes/liquify.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
name: Liquify
category: Effects
description: Pushes the pixels under the cursor along the stroke, warping the image without repainting it.
nodes:
pen_input:
type: pen_input
brush_settings:
type: brush_settings
inputs:
spacing: 0.0
spacing_min_px: 4.0
spacing: 0.05
spacing_min_px: 0.0
size: 0.3
liquify:
type: liquify
Expand Down
Loading
Loading