Files
magnific/magnific_cli/utils/console.py
T
valknar 941fd14ccf refactor: rename project from Freepik to Magnific
Rename all identifiers, strings, file names, env vars, CLI entry point,
ASCII banner, and API endpoint to reflect the company rebrand.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:06:16 +02:00

214 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Rich console singleton, theme, banner, and all display helpers."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
import rich.box
from rich.align import Align
from rich.columns import Columns
from rich.console import Console
from rich.panel import Panel
from rich.rule import Rule
from rich.syntax import Syntax
from rich.table import Table
from rich.text import Text
from rich.theme import Theme
MAGNIFIC_THEME = Theme(
{
"info": "bold cyan",
"success": "bold green",
"warning": "bold yellow",
"error": "bold red",
"model": "bold magenta",
"taskid": "bold blue",
"path": "bold white underline",
"dim.label": "dim white",
"highlight": "bold magenta",
"brand": "bold magenta",
}
)
console = Console(theme=MAGNIFIC_THEME, highlight=True)
err_console = Console(stderr=True, theme=MAGNIFIC_THEME)
BANNER = """\
[bold magenta]███╗ ███╗ █████╗ ██████╗ ███╗ ██╗██╗███████╗██╗ ██████╗ [/bold magenta]
[bold magenta]████╗ ████║██╔══██╗██╔════╝ ████╗ ██║██║██╔════╝██║██╔════╝ [/bold magenta]
[bold magenta]██╔████╔██║███████║██║ ███╗██╔██╗ ██║██║█████╗ ██║██║ [/bold magenta]
[bold magenta]██║╚██╔╝██║██╔══██║██║ ██║██║╚██╗██║██║██╔══╝ ██║██║ [/bold magenta]
[bold magenta]██║ ╚═╝ ██║██║ ██║╚██████╔╝██║ ╚████║██║██║ ██║╚██████╗ [/bold magenta]
[bold magenta]╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚═════╝[/bold magenta]
[dim] AI Media Generation CLI • v0.1.0[/dim]"""
def print_banner() -> None:
console.print()
console.print(Align.center(Text.from_markup(BANNER)))
console.print(Rule(style="magenta dim"))
console.print()
@dataclass
class GenerationResult:
task_id: str
model: str
output_path: Path
width: Optional[int] = None
height: Optional[int] = None
seed: Optional[int] = None
duration: Optional[str] = None
task_type: str = "image"
def print_result(result: GenerationResult) -> None:
table = Table(
show_header=False,
box=rich.box.SIMPLE,
padding=(0, 1),
show_edge=False,
)
table.add_column(style="dim.label", width=16, no_wrap=True)
table.add_column(style="bold", overflow="fold")
table.add_row("Model", f"[model]{result.model}[/model]")
table.add_row("Task ID", f"[taskid]{result.task_id[:16]}…[/taskid]")
if result.width and result.height:
table.add_row("Dimensions", f"{result.width} × {result.height} px")
if result.duration:
table.add_row("Duration", f"{result.duration}s")
if result.seed is not None:
table.add_row("Seed", str(result.seed))
else:
table.add_row("Seed", "[dim]random[/dim]")
table.add_row("Saved to", f"[path]{result.output_path}[/path]")
title_map = {
"image": "[success] Image Generated [/success]",
"video": "[success] Video Generated [/success]",
"upscale-image": "[success] Image Upscaled [/success]",
"upscale-video": "[success] Video Upscaled [/success]",
"icon": "[success] Icon Generated [/success]",
"expand": "[success] Image Expanded [/success]",
"describe": "[success] Image Described [/success]",
}
title = title_map.get(result.task_type, "[success] Done [/success]")
console.print(Panel(table, title=title, border_style="green", padding=(1, 2)))
def print_describe_result(task_id: str, prompt_text: str, output_path: Optional[Path] = None) -> None:
content = Text(prompt_text, style="italic")
footer = ""
if output_path:
footer = f"\n\n[dim]Saved to:[/dim] [path]{output_path}[/path]"
console.print(
Panel(
Text.from_markup(f"{prompt_text}{footer}"),
title="[success] Image Description [/success]",
subtitle=f"[dim]Task: {task_id[:16]}…[/dim]",
border_style="green",
padding=(1, 2),
)
)
def print_no_wait(task_id: str, task_type: str, model: str) -> None:
console.print(
Panel(
Text.from_markup(
f"[info]Task submitted successfully.[/info]\n\n"
f"[dim.label]Task ID:[/dim.label] [taskid]{task_id}[/taskid]\n"
f"[dim.label]Type:[/dim.label] {task_type}\n"
f"[dim.label]Model:[/dim.label] [model]{model}[/model]\n\n"
f"[dim]The task is processing asynchronously. Results will be available\n"
f"on the Magnific dashboard when complete.[/dim]"
),
title="⏳ Task Queued",
border_style="cyan",
padding=(1, 2),
)
)
def print_error(message: str, hint: Optional[str] = None) -> None:
body = f"[error]{message}[/error]"
if hint:
body += f"\n\n[dim]Hint:[/dim] {hint}"
err_console.print(
Panel(
Text.from_markup(body),
title="[error] Error [/error]",
border_style="red",
padding=(1, 2),
)
)
def print_warning(message: str) -> None:
console.print(
Panel(
Text.from_markup(f"[warning]{message}[/warning]"),
title="[warning] Warning [/warning]",
border_style="yellow",
padding=(0, 2),
)
)
def print_config_table(config_dict: dict, masked_keys: set[str] | None = None) -> None:
masked_keys = masked_keys or {"api_key"}
table = Table(
title="[brand]Magnific CLI Configuration[/brand]",
show_header=True,
header_style="bold magenta",
border_style="magenta",
box=rich.box.ROUNDED,
padding=(0, 1),
)
table.add_column("Key", style="dim.label", width=28)
table.add_column("Value", style="bold", overflow="fold")
for key, value in config_dict.items():
if key in masked_keys and value:
display = f"[dim]{'*' * 8}{str(value)[-4:]}[/dim]" if len(str(value)) > 4 else "[dim]****[/dim]"
elif value is None or value == "":
display = "[dim]not set[/dim]"
else:
display = str(value)
table.add_row(key, display)
console.print(table)
def print_config_toml(config_dict: dict, masked_keys: set[str] | None = None) -> None:
masked_keys = masked_keys or {"api_key"}
lines = []
for key, value in config_dict.items():
if key in masked_keys:
continue
if value is None:
continue
if isinstance(value, str):
lines.append(f'{key} = "{value}"')
elif isinstance(value, bool):
lines.append(f"{key} = {str(value).lower()}")
else:
lines.append(f"{key} = {value}")
toml_str = "\n".join(lines)
console.print(
Panel(
Syntax(toml_str, "toml", theme="monokai", background_color="default"),
title="[brand]~/.config/magnific-cli/config.toml[/brand]",
border_style="magenta",
padding=(1, 2),
)
)