Private/PresentationNativeImages.ps1

function New-TerminalSpectreRenderOptions {
    param(
        [Parameter(Mandatory)][int]$Width,
        [Parameter(Mandatory)][int]$Height,
        [Parameter(Mandatory)][TerminalSlides.Schema.V1.TerminalCapability]$Capability
    )

    $capabilities = [TerminalSpectreCapabilities]::new()
    $capabilities.Ansi = $Capability.AnsiSupport
    $capabilities.Interactive = $false
    $capabilities.Unicode = $Capability.UnicodeSupport
    $capabilities.Links = $false
    $capabilities.Legacy = $false
    $capabilities.IsTerminal = $true
    $capabilities.ColorSystem = if ($Capability.TrueColorSupport) {
        [Spectre.Console.ColorSystem]::TrueColor
    }
    elseif ($Capability.Color256Support) {
        [Spectre.Console.ColorSystem]::EightBit
    }
    else {
        [Spectre.Console.ColorSystem]::NoColors
    }

    return [Spectre.Console.Rendering.RenderOptions]::new(
        $capabilities,
        [Spectre.Console.Size]::new([Math]::Max(1, $Width), [Math]::Max(1, $Height))
    )
}

function Get-TerminalSixelFitWidth {
    param(
        [Parameter(Mandatory)][int]$ImageWidth,
        [Parameter(Mandatory)][int]$ImageHeight,
        [Parameter(Mandatory)][int]$AvailableWidth,
        [Parameter(Mandatory)][int]$AvailableHeight,
        [object]$CellSize
    )

    if (-not $CellSize) {
        $CellSize = [PwshSpectreConsole.Terminal.Compatibility]::GetCellSize()
    }
    if ($ImageWidth -le 0 -or $ImageHeight -le 0 -or
        $CellSize.PixelWidth -le 0 -or $CellSize.PixelHeight -le 0) {
        throw 'Sixel image and terminal cell dimensions must be positive.'
    }

    $heightLimitedPixelWidth = [Math]::Floor(
        ([double]$AvailableHeight * $CellSize.PixelHeight * $ImageWidth) / $ImageHeight
    )
    $heightLimitedCellWidth = [Math]::Floor(
        $heightLimitedPixelWidth / $CellSize.PixelWidth
    )
    $fitWidth = [int][Math]::Min($AvailableWidth, $heightLimitedCellWidth)
    if ($fitWidth -lt 1) { return 0 }
    return $fitWidth
}

function Get-TerminalNativeImageOverlay {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][TerminalSlides.Schema.V1.TerminalPresentation]$Presentation,
        [Parameter(Mandatory)][int]$SlideIndex,
        [Parameter(Mandatory)][int]$RevealStep,
        [Parameter(Mandatory)][ValidateSet('Slide', 'Overview', 'Help', 'Blank')][string]$DisplayMode,
        [Parameter(Mandatory)][TerminalSlides.Schema.V1.TerminalCapability]$Capability,
        [object]$LayoutPlan
    )

    if ($DisplayMode -ne 'Slide') { return }

    $plan = if ($LayoutPlan) {
        $LayoutPlan
    }
    else {
        Get-TerminalSlideLayoutPlan -Presentation $Presentation -SlideIndex $SlideIndex `
            -RevealStep $RevealStep -Capability $Capability
    }
    $overlays = [System.Text.StringBuilder]::new()

    foreach ($placement in $plan.Placements) {
        $element = $placement.Element
        if ($element.Kind -ne [TerminalSlides.Schema.V1.ElementKind]::Image) { continue }

        $availableWidth = [Math]::Max(1, $placement.Region.Width - ($element.Padding * 2))
        $availableHeight = [Math]::Max(
            1,
            ($placement.Region.Y + $placement.Region.Height) - $placement.StartY
        )
        try {
            $resolvedPath = Resolve-TerminalImagePath -Element $element
            if (-not (Test-Path -LiteralPath $resolvedPath -PathType Leaf)) {
                throw "Image file '$resolvedPath' was not found."
            }

            $image = Get-SpectreImage -ImagePath $resolvedPath -MaxWidth $availableWidth `
                -Format Sixel -ErrorAction Stop
            $fitWidth = Get-TerminalSixelFitWidth -ImageWidth $image.Width `
                -ImageHeight $image.Height -AvailableWidth $availableWidth `
                -AvailableHeight $availableHeight
            if ($fitWidth -lt 1) {
                throw 'The image aspect ratio cannot fit inside the available terminal cells.'
            }
            if ($image.PSObject.Properties.Name -contains 'MaxWidth') {
                $image.MaxWidth = $fitWidth
            }
            $options = New-TerminalSpectreRenderOptions -Width $fitWidth `
                -Height $availableHeight -Capability $Capability
            $rawImage = (@($image.Render($options, $fitWidth)).Text -join '').TrimEnd("`r", "`n")
            if (-not $rawImage.Contains("`eP")) {
                throw 'The image renderer did not return a Sixel control stream.'
            }

            $row = $placement.StartY + 1
            $column = $placement.Region.X + $element.Padding + 1
            [void]$overlays.Append("`e[$row;${column}H")
            [void]$overlays.Append($rawImage)
        }
        catch {
            Write-Verbose "Sixel image renderer error: $($_.Exception.Message)"
            Write-Warning "Sixel rendering is unavailable for '$($element.Payload.Path)'; keeping the existing image fallback."
        }
    }

    if ($overlays.Length -eq 0) { return }
    [void]$overlays.Append("`e[H")
    return $overlays.ToString()
}