Development
ReShiki uses one Rust application executable for the editor, chemistry and native integrations, and Astro Starlight for documentation. Python/RDKit is an optional test reference.
Build and run
Section titled “Build and run”Install Rust 1.99.0 with rustfmt and Clippy, a C/C++ compiler, and Python 3.12 for the build scripts. The repository pins this compiler and its rustfmt/Clippy components in rust-toolchain.toml, and CI uses the same version. On Windows, use a Visual Studio tools shell matching your Rust target.
On macOS, Cargo defaults C/C++ dependencies to Apple Clang from /usr/bin, so a GCC installation earlier on PATH does not change the compiler. Set CC_aarch64_apple_darwin and CXX_aarch64_apple_darwin (or the x86_64_apple_darwin equivalents for Intel) explicitly to override these repository defaults.
git clone https://github.com/Ameyanagi/ReShiki.git reshikicd reshikicargo run --lockedFor Windows ARM, build with --target aarch64-pc-windows-msvc from an ARM64 Visual Studio tools shell. The application relaunches itself for bounded InChI, geometry and naming work, and for some platform services; see worker modes. There are no sibling runtime executables or Swift build steps. For offline Cargo builds, populate the dependency cache first and set CARGO_NET_OFFLINE=true.
Native checks
Section titled “Native checks”python3 scripts/build_inchi_helper.pyexport RESHIKI_INCHI_HELPER="$PWD/artifacts/inchi-helper/reshiki-inchi-helper"export RESHIKI_REQUIRE_INCHI_HELPER=1cargo fmt --all --checkcargo clippy --workspace --all-targets --locked -- -D warningscargo check --workspace --all-targets --lockedcargo build --locked --bin reshikiexport RESHIKI_NAMING_HELPER="$PWD/target/debug/reshiki"cargo test --workspace --lockedcargo run --locked -- --engine-checkTo test one library layer, select its crate. Integration tests, binaries and examples belong to the root reshiki package, so qualify them with -p reshiki:
cargo test --locked -p reshiki-chemistrycargo test --locked -p reshiki-modelcargo test --locked -p reshiki-iocargo test --locked -p reshiki-agentcargo test --locked -p reshiki-mcpcargo test --locked -p reshiki-metafilecargo test --locked -p reshiki --test theme_referenceA single-package build resolves features for that package alone, so the first run may compile some dependencies again with a smaller feature set. reshiki-metafile is not a default member, so plain cargo test skips its tests. See crates and layering for what each crate contains.
The naming project’s five crates now live in Ameyanagi/nazuchem. ReShiki uses nazuchem-ir, nazuchem-kernel, nazuchem-n2s and nazuchem-s2n as git dependencies at the nazuchem revision pinned in Cargo.toml; it does not use the facade crate nazuchem. Run the engine unit tests and oracle gates in that repository. ReShiki’s cargo test --workspace --locked covers the adapters, naming integration tests and worker response snapshot; it does not run dependency unit tests. Use the structure-to-name harness for focused ReShiki naming checks.
Naming integration tests start the built application in its --naming-worker mode. Build it first and point RESHIKI_NAMING_HELPER at it with an absolute path; if you set CARGO_TARGET_DIR, use the binary there. Without the variable, the tests fail with “Naming integration tests require RESHIKI_NAMING_HELPER…”. Rebuild the binary after you change naming or worker code, because the tests run it as built.
The structure-to-name harness measures naming coverage and openclatura parity with an in-process OPSIN round trip, without a helper executable. Its fast subset runs normally; the full corpus is opt-in and runs nightly in CI.
The development-only helper and fault-injection stub let library tests exercise subprocess transport independently of the GUI entry point. Keep RESHIKI_INCHI_HELPER set while running those tests. In PowerShell, use python, set $env:RESHIKI_INCHI_HELPER = (Resolve-Path artifacts/inchi-helper/reshiki-inchi-helper.exe).Path, and set $env:RESHIKI_REQUIRE_INCHI_HELPER = '1'. After cargo build --locked --bin reshiki, set $env:RESHIKI_NAMING_HELPER = (Resolve-Path target/debug/reshiki.exe).Path. Required mode fails if native test binaries are missing. Default tests do not require Python or RDKit. Windows clipboard tests replace the desktop clipboard with test data.
Archive and installer checks run the installed app twice with Python, uv and the checkout unavailable. They reject separate worker executables, Python payloads and chemistry-environment creation. Release CI also runs the complete InChI framing and heap suite against the application’s --inchi-worker mode.
Working on nazuchem
Section titled “Working on nazuchem”To test unmerged engine changes, check out nazuchem beside ReShiki and add this temporary patch to ReShiki’s root Cargo.toml:
[patch."https://github.com/Ameyanagi/nazuchem"]nazuchem-ir = { path = "../nazuchem/crates/nazuchem-ir" }nazuchem-kernel = { path = "../nazuchem/crates/nazuchem-kernel" }nazuchem-n2s = { path = "../nazuchem/crates/nazuchem-n2s" }nazuchem-s2n = { path = "../nazuchem/crates/nazuchem-s2n" }Patch all four crates together. A partial patch can resolve two copies of nazuchem-kernel and cause type mismatches between the local and git crates. --locked fails while the patch changes dependency resolution, so omit it for local patch builds and tests. Keep the patch and its Cargo.lock changes uncommitted. Remove the patch and undo its lockfile changes before updating the pin.
After upstream CI is green at the selected revision, change all four rev values in [workspace.dependencies] to that commit, then update the lockfile:
cargo update -p nazuchem-ir -p nazuchem-kernel -p nazuchem-n2s -p nazuchem-s2nAs part of each pin bump, review crates/model/src/molecule_names/ir.rs, adapted
from nazuchem-s2n’s src/ir_input.rs at the pinned revision. Changes to upstream
IR preparation semantics require a manual re-sync of this adapter. The worker
snapshot deliberately rejects IR versions other than 1; review its version guard
when changing the IR contract.
Then replay the worker response snapshot. It calls the worker’s request handler in-process, so it checks the engines’ output byte-for-byte without the helper processes:
cargo test --locked --lib -p reshiki naming::s2n_harness::worker_snapshot::s2n_worker_snapshot -- --exact --nocaptureThe other naming tests do start helper processes; run them as CI does, with RESHIKI_INCHI_HELPER and RESHIKI_NAMING_HELPER set to the rebuilt target/debug/reshiki (with .exe on Windows) and RESHIKI_REQUIRE_INCHI_HELPER=1.
Opt-in renderer tests
Section titled “Opt-in renderer tests”Tests that need a real renderer or a baseline folder are marked #[ignore]. Run them with -- --ignored, or with -- --include-ignored to run the ordinary tests too. Add --test-threads=1 for renderer pixel tests. On macOS the only headless renderer is WGPU on Metal; Tiny Skia is built only on Windows.
| Variable | Used by | Meaning |
|---|---|---|
RESHIKI_DATA_DIR |
View parity, UI snapshots and other app-level renderer tests | The app’s data folder: App::new reads recovery drafts and the template library from it. Point it at a new, empty folder before each run, so your own data cannot change the result. |
RESHIKI_UI_QA_DIR |
ui_layout_snapshots |
Folder for PNG snapshots of key UI states at 1280 and 1040 px. Without it, the test writes to reshiki-ui-qa in the temporary folder. |
RESHIKI_VIEW_PARITY |
views_match_captured_baseline, entries_match_captured_baseline |
Baseline folder for view and right-click menu parity. RESHIKI_VIEW_PARITY_CAPTURE=1 writes the baseline; otherwise the test compares. RESHIKI_VIEW_PARITY_ONLY limits a capture to a case-name prefix during development; compare mode refuses it. |
RESHIKI_CANVAS_PIXELS |
Canvas, paper-layer, text-placement and icon pixel baselines | Baseline folder. RESHIKI_CANVAS_CAPTURE_BASELINE=1 writes the baseline; otherwise the test compares. |
RESHIKI_PERF_RENDERER |
Pixel, view-parity and canvas performance tests | Headless backend: wgpu, or tiny-skia on Windows. Without it, the default backend runs. |
RESHIKI_MODEL_PARITY |
reshiki-model captured-baseline tests |
Baseline folder for model output. RESHIKI_MODEL_CAPTURE_BASELINE=1 writes it. |
Capture a baseline on the parent commit, then compare on your change:
export RESHIKI_DATA_DIR="$PWD/artifacts/test-data"rm -rf "$RESHIKI_DATA_DIR" && mkdir -p "$RESHIKI_DATA_DIR"RESHIKI_VIEW_PARITY="$PWD/artifacts/view-parity" RESHIKI_VIEW_PARITY_CAPTURE=1 \ cargo test --locked --bin reshiki views_match_captured_baseline -- --ignored --nocaptureFor the comparison, empty RESHIKI_DATA_DIR again and run the same command without RESHIKI_VIEW_PARITY_CAPTURE. Set RESHIKI_NAMING_HELPER and RESHIKI_INCHI_HELPER the same way for both runs: with the naming helper set, view parity also registers the cases that need chemical names.
On Windows, CI runs every ignored app::naming:: test, the app::nmr:: and canvas::nmr:: tests with --include-ignored --test-threads=1, and a few named focus and menu tests (.github/workflows/checks.yml). A new naming renderer test therefore runs there without a workflow change.
Optional RDKit reference tests
Section titled “Optional RDKit reference tests”Install uv 0.13.5 or later and run uv sync --locked --python 3.12.15. On Windows, use --python cpython-3.12.15-windows-x86_64-none, including on ARM.
Run reference/source HTTPS downloads through the locked environment so they use the updated certifi CA bundle. Offline packaging does not need certifi. The dated security review records runtime requirements and accepted exceptions.
The pinned RDKit reference provides wheels for Apple Silicon macOS, Windows x64 and Linux x64/ARM64, but not Intel macOS. Intel Mac development and packaging use the native checks above; run optional reference comparisons on a supported platform. The distributed Intel app needs no Python or RDKit.
Live layout comparisons on Linux and Windows need fresh references for their installed math libraries. Setup downloads the pinned RDKit source and verified Boost headers; it only builds test tools.
On Linux:
uv run --locked python scripts/setup_linux_depict_reference.pysource artifacts/depict-live/environment.shOn Windows, run setup from an x64 Visual Studio tools shell:
uv run --locked python scripts/setup_windows_depict_reference.pyThen, in the tools shell matching your Rust target, load the reference paths:
. ./artifacts/depict-live-windows/environment.ps1Run the comparisons on a supported reference platform:
cargo test --workspace --locked --features rdkit-reference --no-fail-fastuv run --locked python -m unittest discover -s tests -p 'test_*.py'uv run --locked python -m unittest discover -s reference -p 'test_*.py'uv run --locked python scripts/check_reference_dependencies.pyThe rdkit-reference feature enables PythonEngine and independent differential tests. It is disabled in normal builds. Tests use the checkout’s .venv; RESHIKI_REFERENCE_PYTHON selects another prepared interpreter. Windows ARM uses x64 reference tools under emulation while the app and Rust tests remain native ARM64.
Everything that needs or captures from the pinned Python/RDKit reference lives in reference/. reference/engine/ is the Python worker. Each reference/<name>.rs is an rdkit-reference integration target, declared in Cargo.toml with an explicit path. The *_reference.py, build_*_oracle.py and *.cpp files are oracles and native observers, reference/test_*.py are the Python reference tests, and the Markdown notes record how fixtures were captured. Fixtures stay in tests/fixtures/, and assets/drawing_style.json holds the drawing style shared with the application.
Pull requests run native tests and saved InChI, depiction, and aromatic-response fixtures on macOS ARM64, Linux x64, and Windows x64. Documentation-only changes skip native checks. Expected results come from pinned RDKit captures; fixture updates retain their source hashes and platform provenance.
The Browser build (wasm32) job checks the browser build on the same pull requests: Clippy with -D warnings for the editor and the headless library (reshiki-web) for wasm32-unknown-unknown, with the crates and target scripts/build_wasm.sh builds, then bun test in packages/editor against a debug build of the headless library. It skips with the native checks for documentation-only changes, and only release tags build the optimised bundle. The web job lints packages/editor with oxlint on every pull request. To run the same checks locally:
cargo clippy -p reshiki-web -p reshiki --lib --bin reshiki --target wasm32-unknown-unknown --locked -- -D warningsscripts/build_wasm.sh --testThe complete live RDKit suite runs weekly, before publishing a tagged release, or on demand. Four parallel shards per platform retain every integration target, plus workspace unit and documentation tests:
gh workflow run checks.yml --ref main -f live_reference=trueTo refresh aromatic-fixture provenance after an unrelated worker-source change, dispatch checks.yml with capture_aromatic=true. Its three platform jobs replay the existing request corpus through the pinned Python worker, retain the historical template inputs, and require an unchanged request/response hash. Download the aromatic-capture-* artifacts, compare their records and source hashes, then commit the updated captures. A changed response fails this refresh path and requires a separate review; expected results are never generated by the native implementation.
Windows ARM64 and Linux ARM64 remain release targets with package and installer checks.
Pre-commit checks
Section titled “Pre-commit checks”Install Bun 1.4.4, then run bun install --frozen-lockfile to install the web tools and Lefthook hooks. Use bun run --bun lefthook install to reinstall hooks. Python changes also need the uv development environment above.
Hooks check staged file types: Oxlint and bun fmt for web and configuration files, Ruff/ty for Python, and Cargo fmt and Clippy for Rust. Clippy type-checks libraries and binaries; CI lints tests and examples on every pull request. They do not rewrite or restage changes. Rust checks run sequentially. The repository uses LF line endings, including on Windows.
bun run lint:jsbun run format:checkuv run --locked ruff check reference scripts testsuv run --locked ruff format --check reference scripts testsuv run --locked ty checkTo format, use bun run format, uv run --locked ruff format reference scripts tests, or cargo fmt --all.
Documentation
Section titled “Documentation”bun run docs:devbun run docs:checkbun run docs:buildEdit the visual manual in website/src/content/docs/guide/ and developer notes in docs/*.md. The sync script generates ignored Starlight copies of the developer notes; edit their originals. Landing page, navigation and theme live in website/. The production build checks local page and asset links. Follow writing and terms for wording, UI labels and feature names.
The Documentation workflow checks pull requests and deploys main to GitHub Pages. Pages must use GitHub Actions as its source. See release builds and signing for distribution setup.
UI conventions
Section titled “UI conventions”Every floating surface (menus, popovers, palettes, dialogs) wraps its shadow in crate::appearance::surface_shadow (src/appearance.rs). On Windows it returns no shadow, because Windows can render with Tiny Skia, whose shadow pass ignores the damage clip and darkens retained pixels on partial redraws. The windows-2022 CI job runs the app::nmr partial-redraw test (nmr_surfaces_partial_redraws_preserve_unchanged_pixels), which checks the NMR surfaces.
Windows development
Section titled “Windows development”Use the MSVC Rust toolchain with Visual Studio Build Tools and the Windows SDK. Measure performance with cargo run --release --locked. Windows automatic startup uses Direct3D 12 consistently for adapter discovery and Iced’s WGPU initialization, then selects Tiny Skia directly when discovery finds only CPU rendering or no adapters. Explicit ICED_BACKEND preferences bypass automatic configuration; existing WGPU_BACKEND values are preserved, including empty or invalid lists. --graphics-info reports the requested WGPU APIs, discovered adapters and renderer preference; it does not create a window or validate a graphics device. This reduces unnecessary graphics driver initialization but does not recover from native driver crashes. Clipboard, printing and Office integration are Rust code linked into the app. See the Windows guide.