UI Layer

Command-line interface and user interaction components.

CLI

Command-line interface for the Svalboard Keymap Image Maker tool.

This module provides the CLI entry points for the skim tool using Click. It defines the main command group and subcommands for generating keymap images and configuration files.

Commands:
  • skim generate: Generate keymap visualization images

  • skim configure: Generate or output configuration files

Example

Generate images from a keymap file:

$ skim generate --keymap layout.kbi --output-dir ./images

Generate with custom configuration:

$ skim -v INFO generate -k layout.vil -c config.yaml -f png

Create configuration from Keybard file:

$ skim configure -k layout.kbi -o skim-config.yaml

Read keymap from stdin:

$ qmk c2json -kb svalboard/trackball/pmw3389/right -km vial --no-cpp $QMK_ROOT/keyboards/svalboard/keymaps/vial/keymap.c | skim generate - -o ./out
class skim.cli.AliasedGroup(name=None, commands=None, invoke_without_command=False, no_args_is_help=None, subcommand_metavar=None, chain=False, result_callback=None, **kwargs)[source]

Bases: Group

Click Group that supports command name abbreviation.

Allows users to invoke commands using unique prefixes instead of full command names. For example, skim gen matches generate if no other command starts with “gen”.

Example

$ skim gen –keymap foo.kbi # Matches ‘generate’ $ skim conf -k bar.kbi # Matches ‘configure’

Parameters:
  • name (str | None)

  • commands (cabc.MutableMapping[str, Command] | cabc.Sequence[Command] | None)

  • invoke_without_command (bool)

  • no_args_is_help (bool | None)

  • subcommand_metavar (str | None)

  • chain (bool)

  • result_callback (t.Callable[..., t.Any] | None)

  • kwargs (t.Any)

get_command(ctx, cmd_name)[source]

Resolve a command by name or unique prefix.

Parameters:
  • ctx (Context) – Click context.

  • cmd_name (str) – Command name or prefix to resolve.

Return type:

Command | None

Returns:

Matching Command object, or None if not found.

Raises:

click.UsageError – If prefix matches multiple commands.

resolve_command(ctx, args)[source]

Resolve command and return full name for help text.

Return type:

tuple

Parameters:

Logging Configuration

Logging configuration for Skim CLI output.

This module provides logging setup with colored output and emoji indicators for different log levels. The configuration is optimized for CLI usage with progress feedback and debug information.

Log level formatting:
  • DEBUG: Green with bug emoji (🐞)

  • INFO: Normal with contextual emoji (from record.emoji)

  • WARNING: Yellow with warning emoji (⚠️)

  • ERROR/CRITICAL: Red with error emoji (🚨)

Example

>>> from skim.ui.logging_config import setup_logging
>>> setup_logging("INFO", quiet=False)
>>> import logging
>>> logger = logging.getLogger(__name__)
>>> logger.info("Processing...", extra={"emoji": "🔄"})
🔄 Processing...
class skim.application.logging_config.ColoredFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]

Bases: Formatter

Custom log formatter with ANSI colors and emoji support.

Applies color coding based on log level and prepends optional emoji indicators to log messages. The emoji can be specified per-record using the “emoji” extra field.

ANSI color codes are used for terminal output:
  • DEBUG: Green

  • INFO: Default (reset)

  • WARNING: Yellow

  • ERROR+: Red

GREY

ANSI code for gray text.

GREEN

ANSI code for green text.

YELLOW

ANSI code for yellow text.

RED

ANSI code for red text.

BOLD_RED

ANSI code for bold red text.

BLUE

ANSI code for blue text.

RESET

ANSI code to reset formatting.

Example

formatter = ColoredFormatter() handler.setFormatter(formatter)

GREY = '\x1b[38;5;240m'
GREEN = '\x1b[32m'
YELLOW = '\x1b[33m'
RED = '\x1b[31m'
BOLD_RED = '\x1b[31;1m'
BLUE = '\x1b[34m'
RESET = '\x1b[0m'
format(record)[source]

Format a log record with color and emoji.

Determines the appropriate color based on log level and prepends an emoji if specified in the record’s extra data.

Parameters:

record (LogRecord) – The log record to format.

Return type:

str

Returns:

Formatted string with ANSI color codes and optional emoji.

skim.application.logging_config.setup_logging(verbosity, quiet)[source]

Configure the logging system for CLI output.

Sets up the root logger with colored output to stderr. The verbosity level can be specified as a string matching standard logging levels.

Parameters:
  • verbosity (str) – Log level as string. Valid values: “DEBUG”, “INFO”, “WARNING”, “ERROR”, “CRITICAL”, “NONE”

  • quiet (bool) – If True, suppresses all log output regardless of verbosity. Equivalent to setting verbosity to “NONE”.

Return type:

None

Example

>>> setup_logging("INFO", quiet=False)
>>> logging.info("This will be shown")
>>> setup_logging("WARNING", quiet=False)
>>> logging.info("This will NOT be shown")
>>> setup_logging("DEBUG", quiet=True)
>>> logging.debug("This will NOT be shown (quiet=True)")

TUI Configurator

Main TUI application for skim configuration editing.

class skim.tui.app.LayerAdded(index, source_tab)[source]

Bases: Message

Posted when a layer is added in either tab.

Parameters:
  • index (int)

  • source_tab (str)

__init__(index, source_tab)[source]
Parameters:
  • index (int)

  • source_tab (str)

Return type:

None

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_layer_added'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
class skim.tui.app.LayerRemoved(index, source_tab)[source]

Bases: Message

Posted when a layer is removed in either tab.

Parameters:
  • index (int)

  • source_tab (str)

__init__(index, source_tab)[source]
Parameters:
  • index (int)

  • source_tab (str)

Return type:

None

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_layer_removed'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
class skim.tui.app.LayerUpdated[source]

Bases: Message

Posted when a layer’s metadata (name, label, etc.) is changed.

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_layer_updated'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
class skim.tui.app.QuitConfirmScreen(name=None, id=None, classes=None)[source]

Bases: ModalScreen[str]

Modal dialog for save-on-quit with unsaved changes.

Returns “save” to save and quit, “discard” to quit without saving, or None if dismissed.

Parameters:
  • name (str | None)

  • id (str | None)

  • classes (str | None)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='s', action='save_quit', description='Save & Quit', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='d', action='discard', description='Discard', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_button_pressed(event)[source]
Return type:

None

Parameters:

event (Pressed)

action_save_quit()[source]
Return type:

None

action_discard()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.app.SaveTargetScreen(config_path)[source]

