move and rename module
This commit is contained in:
179
src/mail/config.py
Normal file
179
src/mail/config.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""Configuration system for Mail email reader using Pydantic."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Literal, Optional
|
||||
|
||||
import toml
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TaskBackendConfig(BaseModel):
|
||||
"""Configuration for task management backend."""
|
||||
|
||||
backend: Literal["taskwarrior", "dstask"] = "taskwarrior"
|
||||
taskwarrior_path: str = "task"
|
||||
dstask_path: str = Field(
|
||||
default_factory=lambda: str(Path.home() / ".local" / "bin" / "dstask")
|
||||
)
|
||||
|
||||
|
||||
class EnvelopeDisplayConfig(BaseModel):
|
||||
"""Configuration for envelope list item rendering."""
|
||||
|
||||
# Sender display
|
||||
max_sender_length: int = 25
|
||||
|
||||
# Date/time display
|
||||
date_format: str = "%m/%d"
|
||||
time_format: str = "%H:%M"
|
||||
show_date: bool = True
|
||||
show_time: bool = True
|
||||
|
||||
# Grouping
|
||||
group_by: Literal["relative", "absolute"] = "relative"
|
||||
# relative: "Today", "Yesterday", "This Week", etc.
|
||||
# absolute: "December 2025", "November 2025", etc.
|
||||
|
||||
# Layout
|
||||
lines: Literal[2, 3] = 2
|
||||
# 2: sender/date on line 1, subject on line 2
|
||||
# 3: sender/date on line 1, subject on line 2, preview on line 3
|
||||
|
||||
show_checkbox: bool = True
|
||||
show_preview: bool = False # Only used when lines=3
|
||||
|
||||
# NerdFont icons for status
|
||||
icon_unread: str = "\uf0e0" # nf-fa-envelope (filled)
|
||||
icon_read: str = "\uf2b6" # nf-fa-envelope_open (open)
|
||||
icon_flagged: str = "\uf024" # nf-fa-flag
|
||||
icon_attachment: str = "\uf0c6" # nf-fa-paperclip
|
||||
|
||||
|
||||
class KeybindingsConfig(BaseModel):
|
||||
"""Keybinding customization."""
|
||||
|
||||
next_message: str = "j"
|
||||
prev_message: str = "k"
|
||||
delete: str = "#"
|
||||
archive: str = "e"
|
||||
open_by_id: str = "o"
|
||||
quit: str = "q"
|
||||
toggle_header: str = "h"
|
||||
create_task: str = "t"
|
||||
reload: str = "%"
|
||||
toggle_sort: str = "s"
|
||||
toggle_selection: str = "space"
|
||||
clear_selection: str = "escape"
|
||||
scroll_page_down: str = "pagedown"
|
||||
scroll_page_down: str = "space"
|
||||
scroll_page_up: str = "b"
|
||||
toggle_main_content: str = "w"
|
||||
open_links: str = "l"
|
||||
toggle_view_mode: str = "m"
|
||||
|
||||
|
||||
class ContentDisplayConfig(BaseModel):
|
||||
"""Configuration for message content display."""
|
||||
|
||||
# View mode: "markdown" for pretty rendering, "html" for raw/plain display
|
||||
default_view_mode: Literal["markdown", "html"] = "markdown"
|
||||
|
||||
|
||||
class LinkPanelConfig(BaseModel):
|
||||
"""Configuration for the link panel."""
|
||||
|
||||
# Whether to close the panel after opening a link
|
||||
close_on_open: bool = False
|
||||
|
||||
|
||||
class MailOperationsConfig(BaseModel):
|
||||
"""Configuration for mail operations."""
|
||||
|
||||
# Folder to move messages to when archiving
|
||||
archive_folder: str = "Archive"
|
||||
|
||||
|
||||
class ThemeConfig(BaseModel):
|
||||
"""Theme/appearance settings."""
|
||||
|
||||
theme_name: str = "monokai"
|
||||
|
||||
|
||||
class MailAppConfig(BaseModel):
|
||||
"""Main configuration for Mail email reader."""
|
||||
|
||||
task: TaskBackendConfig = Field(default_factory=TaskBackendConfig)
|
||||
envelope_display: EnvelopeDisplayConfig = Field(
|
||||
default_factory=EnvelopeDisplayConfig
|
||||
)
|
||||
content_display: ContentDisplayConfig = Field(default_factory=ContentDisplayConfig)
|
||||
link_panel: LinkPanelConfig = Field(default_factory=LinkPanelConfig)
|
||||
mail: MailOperationsConfig = Field(default_factory=MailOperationsConfig)
|
||||
keybindings: KeybindingsConfig = Field(default_factory=KeybindingsConfig)
|
||||
theme: ThemeConfig = Field(default_factory=ThemeConfig)
|
||||
|
||||
@classmethod
|
||||
def get_config_path(cls) -> Path:
|
||||
"""Get the path to the config file."""
|
||||
# Check environment variable first
|
||||
env_path = os.getenv("LUK_MAIL_CONFIG")
|
||||
if env_path:
|
||||
return Path(env_path)
|
||||
|
||||
# Default to ~/.config/luk/mail.toml
|
||||
return Path.home() / ".config" / "luk" / "mail.toml"
|
||||
|
||||
@classmethod
|
||||
def load(cls, config_path: Optional[Path] = None) -> "MailAppConfig":
|
||||
"""Load config from TOML file with defaults for missing values."""
|
||||
if config_path is None:
|
||||
config_path = cls.get_config_path()
|
||||
|
||||
if config_path.exists():
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
data = toml.load(f)
|
||||
logger.info(f"Loaded config from {config_path}")
|
||||
return cls.model_validate(data)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error loading config from {config_path}: {e}")
|
||||
logger.warning("Using default configuration")
|
||||
return cls()
|
||||
else:
|
||||
logger.info(f"No config file at {config_path}, using defaults")
|
||||
return cls()
|
||||
|
||||
def save(self, config_path: Optional[Path] = None) -> None:
|
||||
"""Save current config to TOML file."""
|
||||
if config_path is None:
|
||||
config_path = self.get_config_path()
|
||||
|
||||
# Ensure parent directory exists
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(config_path, "w") as f:
|
||||
toml.dump(self.model_dump(), f)
|
||||
logger.info(f"Saved config to {config_path}")
|
||||
|
||||
|
||||
# Global config instance (lazy-loaded)
|
||||
_config: Optional[MailAppConfig] = None
|
||||
|
||||
|
||||
def get_config() -> MailAppConfig:
|
||||
"""Get the global config instance, loading it if necessary."""
|
||||
global _config
|
||||
if _config is None:
|
||||
_config = MailAppConfig.load()
|
||||
return _config
|
||||
|
||||
|
||||
def reload_config() -> MailAppConfig:
|
||||
"""Force reload of the config from disk."""
|
||||
global _config
|
||||
_config = MailAppConfig.load()
|
||||
return _config
|
||||
Reference in New Issue
Block a user