Domain Layer¶
Core domain models and configuration classes.
Domain Types¶
Domain types and value objects for skim keymap processing.
This module defines the core domain types used throughout the skim application, including enumerations for alignment, keyboard sides, and key directions, as well as data classes for representing processed key data.
These types form the foundation of the domain model and are used across multiple layers of the application.
- skim.domain.domain_types.SEPARATOR_CHAR¶
Unicode box-drawing character (│) used to separate tap and hold labels on dual-function keys.
- skim.domain.domain_types.NBSP_CHAR¶
Non-breaking space character used to allow the same behavior as hold-tap symbols, but for any two sides of a lable.
Example
>>> from skim.domain.domain_types import KeyboardSide, KeyDirection
>>> side = KeyboardSide.LEFT
>>> side.opposite
<KeyboardSide.RIGHT: 'right'>
>>> direction = KeyDirection.NORTH
>>> direction.opposite
<KeyDirection.SOUTH: 'south'>
- class skim.domain.domain_types.Alignment(value)[source]¶
Bases:
EnumAlignment options for positioning elements.
Used for specifying horizontal or vertical alignment of graphical elements within their containers, such as keys within clusters or labels within keys.
- START¶
Align to the start (left for horizontal, top for vertical).
- CENTER¶
Center alignment.
- END¶
Align to the end (right for horizontal, bottom for vertical).
Example
>>> align = Alignment.START >>> align.opposite <Alignment.END: 'end'> >>> Alignment.CENTER.opposite <Alignment.CENTER: 'center'>
- START = 'start'¶
- CENTER = 'center'¶
- END = 'end'¶
- class skim.domain.domain_types.KeyboardSide(value)[source]¶
Bases:
EnumEnumeration of keyboard sides (left or right hand).
Used to distinguish between the two halves of the split Svalboard keyboard, which affects rendering orientation and key positioning.
- LEFT¶
The left-hand side of the keyboard.
- RIGHT¶
The right-hand side of the keyboard.
Example
>>> side = KeyboardSide.LEFT >>> side.opposite <KeyboardSide.RIGHT: 'right'>
- LEFT = 'left'¶
- RIGHT = 'right'¶
- property opposite: KeyboardSide¶
Get the opposite keyboard side.
- Returns:
LEFT if the current side is RIGHT, and vice versa.
Example
>>> KeyboardSide.RIGHT.opposite <KeyboardSide.LEFT: 'left'>
- class skim.domain.domain_types.KeyDirection(value)[source]¶
Bases:
EnumCardinal directions for directional keys in finger clusters.
Each finger cluster on the Svalboard has directional keys arranged around a center key. This enum represents the four cardinal directions used for these surrounding keys.
The directions correspond to the physical movement required to press the key relative to the finger’s home position:
NORTH: Pushing the finger forward (away from palm)
SOUTH: Pulling the finger backward (toward palm)
EAST: Moving toward the thumb
WEST: Moving away from the thumb
- NORTH¶
The northward (forward) direction.
- SOUTH¶
The southward (backward) direction.
- EAST¶
The eastward (thumb-ward) direction.
- WEST¶
The westward (away from thumb) direction.
Example
>>> direction = KeyDirection.NORTH >>> direction.opposite <KeyDirection.SOUTH: 'south'>
- NORTH = 'north'¶
- SOUTH = 'south'¶
- EAST = 'east'¶
- WEST = 'west'¶
- property opposite: KeyDirection¶
Get the opposite cardinal direction.
- Returns:
NORTH becomes SOUTH, EAST becomes WEST, and vice versa.
- Return type:
The opposite direction
Example
>>> KeyDirection.EAST.opposite <KeyDirection.WEST: 'west'>
- class skim.domain.domain_types.KeymapType(value)[source]¶
Bases:
EnumEnumeration of supported keymap file formats.
Identifies the source format of a keymap file, which determines how the file content should be parsed and interpreted.
- C2JSON¶
QMK’s c2json format, produced by
qmk c2jsoncommand. Contains raw layer data in QMK’s internal ordering.
- VIAL¶
Vial firmware format (.vil files). Contains keymap data with Vial-specific key ordering.
- KEYBARD¶
Keybard application format (.kbi files). Contains keymap data with Keybard-specific key ordering.
Example
>>> KeymapType.VIAL.value 'vial'
- C2JSON = 'c2json'¶
- VIAL = 'vial'¶
- KEYBARD = 'keybard'¶
- class skim.domain.domain_types.SvalboardTargetKey(label='', layer_switch=None, is_transparent=False)[source]¶
Bases:
objectProcessed key data ready for rendering.
Represents a single key after all keycode transformations have been applied. Contains the display label and optional metadata about layer-switching behavior.
This is a frozen (immutable) dataclass, ensuring that key data cannot be accidentally modified after creation.
- label¶
The text label to display on the key. May contain special characters like the separator character (│) for dual-function keys. Defaults to an empty string.
- layer_switch¶
The target layer index if this key switches layers (e.g., MO(1), LT(2, KC_A)), or None if the key doesn’t switch layers. Layer indices are 0-based. Defaults to None.
- is_transparent¶
True when the source keycode belongs to QMK’s transparent family (KC_TRANSPARENT, KC_TRNS, _______). Downstream stages may use this to render the key as a faded fall-through of the base-layer label. Defaults to False.
Example
>>> # Simple key with just a label >>> key = SvalboardTargetKey(label="A") >>> key.label 'A'
>>> # Layer-tap key (tap for A, hold for layer 1) >>> key = SvalboardTargetKey(label="A│L1", layer_switch=1) >>> key.layer_switch 1
- skim.domain.domain_types.SEPARATOR_CHAR = '│'¶
Unicode box-drawing character used to separate tap and hold labels.
This character (U+2502, BOX DRAWINGS LIGHT VERTICAL) is used to visually separate the “tap” and “hold” portions of dual-function key labels, such as layer-tap keys (LT) and mod-tap keys (MT).
Example
A layer-tap key that types ‘A’ on tap and activates layer 1 on hold would be displayed as: “A│L1”
- skim.domain.domain_types.NBSP_CHAR = '\xa0'¶
Non-breaking space character for key labels.
Used in key labels that want to display symbol and text and align the symbol the same way the skim controls hold-tap symbols alignment. Anything on the left of the non-breaking space behaves just like the hold symbol in a hold-tap key.
Example
A label like “⌘ Cmd” could use NBSP_CHAR to make the symbol show outward the key cluster: “⌘ Cmd”.
This would display “⌘ Cmd” on keys at the left side of the cluster and “Cmd ⌘” on keys at the right of the cluster.
Adapters¶
Adapters for data transformation and external formats.
This package provides adapters for converting between different data representations, such as: - Transforming raw keycodes to display labels - Normalizing keymap JSON from various sources (Vial, Keybard, QMK) - Converting raw keymaps to renderable target keymaps
- class skim.domain.adapters.KeycodeLabelAdapter(keyboard_config, keycode_mappings)[source]¶
Bases:
objectTransforms QMK keycodes into human-readable display labels.
This adapter processes raw QMK keycode strings and converts them to labels suitable for displaying on keymap visualization images. It supports basic keycodes, macro functions, modifier combinations, and cross-references between keycodes.
The transformation process: 1. Apply pre-processing rules (normalize/alias keycodes) 2. Check for macro function syntax and expand templates 3. Resolve keycode to label using the mapping dictionary 4. Handle alias references (@@KEYCODE; syntax)
- _keycodes¶
Dictionary mapping keycodes to their display labels.
- _pre_processing¶
Dictionary of pre-processing transformations.
- _macro_functions¶
Dictionary of macro function templates.
- _modifier_union¶
Dictionary mapping modifier constants to labels.
- _label_separator¶
Character used to separate tap/hold labels.
Example
>>> from skim.application.loaders import load_keycode_mappings >>> from skim.data.config import SkimConfig >>> config = SkimConfig() >>> mappings = load_keycode_mappings() >>> adapter = KeycodeLabelAdapter(config.keyboard, mappings) >>> adapter.transform("KC_A") ('A', None) >>> adapter.transform("MO(2)") ('L3', 2)
- __init__(keyboard_config, keycode_mappings)[source]¶
Initialize the adapter with keyboard configuration and keycode mappings.
- Parameters:
keyboard_config (
Keyboard) – Keyboard configuration containing layer definitions. Used to resolve layer references by name (e.g., “nav”, “sym”) to their numeric indices.keycode_mappings (
mappingproxy[str,dict[str,str]]) – The keycode internal dictionary representing all the keycode mappings to labels. This dictionary must contain dictionaries for keycodes, pre_processing, macro_functions, and modifier_union.
- Return type:
None
Example
>>> from skim.application.loaders import load_keycode_mappings >>> from skim.data import Keyboard, KeyboardLayer, SkimConfig >>> keyboard = Keyboard(layers=[KeyboardLayer(id="base", name="Base")]) >>> config = SkimConfig() >>> mappings = load_keycode_mappings(config.keycodes) >>> adapter = KeycodeLabelAdapter(keyboard, mappings)
- transform(text)[source]¶
Transform a QMK keycode into a SvalboardTargetKey.
Processes the keycode through pre-processing, macro expansion, and label resolution to produce a SvalboardTargetKey ready for rendering.
- Parameters:
text (
str) – The raw QMK keycode string (e.g., “KC_A”, “LT(1, KC_SPC)”).- Returns:
label: The human-readable display string
layer_switch: The layer index if this key switches layers, or None if no layer switching occurs.
- Return type:
A SvalboardTargetKey containing
Example
>>> adapter.transform("KC_SPACE") SvalboardTargetKey(label='Space', layer_switch=None) >>> adapter.transform("MO(1)") SvalboardTargetKey(label='L2', layer_switch=1)
- class skim.domain.adapters.KeymapJsonAdapter[source]¶
Bases:
objectAdapts keymap JSON data from various formats to QMK ordering.
This adapter normalizes keymap data exported from different applications (Vial, Keybard) into the key ordering expected by QMK firmware. This is necessary because each application has its own internal key ordering that differs from QMK’s standard layout.
The adapter is stateless and uses only static methods, making it suitable for use as a utility class without instantiation.
Example
>>> from skim.domain.domain_types import KeymapType >>> # Transform Keybard format to QMK ordering >>> keybard_layers = [["KC_A"] * 60, ["KC_B"] * 60] >>> qmk_layers = KeymapJsonAdapter.transform(keybard_layers, KeymapType.KEYBARD)
- static transform(json_data, data_type)[source]¶
Transform keymap data from a source format to QMK ordering.
- Parameters:
json_data (
Any) – The raw keymap data from the source format. Structure varies by format: - VIAL: list[list[list[str]]] (layers → clusters → keys) - KEYBARD: list[list[str]] (layers → keys) - C2JSON: list[list[str]] (layers → keys, already QMK order)data_type (
KeymapType) – The source format type indicating how to interpret and transform the data.
- Return type:
- Returns:
Normalized keymap data as list[list[str]] where each inner list contains 60 keycode strings in QMK’s expected order.
Example
>>> KeymapJsonAdapter.transform([["KC_A"] * 60], KeymapType.C2JSON) [['KC_A', 'KC_A', ...]] # Returned unchanged
- class skim.domain.adapters.KeymapTargetAdapter(label_adapter, fallthrough_to_layer_zero=True)[source]¶
Bases:
objectTransforms keymaps from raw keycode strings to renderable target keys.
This adapter is the final stage of keymap processing, converting raw QMK keycode strings into SvalboardTargetKey objects that contain all information needed for rendering: display labels and layer-switching metadata.
The transformation uses a KeycodeLabelAdapter to resolve each keycode to its display label and extract any layer-switching behavior.
- Parameters:
label_adapter (KeycodeLabelAdapter)
fallthrough_to_layer_zero (bool)
- _label_adapter¶
The adapter used to transform individual keycodes.
Example
>>> from skim.application.loaders import load_keycode_mappings >>> from skim.data import SkimConfig >>> from skim.domain.adapters import KeycodeLabelAdapter, KeymapTargetAdapter >>> config = SkimConfig() >>> mappings = load_keycode_mappings(config.keycodes) >>> label_adapter = KeycodeLabelAdapter(config.keyboard, mappings) >>> adapter = KeymapTargetAdapter(label_adapter)
- __init__(label_adapter, fallthrough_to_layer_zero=True)[source]¶
Initialize the adapter with configuration and label transformer.
- Parameters:
label_adapter (
KeycodeLabelAdapter) – The adapter for transforming individual keycodes to labels.fallthrough_to_layer_zero (
bool) – When True (default), transparent keys on layers above 0 borrow their display label from the same key position on layer 0. Set False to leave transparent keys blank.
- Return type:
None
- transform(keymap)[source]¶
Transform an entire keymap from strings to target keys.
Processes each layer in the keymap, converting raw keycode strings to SvalboardTargetKey objects containing display labels and layer metadata. When fallthrough is enabled and layer 0 is present, any transparent key on a higher layer is rewritten to display the layer-0 label at the same position.
- Parameters:
keymap (
SvalboardKeymap[str]) – A keymap containing raw QMK keycode strings at each key position.- Return type:
- Returns:
A new keymap containing SvalboardTargetKey objects at each position, ready for rendering.