Public/Show-Deck.ps1

function Show-Deck {
    <#
    .SYNOPSIS
        Displays a Markdown file as an interactive terminal presentation.

    .DESCRIPTION
        Converts a Markdown file into a live terminal-based presentation with rich
        formatting, colors, and ASCII art. Navigate through slides using arrow keys,
        space, or enter.

    .PARAMETER Path
        Path to the Markdown file containing the presentation.

    .PARAMETER Background
        Override the background color from the Markdown frontmatter.
        Accepts Spectre.Console.Color values (e.g., 'Black', 'DarkBlue', 'Grey15').

    .PARAMETER Foreground
        Override the foreground color from the Markdown frontmatter.
        Accepts Spectre.Console.Color values (e.g., 'White', 'Cyan1', 'Yellow').

    .PARAMETER Border
        Override the border color from the Markdown frontmatter.
        Accepts Spectre.Console.Color values (e.g., 'Blue', 'Magenta1', 'Green').

    .EXAMPLE
        Show-Deck -Path ./presentation.md

        Displays the presentation from the specified Markdown file.

    .EXAMPLE
        Show-Deck -Path ./presentation.md -Foreground Cyan1 -Background Black

        Displays the presentation with custom colors.

    .NOTES
        Requires PwshSpectreConsole module for terminal rendering.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$Path,

        [Parameter()]
        [string]$Background,

        [Parameter()]
        [string]$Foreground,

        [Parameter()]
        [string]$Border
    )

    begin {
        Write-Verbose "Starting presentation from: $Path"
        
        # Ensure PwshSpectreConsole is loaded
        Import-DeckDependency
    }

    process {
        try {
            # Parse the markdown file
            $presentation = ConvertFrom-DeckMarkdown -Path $Path
            Write-Verbose "Loaded $($presentation.Slides.Count) slides"

            # Apply parameter overrides to settings
            if ($PSBoundParameters.ContainsKey('Background')) {
                $presentation.Settings.background = $Background
            }
            if ($PSBoundParameters.ContainsKey('Foreground')) {
                $presentation.Settings.foreground = $Foreground
            }
            if ($PSBoundParameters.ContainsKey('Border')) {
                $presentation.Settings.border = $Border
            }

            # Hide cursor during presentation using ANSI escape codes
            Write-Host "`e[?25l" -NoNewline  # Hide cursor

            try {
                # Full navigation loop (Phase 6)
                $currentSlide = 0
                $totalSlides = $presentation.Slides.Count
                $shouldExit = $false
                
                # Track visible bullets per slide
                $visibleBullets = @{}

                while ($true) {
                    # Move cursor to top-left and redraw (no clear to reduce flicker)
                    Write-Host "`e[H" -NoNewline

                $slide = $presentation.Slides[$currentSlide]
                
                # Initialize visible bullets for this slide if not set
                if (-not $visibleBullets.ContainsKey($currentSlide)) {
                    $visibleBullets[$currentSlide] = 0
                }
                
                # Detect slide type based on content
                if ($slide.Content -match '^\s*#\s+.+$' -and $slide.Content -notmatch '\n[^#]') {
                    # Title slide: Only has # heading, no other content
                    Write-Verbose "Rendering title slide $($currentSlide + 1)/$totalSlides"
                    Show-TitleSlide -Slide $slide -Settings $presentation.Settings -IsFirstSlide:($currentSlide -eq 0)
                }
                elseif ($slide.Content -match '^\s*##\s+.+$' -and $slide.Content -notmatch '\n[^#]') {
                    # Section slide: Only has ## heading, no other content
                    Write-Verbose "Rendering section slide $($currentSlide + 1)/$totalSlides"
                    Show-SectionSlide -Slide $slide -Settings $presentation.Settings
                }
                else {
                    # Content slide: May have ### heading or just content
                    Write-Verbose "Rendering content slide $($currentSlide + 1)/$totalSlides with $($visibleBullets[$currentSlide]) bullets"
                    Show-ContentSlide -Slide $slide -Settings $presentation.Settings -VisibleBullets $visibleBullets[$currentSlide]
                }

                # Get user input
                $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
                $action = Get-SlideNavigation -KeyInfo $key

                # Handle help key
                if ($key.Character -eq '?') {
                    Write-Host "`e[H" -NoNewline
                    
                    # Fill screen with blank lines to clear previous content
                    $windowHeight = $Host.UI.RawUI.WindowSize.Height
                    $windowWidth = $Host.UI.RawUI.WindowSize.Width
                    for ($i = 0; $i -lt $windowHeight; $i++) {
                        Write-Host (" " * $windowWidth)
                    }
                    
                    # Move cursor back to top and render help text
                    Write-Host "`e[H" -NoNewline
                    Write-Host "`n Navigation Controls`n" -ForegroundColor Cyan
                    Write-Host " Forward: " -ForegroundColor Gray -NoNewline
                    Write-Host "Right, Down, Space, Enter, n, Page Down" -ForegroundColor White
                    Write-Host " Backward: " -ForegroundColor Gray -NoNewline
                    Write-Host "Left, Up, Backspace, p, Page Up" -ForegroundColor White
                    Write-Host " Exit: " -ForegroundColor Gray -NoNewline
                    Write-Host "Esc, Ctrl+C" -ForegroundColor White
                    Write-Host "`n Press any key to return to presentation..." -ForegroundColor DarkGray
                    $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
                    continue
                }

                # Handle navigation
                switch ($action) {
                    'Next' {
                        # Check if current slide has hidden bullets
                        if ($slide.PSObject.Properties['TotalProgressiveBullets'] -and 
                            $visibleBullets[$currentSlide] -lt $slide.TotalProgressiveBullets) {
                            # Reveal next bullet
                            $visibleBullets[$currentSlide]++
                            Write-Verbose "Revealed bullet $($visibleBullets[$currentSlide])/$($slide.TotalProgressiveBullets)"
                        }
                        elseif ($currentSlide -lt $totalSlides - 1) {
                            # Move to next slide and reset bullets to 0
                            $currentSlide++
                            $visibleBullets[$currentSlide] = 0
                            Write-Verbose "Advanced to slide $($currentSlide + 1)"
                        }
                        else {
                            # On last slide, trying to go forward shows end screen
                            Write-Host "`e[H" -NoNewline
                            
                            # Center text vertically and horizontally
                            $windowHeight = $Host.UI.RawUI.WindowSize.Height
                            $windowWidth = $Host.UI.RawUI.WindowSize.Width
                            
                            $line1 = "End of Deck"
                            $line2 = "Press ESC to Exit"
                            
                            # Calculate vertical position (center)
                            $verticalPadding = [math]::Floor($windowHeight / 2) - 1
                            
                            # Fill screen with blank lines to clear previous content
                            for ($i = 0; $i -lt $windowHeight; $i++) {
                                Write-Host (" " * $windowWidth)
                            }
                            
                            # Move cursor back to top and render centered text
                            Write-Host "`e[H" -NoNewline
                            
                            # Print vertical padding
                            Write-Host ("`n" * $verticalPadding) -NoNewline
                            
                            # Print first line centered
                            $padding1 = [math]::Max(0, [math]::Floor(($windowWidth - $line1.Length) / 2))
                            Write-Host (" " * $padding1) -NoNewline
                            Write-Host $line1 -ForegroundColor White
                            
                            # Blank line between
                            Write-Host ""
                            
                            # Print second line centered
                            $padding2 = [math]::Max(0, [math]::Floor(($windowWidth - $line2.Length) / 2))
                            Write-Host (" " * $padding2) -NoNewline
                            Write-Host $line2 -ForegroundColor Gray
                            
                            # Wait for ESC or backward navigation
                            do {
                                $exitKey = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
                                $exitAction = Get-SlideNavigation -KeyInfo $exitKey
                                
                                if ($exitAction -eq 'Previous') {
                                    # Go back to last slide
                                    Write-Verbose "Returning to last slide from end screen"
                                    break
                                }
                            } while ($exitKey.VirtualKeyCode -ne 27)  # 27 = ESC
                            
                            # If ESC was pressed, exit; if backward nav, continue loop
                            if ($exitKey.VirtualKeyCode -eq 27) {
                                Write-Verbose "User exited from end screen"
                                $shouldExit = $true
                                break
                            }
                        }
                    }
                    'Previous' {
                        # Check if current slide has revealed bullets that can be hidden
                        if ($slide.PSObject.Properties['TotalProgressiveBullets'] -and 
                            $visibleBullets[$currentSlide] -gt 0) {
                            # Hide last bullet
                            $visibleBullets[$currentSlide]--
                            Write-Verbose "Hid bullet, now showing $($visibleBullets[$currentSlide])/$($slide.TotalProgressiveBullets)"
                        }
                        elseif ($currentSlide -gt 0) {
                            # Move to previous slide and show all bullets
                            $currentSlide--
                            $prevSlide = $presentation.Slides[$currentSlide]
                            if ($prevSlide.PSObject.Properties['TotalProgressiveBullets']) {
                                $visibleBullets[$currentSlide] = $prevSlide.TotalProgressiveBullets
                            }
                            else {
                                $visibleBullets[$currentSlide] = 0
                            }
                            Write-Verbose "Moved back to slide $($currentSlide + 1)"
                        }
                    }
                    'Exit' {
                        Write-Verbose "User requested exit"
                        $shouldExit = $true
                        break
                    }
                    'None' {
                        # Unhandled key, ignore
                        Write-Verbose "Unhandled key: $($key.Key)"
                    }
                }

                # Check if we should exit
                if ($shouldExit) {
                    break
                }
            }

            Write-Verbose "Presentation ended"
            
            # Show goodbye message
            Clear-Host
            $windowHeight = $Host.UI.RawUI.WindowSize.Height
            $windowWidth = $Host.UI.RawUI.WindowSize.Width
            $message = "Goodbye! <3"
            
            # Center vertically and horizontally
            $verticalPadding = [math]::Floor($windowHeight / 2)
            $horizontalPadding = [math]::Max(0, [math]::Floor(($windowWidth - $message.Length) / 2))
            
            Write-Host ("`n" * $verticalPadding) -NoNewline
            Write-Host (" " * $horizontalPadding) -NoNewline
            Write-Host $message -ForegroundColor Magenta
            
            Start-Sleep -Milliseconds 800
            Clear-Host
            }
            finally {
                # Show cursor again
                Write-Host "`e[?25h" -NoNewline  # Show cursor
            }
        }
        catch {
            $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                $_.Exception,
                'PresentationFailed',
                [System.Management.Automation.ErrorCategory]::InvalidOperation,
                $Path
            )
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }
    }

    end {
        Write-Verbose "Show-Deck complete"
    }
}