A standalone Python library for manipulating ZMK keyboard layouts with a modern fluent API. Extracted from the glovebox project to enable ecosystem growth while maintaining full backward compatibility.
Work in progress
- Fluent API: Intuitive, chainable interface for layout operations
- Type Safety: Comprehensive type hints with mypy strict mode support
- Provider Pattern: Clean abstraction allows integration with any keyboard framework
- Optional Dependencies: Core functionality with optional templating, display, and parsing features
- High Performance: <1 second for all operations, minimal memory footprint
- Comprehensive: Full ZMK layout support including behaviors, combos, macros, and more
- Round-trip Support: Parse → Modify → Save with perfect fidelity
from zmk_layout import Layout
# Load and modify a layout
layout = Layout("keyboard.keymap")
layout.layers.add("gaming")
layout.layers.get("gaming").set(0, "&kp W").set(1, "&kp A").set(2, "&kp S").set(3, "&kp D")
layout.save("modified.keymap")
# Fluent interface for complex operations
(layout.layers
.add("nav")
.get("nav")
.set_range(0, 4, ["&kp UP", "&kp DOWN", "&kp LEFT", "&kp RIGHT"])
.copy_from("default"))
# Add behaviors
layout.behaviors.add_hold_tap("ht_balanced", tap="&kp A", hold="&kp LCTRL")
layout.behaviors.add_combo("copy", keys=[0, 1], binding="&kp LC(C)")
layout.behaviors.add_macro("hello", sequence=["&kp H", "&kp E", "&kp L", "&kp L", "&kp O"])
# Core library
pip install zmk-layout
# With all optional features
pip install zmk-layout[full]
# Specific feature sets
pip install zmk-layout[templating] # Jinja2 template support
pip install zmk-layout[display] # Rich display formatting
pip install zmk-layout[parsing] # Lark parser for devicetree files
# Clone the repository
git clone https://github.com/CaddyGlow/zmk-layout.git
cd zmk-layout
# Install with development dependencies using uv
uv sync --all-extras --dev
# Or use make
make install
# Run all checks (ruff, mypy, tests)
make check
# Auto-fix and format code
make fix
# Run tests only
make test
# Build packages
make build
# Clean build artifacts
make clean
from zmk_layout import Layout
# Create empty layout
layout = Layout.create_empty(size=36)
# Load from file
layout = Layout.from_keymap("keyboard.keymap")
layout = Layout.from_json("layout.json")
# Layer management
layout.layers.add("symbols", position=1)
layout.layers.remove("gaming")
layout.layers.move("nav", position=0)
layout.layers.reorder(["default", "nav", "symbols"])
# Query layers
gaming_layers = layout.find_layers(lambda name: "gaming" in name)
all_layers = layout.layers.list()
# Context manager with auto-save
with Layout("keyboard.keymap") as layout:
layout.layers.add("test")
# Automatically saves on exit
# Batch operations
layout.batch_operation([
lambda l: l.layers.add("layer1"),
lambda l: l.layers.add("layer2"),
lambda l: l.behaviors.add_macro("test", ["&kp T"])
])
# Statistics and analysis
stats = layout.get_statistics()
print(f"Layers: {stats['layer_count']}")
print(f"Behaviors: {stats['behavior_count']}")
# Validation
layout.validate() # Raises exception if invalid
from zmk_layout import Layout
from zmk_layout.providers import LayoutProviders
# Custom provider implementation
class MyConfigProvider:
def get_behavior_definitions(self):
return [...]
def get_validation_rules(self):
return {...}
# Use custom providers
providers = LayoutProviders(
configuration=MyConfigProvider(),
template=MyTemplateProvider(),
logger=MyLogger(),
file=MyFileAdapter()
)
layout = Layout("keyboard.keymap", providers=providers)
The library uses a clean, modular architecture:
zmk_layout/
├── core/ # Core layout classes and fluent API
├── models/ # Pydantic models for layout data
├── parsers/ # Keymap and devicetree parsing
├── generators/ # ZMK code generation
├── providers/ # Provider interfaces for external dependencies
└── utils/ # Utilities and helpers
- Provider Pattern: Abstracts external dependencies for maximum flexibility
- Fluent Interface: Chainable methods for intuitive API usage
- Strategy Pattern: Pluggable parsing and generation strategies
- Factory Pattern: Consistent object creation throughout
Comprehensive test coverage with 200+ tests:
# Run all tests with coverage
make test
# Run all checks (ruff, mypy, tests)
make check
# Run specific test categories
pytest tests/core/ -v # Core functionality tests
pytest tests/fluent/ -v # Fluent API tests
pytest tests/parsers/ -v # Parser tests
pytest tests/models/ -v # Model tests
All operations complete in <1 second with minimal memory footprint:
- Layout loading: ~50ms
- Layer operations: <1ms each
- Full compilation: ~100ms
- Memory usage: <10MB for typical layouts
We welcome contributions! Please see our Contributing Guidelines for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes with tests
- Ensure quality gates pass:
make fix # Auto-fix and format code make check # Run all checks (must pass)
- Commit your changes (
git commit -m 'feat: add amazing feature'
) - Push to your fork (
git push origin feature/amazing-feature
) - Open a Pull Request
- We use
ruff
for formatting and linting - Type hints are required (mypy strict mode)
- Tests are required for new features
- Documentation updates for API changes
MIT License - see LICENSE file for details.
- Extracted from the glovebox project
- Built for the ZMK firmware ecosystem
- Inspired by modern Python library design patterns
- GitHub Repository
- Documentation (Coming Soon)
- PyPI Package (Coming Soon)
- Glovebox Integration Plan
Note: This library is in active development.