Skip to content

topmark.cli.presentation

topmark / cli / presentation

CLI presentation layer for semantic styling.

This module maps backend-agnostic semantic style roles (StyleRole) to concrete terminal styling callables (currently backed by yachalk).

Design goals
  • Keep all ANSI / terminal styling confined to the CLI layer.
  • Centralize the mapping from semantic roles → concrete styles.
  • Allow theming and stackable style overrides.
  • Keep core and rendering layers free of presentation backends.

Key concepts

  • StyleRole: Semantic meaning (ERROR, WARNING, CHANGED, etc.).
  • Theme: A mapping from StyleRole to a concrete text styler.
  • DEFAULT_THEME: The built-in theme used by default.
  • style_for_role(role, theme=...): Resolve a styler for a role.

Theme and overrides

Themes support stackable overrides. A Theme consists of:

- `base`: The default mapping from `StyleRole` → styler.
- `overrides`: Optional per-role overrides.

Lookup order:

overrides → base → no-op (identity function)

Example: custom theme with overrides

from yachalk import chalk
from topmark.cli.presentation import Theme, DEFAULT_THEME, style_for_role
from topmark.core.presentation import StyleRole

custom_theme = Theme(
    base=DEFAULT_THEME.base,
    overrides={
        StyleRole.ERROR: chalk.bg_red.white.bold,
        StyleRole.WARNING: chalk.yellow,
    },
)

styler = style_for_role(StyleRole.ERROR, theme=custom_theme)
print(styler("Something went wrong"))

Stacking overrides

Overrides can be layered by reusing the same base mapping and adding new override dictionaries:

soft_theme = Theme(
    base=DEFAULT_THEME.base,
    overrides={
        StyleRole.CHANGED: chalk.cyan,
    },
)

minimal_theme = Theme(
    base=soft_theme.base,
    overrides={
        StyleRole.ERROR: chalk.red,
    },
)

This design keeps styling flexible while maintaining a single semantic source of truth(StyleRole) across the application.

TextStyler module-attribute

TextStyler = Callable[[str], str]

Callable which styles a str.

Theme dataclass

Theme(*, base, overrides=None)

Theme for mapping StyleRole values to concrete terminal stylers.

A theme consists of a required base mapping plus optional per-role overrides.

Lookups are performed in this order

1) overrides (if provided) 2) base mapping 3) _noop fallback

This keeps the default styling stable while allowing caller-controlled theming (e.g., alternate palettes, accessibility themes, tests).

Attributes:

Name Type Description
base Mapping[StyleRole, TextStyler]

Base mapping from StyleRole to a TextStyler.

overrides Mapping[StyleRole, TextStyler] | None

Optional role-specific overrides.

styler_for

styler_for(role)

Return the concrete TextStyler for a semantic role.

Source code in src/topmark/cli/presentation.py
def styler_for(self, role: StyleRole) -> TextStyler:
    """Return the concrete `TextStyler` for a semantic role."""
    if self.overrides is not None and role in self.overrides:
        return self.overrides[role]
    return self.base.get(role, no_style_for_role)

no_style_for_role

no_style_for_role(s)

Return string verbatim (no-op).

Source code in src/topmark/cli/presentation.py
def no_style_for_role(s: str) -> str:
    """Return string verbatim (no-op)."""
    return s

style_for_role

style_for_role(role, *, styled=True, theme=DEFAULT_THEME)

Return a string styler for the given semantic StyleRole.

This function maps core semantic roles onto concrete terminal styling. It is intentionally CLI-only (depends on yachalk).

Callers should still gate styling themselves when color is disabled and fall back to a no-op.

Parameters:

Name Type Description Default
role StyleRole

The semantic style role to map.

required
styled bool

Whether to render styled.

True
theme Theme

Theme used to resolve the concrete TextStyler.

DEFAULT_THEME

Returns:

Type Description
TextStyler

A callable that styles a string.

Source code in src/topmark/cli/presentation.py
def style_for_role(
    role: StyleRole,
    *,
    styled: bool = True,
    theme: Theme = DEFAULT_THEME,
) -> TextStyler:
    """Return a string styler for the given semantic `StyleRole`.

    This function maps core semantic roles onto concrete terminal styling.
    It is intentionally CLI-only (depends on `yachalk`).

    Callers should still gate styling themselves when color is disabled and
    fall back to a no-op.

    Args:
        role: The semantic style role to map.
        styled: Whether to render styled.
        theme: Theme used to resolve the concrete `TextStyler`.

    Returns:
        A callable that styles a string.
    """
    return theme.styler_for(role) if styled else no_style_for_role

maybe_style

maybe_style(text, *, styler, styled)

Conditionally apply a styling function.

This is a tiny helper used by TEXT emitters to avoid scattering if color: checks throughout rendering code.

Parameters:

Name Type Description Default
text str

Input text to render.

required
styler TextStyler

Callable that applies styling to a string (for example, a chalk.* function).

required
styled bool

When False, return text unchanged.

required

Returns:

Type Description
str

Styled text when enabled; otherwise the original text.

Source code in src/topmark/cli/presentation.py
def maybe_style(
    text: str,
    *,
    styler: TextStyler,
    styled: bool,
) -> str:
    """Conditionally apply a styling function.

    This is a tiny helper used by TEXT emitters to avoid scattering `if color:` checks throughout
    rendering code.

    Args:
        text: Input text to render.
        styler: Callable that applies styling to a string (for example, a `chalk.*` function).
        styled: When False, return `text` unchanged.

    Returns:
        Styled text when enabled; otherwise the original `text`.
    """
    return styler(text) if styled else text