Skip to content
Merged
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
11 changes: 10 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,22 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v4
# The User Guide PDF and the screenshots are NOT built in CI: neither
# pandoc/TeX Live nor Nuke ship on the GitHub-hosted runner, and installing
# the TeX Live toolchain on every release is slow and heavy. Both are built
# locally and committed instead (`make docs`); CI only bundles the
# committed docs/user-guide.pdf and docs/images/*.png into the release zip.
- name: Build release zip
run: |
mkdir -p staging/Labelmaker
mkdir -p staging/Labelmaker/docs/images
cp __init__.py labelmaker.py labelmaker_config.py labelmaker_prefs.py \
labelmaker_deoverlap.py labelmaker_prefs_dialog.py \
labelmaker_config_editor.py menu.py base_config.json README.md \
staging/Labelmaker/
# Bundle the committed User Guide PDF and the screenshots the README
# references, so its relative image links resolve.
cp docs/user-guide.pdf staging/Labelmaker/docs/
cp docs/images/*.png staging/Labelmaker/docs/images/
touch "staging/Labelmaker/${{ github.ref_name }}"
cd staging
zip -r "../Labelmaker-${{ github.ref_name }}.zip" Labelmaker/
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,13 @@ dmypy.json

# Pyre type checker
.pyre/
.tools/

# Nuke autosave files
*.nk.autosave

# Throwaway HOME for doc capture sessions
.capture-home/

# Intermediate Markdown generated from the README for the User Guide PDF
docs/.build/
82 changes: 82 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Labelmaker — project instructions

Labelmaker is a Nuke plugin that replaces Nuke's autolabel system with rich,
multi-line node labels, plus an Edit-menu Preferences dialog, a Config Editor, and
a De-overlap command.

## Documentation must stay in sync with the code

**Whenever a change affects user-facing behaviour, update the documentation and
regenerate its screenshots in the same change.** User-facing means any of:

- a feature or the per-node-class labels (`labelmaker.py`, `base_config.json`)
- the Edit-menu commands (`menu.py`)
- the Preferences fields (`labelmaker_prefs.py`, `labelmaker_prefs_dialog.py`)
- the Config Editor UI (`labelmaker_config_editor.py`)
- the config JSON format or the config cascade (`labelmaker_config.py`)

The documentation has a single source of truth, `README.md`:

- `README.md` — the complete reference (install, features, configuration,
preferences); references `docs/images/*.png`
- `docs/user-guide.pdf` — the User Guide, built from `README.md` by `make pdf`
(pandoc + xelatex). `docs/pandoc/build_user_guide_md.py` derives the Markdown by
dropping the install section and adding a title block; it is styled with the
project LaTeX class `docs/latex/training_doc.cls` (+ `docs/latex/logo.pdf`) and
`docs/pandoc/pdf.yaml`. The PDF is **built locally and committed** — the
GitHub runner has neither pandoc/TeX Live nor Nuke, so nothing in `docs/` is
regenerated in CI. Rebuild it with `make pdf` and commit the result whenever the
README changes.
- `docs/images/*.png` — screenshots, generated (committed to the repo)

Keep the README free of pandoc-specific image attributes (`{ width=… }`): it
renders on GitHub as GFM, which shows those as literal text. PDF image layout and
sizing is handled by `docs/pandoc/float-images.lua` (a pandoc filter that anchors
each screenshot beside its paragraph in a two-column minipage row — text left,
image pinned right at half width — and centres the hero image) plus
`docs/pandoc/pdf.yaml`, not per-image.

### Source → documentation map

| If you change … | Update … | Regenerate … |
|---|---|---|
| `base_config.json`, `labelmaker.py` (label content) | feature sections of `README.md` | DAG screenshots — `docs/screenshots/features.nk` (run `make screenshots-dag`) |
| `menu.py` (menu commands) | menu references in `README.md` | panel screenshots (`make screenshots-panels`) |
| `labelmaker_prefs*.py` (preferences) | Preferences table/section in `README.md` | `prefs_dialog.png` (`make screenshots-panels`) |
| `labelmaker_config_editor.py` | Config Editor section in `README.md` | `config_editor.png` (`make screenshots-panels`); keep `setObjectName` targets in sync with `docs/screenshots/panels.scenarios.json` |
| config format/cascade | Configuration section in `README.md` | — |

### How to regenerate

Screenshots come from [nuke-screenshotter](https://github.com/charlesangus/nuke-screenshotter)
(`pip install` it once), driving a real Nuke under `xvfb-run`:

```sh
make screenshots # regenerate docs/images/*.png (needs Nuke; commit the PNGs)
make pdf # rebuild docs/user-guide.pdf (needs pandoc + xelatex; commit the PDF)
make docs # both
```

- DAG/autolabel shots come from `docs/screenshots/features.nk` (regenerate that
scene with `nuke -t docs/screenshots/build_features_nk.py`).
- Prefs/Config-Editor window shots come from `docs/screenshots/panels.scenarios.json`.
- Both runs load Labelmaker into the capture session via
`docs/screenshots/bootstrap/menu.py` (put on `NUKE_PATH` by the Makefile).
Getting the autolabels (not the bare class names) to render used to require a
cut/paste redraw hack in that bootstrap; nuke-screenshotter v1.2 (#6) does
the required label warm-up in its capture path, so the hack has been removed.
Screenshot regeneration therefore needs the **v1.2-or-later** screenshotter
(`pip install --upgrade "git+https://github.com/charlesangus/nuke-screenshotter"`).

CI (`.github/workflows/release.yml`) does **not** build any docs — the GitHub
runner has neither Nuke (for screenshots) nor pandoc/TeX Live (for the PDF), and
installing the TeX Live toolchain on every release is slow and heavy. It only
bundles the committed `docs/user-guide.pdf` and `docs/images/*.png` into the
release zip, so **both the PDF and the PNGs must be committed**. Regenerate them
locally with `make docs` and commit before tagging a release.

## Conventions

- Run `ruff check .` and `pytest tests/` before committing (see `pyproject.toml`).
- PySide6 imports in `labelmaker*.py` are deferred (not at module level) to avoid
import-order issues in Nuke; keep them that way.
76 changes: 76 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Makefile — regenerate Labelmaker's documentation.
#
# make screenshots regenerate all PNGs under docs/images/ (requires Nuke +
# nuke-screenshotter + xvfb-run on headless Linux)
# make pdf build docs/user-guide.pdf from the README (requires
# pandoc + xelatex only — no Nuke). CI does NOT build the
# docs; run this locally and commit the PDF (see CLAUDE.md).
# make docs screenshots + pdf (full local regen)
# make clean remove generated PDFs
#
# The screenshots come from nuke-screenshotter:
# pip install "git+https://github.com/charlesangus/nuke-screenshotter"
# Override the Nuke path or screenshotter command if they are not on PATH:
# make screenshots NUKE=/path/to/nuke SHOTTER=/path/to/nuke-screenshotter

NUKE ?= nuke
SHOTTER ?= nuke-screenshotter
ZOOM ?= 2.0

DOCS := docs
IMAGES := $(DOCS)/images
BOOTSTRAP := $(DOCS)/screenshots/bootstrap
FEATURES := $(DOCS)/screenshots/features.nk
SCENARIOS := $(DOCS)/screenshots/panels.scenarios.json
PDF_YAML := $(DOCS)/pandoc/pdf.yaml
FLOAT_FILTER := $(DOCS)/pandoc/float-images.lua

# The User Guide PDF is derived from the README (single source of truth) by
# build_user_guide_md.py, which drops the install section and adds a title block.
BUILD_DIR := $(DOCS)/.build
GUIDE_MD := $(BUILD_DIR)/user-guide.md
GUIDE_SCRIPT := $(DOCS)/pandoc/build_user_guide_md.py

PDFS := $(DOCS)/user-guide.pdf

# Run the capture session in a CLEAN, reproducible Nuke environment:
# - NUKE_PATH is ONLY our bootstrap (no inherited NUKE_PATH), so the capture
# loads Labelmaker and nothing else.
# - HOME points at a throwaway dir so the user's personal ~/.nuke plugins (and
# their callbacks, which would otherwise fire during the capture) are not
# sourced. Nuke writes a fresh, empty ~/.nuke there.
# This keeps the docs identical no matter whose machine regenerates them.
export NUKE_PATH := $(CURDIR)/$(BOOTSTRAP)
CAPTURE_HOME := $(CURDIR)/.capture-home

.PHONY: docs screenshots screenshots-dag screenshots-panels pdf clean help

docs: screenshots pdf ## Regenerate screenshots and PDFs (needs Nuke)

screenshots: screenshots-dag screenshots-panels ## Regenerate every PNG (needs Nuke)

screenshots-dag: ## Capture the DAG/autolabel screenshots from features.nk
mkdir -p "$(CAPTURE_HOME)/.nuke"
HOME="$(CAPTURE_HOME)" $(SHOTTER) $(FEATURES) $(IMAGES) --zoom $(ZOOM) --nuke-exec $(NUKE)

screenshots-panels: ## Capture the Preferences / Config Editor windows
mkdir -p "$(CAPTURE_HOME)/.nuke"
HOME="$(CAPTURE_HOME)" $(SHOTTER) --scenarios $(SCENARIOS) --output-dir $(IMAGES) --nuke-exec $(NUKE)

pdf: $(PDFS) ## Build the User Guide PDF from the README (needs pandoc + xelatex)

# Build docs/user-guide.pdf from README.md. The README's image paths are
# repo-root-relative (docs/images/...), so the resource path starts at $(CURDIR).
$(DOCS)/user-guide.pdf: README.md $(GUIDE_SCRIPT) $(PDF_YAML) $(FLOAT_FILTER) $(DOCS)/latex/training_doc.cls
mkdir -p $(BUILD_DIR)
python3 $(GUIDE_SCRIPT) README.md $(GUIDE_MD)
TEXINPUTS="$(CURDIR)/$(DOCS)/latex:$$TEXINPUTS" \
pandoc --defaults $(PDF_YAML) --lua-filter $(FLOAT_FILTER) --resource-path "$(CURDIR):$(DOCS):$(DOCS)/latex" -o $@ $(GUIDE_MD)

clean: ## Remove generated PDFs and the intermediate build dir
rm -f $(PDFS)
rm -rf $(BUILD_DIR)

help: ## List targets
@grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) \
| awk 'BEGIN{FS=":.*?## "}{printf " %-20s %s\n", $$1, $$2}'
Loading
Loading