"""µFrame Intermediate Representation — node types for the IR tree. Every .uf source parses into a tree of IRNode subclasses. The layout engine measures, positions, and paints these nodes into a CharGrid. """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum, auto from typing import Any # --------------------------------------------------------------------------- # Enums # --------------------------------------------------------------------------- class BorderWeight(Enum): LIGHT = auto() HEAVY = auto() DOUBLE = auto() ROUNDED = auto() class HeadingLevel(Enum): H1 = 1 H2 = 2 H3 = 3 class DividerStyle(Enum): LIGHT = auto() HEAVY = auto() DOUBLE = auto() DASH = auto() DOT = auto() class ListStyle(Enum): BULLET = auto() DASH = auto() NUMBER = auto() ARROW = auto() class Align(Enum): LEFT = auto() CENTER = auto() RIGHT = auto() # --------------------------------------------------------------------------- # Style # --------------------------------------------------------------------------- @dataclass class Style: """Visual style attached to any node.""" fg: str | None = None # 3-digit hex bg: str | None = None # 3-digit hex bold: bool = False italic: bool = False underline: bool = False align: Align = Align.LEFT # --------------------------------------------------------------------------- # Inline text spans (parsed from @modifier{} syntax in text content) # --------------------------------------------------------------------------- @dataclass class TextSpan: """A run of text with optional inline styling.""" text: str bold: bool = False italic: bool = False underline: bool = False fg: str | None = None bg: str | None = None # --------------------------------------------------------------------------- # Layout rect — assigned by the layout engine # --------------------------------------------------------------------------- @dataclass class Rect: x: int = 0 y: int = 0 w: int = 0 h: int = 0 # --------------------------------------------------------------------------- # Base node # --------------------------------------------------------------------------- @dataclass class IRNode: """Base class for all IR nodes.""" children: list[IRNode] = field(default_factory=list) style: Style = field(default_factory=Style) rect: Rect = field(default_factory=Rect) source_line: int | None = None # Set by measure pass min_width: int = 0 min_height: int = 0 pref_width: int = 0 pref_height: int = 0 # --------------------------------------------------------------------------- # Layout nodes # --------------------------------------------------------------------------- @dataclass class Page(IRNode): """Root container. One per .uf file.""" title: str = "" width: int = 64 @dataclass class Box(IRNode): """Bordered panel with optional title.""" title: str = "" weight: BorderWeight = BorderWeight.LIGHT @dataclass class Row(IRNode): """Horizontal layout — children split available width.""" gap: int = 1 @dataclass class Col(IRNode): """Explicit column in a row. Width in chars or None (auto).""" col_width: int | None = None @dataclass class Spacer(IRNode): """Vertical whitespace.""" lines: int = 1 @dataclass class Pad(IRNode): """Inner margin for a container.""" top: int = 0 right: int = 0 bottom: int = 0 left: int = 0 # --------------------------------------------------------------------------- # Content nodes # --------------------------------------------------------------------------- @dataclass class Heading(IRNode): """Styled heading (levels 1–3).""" level: HeadingLevel = HeadingLevel.H1 text: str = "" @dataclass class Text(IRNode): """Text content with optional @modifier{} inline styles.""" content: str = "" spans: list[TextSpan] = field(default_factory=list) @dataclass class Label(IRNode): """Aligned key-value pair.""" key: str = "" value: str = "" @dataclass class Divider(IRNode): """Full-width horizontal rule.""" divider_style: DividerStyle = DividerStyle.LIGHT @dataclass class Link(IRNode): """Clickable link — visual in ASCII, interactive in Micron.""" display: str = "" dest: str = "" @dataclass class ListNode(IRNode): """Bulleted or numbered list.""" list_style: ListStyle = ListStyle.BULLET @dataclass class ListItem(IRNode): """Single entry in a ListNode.""" content: str = "" # --------------------------------------------------------------------------- # Placeholder nodes for future phases # --------------------------------------------------------------------------- @dataclass class Gauge(IRNode): """Horizontal bar chart (Phase 4).""" label: str = "" value: float = 0 max_val: float = 100 bar_width: int = 28 warn: float | None = None crit: float | None = None @dataclass class Sparkline(IRNode): """Braille sparkline (Phase 4).""" label: str = "" values: list[float] = field(default_factory=list) spark_width: int = 20 @dataclass class Status(IRNode): """Status indicator (Phase 4).""" label: str = "" state: str = "unknown" @dataclass class Table(IRNode): """Box-drawn table (Phase 4).""" title: str = "" columns: list[tuple[str, int]] = field(default_factory=list) # (name, width) rows: list[list[str]] = field(default_factory=list)