Application Layer¶
Business logic and processing components.
Image Generator¶
Image Generator Service for keyboard layout visualization.
This module provides the ImageGenerator which orchestrates the complete
pipeline for generating keymap visualization images. It coordinates parsing,
transformation, and compilation to produce SVG or PNG output files.
- The generation pipeline consists of:
Load and merge configuration (user + defaults)
Parse keymap file (c2json, Vial, or Keybard format)
Transform keycodes to display labels
Build layer color gradients and toggle matrices
Compile Typst templates to output images
Example
Basic usage from CLI:
generator = ImageGenerator(
config_path=Path("config.yaml"),
output_dir=Path("./output"),
format="svg",
)
generator.generate(keymap_path=Path("keymap.kbi"))
Generating specific layers:
generator.generate(
keymap_path=Path("keymap.vil"),
layers=["1", "2", "overview"],
)
- skim.application.image_generator.batched(input_list, chunk_size)[source]¶
Yield successive fixed-size chunks from input_list.
A utility generator for splitting lists into equal-sized batches. Used for converting flat keycode lists into the 10x6 matrix format required by Layer objects.
- Parameters:
- Yields:
List slices of chunk_size elements.
- Return type:
Example
>>> list(batched([1, 2, 3, 4, 5, 6], 2)) [[1, 2], [3, 4], [5, 6]]
- class skim.application.image_generator.ImageGenerator(config_path, output_dir, format)[source]¶
Bases:
objectOrchestrates the generation of keymap visualization images.
This is the main service class that coordinates all components of the skim image generation pipeline. It handles configuration loading, keymap parsing, keycode transformation, and Typst compilation.
The generator supports multiple input formats (c2json, Vial, Keybard) and can produce both individual layer images and overview images showing all layers together.
- _config_path¶
Path to user configuration file, or None for defaults.
- _output_dir¶
Directory where generated images will be saved.
- _format¶
Output format, either “svg” or “png”.
- _assets_path¶
Path to bundled asset files (templates, fonts).
Example
>>> generator = ImageGenerator( ... config_path=None, # Use defaults ... output_dir=Path("./images"), ... format="svg", ... ) >>> generator.generate(keymap_path=Path("my-keymap.kbi")) # Creates ./images/keymap-1.svg, ./images/keymap-2.svg, etc.
- __init__(config_path, output_dir, format)[source]¶
Initialize the image generator.
- Parameters:
- Return type:
None
Example
>>> generator = ImageGenerator( ... config_path=Path("skim-config.yaml"), ... output_dir=Path("./output"), ... format="png", ... )
- generate(keymap_path=None, keymap_content=None, layers=None, check_overwrite=False)[source]¶
Generate keymap visualization images.
Main entry point for the generation pipeline. Accepts keymap data either as a file path or raw content string, parses it, and generates the requested layer images.
- The generation process:
Load configuration (user config merged with defaults)
Parse keymap content and detect format
Setup keycode transformer with mappings
Build Layer objects with labels, colors, and toggles
Determine target files and check overwrite (if enabled)
Compile Typst templates to output images
- Parameters:
keymap_path (
Optional[Path]) – Path to keymap file (.kbi, .vil, or .json). Either this or keymap_content must be provided.keymap_content (
Optional[str]) – Raw JSON string content of keymap. Used when reading from stdin or other sources.layers (
Optional[list[str]]) –Optional list of layer specifiers to generate. Supported values:
”all” or “all-layers”: Generate all individual layers
”overview”: Generate overview image with all layers
”N”: Generate layer N (1-indexed)
”N-M”: Generate layers N through M (inclusive, 1-indexed)
If None, generates all layers plus overview.
check_overwrite (
bool) – If True, calculates expected output files and raises FileExistsError if any exist. Does not generate images in this case.
- Return type:
- Returns:
List of Path objects for the files that would be (or were) generated. If check_overwrite is True and files exist, raises exception.
- Raises:
ValueError – If no keymap is provided, or if the keymap format cannot be detected or is unsupported.
FileNotFoundError – If keymap_path doesn’t exist.
FileExistsError – If check_overwrite is True and output files exist.
Example
>>> # Generate all layers from file >>> generator.generate(keymap_path=Path("layout.kbi"))
Config Generator¶
Configuration Generator for creating Skim config from source files.
This module provides the ConfigGenerator which extracts metadata
from Keybard keymap files and QMK color headers to generate Skim YAML
configuration files. This automates the initial configuration setup
for users migrating from these tools.
- The generator extracts:
Layer colors (converted from HSV to hex)
Layer names from cosmetic settings
Custom keycode short names for display labels
Named color definitions from QMK headers
Example
Generating configuration from a Keybard file:
generator = ConfigGenerator()
yaml_config = generator.generate(
keybard_content=keybard_json_str,
qmk_header_content=qmk_color_h_content,
adjust_lightness=0.31,
)
Path("skim-config.yaml").write_text(yaml_config)
- class skim.application.config_generator.ConfigGenerator[source]¶
Bases:
objectGenerates Skim configuration YAML from source keymap files.
This class extracts relevant metadata from Keybard (.kbi) files and optional QMK color headers to produce a ready-to-use Skim configuration. The generated config includes layer definitions, color schemes, and keycode display overrides.
Example
>>> generator = ConfigGenerator() >>> yaml_output = generator.generate(keybard_content) >>> print(yaml_output) layers: - base_color: '#347156' label: BASE name: Base id: '0' ...
- generate(keybard_content, qmk_header_content=None, adjust_lightness=None, adjust_saturation=None)[source]¶
Generate YAML configuration from Keybard content.
Parses the Keybard JSON file to extract layer colors, names, and custom keycode definitions. Optionally integrates QMK color definitions from a color.h header file.
- Parameters:
keybard_content (
str) – JSON string content of the Keybard .kbi file.qmk_header_content (
Optional[str]) – Optional C header content from QMK color.h file containing RGB_* and HSV_* color definitions.adjust_lightness (
Optional[float]) – Optional target lightness (0.0-1.0) to apply to all extracted colors. Useful for ensuring readable contrast in generated images.adjust_saturation (
Optional[float]) – Optional maximum saturation (0.0-1.0) to cap all extracted colors. Original saturation is reduced to this value if it exceeds the target.
- Return type:
- Returns:
YAML-formatted string containing the generated Skim configuration. Ready to be written to a .yaml file.
- Raises:
ValueError – If keybard_content contains invalid JSON.
Example
>>> generator = ConfigGenerator() >>> config = generator.generate( ... keybard_content=Path("layout.kbi").read_text(), ... qmk_header_content=Path("color.h").read_text(), ... adjust_lightness=0.31, ... ) >>> Path("skim-config.yaml").write_text(config)
Keycode Transformer¶
Transform QMK keycodes into display labels for keymap visualization.
This module provides the KeycodeTransformer which converts raw QMK
keycode strings into human-readable labels suitable for display in keymap
images. The transformer handles various QMK keycode formats including:
Basic keycodes (KC_A, KC_SPACE, KC_ENTER)
Modifier functions (S(KC_A), C(KC_C), MEH(KC_X))
Layer functions (MO(1), TG(2), LT(3,KC_SPACE))
Alias references (@@KEYCODE;)
NerdFont icons (%%nf-md-icon;)
Example
Basic transformation:
transformer = KeycodeTransformer(
keycodes={"KC_A": "A", "KC_SPACE": "Space"},
reversed_alias={"LSFT(KC_1)": "@@KC_EXLM;"},
modifiers={"S": "@@MOD_SHIFT;"},
layer_symbols={"MO": "⬓", "TG": "⬔"},
)
label = transformer.transform("KC_A") # Returns "A"
label = transformer.transform("MO(2)") # Returns "⬓"
Batch transformation:
keycodes = ["KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y"]
labels = transformer.transform_list(keycodes)
- class skim.application.keycode_transformer.KeycodeTransformer(keycodes, reversed_alias, modifiers, layer_symbols)[source]¶
Bases:
objectTransforms QMK keycodes into human-readable skim display labels.
This class handles the complex mapping from QMK keycode syntax to the labels displayed on keyboard visualizations. It supports:
Alias resolution:
@@KEYCODE;patterns are resolved to their corresponding labels, enabling label reuse and composition.Reversed alias mapping: Converts modifier+key combinations to their alias forms (e.g.,
LSFT(KC_1)→KC_EXLM).Modifier functions: Prefixes labels with modifier symbols for
S(),C(),A(),G(),MEH(),HYPR()functions.Layer functions: Converts layer-switching keycodes (
MO(),TG(),LT(), etc.) to symbolic representations.NerdFont passthrough:
%%nf-CLASS;patterns are preserved for icon rendering in Typst.
- Parameters:
- _keycodes¶
Main keycode to label mapping dictionary.
- _reversed_alias¶
Keycode function pattern to alias mapping.
- _modifiers¶
Modifier function name to prefix label mapping.
- _layer_symbols¶
Layer function name to symbol mapping.
Example
>>> transformer = KeycodeTransformer( ... keycodes={"KC_A": "A", "KC_EXLM": "!"}, ... reversed_alias={"LSFT(KC_1)": "@@KC_EXLM;"}, ... modifiers={"S": "Shift"}, ... layer_symbols={"MO": "⬓"}, ... ) >>> transformer.transform("KC_A") 'A' >>> transformer.transform("LSFT(KC_1)") '!' >>> transformer.transform("MO(2)") '⬓'
- __init__(keycodes, reversed_alias, modifiers, layer_symbols)[source]¶
Initialize the keycode transformer with mapping dictionaries.
- Parameters:
keycodes (
dict[str,str]) – Dictionary mapping QMK keycode names to display labels. Example: {“KC_A”: “A”, “KC_SPACE”: “Space”}reversed_alias (
dict[str,str]) – Dictionary mapping keycode function patterns to alias references. Example: {“LSFT(KC_1)”: “@@KC_EXLM;”}modifiers (
dict[str,str]) – Dictionary mapping modifier function names to their display prefixes. Example: {“S”: “@@MOD_SHIFT;”, “C”: “Ctrl”}layer_symbols (
dict[str,str]) – Dictionary mapping layer function names to their symbolic representations. Example: {“MO”: “⬓”, “TG”: “⬔”}
- Return type:
None
- transform(keycode)[source]¶
Transform a single QMK keycode into a display label.
Applies the full transformation pipeline: 1. Apply reversed alias mapping if applicable 2. Parse and handle modifier functions (S, C, A, G, MEH, HYPR) 3. Parse and handle layer functions (MO, TG, LT, etc.) 4. Resolve basic keycode lookup with alias expansion
- Parameters:
keycode (
str) – QMK keycode string to transform. Examples: “KC_A”, “S(KC_B)”, “MO(2)”, “LT(1,KC_SPACE)”- Return type:
- Returns:
Human-readable label string for display. Empty string if the keycode is empty or None.
- Raises:
ValueError – If a circular alias reference is detected during resolution (e.g., A references B which references A).
Example
>>> transformer.transform("KC_SPACE") 'Space' >>> transformer.transform("S(KC_A)") 'Shift A' >>> transformer.transform("") ''
- transform_list(keycodes)[source]¶
Transform a list of keycodes to display labels.
Convenience method for batch transforming multiple keycodes.
- Parameters:
keycodes (
list[str]) – List of QMK keycode strings to transform.- Return type:
- Returns:
List of transformed label strings in the same order.
Example
>>> transformer.transform_list(["KC_A", "KC_B", "KC_C"]) ['A', 'B', 'C']
- extract_layer_id(keycode)[source]¶
Extract the target layer ID from a layer-switching keycode.
Parses layer function keycodes to extract the layer identifier used for building layer toggle matrices.
Supported layer functions: MO, LM, LT, OSL, TG, TO, TT, DF, PDF
- Parameters:
keycode (
str) – Keycode string to analyze. Examples: “MO(2)”, “TG(_SYS)”, “LT(1,KC_SPACE)”- Return type:
- Returns:
Layer identifier string if the keycode is a layer function, None otherwise. The identifier may be numeric (“2”) or symbolic (“_NAV”).
Example
>>> transformer.extract_layer_id("MO(2)") '2' >>> transformer.extract_layer_id("TG(_NAV)") '_NAV' >>> transformer.extract_layer_id("KC_A") None
Keycode Loader¶
Keycode mapping loader for QMK keycode to label translations.
This module provides the KeycodeMappingLoader for loading and merging
QMK keycode mappings from YAML files. These mappings define how raw QMK keycodes
are translated to human-readable labels for display in keymap images.
- The mapping files contain several dictionaries:
keycodes: Direct keycode to label mappings (e.g., “KC_A” -> “A”)reversed_alias: Function patterns to alias keycodes (e.g., “LSFT(KC_1)” -> “KC_EXLM”)modifiers: Modifier function prefixes (e.g., “S” -> “Shift”)layer_symbols: Layer function symbols (e.g., “MO” -> “⬓”)
Example
Loading and using keycode mappings:
loader = KeycodeMappingLoader()
mappings = loader.load_bundled()
# Access individual mapping dictionaries
keycodes = mappings["keycodes"]
modifiers = mappings["modifiers"]
Merging custom mappings with bundled defaults:
loader = KeycodeMappingLoader()
base = loader.load_bundled()
custom = loader.load_from_file(Path("my-keycodes.yaml"))
merged = loader.merge_mappings(base, custom)
- class skim.application.keycode_loader.KeycodeMappingLoader[source]¶
Bases:
objectLoads and manages QMK keycode to display label mappings.
Provides methods to load keycode mappings from YAML files, including the bundled default mappings and user-provided custom mappings. Supports merging multiple mapping sources with custom overrides taking precedence over defaults.
Example
>>> loader = KeycodeMappingLoader() >>> mappings = loader.load_bundled() >>> mappings["keycodes"]["KC_A"] 'A' >>> mappings["modifiers"]["S"] '%%nf-md-apple_keyboard_shift;'
- load_bundled()[source]¶
Load the bundled default keycode mappings.
- Return type:
- Returns:
Dictionary containing ‘keycodes’, ‘reversed_alias’, ‘modifiers’, and ‘layer_symbols’ mapping dictionaries.
- Raises:
FileNotFoundError – If the bundled mapping file is missing.
Example
>>> loader = KeycodeMappingLoader() >>> mappings = loader.load_bundled() >>> "KC_SPACE" in mappings["keycodes"] True
- load_from_file(path)[source]¶
Load keycode mappings from a YAML file.
- Parameters:
path (
Path) – Path to the YAML file containing keycode mappings.- Return type:
- Returns:
Dictionary containing the parsed mapping data.
- Raises:
FileNotFoundError – If the specified file doesn’t exist.
yaml.YAMLError – If the file contains invalid YAML.
Example
>>> loader = KeycodeMappingLoader() >>> custom = loader.load_from_file(Path("custom-keycodes.yaml"))
- merge_mappings(base, override)[source]¶
Merge two mapping dictionaries with override taking precedence.
Combines base and override mappings, where values in the override dictionary replace corresponding values in the base. This allows users to customize specific keycodes while retaining defaults for everything else.
- Parameters:
- Return type:
- Returns:
New dictionary with merged mappings from both sources.
Example
>>> loader = KeycodeMappingLoader() >>> base = loader.load_bundled() >>> custom = {"keycodes": {"KC_A": "Custom A"}, "modifiers": {}} >>> merged = loader.merge_mappings(base, custom) >>> merged["keycodes"]["KC_A"] 'Custom A'
Layer Transformer¶
Layer adaptor for transforming keymap layouts between formats.
This module provides the LayerAdaptor which transforms keymap layer
data from Keybard and Vial source formats into the internal Skim format.
Different keyboard firmware tools use different key ordering conventions,
and this adaptor handles the necessary reordering.
- The Svalboard keyboard has a specific physical layout:
8 finger clusters (4 per hand, 6 keys each)
2 thumb clusters (1 per hand, 6 keys each)
Total: 60 keys per layer
- Source formats (Keybard/Vial) order keys as:
Left Thumb → Left Fingers → Right Thumb → Right Fingers
- Internal Skim format orders keys as:
Right Fingers → Left Fingers → Right Thumb → Left Thumb
Example
Converting Keybard layers:
from skim.application.layer_transformer import LayerAdaptor
keybard_layers = [["KC_A", "KC_B", ...], ...] # 60 keys per layer
skim_layers = LayerAdaptor.from_keybard(keybard_layers)
Converting Vial layers:
vial_layers = [[[...], [...]], ...] # Nested cluster format
skim_layers = LayerAdaptor.from_vial(vial_layers)
- class skim.application.layer_transformer.LayerAdaptor[source]¶
Bases:
objectTransforms keymap layer layouts from Keybard/Vial to Skim format.
This class provides static methods for converting between different keymap file formats. The transformation involves reordering keys from source format conventions to the internal Skim rendering order.
The Svalboard’s physical layout requires specific cluster ordering for correct visual representation. This adaptor handles the mapping from how keyboard firmware stores keycodes to how Skim needs them for image generation.
Note
All methods are class methods and don’t require instantiation. The class is used as a namespace for related transformation functions.
Example
>>> # Direct usage without instantiation >>> keybard_layer = ["KC_A"] * 60 >>> skim_layers = LayerAdaptor.from_keybard([keybard_layer]) >>> len(skim_layers[0]) 60
- classmethod from_keybard(layers)[source]¶
Transform Keybard layer format to internal Skim format.
Keybard stores keycodes as a flat list in source order. This method reorders the keys for each layer to match Skim’s rendering expectations.
- Parameters:
layers (
list[list[str]]) – List of layers, where each layer is a flat list of 60 keycode strings in Keybard order.- Return type:
- Returns:
List of layers with keycodes reordered to Skim format.
Example
>>> keybard_layers = parser.parse(content) >>> skim_layers = LayerAdaptor.from_keybard(keybard_layers)
- classmethod from_vial(layers)[source]¶
Transform Vial layer format to internal Skim format.
Vial stores keycodes grouped by clusters (nested lists). This method flattens the cluster structure and reorders keys for each layer to match Skim’s rendering expectations.
- Parameters:
layers (
list[list[list[str]]]) – List of layers, where each layer contains a list of clusters, and each cluster is a list of keycode strings.- Return type:
- Returns:
List of layers with keycodes flattened and reordered to Skim format.
Example
>>> vial_layers = parser.parse(content) >>> skim_layers = LayerAdaptor.from_vial(vial_layers)
Typst Compiler¶
Wrapper for the Typst document compiler.
This module provides the TypstCompiler which wraps the typst-py library
to compile Typst documents into output formats (SVG, PNG, PDF). The compiler
is used to render keymap visualization templates with layer data.
Example
Compiling a Typst template:
from skim.application.typst_compiler import TypstCompiler
from pathlib import Path
import json
compiler = TypstCompiler()
compiler.compile(
input_path=Path("template.typ"),
output_path=Path("output.svg"),
sys_inputs={"keymap": json.dumps(keymap_data)},
font_paths=[Path("fonts/")],
)
- class skim.application.typst_compiler.TypstCompiler[source]¶
Bases:
objectWrapper around the typst-py compiler for document rendering.
Provides a simplified interface to the Typst compilation engine with support for custom fonts and system inputs. The compiler handles the conversion of Typst markup documents to various output formats.
- The wrapper configures Typst with settings optimized for skim’s use case:
Fixed PPI (120) for consistent image sizing
System fonts ignored for reproducible output
Custom font paths for NerdFont icon support
Example
>>> compiler = TypstCompiler() >>> compiler.compile( ... input_path="keymap.typ", ... output_path="keymap.svg", ... sys_inputs={"data": '{"layers": [...]}'}, ... )
- compile(input_path, output_path, sys_inputs, font_paths=None)[source]¶
Compile a Typst document to an output file.
Renders the input Typst template with the provided system inputs and writes the result to the output path. The output format is determined by the output file extension (.svg, .png, or .pdf).
- Parameters:
input_path (
str|Path) – Path to the .typ input file containing the template.output_path (
str|Path) – Path for the generated output file. May include{p}placeholder for page numbers when generating multi-page output (e.g., “layer-{p}.svg”).sys_inputs (
dict[str,str]) – Dictionary of string key-value pairs passed to the Typst template as system inputs. The template accesses these viasys.inputs. Values should be JSON-serialized strings.font_paths (
Optional[list[str|Path]]) – Optional list of directories to search for font files. Fonts in these directories are available to the Typst template.
- Raises:
FileNotFoundError – If the input file doesn’t exist.
typst.TypstError – If compilation fails due to template errors.
- Return type:
Example
>>> compiler.compile( ... input_path="keymap-layers.typ", ... output_path="output/layer-{p}.svg", ... sys_inputs={ ... "keymap": '{"layers": [...]}', ... "appearance": '{"colors": {...}}', ... }, ... font_paths=["assets/fonts"], ... )
Parsers¶
C2JSON Parser¶
Parser for QMK c2json keymap format.
This module provides the C2JsonParser for parsing keymap files
in QMK’s c2json format. This format is produced by the qmk c2json command
and stores keycodes in a structured JSON format.
The c2json format structure:
{
"layers": [
["KC_Q", "KC_W", "KC_E", ...], // Layer 0
["KC_1", "KC_2", "KC_3", ...], // Layer 1
...
],
"keyboard": "...",
"keymap": "...",
...
}
Example
>>> parser = C2JsonParser()
>>> layers = parser.parse(json_content)
>>> layers[0] # First layer keycodes
['KC_Q', 'KC_W', 'KC_E', ...]
- class skim.application.parsers.c2json_parser.C2JsonParser[source]¶
Bases:
objectParses QMK keymap files in c2json JSON format.
The c2json format is the standard JSON export format for QMK keymaps, generated by the qmk c2json command. It contains keycode definitions organized by layers.
Unlike Keybard or Vial formats, c2json keycodes are already in the correct order for Skim and don’t require reordering transformation.
Example
>>> parser = C2JsonParser() >>> with open("keymap.json") as f: ... content = f.read() >>> layers = parser.parse(content) >>> len(layers) 8 # Number of layers in the keymap
- parse(content)[source]¶
Parse c2json content and extract keycode layers.
Validates the JSON structure and extracts the layers array containing keycode strings for each layer.
- Parameters:
content (
str) – JSON string content of the keymap file.- Return type:
- Returns:
List of layers, where each layer is a list of keycode strings. Keycodes are in the order they appear in the c2json file.
- Raises:
ValueError – If JSON is invalid, missing ‘layers’ key, or layers have incorrect structure.
Example
>>> content = '{"layers": [["KC_A", "KC_B"], ["KC_1", "KC_2"]]}' >>> parser.parse(content) [['KC_A', 'KC_B'], ['KC_1', 'KC_2']]
Vial Parser¶
Parser for Vial .vil keymap format.
This module provides the VialParser for parsing keymap files
exported from the Vial keyboard configurator. Vial files use a nested
structure with clusters of keys rather than flat layer arrays.
The Vial format structure:
{
"version": 1,
"layout": [
[ // Layer 0
["KC_Q", "KC_W", ...], // Cluster 0
["KC_E", "KC_R", ...], // Cluster 1
...
],
[ // Layer 1
...
],
...
],
...
}
Example
>>> parser = VialParser()
>>> layers = parser.parse(vil_content)
>>> len(layers[0]) # Keys per layer after flattening
60
- class skim.application.parsers.vial_parser.VialParser[source]¶
Bases:
objectParses Vial keymap files (.vil) and transforms to Skim format.
Vial is a popular GUI keyboard configurator that exports keymaps in a nested JSON structure organized by clusters. This parser flattens the cluster structure and reorders keys to match Skim’s expected layout format.
The transformation is handled by
LayerAdaptorafter initial parsing.Example
>>> parser = VialParser() >>> with open("keymap.vil") as f: ... content = f.read() >>> layers = parser.parse(content) >>> len(layers) # Number of layers 8
- parse(content)[source]¶
Parse Vial content and extract keycode layers.
Parses the nested Vial JSON structure, flattens the cluster organization, and transforms the key ordering to match Skim’s internal format.
- Parameters:
content (
str) – JSON string content of the .vil keymap file.- Return type:
- Returns:
List of layers, where each layer is a flat list of 60 keycode strings in Skim’s internal order.
- Raises:
ValueError – If JSON is invalid, missing ‘layout’ key, or structure is incorrect.
Example
>>> content = Path("my-keymap.vil").read_text() >>> layers = parser.parse(content) >>> layers[0][:6] # First 6 keys of first layer ['KC_Q', 'KC_W', 'KC_E', 'KC_R', 'KC_T', 'KC_Y']
Keybard Parser¶
Parser for Keybard .kbi keymap format.
This module provides the KeybardParser for parsing keymap files
from the Keybard keyboard configurator. Keybard is a configuration tool
for the Svalboard keyboard that stores keymaps with rich metadata.
The Keybard format structure:
{
"keymap": [
["KC_Q", "KC_W", ...], // Layer 0 (60 keys)
["KC_1", "KC_2", ...], // Layer 1
...
],
"layer_colors": [
{"hue": 85, "sat": 153, "val": 255},
...
],
"custom_keycodes": [
{"name": "MY_KEY", "shortName": "MK"},
...
],
"cosmetic": {
"layer": {"0": "Base", "1": "Nav", ...}
},
...
}
Example
>>> parser = KeybardParser()
>>> layers = parser.parse(kbi_content)
>>> metadata = parser.extract_metadata(kbi_content)
>>> metadata["layer_names"]
{'0': 'Base', '1': 'Navigation', ...}
- class skim.application.parsers.keybard_parser.KeybardParser[source]¶
Bases:
objectParses Keybard keymap files (.kbi) and extracts metadata.
Keybard is the primary configuration tool for Svalboard keyboards. This parser extracts both the keycode layers and rich metadata including layer colors, names, and custom keycode definitions.
- The parser provides two main methods:
parse(): Extracts keycode layers (for image generation)extract_metadata(): Extracts colors, names, and custom keys (for configuration generation)
Example
>>> parser = KeybardParser() >>> layers = parser.parse(content) >>> metadata = parser.extract_metadata(content) >>> metadata["layer_colors"][0]["hue"] 85
- parse(content)[source]¶
Parse Keybard content and extract keycode layers.
Parses the Keybard JSON structure and transforms the key ordering to match Skim’s internal format using
LayerAdaptor.- Parameters:
content (
str) – JSON string content of the .kbi keymap file.- Return type:
- Returns:
List of layers, where each layer is a flat list of 60 keycode strings in Skim’s internal order.
- Raises:
ValueError – If JSON is invalid, missing ‘keymap’ key, or structure is incorrect.
Example
>>> content = Path("my-layout.kbi").read_text() >>> layers = parser.parse(content) >>> len(layers) 8
- extract_metadata(content)[source]¶
Extract metadata (colors, names, custom keycodes) from Keybard content.
Parses the Keybard JSON to extract configuration metadata that can be used to generate Skim configuration files.
- Extracted metadata includes:
layer_colors: List of HSV color dicts for each layercustom_keycodes: List of custom keycode definitionslayer_names: Dict mapping layer indices to display names
- Parameters:
content (
str) – JSON string content of the .kbi keymap file.- Returns:
layer_colors: List[Dict] with keys: hue, sat, val (0-255)custom_keycodes: List[Dict] with keys: name, shortNamelayer_names: Dict[str, str] mapping “0”, “1”, etc. to names
- Return type:
Dictionary containing
- Raises:
ValueError – If content contains invalid JSON.
Example
>>> metadata = parser.extract_metadata(content) >>> metadata["layer_colors"] [{'hue': 85, 'sat': 153, 'val': 255}, ...] >>> metadata["layer_names"] {'0': 'Base', '1': 'Navigation'}
QMK Color Parser¶
Parser for QMK color.h header files.
This module provides the QmkColorParser for extracting color
definitions from QMK firmware color.h header files. These files contain
#define statements for named colors in both RGB and HSV formats.
Supported formats:
# define RGB_CORAL 0xFF, 0x7F, 0x50
# define HSV_TEAL 128, 255, 255
Example
>>> parser = QmkColorParser()
>>> colors = parser.parse(color_h_content)
>>> colors
{'CORAL': '#FF7F50', 'TEAL': '#00FFFF'}
- class skim.application.parsers.qmk_color_parser.QmkColorParser[source]¶
Bases:
objectParses QMK color definitions from C header files.
Extracts
RGB_*andHSV_*color definitions from QMK color.h files, converting them to hexadecimal color strings. When both RGB and HSV definitions exist for the same color name, HSV takes precedence.QMK uses 0-255 range for all HSV components (not 0-360 for hue).
Example
>>> parser = QmkColorParser() >>> content = ''' ... #define RGB_RED 0xFF, 0x00, 0x00 ... #define HSV_BLUE 170, 255, 255 ... ''' >>> parser.parse(content) {'RED': '#FF0000', 'BLUE': '#0000FF'}
- parse(content)[source]¶
Parse C header content and return color name to hex mapping.
Extracts both
RGB_*andHSV_*color definitions from the content. For each color name (without theRGB_/HSV_prefix), returns the hexadecimal color value. If both RGB and HSV are defined for the same name, HSV definition takes precedence.- Parameters:
content (
str) – C header file content containing #define statements.- Return type:
- Returns:
Dictionary mapping color names (without prefix) to hex strings. Example: {“RED”: “#FF0000”, “BLUE”: “#0000FF”}
Example
>>> content = ''' ... #define RGB_CORAL 0xFF, 0x7F, 0x50 ... #define HSV_CORAL 11, 176, 255 ... #define RGB_TEAL 0x00, 0x80, 0x80 ... ''' >>> parser.parse(content) {'CORAL': '#FF7A4D', 'TEAL': '#008080'} # Note: CORAL uses HSV definition (precedence)