public/dialogs/Show-UiInputDialog.ps1

function Show-UiInputDialog {
    <#
    .SYNOPSIS
        Displays a themed input dialog for text entry with validation support.
    .DESCRIPTION
        Shows a custom WPF input dialog that respects the current theme.
    .PARAMETER Title
        Dialog window title.
    .PARAMETER Prompt
        Prompt text to display above the input field.
    .PARAMETER DefaultValue
        Default value to populate in the input field.
    .PARAMETER ValidatePattern
        Regular expression pattern to validate input against.
    .PARAMETER Password
        When specified, uses a PasswordBox for secure input (displays dots instead of characters).
    .EXAMPLE
        $name = Show-UiInputDialog -Title 'Enter Name' -Prompt 'Please enter your name:'
    .EXAMPLE
        $email = Show-UiInputDialog -Title 'Email' -Prompt 'Enter email address:' -ValidatePattern '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'
    .EXAMPLE
        $secret = Show-UiInputDialog -Title 'Secret' -Prompt 'Enter password:' -Password
    #>

    [CmdletBinding()]
    param(
        [string]$Title = 'Input',

        [Parameter(Mandatory)]
        [string]$Prompt,

        [string]$DefaultValue = '',

        [string]$ValidatePattern,

        [switch]$Password
    )

    Write-Debug "Title='$Title' Prompt='$Prompt' Password=$Password"

    # Create dialog window using shared helper
    $dlg = New-DialogWindow -Title $Title -Width 420 -AppIdSuffix 'InputDialog' -OverlayGlyph ([PsUi.ModuleContext]::GetIcon('Edit'))

    $window       = $dlg.Window
    $contentPanel = $dlg.ContentPanel
    $colors       = $dlg.Colors

    # Button panel at bottom
    $buttonPanel = [System.Windows.Controls.StackPanel]@{
        Orientation         = 'Horizontal'
        HorizontalAlignment = 'Right'
        Margin              = [System.Windows.Thickness]::new(0, 12, 0, 0)
    }
    [System.Windows.Controls.DockPanel]::SetDock($buttonPanel, 'Bottom')
    [void]$contentPanel.Children.Add($buttonPanel)

    # Input stack for prompt + input + error
    $inputStack = [System.Windows.Controls.StackPanel]@{
        Margin = [System.Windows.Thickness]::new(0)
    }
    [void]$contentPanel.Children.Add($inputStack)

    # Prompt text in scroll viewer
    $scrollViewer = [System.Windows.Controls.ScrollViewer]@{
        VerticalScrollBarVisibility   = 'Auto'
        HorizontalScrollBarVisibility = 'Disabled'
        Padding                       = [System.Windows.Thickness]::new(0, 0, 8, 0)
        MaxHeight                     = 400
    }

    $promptText = [System.Windows.Controls.TextBlock]@{
        Text         = $Prompt
        FontSize     = 13
        TextWrapping = 'Wrap'
        Foreground   = ConvertTo-UiBrush $colors.ControlFg
        Margin       = [System.Windows.Thickness]::new(0, 0, 0, 8)
    }
    $scrollViewer.Content = $promptText
    [void]$inputStack.Children.Add($scrollViewer)

    # Input control (TextBox or PasswordBox)
    if ($Password) {
        $inputBox = [System.Windows.Controls.PasswordBox]@{
            Height   = 26
            Padding  = [System.Windows.Thickness]::new(2, 0, 2, 0)
            FontSize = 12
        }
        Set-TextBoxStyle -PasswordBox $inputBox
    }
    else {
        $inputBox = [System.Windows.Controls.TextBox]@{
            Text    = $DefaultValue
            Height  = 26
            Padding = [System.Windows.Thickness]::new(2, 0, 2, 0)
        }
        Set-TextBoxStyle -TextBox $inputBox
    }
    [void]$inputStack.Children.Add($inputBox)

    # Validation error text (hidden by default)
    $errorText = [System.Windows.Controls.TextBlock]@{
        Foreground = ConvertTo-UiBrush $colors.Error
        FontSize   = 11
        Visibility = 'Collapsed'
        Margin     = [System.Windows.Thickness]::new(0, 4, 0, 0)
    }
    [void]$inputStack.Children.Add($errorText)

    # OK button with validation
    $okBtn = [System.Windows.Controls.Button]@{
        Content = 'OK'
        Width   = 80
        Height  = 28
        Margin  = [System.Windows.Thickness]::new(0, 0, 4, 0)
    }
    Set-ButtonStyle -Button $okBtn -Accent

    $validateAndClose = {
        $text = if ($inputBox -is [System.Windows.Controls.PasswordBox]) {
            $inputBox.Password
        }
        else {
            $inputBox.Text
        }

        # Run validation if pattern provided
        if ($ValidatePattern -and $text -notmatch $ValidatePattern) {
            $errorText.Text = 'Input does not match the required format'
            $errorText.Visibility = 'Visible'
            $inputBox.BorderBrush = ConvertTo-UiBrush $colors.Error
        }
        else {
            # For password mode, store the SecurePassword directly to avoid plaintext in memory
            if ($inputBox -is [System.Windows.Controls.PasswordBox]) {
                $window.Tag = $inputBox.SecurePassword.Copy()
            }
            else {
                $window.Tag = $text
            }
            $window.Close()
        }
    }

    $okBtn.Add_Click($validateAndClose)
    [void]$buttonPanel.Children.Add($okBtn)
    $okBtn.IsDefault = $true

    # Cancel button
    $cancelBtn = [System.Windows.Controls.Button]@{
        Content = 'Cancel'
        Width   = 80
        Height  = 28
        Margin  = [System.Windows.Thickness]::new(4, 0, 0, 0)
    }
    Set-ButtonStyle -Button $cancelBtn
    $cancelBtn.Add_Click({
        $window.Tag = $null
        $window.Close()
    })
    [void]$buttonPanel.Children.Add($cancelBtn)
    $cancelBtn.IsCancel = $true

    # Clear error on text change
    if ($inputBox -is [System.Windows.Controls.PasswordBox]) {
        $inputBox.Add_PasswordChanged({
            $errorText.Visibility = 'Collapsed'
            $inputBox.BorderBrush = ConvertTo-UiBrush $colors.Border
        }.GetNewClosure())
    }
    else {
        $inputBox.Add_TextChanged({
            $errorText.Visibility = 'Collapsed'
            $inputBox.BorderBrush = ConvertTo-UiBrush $colors.Border
        }.GetNewClosure())
    }

    # Standard window behavior with focus and select-all on input
    Initialize-UiWindowLoaded -Window $window -FocusElement $inputBox -SelectAll

    Set-UiDialogPosition -Dialog $window

    Write-Debug "Showing modal dialog"
    [void]$window.ShowDialog()

    $result = $window.Tag
    Write-Debug "Result: $(if ($null -eq $result) { '<null>' } elseif ($Password) { '<masked>' } else { $result })"
    return $result
}