Bases: ModalScreen[str | None]

Modal dialog to choose where to save when -c was used without -o.

Returns “overwrite” to save back to the config file, “default” to save to skim-config.yaml in cwd, or None if dismissed.

Parameters:

config_path (Path)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='escape', action='dismiss_dialog', description='Cancel', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

__init__(config_path)[source]

Initialize the screen.

Parameters:
  • name – The name of the screen.

  • id – The ID of the screen in the DOM.

  • classes – The CSS classes for the screen.

  • config_path (Path)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_button_pressed(event)[source]
Return type:

None

Parameters:

event (Pressed)

on_key(event)[source]
Return type:

None

action_dismiss_dialog()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.app.OverwriteConfirmScreen(path, display_name=None)[source]

Bases: ModalScreen[bool]

Modal dialog to confirm overwriting an existing file.

Returns True to overwrite, False to cancel.

Parameters:
  • path (Path)

  • display_name (str | None)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='escape', action='dismiss_dialog', description='Cancel', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

__init__(path, display_name=None)[source]

Initialize the screen.

Parameters:
  • name – The name of the screen.

  • id – The ID of the screen in the DOM.

  • classes – The CSS classes for the screen.

  • path (Path)

  • display_name (str | None)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_button_pressed(event)[source]
Return type:

None

Parameters:

event (Pressed)

on_key(event)[source]
Return type:

None

action_dismiss_dialog()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.app.ErrorDialog(message)[source]

Bases: ModalScreen[None]

Modal dialog to show an error message.

Parameters:

message (str)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='escape', action='dismiss_dialog', description='OK', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

__init__(message)[source]

Initialize the screen.

Parameters:
  • name – The name of the screen.

  • id – The ID of the screen in the DOM.

  • classes – The CSS classes for the screen.

  • message (str)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_button_pressed(event)[source]
Return type:

None

Parameters:

event (Pressed)

action_dismiss_dialog()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.app.HelpScreen(content)[source]

Bases: ModalScreen[None]

Modal dialog to show contextual help as rendered markdown.

Parameters:

content (str)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='escape', action='dismiss_help', description='Close', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='q', action='dismiss_help', description='Close', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

__init__(content)[source]

Initialize the screen.

Parameters:
  • name – The name of the screen.

  • id – The ID of the screen in the DOM.

  • classes – The CSS classes for the screen.

  • content (str)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_mount()[source]
Return type:

None

on_key(event)[source]
Return type:

None

Parameters:

event (Key)

action_dismiss_help()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.app.SkimConfigApp(config_data, output_path=None, config_path=None, force=False)[source]

Bases: App

Interactive skim configuration editor.

Parameters:
ENABLE_COMMAND_PALETTE: ClassVar[bool] = False

Should the [command palette][textual.command.CommandPalette] be enabled for the application?

TITLE: str | None = 'skim configure'

A class variable to set the default title for the application.

To update the title while the app is running, you can set the [title][textual.app.App.title] attribute. See also [the Screen.TITLE attribute][textual.screen.Screen.TITLE].

CSS: ClassVar[str] = '\n    QuitConfirmScreen, SaveTargetScreen, OverwriteConfirmScreen, ErrorDialog, HelpScreen {\n        align: center middle;\n    }\n    #quit-dialog, #save-target-dialog, #error-dialog {\n        padding: 1 2;\n        width: 55;\n        height: auto;\n        border: thick $background 80%;\n        background: $surface;\n    }\n    #overwrite-dialog {\n        padding: 1 2;\n        width: 82;\n        height: auto;\n        border: thick $background 80%;\n        background: $surface;\n    }\n    #question {\n        text-align: center;\n        width: 100%;\n        height: auto;\n        margin-bottom: 1;\n    }\n    #error-message {\n        text-align: center;\n        width: 100%;\n        height: auto;\n        margin-bottom: 1;\n    }\n    #quit-buttons, #overwrite-buttons, #error-buttons {\n        width: 100%;\n        height: auto;\n        align-horizontal: center;\n    }\n    #quit-buttons Button, #overwrite-buttons Button, #error-buttons Button {\n        margin: 0 1;\n        padding: 0 3;\n    }\n    #help-dialog {\n        padding: 0;\n        width: 70;\n        height: auto;\n        max-height: 80%;\n        border: thick $background 80%;\n        background: $surface;\n    }\n    #help-dialog _HelpMarkdown {\n        padding: 1 3;\n        overflow-y: auto;\n        max-height: 100%;\n    }\n    #help-dialog MarkdownH1 {\n        background: transparent;\n        margin: 0 0 1 0;\n    }\n    #help-dialog MarkdownH2,\n    #help-dialog MarkdownH3 {\n        background: transparent;\n    }\n    #save-target-buttons {\n        width: 100%;\n        height: auto;\n        align-horizontal: center;\n    }\n    #save-target-buttons Button {\n        width: 100%;\n        margin: 0 0 1 0;\n    }\n    /* Global compact styling */\n    Input {\n        height: 3;\n        width: 1fr;\n        margin: 0;\n    }\n    Switch {\n        height: auto;\n        min-height: 1;\n    }\n    Select {\n        width: 1fr;\n        max-width: 30;\n    }\n    .field-row {\n        height: auto;\n        margin: 0;\n        padding: 0;\n    }\n    .field-label {\n        width: 22;\n        height: 3;\n        padding: 1 0 0 0;\n    }\n    .section-title {\n        text-style: bold;\n        color: $accent;\n        margin: 1 0 0 0;\n    }\n    .section-title-first {\n        margin: 0;\n    }\n    AutoComplete {\n        & AutoCompleteList {\n            border-left: wide $accent;\n        }\n    }\n    ListItem {\n        layout: horizontal;\n    }\n    ListItem > Static {\n        text-wrap: nowrap;\n        text-overflow: ellipsis;\n        width: 1fr;\n    }\n    ListItem.moving {\n        background: $accent 30%;\n    }\n    ListItem.moving > Static {\n        color: $accent;\n    }\n    ListItem > .lc-swatch {\n        width: 4;\n    }\n    ListItem > .move-indicator {\n        dock: right;\n        width: 3;\n        color: $accent;\n    }\n    .list-buttons {\n        height: auto;\n    }\n    .list-buttons Button {\n        min-width: 12;\n        margin: 0 1 0 0;\n    }\n    '

