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:
  1. Load and merge configuration (user + defaults)

  2. Parse keymap file (c2json, Vial, or Keybard format)

  3. Transform keycodes to display labels

  4. Build layer color gradients and toggle matrices

  5. 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:
  • input_list (list[Any]) – The list to split into chunks.

  • chunk_size (int) – The size of each chunk.

Yields:

List slices of chunk_size elements.

Return type:

Generator[list[Any], None, None]

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: object

Orchestrates 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.

Parameters:
  • config_path (Path | None)

  • output_dir (Path)

  • format (str)

_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:
  • config_path (Path | None) – Path to user configuration YAML file. If None, bundled default configuration will be used.

  • output_dir (Path) – Directory path where output images will be written. Will be created if it doesn’t exist.

  • format (str) – Output image format. Must be “svg” or “png”.

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:
  1. Load configuration (user config merged with defaults)

  2. Parse keymap content and detect format

  3. Setup keycode transformer with mappings

  4. Build Layer objects with labels, colors, and toggles

  5. Determine target files and check overwrite (if enabled)

  6. 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:

list[Path]

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: object

Generates 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:

str

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: object

Transforms 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:

str

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:

list[str]

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:

str | None

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: object

Loads 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:

dict[str, Any]

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:

dict[str, Any]

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:
  • base (dict[str, Any]) – Base mapping dictionary (typically bundled defaults).

  • override (dict[str, Any]) – Override mapping dictionary (user customizations).

Return type:

dict[str, Any]

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: object

Transforms 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:

list[list[str]]

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:

list[list[str]]

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: object

Wrapper 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 via sys.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:

None

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: object

Parses 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:

list[list[str]]

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: object

Parses 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 LayerAdaptor after 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:

list[list[str]]

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: object

Parses 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:

list[list[str]]

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 layer

  • custom_keycodes: List of custom keycode definitions

  • layer_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, shortName

  • layer_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: object

Parses QMK color definitions from C header files.

Extracts RGB_* and HSV_* 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_* and HSV_* color definitions from the content. For each color name (without the RGB_/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:

dict[str, str]

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)