Building locally
You don’t need to build anything to use the firmware: every push to main
that touches config/ builds it on GitHub Actions, and
Flashing downloads the result. Build locally to try keymap
changes before pushing them, or to work on the firmware itself.
The repo is a ZMK user config with a local west workspace, and mise runs every task. These steps are for Arch Linux.
1. System packages
sudo pacman -S --needed cmake ninja dtc git dfu-util mise2. Zephyr SDK 0.17.0
The ARM toolchain goes into tools/, which git ignores:
mkdir -p tools
wget -P tools https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.0/zephyr-sdk-0.17.0_linux-x86_64_minimal.tar.xz
tar xf tools/zephyr-sdk-0.17.0_linux-x86_64_minimal.tar.xz -C tools/
tools/zephyr-sdk-0.17.0/setup.sh -t arm-zephyr-eabi -h -c
rm tools/zephyr-sdk-0.17.0_linux-x86_64_minimal.tar.xzWarning
Don’t use the AUR zephyr-sdk package. It installs SDK 1.0, which targets
Zephyr 4.2 and later; this firmware is on Zephyr 4.1, and 0.17.0 is the
matching SDK. .mise.toml points ZEPHYR_SDK_INSTALL_DIR at
tools/zephyr-sdk-0.17.0 and pins ZEPHYR_TOOLCHAIN_VARIANT.
3. The workspace (once)
The build runs on the system Python from pacman (3.14 at the time of
writing). mise doesn’t provide Python here and there is no .venv. Install
the modules a build imports:
sudo pacman -S --needed python-yaml python-pyelftools python-packaging python-protobuf python-pkg_resources
yay -S python-west python-pykwalify # not in the official repospython-protobuf and python-pkg_resources are for the left half’s Studio
build, which generates nanopb code. python-pkg_resources is the CachyOS
package name; on plain Arch, pkg_resources comes with python-setuptools.
Then:
mise run setupsetup first checks that those seven packages import. If any are missing, it
prints the install line and stops. Then it runs west init (only when
.west/ doesn’t exist yet), west update and west zephyr-export, and
finally the SDK’s setup.sh -h -c. That relocates the SDK host tools such
as dtc, which embed their absolute path, and registers the SDK with CMake.
Re-run mise run setup whenever you move or rename the checkout.
ZMK lands in zmk/, Zephyr in zephyr/, the other west projects in
modules/ and the repo root; all of them are git-ignored.
4. Check the workspace
mise run doctor # fast, offline: is the workspace what config/west.yml says?
mise run doctor-fix # repair it
mise run doctor-ci # optional: compare with the last green main build (needs gh)
mise run deps-check # the other direction: has upstream moved past the pins?A west workspace can drift from config/west.yml without west update
saying so: west names a project’s remote from the manifest but never
repoints an existing clone. doctor compares every project’s HEAD with the
manifest-rev ref west keeps in it, checks every remote URL against the
manifest, and checks that the nice_nano//zmk board exists and that Zephyr
is 4.1. Run it first whenever a build fails in a way that looks
environmental; see Troubleshooting.
deps-check reads the # track: comment beside each pin and reports the
projects whose upstream has moved, with one git ls-remote each. See
Firmware for the pins and how to take
an update.
5. Build
mise run build-left # left half: Studio, the custom RPC modules, zmk-rgb-layer
mise run build-right # right half
mise run build-reset # settings_reset.uf2They write eyelash_sofle_left.uf2, eyelash_sofle_right.uf2 and
settings_reset.uf2 in the repo root. Under the hood, each is one
west build -s zmk/app -b nice_nano//zmk with the shield
(eyelash_sofle_left nice_view or eyelash_sofle_right nice_view), the
config/ directory and the in-repo zmk-rgb-layer module; the left half
adds Studio and the studio-rpc-usb-uart snippet for USB.
6. Flash
mise run flash-left # or flash-right, flash-reset
mise run build-flash # build both halves, flash both, print the memory budgetEach flash-* task asks you to double-tap reset on the half, waits up to 60
seconds for NICENANO to mount, copies the file and syncs.
The keymap render
After editing config/eyelash_sofle.keymap, regenerate the drawing:
mise run drawIt renders keymap-drawer/eyelash_sofle.svg with
keymap-drawer, using the same
version and physical layout as CI, so the result matches what the Draw Keymap
workflow commits on main. That SVG is what the
Keymap page on this site shows.
The keymap PDF
The site publishes a printable keymap at /sofleii-keymap.pdf, four A4 portrait pages: a one-page summary of the basics, then every layer’s grid, three to a page. The Pages workflow prints the print view with headless Chromium after each site build. To make it locally:
mise run docs-pdf # build the site, print site/public/sofleii-keymap.pdf
mise run docs-pdf-open # the same, then open itIt needs chromium or google-chrome. Both CI and the task run
.github/scripts/print-keymap-pdf.sh, so the PDFs match.
This site
The docs site lives in site/ and builds with Hugo:
mise run docs # dev server on http://127.0.0.1:1313/
mise run docs-build # minified build into site/public/The keymap render, the hold-tap and combo tables and the dependency pins on this site are read from the repo’s own files on every build, so they never need updating by hand.