Inline CSS, useful for quick scripts. This is loaded after CSS_PATH, and therefore takes priority in the event of a specificity clash.

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='ctrl+q', action='request_quit', description='Quit', show=True, key_display='⌃Q', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='ctrl+s', action='save', description='Save', show=True, key_display='⌃S', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='ctrl+p', action='previous_tab', description='Previous Tab', show=True, key_display='⌃P', priority=True, tooltip='', id=None, system=False, group=None), Binding(key='ctrl+n', action='next_tab', description='Next Tab', show=True, key_display='⌃N', priority=True, tooltip='', id=None, system=False, group=None), Binding(key='up', action="focus_direction('up')", description='', show=False, key_display=None, priority=True, tooltip='', id=None, system=False, group=None), Binding(key='down', action="focus_direction('down')", description='', show=False, key_display=None, priority=True, tooltip='', id=None, system=False, group=None), Binding(key='left', action="focus_direction('left')", description='', show=False, key_display=None, priority=True, tooltip='', id=None, system=False, group=None), Binding(key='right', action="focus_direction('right')", description='', show=False, key_display=None, priority=True, tooltip='', id=None, system=False, group=None), Binding(key='ctrl+e', action="scroll_view('down')", description='Scroll down', show=True, key_display='⌃E', priority=True, tooltip='', id=None, system=False, group=None), Binding(key='ctrl+y', action="scroll_view('up')", description='Scroll up', show=True, key_display='⌃Y', priority=True, tooltip='', id=None, system=False, group=None), Binding(key='f1', action='show_help', description='Help', show=True, key_display='F1,⌥H', priority=True, tooltip='', id=None, system=False, group=None), Binding(key='alt+h', action='show_help', description='', show=False, key_display=None, priority=True, tooltip='', id=None, system=False, group=None)]

The default key bindings.

__init__(config_data, output_path=None, config_path=None, force=False)[source]

Create an instance of an app.

Parameters:
  • driver_class – Driver class or None to auto-detect. This will be used by some Textual tools.

  • css_path – Path to CSS or None to use the CSS_PATH class variable. To load multiple CSS files, pass a list of strings or paths which will be loaded in order.

  • watch_css – Reload CSS if the files changed. This is set automatically if you are using textual run with the dev switch.

  • ansi_color – Allow ANSI colors if True, or convert ANSI colors to RGB if False.

  • config_data (dict[str, Any])

  • output_path (Path | None)

  • config_path (Path | None)

  • force (bool)

Raises:

CssPathError – When the supplied CSS path(s) are an unexpected type.

Return type:

None

compose()[source]

Yield child widgets for a container.

This method should be implemented in a subclass.

Return type:

Iterable[Widget]

property has_unsaved_changes: bool
action_request_quit()[source]
Return type:

None

action_show_help()[source]

Show contextual help for the currently focused widget.

Return type:

None

action_previous_tab()[source]
Return type:

None

action_next_tab()[source]
Return type:

None

action_save(*, exit_after=False)[source]
Return type:

None

Parameters:

exit_after (bool)

on_layer_added(event)[source]
Return type:

None

Parameters:

event (LayerAdded)

on_layer_updated(event)[source]
Return type:

None

Parameters:

event (LayerUpdated)

on_layer_removed(event)[source]
Return type:

None

Parameters:

event (LayerRemoved)

action_scroll_view(direction)[source]

Scroll the VerticalScroll in the active tab (skip ListViews).

Return type:

None

Parameters:

direction (str)

action_focus_direction(direction)[source]

Move focus to the nearest focusable widget in the given direction.

Return type:

None

Parameters:

direction (str)

on_descendant_focus(event)[source]
Return type:

None

Parameters:

event (DescendantFocus)

TUI Widgets

Shared reusable TUI widgets for skim configuration editor.

class skim.tui.widgets.SkimFooter(*children, name=None, id=None, classes=None, disabled=False, show_command_palette=True, compact=False)[source]

Bases: Footer

Footer with controlled binding order and paired key display.

Parameters:
  • children (Widget)

  • name (str | None)

  • id (str | None)

  • classes (str | None)

  • disabled (bool)

  • show_command_palette (bool)

  • compact (bool)

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = False

Widget’s children may receive focus.

class skim.tui.widgets.SkimStandaloneInput(*args, help_key=None, **kwargs)[source]

Bases: Input

Input for standalone fields outside edit panes.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='tab', action='focus_next', description='Next field', show=True, key_display='↓,⇥', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='shift+tab', action='focus_previous', description='Previous field', show=True, key_display='↑,⇤', priority=False, tooltip='', id=None, system=False, group=None)]
Key(s) | Description |
:- | :- |
left | Move the cursor left. |
shift+left | Move cursor left and select. |
ctrl+left | Move the cursor one word to the left. |
right | Move the cursor right or accept the completion suggestion. |
ctrl+shift+left | Move cursor left a word and select. |
shift+right | Move cursor right and select. |
ctrl+right | Move the cursor one word to the right. |
backspace | Delete the character to the left of the cursor. |
ctrl+shift+right | Move cursor right a word and select. |
ctrl+shift+a | Select all text in the input. |
home,ctrl+a | Go to the beginning of the input. |
end,ctrl+e | Go to the end of the input. |
shift+home | Select up to the input start. |
shift+end | Select up to the input end. |
delete,ctrl+d | Delete the character to the right of the cursor. |
enter | Submit the current value of the input. |
ctrl+w | Delete the word to the left of the cursor. |
ctrl+u | Delete everything to the left of the cursor. |
ctrl+f | Delete the word to the right of the cursor. |
ctrl+k | Delete everything to the right of the cursor. |
ctrl+x | Cut selected text. |
ctrl+c | Copy selected text. |
ctrl+v | Paste text from the clipboard. |
__init__(*args, help_key=None, **kwargs)[source]

Initialise the Input widget.

Parameters:
  • value – An optional default value for the input.

  • placeholder – Optional placeholder text for the input.

  • highlighter – An optional highlighter for the input.

  • password – Flag to say if the field should obfuscate its content.

  • restrict – A regex to restrict character inputs.

  • type – The type of the input.

  • max_length – The maximum length of the input, or 0 for no maximum length.

  • suggester – [Suggester][textual.suggester.Suggester] associated with this input instance.

  • validators – An iterable of validators that the Input value will be checked against.

  • validate_on – Zero or more of the values “blur”, “changed”, and “submitted”, which determine when to do input validation. The default is to do validation for all messages.

  • valid_empty – Empty values are valid.

  • select_on_focus – Whether to select all text on focus.

  • name – Optional name for the input widget.

  • id – Optional ID for the widget.

  • classes – Optional initial classes for the widget.

  • disabled – Whether the input is disabled or not.

  • tooltip – Optional tooltip.

  • compact – Enable compact style (without borders).

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.widgets.SkimInput(*args, help_key=None, **kwargs)[source]

