"""Micron emitter — read CharGrid and output Micron markup with style tags. Scans each line left-to-right, tracks style state, and opens/closes Micron format codes at style transitions. Box-drawing characters pass through as literal text. Micron format reference: `!bold`! `*italic`* `_underline`_ `Fhex text`f `Bhex text`b `c center`a `r right`a [label`dest] [label`dest.mu] >H1 >>H2 >>>H3 """ from __future__ import annotations from uframe.grid import CharGrid, CellStyle from uframe.ir import Page, Heading, HeadingLevel def _emit_style_open(style: CellStyle) -> str: """Emit Micron opening tags for a style.""" tags: list[str] = [] if style.bold: tags.append("`!") if style.italic: tags.append("`*") if style.underline: tags.append("`_") if style.fg: tags.append(f"`F{style.fg}") if style.bg: tags.append(f"`B{style.bg}") return "".join(tags) def _emit_style_close(style: CellStyle) -> str: """Emit Micron closing tags for a style (in reverse order).""" tags: list[str] = [] if style.bg: tags.append("`b") if style.fg: tags.append("`f") if style.underline: tags.append("`_") if style.italic: tags.append("`*") if style.bold: tags.append("`!") return "".join(tags) _EMPTY_STYLE = CellStyle() def emit_micron(grid: CharGrid, page_title: str = "") -> str: """Emit the CharGrid as Micron markup.""" lines: list[str] = [] for row in range(grid.height): line_parts: list[str] = [] cur_style = _EMPTY_STYLE in_link: str | None = None for col in range(grid.width): cell = grid.cells[row][col] ch = cell.char style = cell.style link = cell.link # Handle link transitions if link != in_link: if in_link is not None: # Close previous link: [label`dest] line_parts.append(f"`{in_link}]") if link is not None: # Close any open style before link if cur_style != _EMPTY_STYLE: line_parts.append(_emit_style_close(cur_style)) cur_style = _EMPTY_STYLE # Open new link — backtick enters formatting mode # where the parser recognizes `[` as link start line_parts.append("`[") in_link = link # Handle style transitions (not inside links — links handle their own style) if link is None and style != cur_style: # Close previous style if cur_style != _EMPTY_STYLE: line_parts.append(_emit_style_close(cur_style)) # Open new style if style != _EMPTY_STYLE: line_parts.append(_emit_style_open(style)) cur_style = style line_parts.append(ch) # Close any trailing link: [label`dest] if in_link is not None: line_parts.append(f"`{in_link}]") in_link = None # Close any trailing style if cur_style != _EMPTY_STYLE: line_parts.append(_emit_style_close(cur_style)) line = "".join(line_parts).rstrip() lines.append(line) # Strip trailing blank lines while lines and not lines[-1]: lines.pop() return "\n".join(lines)