Private/Rendering/ConvertTo-AnsiOutput.ps1

function ConvertTo-AnsiOutput {
    <#
    .SYNOPSIS
        Renders a fully-measured view tree as a complete ANSI escape sequence string.

    .DESCRIPTION
        Walks the measured view tree produced by Measure-ElmViewTree. For each leaf
        Text node, emits a cursor-position sequence (ESC[{Y+1};{X+1}H) followed by
        the styled content from Apply-ElmStyle. The output is prefixed with a
        clear-screen (ESC[2J) sequence.

        Does not emit hide/show cursor sequences - cursor visibility is managed by
        the event loop's try/finally block. Use this for the initial render or after
        a FullRedraw patch. For incremental updates, use ConvertTo-AnsiPatch instead.

    .PARAMETER Root
        The root of a measured view tree (output of Measure-ElmViewTree).

    .OUTPUTS
        [string] - A single ANSI escape sequence string ready for Console output.

    .EXAMPLE
        $tree = New-ElmBox -Children @(New-ElmText -Content 'Hello') -Width 'Fill'
        $measured = Measure-ElmViewTree -Root $tree -TermWidth 80 -TermHeight 24
        $ansi = ConvertTo-AnsiOutput -Root $measured
        [Console]::Out.Write($ansi)

    .NOTES
        Caller is responsible for flushing the output queue. Box nodes are traversed
        but do not themselves emit any ANSI sequences in the current implementation.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [PSCustomObject]$Root
    )

    $esc = [char]27
    $sb  = [System.Text.StringBuilder]::new()

    [void]$sb.Append("$esc[2J")

    $stack = [System.Collections.Stack]::new()
    $stack.Push($Root)

    while ($stack.Count -gt 0) {
        $node = $stack.Pop()

        if ($node.Type -eq 'Text') {
            $row     = $node.Y + 1
            $col     = $node.X + 1
            $content = Apply-ElmStyle -Content $node.Content -Style $node.Style -Width $node.Width
            [void]$sb.Append("$esc[$row;${col}H$content")
        } elseif ($node.Type -eq 'Box') {
            for ($i = $node.Children.Count - 1; $i -ge 0; $i--) {
                $stack.Push($node.Children[$i])
            }
        }
    }

    return $sb.ToString()
}