Bases: Input

Input with footer bindings for edit-pane field navigation.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='tab', action='focus_next', description='Next field', show=True, key_display='↓,⇥', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='shift+tab', action='focus_previous', description='Previous field', show=True, key_display='↑,⇤', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='enter', action='submit', description='Confirm changes', show=True, key_display='⏎', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='escape', action='cancel_edit', description='Discard changes', show=True, key_display='\U000f12b7', priority=False, tooltip='', id=None, system=False, group=None)]
Key(s) | Description |
:- | :- |
left | Move the cursor left. |
shift+left | Move cursor left and select. |
ctrl+left | Move the cursor one word to the left. |
right | Move the cursor right or accept the completion suggestion. |
ctrl+shift+left | Move cursor left a word and select. |
shift+right | Move cursor right and select. |
ctrl+right | Move the cursor one word to the right. |
backspace | Delete the character to the left of the cursor. |
ctrl+shift+right | Move cursor right a word and select. |
ctrl+shift+a | Select all text in the input. |
home,ctrl+a | Go to the beginning of the input. |
end,ctrl+e | Go to the end of the input. |
shift+home | Select up to the input start. |
shift+end | Select up to the input end. |
delete,ctrl+d | Delete the character to the right of the cursor. |
enter | Submit the current value of the input. |
ctrl+w | Delete the word to the left of the cursor. |
ctrl+u | Delete everything to the left of the cursor. |
ctrl+f | Delete the word to the right of the cursor. |
ctrl+k | Delete everything to the right of the cursor. |
ctrl+x | Cut selected text. |
ctrl+c | Copy selected text. |
ctrl+v | Paste text from the clipboard. |
__init__(*args, help_key=None, **kwargs)[source]

Initialise the Input widget.

Parameters:
  • value – An optional default value for the input.

  • placeholder – Optional placeholder text for the input.

  • highlighter – An optional highlighter for the input.

  • password – Flag to say if the field should obfuscate its content.

  • restrict – A regex to restrict character inputs.

  • type – The type of the input.

  • max_length – The maximum length of the input, or 0 for no maximum length.

  • suggester – [Suggester][textual.suggester.Suggester] associated with this input instance.

  • validators – An iterable of validators that the Input value will be checked against.

  • validate_on – Zero or more of the values “blur”, “changed”, and “submitted”, which determine when to do input validation. The default is to do validation for all messages.

  • valid_empty – Empty values are valid.

  • select_on_focus – Whether to select all text on focus.

  • name – Optional name for the input widget.

  • id – Optional ID for the widget.

  • classes – Optional initial classes for the widget.

  • disabled – Whether the input is disabled or not.

  • tooltip – Optional tooltip.

  • compact – Enable compact style (without borders).

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Return type:

None

action_cancel_edit()[source]

No-op — handled by ListDetailPane.on_key via event bubbling.

Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.widgets.SkimListView(*args, help_key=None, **kwargs)[source]

Bases: ListView

ListView with footer bindings for navigation and edit.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='up', action='cursor_up', description='Prev item', show=True, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='down', action='cursor_down', description='Next item', show=True, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='enter', action='select_cursor', description='Edit', show=True, key_display='⏎', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='m', action='move_mode', description='Move', show=True, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='up', action='move_up', description='Move up', show=True, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='down', action='move_down', description='Move down', show=True, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='enter', action='confirm_move', description='Confirm position', show=True, key_display='⏎', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='escape', action='cancel_move', description='Discard changes', show=True, key_display='\U000f12b7', priority=False, tooltip='', id=None, system=False, group=None)]
Key(s) | Description |
:- | :- |
enter | Select the current item. |
up | Move the cursor up. |
down | Move the cursor down. |
__init__(*args, help_key=None, **kwargs)[source]

Initialize a ListView.

Parameters:
  • *children – The ListItems to display in the list.

  • initial_index – The index that should be highlighted when the list is first mounted.

  • name – The name of the widget.

  • id – The unique ID of the widget used in CSS/query selection.

  • classes – The CSS classes of the widget.

  • disabled – Whether the ListView is disabled or not.

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Return type:

None

check_action(action, parameters)[source]

Check whether an action is enabled.

Implement this method to add logic for [dynamic actions](/guide/actions#dynamic-actions) / bindings.

Parameters:
  • action (str) – The name of an action.

  • parameters (tuple[object, ...]) – A tuple of any action parameters.

Return type:

bool | None

Returns:

True if the action is enabled+visible,

False if the action is disabled+hidden, None if the action is disabled+visible (grayed out in footer)

action_move_mode()[source]

No-op — handled by LayerListPane.on_key via event bubbling.

Return type:

None

action_move_up()[source]

No-op — handled by LayerListPane.on_key via event bubbling.

Return type:

None

action_move_down()[source]

No-op — handled by LayerListPane.on_key via event bubbling.

Return type:

None

action_confirm_move()[source]

No-op — handled by LayerListPane.on_key via event bubbling.

Return type:

None

action_cancel_move()[source]

No-op — handled by LayerListPane.on_key via event bubbling.

Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = False

Widget’s children may receive focus.

class skim.tui.widgets.SkimButton(*args, help_key=None, **kwargs)[source]

Bases: Button

Button that responds to both Enter and Space.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='enter', action='press', description='Activate', show=True, key_display='⏎,␣', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='space', action='press', description='Activate', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]

A list of key bindings.

__init__(*args, help_key=None, **kwargs)[source]

Create a Button widget.

Parameters:
  • label – The text that appears within the button.

  • variant – The variant of the button.

  • name – The name of the button.

  • id – The ID of the button in the DOM.

  • classes – The CSS classes of the button.

  • disabled – Whether the button is disabled or not.

  • tooltip – Optional tooltip.

  • action – Optional action to run when clicked.

  • compact – Enable compact button style.

  • flat – Enable alternative flat look buttons.

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.widgets.SkimSwitch(*args, help_key=None, **kwargs)[source]

Bases: Switch

Switch with footer binding for toggle action.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='enter', action='toggle_switch', description='Toggle', show=True, key_display='⏎,␣', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='space', action='toggle_switch', description='Toggle', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]
Key(s) | Description |
:- | :- |
enter,space | Toggle the switch state. |
__init__(*args, help_key=None, **kwargs)[source]

Initialise the switch.

