Skip to content

Config API

The config module manages application configuration.

Overview

Configuration is stored in ~/.config/astronomo/config.toml. The module provides typed access to settings with validation and defaults.

API Reference

config

Configuration management for Astronomo.

This module provides configuration storage with TOML persistence and sensible defaults.

AppearanceConfig dataclass

AppearanceConfig(
    theme: str = "textual-dark",
    syntax_highlighting: bool = True,
    show_emoji: bool = True,
    max_content_width: int = 80,
    show_images: bool = False,
    image_quality: str = "medium",
)

Appearance settings.

Attributes:

Name Type Description
theme str

Textual theme name (e.g., "textual-dark", "nord", "gruvbox")

syntax_highlighting bool

Enable syntax highlighting in preformatted blocks

show_emoji bool

Display emoji characters (False shows text descriptions)

max_content_width int

Maximum width for text content (0 to disable, minimum 40)

show_images bool

Display images inline (requires chafapy optional dependency)

image_quality str

Image rendering quality ("low", "medium", or "high")

from_dict classmethod

from_dict(data: dict[str, Any]) -> Self

Create from dictionary with validation and fallback to defaults.

Source code in src/astronomo/config.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
    """Create from dictionary with validation and fallback to defaults."""
    defaults = cls()
    theme = data.get("theme", defaults.theme)
    # Validate theme - fall back to default if invalid
    if not isinstance(theme, str) or theme not in VALID_THEMES:
        theme = defaults.theme

    syntax_highlighting = data.get(
        "syntax_highlighting", defaults.syntax_highlighting
    )
    if not isinstance(syntax_highlighting, bool):
        syntax_highlighting = defaults.syntax_highlighting

    show_emoji = data.get("show_emoji", defaults.show_emoji)
    if not isinstance(show_emoji, bool):
        show_emoji = defaults.show_emoji

    max_content_width = data.get("max_content_width", defaults.max_content_width)
    if (
        not isinstance(max_content_width, int)
        or max_content_width < 0
        or (max_content_width > 0 and max_content_width < 40)
    ):
        max_content_width = defaults.max_content_width

    show_images = data.get("show_images", defaults.show_images)
    if not isinstance(show_images, bool):
        show_images = defaults.show_images

    image_quality = data.get("image_quality", defaults.image_quality)
    if not isinstance(image_quality, str) or image_quality not in (
        "low",
        "medium",
        "high",
    ):
        image_quality = defaults.image_quality

    return cls(
        theme=theme,
        syntax_highlighting=syntax_highlighting,
        show_emoji=show_emoji,
        max_content_width=max_content_width,
        show_images=show_images,
        image_quality=image_quality,
    )

to_dict

to_dict() -> dict[str, Any]

Convert to dictionary for TOML serialization.

Source code in src/astronomo/config.py
def to_dict(self) -> dict[str, Any]:
    """Convert to dictionary for TOML serialization."""
    return {
        "theme": self.theme,
        "syntax_highlighting": self.syntax_highlighting,
        "show_emoji": self.show_emoji,
        "max_content_width": self.max_content_width,
        "show_images": self.show_images,
        "image_quality": self.image_quality,
    }

BrowsingConfig dataclass

BrowsingConfig(
    home_page: str | None = None,
    timeout: int = 30,
    max_redirects: int = 5,
    identity_prompt: str = "when_ambiguous",
)

Browsing behavior settings.

Attributes:

Name Type Description
home_page str | None

Default home page URL (None if not set)

timeout int

Request timeout in seconds

max_redirects int

Maximum number of redirects to follow

identity_prompt str

When to show identity selection modal

from_dict classmethod

from_dict(data: dict[str, Any]) -> Self

Create from dictionary with validation and fallback to defaults.

Source code in src/astronomo/config.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
    """Create from dictionary with validation and fallback to defaults."""
    defaults = cls()

    home_page = data.get("home_page")
    # Empty string or non-string treated as None (not set)
    if not isinstance(home_page, str) or not home_page.strip():
        home_page = None

    timeout = data.get("timeout", defaults.timeout)
    if not isinstance(timeout, int) or timeout <= 0:
        timeout = defaults.timeout

    max_redirects = data.get("max_redirects", defaults.max_redirects)
    if not isinstance(max_redirects, int) or max_redirects < 0:
        max_redirects = defaults.max_redirects

    identity_prompt = data.get("identity_prompt", defaults.identity_prompt)
    if (
        not isinstance(identity_prompt, str)
        or identity_prompt not in VALID_IDENTITY_PROMPTS
    ):
        identity_prompt = defaults.identity_prompt

    return cls(
        home_page=home_page,
        timeout=timeout,
        max_redirects=max_redirects,
        identity_prompt=identity_prompt,
    )

to_dict

to_dict() -> dict[str, Any]

