feat: added a twist
This commit is contained in:
121
backend/uframe/emit_micron.py
Normal file
121
backend/uframe/emit_micron.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""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.
|
||||
|
||||
Args:
|
||||
grid: the rendered character grid
|
||||
page_title: optional page title for a leading >Title line
|
||||
|
||||
Returns:
|
||||
Micron source string
|
||||
"""
|
||||
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
|
||||
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)
|
||||
Reference in New Issue
Block a user