Parameters:
  • value – The initial value of the switch.

  • animate – True if the switch should animate when toggled.

  • name – The name of the switch.

  • id – The ID of the switch in the DOM.

  • classes – The CSS classes of the switch.

  • disabled – Whether the switch is disabled or not.

  • tooltip – Optional tooltip.

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.widgets.SkimSelect(*args, help_key=None, **kwargs)[source]

Bases: Select

Select with footer binding for menu action.

Parameters:
  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

BINDINGS: ClassVar[list[BindingType]] = [Binding(key='enter', action='show_overlay', description='Show options', show=True, key_display='⏎,␣', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='space', action='show_overlay', description='Show options', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='tab', action='focus_next', description='Next field', show=True, key_display='↓,⇥', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='shift+tab', action='focus_previous', description='Previous field', show=True, key_display='↑,⇤', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='escape', action='cancel_edit', description='Discard changes', show=True, key_display='\U000f12b7', priority=False, tooltip='', id=None, system=False, group=None), Binding(key='up', action='skip_arrow', description='', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None), Binding(key='down', action='skip_arrow', description='', show=False, key_display=None, priority=False, tooltip='', id=None, system=False, group=None)]
Key(s) | Description |
:- | :- |
enter,down,space,up | Activate the overlay |
__init__(*args, help_key=None, **kwargs)[source]

Initialize the Select control.

Parameters:
  • options – Options to select from. If no options are provided then allow_blank must be set to True.

  • prompt – Text to show in the control when no option is selected.

  • allow_blank – Enables or disables the ability to have the widget in a state with no selection made, in which case its value is set to the constant [Select.NULL][textual.widgets.Select.NULL].

  • value – Initial value selected. Should be one of the values in options. If no initial value is set and allow_blank is False, the widget will auto-select the first available option.

  • type_to_search – If True, typing will search for options.

  • name – The name of the select control.

  • id – The ID of the control in the DOM.

  • classes – The CSS classes of the control.

  • disabled – Whether the control is disabled or not.

  • tooltip – Optional tooltip.

  • compact – Enable compact select (without borders).

  • args (Any)

  • help_key (str | None)

  • kwargs (Any)

Raises:

EmptySelectError – If no options are provided and allow_blank is False.

Return type:

None

action_cancel_edit()[source]

No-op — handled by ListDetailPane.on_key via event bubbling.

Return type:

None

action_skip_arrow()[source]

Yield arrow keys to app-level spatial navigation.

Return type:

None

compose()[source]

Compose Select with overlay and current value.

Return type:

Iterable[Widget]

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.widgets.SkimVerticalScroll(*children, name=None, id=None, classes=None, disabled=False, can_focus=None, can_focus_children=None, can_maximize=None)[source]

Bases: VerticalScroll

VerticalScroll that yields arrow keys for spatial focus navigation.

Arrow keys raise SkipAction so the app-level directional focus handler receives them. Page Up/Down and Home/End still scroll normally.

Parameters:
  • children (Widget)

  • name (str | None)

  • id (str | None)

  • classes (str | None)

  • disabled (bool)

  • can_focus (bool | None)

  • can_focus_children (bool | None)

  • can_maximize (bool | None)

action_scroll_up()[source]
Return type:

None

action_scroll_down()[source]
Return type:

None

action_scroll_left()[source]
Return type:

None

action_scroll_right()[source]
Return type:

None

can_focus: bool = True

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

List Detail Pane

Reusable list/detail pane base class for the skim TUI.

class skim.tui.list_detail_pane.ListDetailPane(pane_id, list_help_key=None, **kwargs)[source]

Bases: Widget

Base class for a list/detail split pane.

Provides: horizontal layout with a list column (35%) and a detail pane, edit mode lifecycle (enter/exit/commit/cancel), snapshot/rollback, focus-out commit, and key handling (Enter/Escape/a/d).

Subclasses implement the abstract methods to supply data and fields.

Parameters:
  • pane_id (str)

  • list_help_key (str | None)

  • kwargs (Any)

DEFAULT_CSS: ClassVar[str] = '\n    ListDetailPane {\n        height: auto;\n    }\n    ListDetailPane .ldp-container {\n        height: auto;\n    }\n    ListDetailPane .ldp-list-col {\n        width: 35%;\n        min-width: 25;\n        height: 100%;\n    }\n    ListDetailPane .ldp-list {\n        height: 1fr;\n        border: solid $accent 50%;\n    }\n    ListDetailPane .ldp-buttons {\n        height: auto;\n        width: 100%;\n    }\n    ListDetailPane .ldp-buttons Button {\n        width: 50%;\n    }\n    ListDetailPane .ldp-detail {\n        padding: 0 1;\n        height: auto;\n        overflow-x: hidden;\n        border: solid $accent 30%;\n    }\n    ListDetailPane .ldp-detail:focus-within {\n        border: solid $accent;\n    }\n    '

Default TCSS.

class EntryAdded(index)[source]

Bases: Message

Posted after an entry is added.

Parameters:

index (int)

__init__(index)[source]
Parameters:

index (int)

Return type:

None

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_list_detail_pane_entry_added'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
class EntryRemoved(index)[source]

Bases: Message

Posted after an entry is removed.

Parameters:

index (int)

__init__(index)[source]
Parameters:

index (int)

Return type:

None

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_list_detail_pane_entry_removed'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
class EntryUpdated[source]

Bases: Message

Posted after a successful commit.

time
bubble: ClassVar[bool] = True
handler_name: ClassVar[str] = 'on_list_detail_pane_entry_updated'

Name of the default message handler.