Convert to dictionary for TOML serialization.

Source code in src/astronomo/config.py
def to_dict(self) -> dict[str, Any]:
    """Convert to dictionary for TOML serialization."""
    data: dict[str, Any] = {
        "timeout": self.timeout,
        "max_redirects": self.max_redirects,
        "identity_prompt": self.identity_prompt,
    }
    if self.home_page is not None:
        data["home_page"] = self.home_page
    return data

Config dataclass

Config(
    appearance: AppearanceConfig = AppearanceConfig(),
    browsing: BrowsingConfig = BrowsingConfig(),
    snapshots: SnapshotsConfig = SnapshotsConfig(),
    mail: MailConfig = MailConfig(),
)

Root configuration container.

Attributes:

Name Type Description
appearance AppearanceConfig

Visual appearance settings

browsing BrowsingConfig

Browsing behavior settings

snapshots SnapshotsConfig

Snapshot settings

mail MailConfig

Mail (GMAP) settings

from_dict classmethod

from_dict(data: dict[str, Any]) -> Self

Create from dictionary with fallback to defaults for missing sections.

Source code in src/astronomo/config.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
    """Create from dictionary with fallback to defaults for missing sections."""
    return cls(
        appearance=AppearanceConfig.from_dict(data.get("appearance", {})),
        browsing=BrowsingConfig.from_dict(data.get("browsing", {})),
        snapshots=SnapshotsConfig.from_dict(data.get("snapshots", {})),
        mail=MailConfig.from_dict(data.get("mail", {})),
    )

to_dict

to_dict() -> dict[str, Any]

Convert to dictionary for TOML serialization.

Source code in src/astronomo/config.py
def to_dict(self) -> dict[str, Any]:
    """Convert to dictionary for TOML serialization."""
    return {
        "appearance": self.appearance.to_dict(),
        "browsing": self.browsing.to_dict(),
        "snapshots": self.snapshots.to_dict(),
        "mail": self.mail.to_dict(),
    }

ConfigManager

ConfigManager(config_path: Path | None = None)

Manages application configuration with TOML persistence.

Provides loading and saving of configuration with automatic creation of default config file on first run.

Parameters:

Name Type Description Default
config_path Path | None

Path to the config file. If None, uses the default location (~/.config/astronomo/config.toml)

None
Source code in src/astronomo/config.py
def __init__(self, config_path: Path | None = None):
    if config_path is not None:
        self.config_path = config_path
        self.config_dir = config_path.parent
    else:
        self.config_dir = Path.home() / ".config" / "astronomo"
        self.config_path = self.config_dir / "config.toml"

    self.config: Config = Config()
    self._load()

home_page property

home_page: str | None

Get the configured home page URL, or None if not set.

identity_prompt property

identity_prompt: str

Get the configured identity prompt behavior.

image_quality property

image_quality: str

Get the configured image quality setting.

max_content_width property

max_content_width: int

Get the configured max content width (0 means disabled).

max_redirects property

max_redirects: int

Get the configured max redirects.

show_emoji property

show_emoji: bool

Get whether emoji should be displayed as-is.

show_images property

show_images: bool

Get whether images should be displayed inline.

snapshots_directory property

snapshots_directory: str | None

Get the configured snapshots directory, or None for default.

syntax_highlighting property

syntax_highlighting: bool

Get whether syntax highlighting is enabled.

theme property

theme: str

Get the configured theme name.

timeout property

timeout: int

Get the configured request timeout.

save

save() -> None

Save current configuration to TOML file.

Note: This will overwrite the file without comments. Use sparingly - prefer the commented template format.

Source code in src/astronomo/config.py
def save(self) -> None:
    """Save current configuration to TOML file.

    Note: This will overwrite the file without comments.
    Use sparingly - prefer the commented template format.
    """
    self._ensure_config_dir()

    data = {
        "version": self.VERSION,
        **self.config.to_dict(),
    }

    with open(self.config_path, "wb") as f:
        tomli_w.dump(data, f)

Configuration File

The default configuration:

[appearance]
theme = "textual-dark"

[browsing]
# home_page = "gemini://geminiprotocol.net/"  # Optional
timeout = 30
max_redirects = 5

Example Usage

from astronomo.config import ConfigManager

# Load configuration (creates default if needed)
config = ConfigManager()

# Access settings
print(config.theme)  # "textual-dark"
print(config.timeout)  # 30
print(config.max_redirects)  # 5

# Use a custom config path
from pathlib import Path
config = ConfigManager(config_path=Path("~/.my-astronomo-config.toml"))

Available Themes

Astronomo uses Textual's built-in themes:

  • textual-dark (default)
  • textual-light
  • textual-ansi
  • nord
  • gruvbox
  • tokyo-night
  • monokai
  • dracula
  • catppuccin-mocha
  • solarized-light