Source code for mtf.interface
"""Human interface abstraction and CLI implementation."""
from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from pathlib import Path
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from rich.prompt import Confirm, Prompt
[docs]
class HumanInterface(ABC):
"""Abstract interface for human interaction."""
[docs]
@abstractmethod
async def show(self, content: str, title: str = "") -> None: ...
[docs]
@abstractmethod
async def ask(self, prompt: str) -> str: ...
[docs]
@abstractmethod
async def confirm(self, prompt: str) -> bool: ...
[docs]
async def ask_for_files(self) -> list[str]:
"""Prompt the user to optionally provide input file paths (images or PDFs).
Returns a (possibly empty) list of valid file path strings.
Default implementation returns an empty list; override in subclasses
that support interactive input.
"""
return []
[docs]
class CLIInterface(HumanInterface):
"""Rich-based CLI interface."""
def __init__(self) -> None:
self._console = Console()
[docs]
async def show(self, content: str, title: str = "") -> None:
panel = Panel(Markdown(content), title=title or "MTF", border_style="blue")
await asyncio.to_thread(self._console.print, panel)
[docs]
async def ask(self, prompt: str) -> str:
return await asyncio.to_thread(Prompt.ask, f"[cyan]{prompt}[/cyan]")
[docs]
async def confirm(self, prompt: str) -> bool:
return await asyncio.to_thread(Confirm.ask, f"[yellow]{prompt}[/yellow]")
[docs]
async def ask_for_files(self) -> list[str]:
"""Interactively ask the user for optional input file paths (images or PDFs)."""
has_files = await self.confirm(
"Do you have experimental images, plots, or PDF documents to include?"
)
if not has_files:
return []
raw = await self.ask(
"Enter file path(s) separated by spaces (PNG, JPG, GIF, WebP, PDF)"
)
paths: list[str] = []
for token in raw.split():
p = Path(token.strip())
if p.exists():
paths.append(str(p))
else:
await asyncio.to_thread(
self._console.print,
f"[red]Warning: file not found and will be skipped: {p}[/red]",
)
return paths