no_dispatch: ClassVar[bool] = False
verbose: ClassVar[bool] = False
__init__(pane_id, list_help_key=None, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • pane_id (str)

  • list_help_key (str | None)

  • kwargs (Any)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

abstract get_entries()[source]

Return the mutable list of data entries.

Return type:

list[dict]

abstract format_entry(index, entry)[source]

Format entry text for the list display.

Return type:

str

Parameters:
abstract compose_detail_fields()[source]

Yield the detail field widgets.

Return type:

Iterable[Widget]

abstract detail_field_ids()[source]

Return the set of Input IDs in the detail pane.

Return type:

set[str]

abstract refresh_fields(entry)[source]

Populate fields from entry data.

Return type:

None

Parameters:

entry (dict)

abstract clear_fields()[source]

Clear all fields.

Return type:

None

abstract create_entry(index)[source]

Create a new default entry for the given insertion index.

Return type:

dict

Parameters:

index (int)

validate_and_apply(entry)[source]

Validate the edit, apply field values to the entry dict.

Return True if valid, False to revert and show error. Default implementation is a no-op that returns True.

Return type:

bool

Parameters:

entry (dict)

rebuild_list()[source]

Rebuild the entire list from get_entries().

Return type:

None

update_all_list_items()[source]

Update the text of all list items in place.

Return type:

None

update_selected_list_item()[source]

Update only the selected list item.

Return type:

None

move_paired_lists()[source]

Return additional lists that must be swapped in sync with entries.

Subclasses override this to return lists (e.g. palette layers, keyboard layers) that are paired 1:1 with get_entries().

Return type:

list[list[dict]]

on_move_swap(entries, pos, target, direction)[source]

Hook called before swapping entries at pos and target.

Subclasses override this to adjust entry data (e.g. QMK indices) before the positional swap happens.

Return type:

None

Parameters:
property move_enabled: bool

Whether this pane supports move mode. Override to enable.

on_button_pressed(event)[source]
Return type:

None

Parameters:

event (Pressed)

on_list_view_selected(event)[source]
Return type:

None

Parameters:

event (Selected)

on_list_view_highlighted(event)[source]
Return type:

None

Parameters:

event (Highlighted)

on_key(event)[source]

Handle move mode, Enter/Escape in edit mode, a/d/m shortcuts on list.

Return type:

None

on_descendant_blur(event)[source]

Commit edit when focus leaves the editing pane.

Return type:

None

Parameters:

event (DescendantBlur)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

Keyboard Tab

Keyboard tab widget for the skim TUI configuration editor.

class skim.tui.keyboard_tab.LayerListPane(config_data, **kwargs)[source]

Bases: ListDetailPane

List/detail pane for keyboard layers.

Parameters:
DEFAULT_CSS: ClassVar[str] = '\n    LayerListPane {\n        height: auto;\n    }\n    '

Default TCSS.

__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

get_entries()[source]

Return the mutable list of data entries.

Return type:

list[dict]

format_entry(index, entry)[source]

Format entry text for the list display.

Return type:

str

Parameters:
compose_detail_fields()[source]

Yield the detail field widgets.

Return type:

Iterable[Widget]

detail_field_ids()[source]

Return the set of Input IDs in the detail pane.

Return type:

set[str]

refresh_fields(entry)[source]

Populate fields from entry data.

Return type:

None

Parameters:

entry (dict)

clear_fields()[source]

Clear all fields.

Return type:

None

create_entry(index)[source]

Create a new default entry for the given insertion index.

Return type:

dict

Parameters:

index (int)

validate_and_apply(entry)[source]

Validate index (0-31, no duplicates), apply fields, re-sort.

Return type:

bool

Parameters:

entry (dict)

on_input_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

property move_enabled: bool

Whether this pane supports move mode. Override to enable.

move_paired_lists()[source]

Return additional lists that must be swapped in sync with entries.

Subclasses override this to return lists (e.g. palette layers, keyboard layers) that are paired 1:1 with get_entries().

Return type:

list[list[dict]]

on_move_swap(entries, pos, target, direction)[source]

Hook called before swapping entries at pos and target.

Subclasses override this to adjust entry data (e.g. QMK indices) before the positional swap happens.

Return type:

None

Parameters:
can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.keyboard_tab.KeyboardTab(config_data, **kwargs)[source]

Bases: Widget

Keyboard configuration tab.

Shows an Information section, a Features section, and a Layers section with a LayerListPane for editing individual layer metadata.

Parameters:
DEFAULT_CSS: ClassVar[str] = '\n    KeyboardTab {\n        height: 1fr;\n        padding: 0 1;\n    }\n    KeyboardTab #info-section {\n        height: auto;\n    }\n    KeyboardTab #features-section {\n        height: auto;\n    }\n    KeyboardTab #features-row {\n        height: auto;\n    }\n    KeyboardTab #layers-section {\n        height: auto;\n    }\n    '

Default TCSS.

__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_mount()[source]
Return type:

None

on_switch_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

on_input_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

on_list_detail_pane_entry_added(event)[source]
Return type:

None

Parameters:

event (EntryAdded)

on_list_detail_pane_entry_removed(event)[source]
Return type:

None

Parameters:

event (EntryRemoved)

on_list_detail_pane_entry_updated(event)[source]
Return type:

None

Parameters:

event (EntryUpdated)

sync_layer_added(index)[source]

Called when a layer color is added in the Style tab.

Return type:

None

Parameters:

index (int)

sync_layer_removed(index)[source]

Called when a layer color is removed in the Style tab.

Return type:

None

Parameters:

index (int)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

Keycodes Tab

Keycodes tab widget for the skim TUI configuration editor.

class skim.tui.keycodes_tab.KeycodeAutoComplete(*args, **kwargs)[source]

Bases: AutoComplete

Token-aware autocomplete for keycode/macro fields.

Splits input on separator characters so autocomplete works inside macro arguments (e.g. LSFT(KC_SPC)). Macro completions like LSFT() are inserted as LSFT( so the user can continue filling in the argument.

Parameters:
__init__(*args, **kwargs)[source]

An autocomplete widget.

Parameters:
  • target – An Input instance or a selector string used to query an Input instance. If a selector is used, remember that widgets are not available until the widget has been mounted (don’t use the selector in compose - use it in on_mount instead).

  • candidates – The candidates to match on, or a function which returns the candidates to match on. If set to None, the candidates will be fetched by directly calling the get_candidates method, which is what you’ll probably want to do if you’re subclassing AutoComplete and supplying your own custom get_candidates method.

  • prevent_default_enter – Prevent the default enter behavior. If True, when you select a dropdown option using the enter key, the default behavior (e.g. submitting an Input) will be prevented.

  • prevent_default_tab – Prevent the default tab behavior. If True, when you select a dropdown option using the tab key, the default behavior (e.g. moving focus to the next widget) will be prevented.

  • args (Any)

  • kwargs (Any)

Return type:

None

get_search_string(target_state)[source]

This value will be passed to the match function.

This could be, for example, the text in the target widget, or a substring of that text.

Return type:

str

Returns:

The search string that will be used to filter the dropdown options.

Parameters:

target_state (TargetState)

apply_completion(value, state)[source]

Apply the completion to the target widget.

This method updates the state of the target widget to the reflect the value the user has chosen from the dropdown list.

Return type:

None

Parameters:
  • value (str)

  • state (TargetState)

should_show_dropdown(search_string)[source]

Determine whether to show or hide the dropdown based on the current state.

This method can be overridden to customize the visibility behavior.

Parameters:

search_string (str) – The current search string.

Returns:

True if the dropdown should be shown, False otherwise.

Return type:

bool

post_completion()[source]

This method is called after a completion is applied. By default, it simply hides the dropdown.

Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.keycodes_tab.OverrideTargetAutoComplete(target, config_data, **kwargs)[source]

Bases: AutoComplete

Context-aware autocomplete for the Override Target field.

Detects @@ (keycode reference) and %% (NerdFont glyph) prefixes at the cursor position and provides appropriate candidates. On completion, inserts the value with a trailing ;.

Parameters:
__init__(target, config_data, **kwargs)[source]

An autocomplete widget.

Parameters:
  • target (Input) – An Input instance or a selector string used to query an Input instance. If a selector is used, remember that widgets are not available until the widget has been mounted (don’t use the selector in compose - use it in on_mount instead).

  • candidates – The candidates to match on, or a function which returns the candidates to match on. If set to None, the candidates will be fetched by directly calling the get_candidates method, which is what you’ll probably want to do if you’re subclassing AutoComplete and supplying your own custom get_candidates method.

  • prevent_default_enter – Prevent the default enter behavior. If True, when you select a dropdown option using the enter key, the default behavior (e.g. submitting an Input) will be prevented.

  • prevent_default_tab – Prevent the default tab behavior. If True, when you select a dropdown option using the tab key, the default behavior (e.g. moving focus to the next widget) will be prevented.

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

get_search_string(target_state)[source]

This value will be passed to the match function.

This could be, for example, the text in the target widget, or a substring of that text.

Return type:

str

Returns:

The search string that will be used to filter the dropdown options.

Parameters:

target_state (TargetState)

get_candidates(target_state)[source]

Get the candidates to match against.

Return type:

list[DropdownItem]

Parameters:

target_state (TargetState)

apply_completion(value, state)[source]

Apply the completion to the target widget.

This method updates the state of the target widget to the reflect the value the user has chosen from the dropdown list.

Return type:

None

Parameters:
  • value (str)

  • state (TargetState)

should_show_dropdown(search_string)[source]

Determine whether to show or hide the dropdown based on the current state.

This method can be overridden to customize the visibility behavior.

Parameters:

search_string (str) – The current search string.

Returns:

True if the dropdown should be shown, False otherwise.

Return type:

bool

post_completion()[source]

This method is called after a completion is applied. By default, it simply hides the dropdown.

Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.keycodes_tab.PreProcessListPane(config_data, **kwargs)[source]

Bases: ListDetailPane

List/detail pane for pre-process keycode entries.

Parameters:
__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

get_entries()[source]

Return the mutable list of data entries.

Return type:

list[dict]

format_entry(index, entry)[source]

Format entry text for the list display.

Return type:

str

Parameters:
compose_detail_fields()[source]

Yield the detail field widgets.

Return type:

Iterable[Widget]

detail_field_ids()[source]

Return the set of Input IDs in the detail pane.

Return type:

set[str]

refresh_fields(entry)[source]

Populate fields from entry data.

Return type:

None

Parameters:

entry (dict)

clear_fields()[source]

Clear all fields.

Return type:

None

create_entry(index)[source]

Create a new default entry for the given insertion index.

Return type:

dict

Parameters:

index (int)

on_input_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.keycodes_tab.OverrideListPane(config_data, **kwargs)[source]

Bases: ListDetailPane

List/detail pane for override keycode entries.

Parameters:
__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

get_entries()[source]

Return the mutable list of data entries.

Return type:

list[dict]

format_entry(index, entry)[source]

Format entry text for the list display.

Return type:

str

Parameters:
compose_detail_fields()[source]

Yield the detail field widgets.

Return type:

Iterable[Widget]

detail_field_ids()[source]

Return the set of Input IDs in the detail pane.

Return type:

set[str]

refresh_fields(entry)[source]

Populate fields from entry data.

Return type:

None

Parameters:

entry (dict)

clear_fields()[source]

Clear all fields.

Return type:

None

create_entry(index)[source]

Create a new default entry for the given insertion index.

Return type:

dict

Parameters:

index (int)

on_input_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.keycodes_tab.KeycodesTab(config_data, **kwargs)[source]

Bases: Widget

Keycodes configuration tab.

Shows two sections – Pre-process and Overrides – each using a ListDetailPane subclass for editing individual keycode mapping entries.

Parameters:
DEFAULT_CSS: ClassVar[str] = '\n    KeycodesTab {\n        height: 1fr;\n        padding: 0 1;\n    }\n    KeycodesTab .keycodes-section {\n        height: 1fr;\n    }\n    KeycodesTab .keycodes-section ListDetailPane {\n        height: 1fr;\n    }\n    KeycodesTab .keycodes-section ListDetailPane .ldp-container {\n        height: 100%;\n    }\n    KeycodesTab .keycodes-section ListDetailPane .ldp-list-col {\n        width: 35%;\n        min-width: 25;\n    }\n    KeycodesTab .keycodes-section ListDetailPane .ldp-list {\n        height: 1fr;\n        border: solid $accent 50%;\n    }\n    KeycodesTab .keycodes-section ListDetailPane .ldp-detail {\n        padding: 0 1;\n        height: auto;\n        overflow-x: hidden;\n        border: solid $accent 30%;\n    }\n    KeycodesTab .keycodes-section ListDetailPane .ldp-detail:focus-within {\n        border: solid $accent;\n    }\n    '

Default TCSS.

__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_mount()[source]
Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

Style Tab

Output tab widget for the skim TUI configuration editor.

class skim.tui.output_tab.ColorAutoComplete(*args, **kwargs)[source]

Bases: AutoComplete

AutoComplete that re-posts Input.Changed after dropdown selection.

The base AutoComplete suppresses Input.Changed during completion via self.prevent(Input.Changed). This subclass re-fires the event so that parent widgets (e.g. swatch updates) react to the new value.

Parameters:
__init__(*args, **kwargs)[source]

An autocomplete widget.

Parameters:
  • target – An Input instance or a selector string used to query an Input instance. If a selector is used, remember that widgets are not available until the widget has been mounted (don’t use the selector in compose - use it in on_mount instead).

  • candidates – The candidates to match on, or a function which returns the candidates to match on. If set to None, the candidates will be fetched by directly calling the get_candidates method, which is what you’ll probably want to do if you’re subclassing AutoComplete and supplying your own custom get_candidates method.

  • prevent_default_enter – Prevent the default enter behavior. If True, when you select a dropdown option using the enter key, the default behavior (e.g. submitting an Input) will be prevented.

  • prevent_default_tab – Prevent the default tab behavior. If True, when you select a dropdown option using the tab key, the default behavior (e.g. moving focus to the next widget) will be prevented.

  • args (Any)

  • kwargs (Any)

Return type:

None

should_show_dropdown(search_string)[source]

Determine whether to show or hide the dropdown based on the current state.

This method can be overridden to customize the visibility behavior.

Parameters:

search_string (str) – The current search string.

Returns:

True if the dropdown should be shown, False otherwise.

Return type:

bool

apply_completion(value, state)[source]

Apply the completion to the target widget.

This method updates the state of the target widget to the reflect the value the user has chosen from the dropdown list.

Return type:

None

Parameters:
  • value (str)

  • state (TargetState)

post_completion()[source]

This method is called after a completion is applied. By default, it simply hides the dropdown.

Return type:

None

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.output_tab.LayerColorListPane(config_data, **kwargs)[source]

Bases: ListDetailPane

List/detail pane for layer colors.

Parameters:
DEFAULT_CSS: ClassVar[str] = '\n    LayerColorListPane {\n        height: auto;\n    }\n    LayerColorListPane .lc-swatch {\n        width: 4;\n        height: 1;\n        dock: right;\n        margin: 0 0 0 1;\n    }\n    LayerColorListPane .color-swatch {\n        width: 4;\n        height: 1;\n        margin: 1 1 0 0;\n        content-align: center middle;\n    }\n    LayerColorListPane .swatch-spacer {\n        width: 4;\n        height: 1;\n        margin: 1 1 0 0;\n    }\n    LayerColorListPane .gradient-swatch {\n        width: 4;\n        height: 3;\n        content-align: center middle;\n    }\n    LayerColorListPane .gradient-preview {\n        height: 3;\n        width: auto;\n        layout: horizontal;\n        padding: 0 2;\n    }\n    LayerColorListPane .gradient-dark {\n        background: #1b1b1b;\n        margin: 0 0 0 2;\n    }\n    LayerColorListPane .gradient-light {\n        background: #ffffff;\n        margin: 0 0 0 1;\n    }\n    LayerColorListPane .lc-manual-step {\n        display: none;\n    }\n    LayerColorListPane.manual-mode .lc-manual-step {\n        display: block;\n    }\n    LayerColorListPane.manual-mode #lc-dynamic-color {\n        display: none;\n    }\n    LayerColorListPane.manual-mode #lc-dynamic-preview {\n        display: none;\n    }\n    '

Default TCSS.

__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

get_entries()[source]

Return the mutable list of data entries.

Return type:

list[dict]

format_entry(index, entry)[source]

Format entry text for the list display.

Return type:

str

Parameters:
compose_detail_fields()[source]

Yield the detail field widgets.

Return type:

Iterable[Widget]

detail_field_ids()[source]

Return the set of Input IDs in the detail pane.

Return type:

set[str]

property move_enabled: bool

Whether this pane supports move mode. Override to enable.

move_paired_lists()[source]

Return additional lists that must be swapped in sync with entries.

Subclasses override this to return lists (e.g. palette layers, keyboard layers) that are paired 1:1 with get_entries().

Return type:

list[list[dict]]

on_move_swap(entries, pos, target, direction)[source]

Hook called before swapping entries at pos and target.

Subclasses override this to adjust entry data (e.g. QMK indices) before the positional swap happens.

Return type:

None

Parameters:
on_key(event)[source]

Override to let Select handle keys normally.

Textual’s on_key fires during event bubbling BEFORE the App-level binding system runs. We must stop the event (to prevent other handlers from interfering) and manually trigger binding checks.

Return type:

None

on_descendant_blur(event)[source]

Suppress focus-out commit while Select overlay is open.

Return type:

None

Parameters:

event (DescendantBlur)

on_select_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

refresh_fields(entry)[source]

Populate fields from entry data.

Return type:

None

Parameters:

entry (dict)

clear_fields()[source]

Clear all fields.

Return type:

None

create_entry(index)[source]

Create a new default entry for the given insertion index.

Return type:

dict

Parameters:

index (int)

on_input_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.

class skim.tui.output_tab.OutputTab(config_data, **kwargs)[source]

Bases: Widget

Output configuration tab.

Has sections for layout, style, palette, and layer colors.

Parameters:
DEFAULT_CSS: ClassVar[str] = '\n    OutputTab {\n        height: 1fr;\n        padding: 0 1;\n    }\n    OutputTab .section {\n        height: auto;\n        border-bottom: solid $accent 20%;\n    }\n    OutputTab .color-swatch {\n        width: 5;\n        height: 1;\n        margin: 1 1 0 0;\n        content-align: center middle;\n    }\n    OutputTab .swatch-spacer {\n        width: 4;\n        height: 1;\n        margin: 1 1 0 0;\n    }\n    '

Default TCSS.

__init__(config_data, **kwargs)[source]

Initialize a Widget.

Parameters:
  • *children – Child widgets.

  • name – The name of the widget.

  • id – The ID of the widget in the DOM.

  • classes – The CSS classes for the widget.

  • disabled – Whether the widget is disabled or not.

  • markup – Enable content markup?

  • config_data (dict[str, Any])

  • kwargs (Any)

Return type:

None

compose()[source]

Called by Textual to create child widgets.

This method is called when a widget is mounted or by setting recompose=True when calling [refresh()][textual.widget.Widget.refresh].

Note that you don’t typically need to explicitly call this method. :rtype: Iterable[Widget]

Example

```python def compose(self) -> ComposeResult:

yield Header() yield Label(“Press the button below:”) yield Button() yield Footer()

```

Return type:

Iterable[Widget]

on_mount()[source]
Return type:

None

on_list_detail_pane_entry_added(event)[source]
Return type:

None

Parameters:

event (EntryAdded)

on_list_detail_pane_entry_removed(event)[source]
Return type:

None

Parameters:

event (EntryRemoved)

sync_layer_added(index)[source]

Called when a layer is added in the Keyboard tab.

Return type:

None

Parameters:

index (int)

sync_layer_removed(index)[source]

Called when a layer is removed in the Keyboard tab.

Return type:

None

Parameters:

index (int)

on_input_changed(event)[source]

Route input changes to the correct config path.

Return type:

None

Parameters:

event (Changed)

on_switch_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

on_select_changed(event)[source]
Return type:

None

Parameters:

event (Changed)

can_focus: bool = False

Widget may receive focus.

can_focus_children: bool = True

Widget’s children may receive focus.