This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Smile Nerd Font Mono is a composite font build system. It merges CJK glyphs from LXGW Wenkai Mono into FiraCode Nerd Font Mono, then patches out the www ligature.
- Python: >= 3.12, managed by
uv. Nopip/pip3/python -m pip— useuvexclusively. - Shell: fish. All shell commands must be fish-compatible.
- All Python invocations must use
uv run python, both in the Makefile and when running scripts manually. - Do not modify the system Python environment. Dependencies are isolated in the project-local
.venv/. - If a system-level package is missing, stop and report the error rather than attempting
emergeor system config changes.
config.toml # Font metadata, weight mappings, Unicode ranges
pyproject.toml # Project dependencies (fonttools)
Makefile # Build orchestration (all Python calls via uv run)
assets/FiraCode/ # Upstream FiraCode Nerd Font Mono TTF files
assets/lxgw-wenkai/ # Upstream LXGW Wenkai Mono TTF files
scripts/merge.py # CJK glyph merge (reads config.toml)
scripts/patch_ligatures.py # www ligature disabler (in-place patching)
scripts/render_preview.py # HTML glyph grid generator (relative font paths)
scripts/gen_live_preview.py # Interactive live preview (base64-embedded fonts)
build/ # Output TTF files (gitignored)
preview/ # Output HTML previews (gitignored)
- Uses
fonttools(fontTools.ttLib.TTFont), not fontforge. - Reads all configuration from
config.toml: font paths, subfamily names, weight classes, and Unicode ranges. - FiraCode UPM is 1950, LXGW UPM is 1000. Glyphs are scaled by
base_upm / cjk_upm(1.95x). - Both fonts use TrueType outlines (
glyftable), not CFF. - Only cmap format 4 (BMP) and format 12 (full Unicode) subtables are updated. Format 6 is skipped.
- Composite glyph dependencies are resolved recursively before insertion.
--weightacceptsregularorlight.
- Finds www-related lookups by content matching, not hardcoded indices.
- Looks for SingleSubst lookups mapping
wtow_w_w.ligaorw.spacer. - Finds ChainContextSubst (format 1) rules referencing those lookups.
- Removes the SubstLookupRecord entries from the chain rules.
- Changes the SingleSubst mappings to self-map (
w->w). - Idempotent: warns and exits cleanly if already patched.
- Generates static HTML with a glyph grid (one cell per codepoint).
- Fonts are loaded via relative
url()in@font-face; font files are copied next to the HTML output. - Supports
--comparefor side-by-side two-font rendering.
- Generates self-contained HTML with fonts base64-embedded in
@font-facedata:URIs. - Reads font family name from the TTF name table (nameID 1 + 2, platformID 3).
- Supports multiple
--fontswith a dropdown to switch between them. - Pure HTML + vanilla JS, no frameworks.
Defines:
[font]:family_name,version[weights.regular]and[weights.light]:firacodepath,lxgwpath,outputpath,subfamily,weight_class[unicode_ranges]: named ranges inXXXX:YYYYhex format
Important: merge.py reads font paths from config.toml. The Makefile has corresponding dependency lines that must be kept in sync manually.
uv syncmake all— runsmergethenpatchmake merge— builds both weights viascripts/merge.pymake patch— runsscripts/patch_ligatures.pyon both built fontsmake preview— generates glyph grid HTML for CJK Unified rangemake live-preview— generates interactive live text preview with both weightsmake clean— removesbuild/andpreview/
To generate the preview PNG (requires Typst >= 0.14, optional):
typst compile --font-path ./build preview.typ preview.png --ppi 288uv run python scripts/merge.py --weight regular
uv run python scripts/patch_ligatures.py build/SmileNerdFontMono-Regular.ttf
# Glyph grid with optional side-by-side comparison
uv run python scripts/render_preview.py \
--font build/SmileNerdFontMono-Regular.ttf \
--range 4E00:9FFF \
--output preview/cjk.html
uv run python scripts/render_preview.py \
--font build/SmileNerdFontMono-Regular.ttf \
--compare assets/lxgw-wenkai/LXGWWenKaiMono-Regular.ttf \
--range 4E00:4EFF \
--output preview/compare.html- Scripts use
argparsefor CLI,pathlib.Pathfor paths,loggingfor output. - Scripts have
if __name__ == "__main__"entry points. - Code comments and CLI help text are in English.