feat: added a twist

This commit is contained in:
2026-04-01 00:53:55 +02:00
parent 0b7deee59e
commit b40c6436cd
76 changed files with 15121 additions and 64 deletions

243
backend/uframe/ir.py Normal file
View File

@@ -0,0 +1,243 @@
"""µ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 13)."""
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)