private/output/Invoke-OnCompleteHandler.ps1
|
function Invoke-OnCompleteHandler { <# .SYNOPSIS Finalizes async output - builds Results tab, updates console, hides loading spinner. TO-DO: This does quite a bit, and probably needs to be refactored into smaller pieces in the future? #> [CmdletBinding()] param( [Parameter(Mandatory)] [hashtable]$Context ) # Extract all context values for readability $Executor = $Context.Executor $state = $Context.State $outputData = $Context.OutputData $outputDataByType = $Context.OutputDataByType $consoleColorMap = $Context.ConsoleColorMap $rawColorMap = $Context.RawColorMap $appendConsoleText = $Context.AppendConsoleText $appendState = $Context.AppendState $consoleParagraph = $Context.ConsoleParagraph $consoleTextBox = $Context.ConsoleTextBox $consoleTab = $Context.ConsoleTab $autoScrollCheckbox = $Context.AutoScrollCheckbox $errorsTabState = $Context.ErrorsTabState $ensureErrorsTab = $Context.EnsureErrorsTab $warningsTabState = $Context.WarningsTabState $ensureWarningsTab = $Context.EnsureWarningsTab $warningCount = $Context.WarningCount $tabControl = $Context.TabControl $tabNotifications = $Context.TabNotifications $window = $Context.Window $hideLoading = $Context.HideLoading $loadingPanel = $Context.LoadingPanel $loadingSpinner = $Context.LoadingSpinner $loadingStack = $Context.LoadingStack $loadingLabel = $Context.LoadingLabel $progressBar = $Context.ProgressBar $progressPanel = $Context.ProgressPanel $headerTitle = $Context.HeaderTitle $Title = $Context.Title $HideUntilContent = $Context.HideUntilContent $ParentWindow = $Context.ParentWindow $statusSpinner = $Context.StatusSpinner $statusSuccess = $Context.StatusSuccess $statusWarning = $Context.StatusWarning $statusIndicator = $Context.StatusIndicator $colors = $Context.Colors $ResultActions = $Context.ResultActions $SingleSelect = $Context.SingleSelect $varValues = $Context.VarValues $funcDefs = $Context.FuncDefs $capturedModules = $Context.CapturedModules $defaultProgressUI = $Context.DefaultProgressUI try { if ($state.DebugEnabled) { $eCount = if ($errorsTabState.List) { $errorsTabState.List.Count } else { 0 } [Console]::WriteLine("[DEBUG] OnComplete fired - Results: $($outputData.Count), Types: $($outputDataByType.Count), Errors: $eCount") } if ($state.IsCancelled) { if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] OnComplete cancelled - window was closed") } return } # Stop queue polling timer and drain any remaining items if ($state.HostQueueTimer.IsEnabled) { $state.HostQueueTimer.Stop() } # Drain remaining pipeline queue items (critical for final batch) while ($Executor.PipelineQueueCount -gt 0) { $pipelineItems = $Executor.DrainPipelineQueue(500) if ($null -eq $pipelineItems -or $pipelineItems.Count -eq 0) { break } foreach ($item in $pipelineItems) { if ($null -eq $item) { continue } # Route ErrorRecords to Errors tab instead of Results if ($item -is [System.Management.Automation.ErrorRecord]) { & $ensureErrorsTab $displayRecord = New-ErrorDisplayRecord -ErrorRecord $item [void]$errorsTabState.List.Add($displayRecord) if ($errorsTabState.Tab.Visibility -eq 'Collapsed') { $errorsTabState.Tab.Visibility = 'Visible' } continue } $displayName = Get-CleanTypeName -Item $item if (!$outputDataByType.Contains($displayName)) { $outputDataByType[$displayName] = [System.Collections.Generic.List[object]]::new() } [void]$outputDataByType[$displayName].Add($item) [void]$outputData.Add([psobject]$item) } } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Final drain - Results: $($outputData.Count), Types: $($outputDataByType.Count)") } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Host queue remaining: $($Executor.HostQueueCount) items") } # Drain remaining host queue items $hostRecordsDrained = 0 while ($Executor.HostQueueCount -gt 0) { $records = $Executor.DrainHostQueue(500) if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Drained " + $records.Count + " host records") } if ($null -eq $records -or $records.Count -eq 0) { break } foreach ($record in $records) { $hadContent = Add-OutputLine -Record $record -AppendFunc $appendConsoleText -ColorMap $consoleColorMap -RawColorMap $rawColorMap -State $appendState -SkipScroll if ($hadContent) { $hostRecordsDrained++ } } } # Make Console tab visible if we drained any host records if ($hostRecordsDrained -gt 0) { if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Making Console tab visible after draining " + $hostRecordsDrained + " records") } if ($consoleTab.Visibility -ne 'Visible') { $consoleTab.Visibility = 'Visible' } $tabNotifications.Console.UnreadCount += $hostRecordsDrained $consoleTab.Header = "Console (+$($tabNotifications.Console.UnreadCount))" } # Park the scroll where the caller asked for — top if requested, bottom if auto-scroll is on if ($Context.ScrollToTop) { $consoleTextBox.ScrollToHome() } elseif ($autoScrollCheckbox -and $autoScrollCheckbox.IsChecked) { $consoleTextBox.ScrollToEnd() } # Hide loading spinner on completion regardless & $hideLoading # If in HideUntilContent mode, check if there's any actual content if ($HideUntilContent) { # If window was already revealed by the final queue drain, don't close it if ($state.WindowRevealed) { if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] OnComplete: Window already revealed, skipping close check") } } else { $hasConsoleOutput = $consoleParagraph.Inlines.Count -gt 0 $hasErrors = $errorsTabState.List -and $errorsTabState.List.Count -gt 0 $hasWarnings = $warningCount.Value -gt 0 $hasResults = $outputData.Count -gt 0 if (!$hasConsoleOutput -and !$hasErrors -and !$hasWarnings -and !$hasResults) { Write-Verbose "No content in any tab, closing silently" # Activate parent window before closing to prevent console stealing focus if ($ParentWindow) { $ParentWindow.Activate() } $window.Close() return } # If we have content but window wasn't revealed yet, reveal it now $state.WindowRevealed = $true $window.Visibility = 'Visible' } } $progressBar.IsIndeterminate = $false $progressBar.Value = 100 $headerTitle.Text = "$Title - Complete" $progressPanel.Visibility = 'Collapsed' # Update status indicator - stop spinner, show success or warning $statusSpinner.Visibility = 'Collapsed' $hasErrors = $errorsTabState.List -and $errorsTabState.List.Count -gt 0 $hasWarnings = $warningCount.Value -gt 0 if ($hasErrors -or $hasWarnings) { $statusWarning.Visibility = 'Visible' $statusIndicator.ToolTip = "Complete with errors/warnings" # Update taskbar overlay to warning/error icon try { $errorOverlay = New-TaskbarOverlayIcon -GlyphChar ([PsUi.ModuleContext]::GetIcon('Alert')) -Color '#FFA500' -BackgroundColor '#FFFFFF' [PsUi.WindowManager]::SetTaskbarOverlay($window, $errorOverlay, 'Completed with warnings') } catch { Write-Debug "Suppressed taskbar warning overlay error: $_" } } else { $statusSuccess.Visibility = 'Visible' $statusIndicator.ToolTip = "Complete" # Update taskbar overlay to success checkmark try { $successOverlay = New-TaskbarOverlayIcon -GlyphChar ([PsUi.ModuleContext]::GetIcon('Accept')) -Color '#107C10' -BackgroundColor '#FFFFFF' [PsUi.WindowManager]::SetTaskbarOverlay($window, $successOverlay, 'Completed successfully') } catch { Write-Debug "Suppressed taskbar success overlay error: $_" } } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] About to check outputData.Count = " + $outputData.Count) } if ($outputData.Count -gt 0) { if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Creating Results tab with " + $outputData.Count + " items") } # Convert to safe data array $dataArray = ConvertTo-SafeDataArray -DataArray @($outputData.ToArray()) # Special handling for single dictionary item $dataForPresenter = $dataArray if ($dataArray.Count -eq 1 -and $dataArray[0] -is [System.Collections.IDictionary]) { $dataForPresenter = $dataArray[0] } $presenterInfo = Get-OutputPresenter -Data $dataForPresenter # Create Results tab with toolbar using helper $toolbarResult = New-ResultsToolbar -Colors $colors -ResultActions $ResultActions $resultsTab = $toolbarResult.Tab $resultsPanel = $toolbarResult.Panel $toolbar = $toolbarResult.Toolbar $toolbar2 = $toolbarResult.Toolbar2 $rightToolbar = $toolbarResult.RightToolbar $filterPanel = $toolbarResult.FilterPanel $resultsBorder = $toolbarResult.ResultsBorder $exportButton = $toolbarResult.ExportButton $copyButton = $toolbarResult.CopyButton $dropdownPopup = $toolbarResult.DropdownPopup $actionDropdownMenuStack = $toolbarResult.ActionDropdownMenuStack $resultsTab.Header = "Results ($($outputData.Count))" # Display results if ($presenterInfo.Type -in @('Collection', 'SingleObject', 'Dictionary')) { $singleSelectParam = if ($SingleSelect) { @{ SingleSelect = $true } } else { @{} } $dataGrid = New-StyledDataGrid @singleSelectParam if ($presenterInfo.Type -eq 'SingleObject') { $dataGrid.AutoGenerateColumns = $true $dataGrid.CanUserSortColumns = $false $list = [System.Collections.Generic.List[object]]::new() foreach ($prop in $dataArray[0].PSObject.Properties) { $list.Add([PSCustomObject]@{ Name = $prop.Name Value = $prop.Value }) } $dataGrid.ItemsSource = [System.Windows.Data.CollectionViewSource]::GetDefaultView($list) $resultsBorder.Child = $dataGrid } elseif ($presenterInfo.Type -eq 'Dictionary') { $dataGrid.AutoGenerateColumns = $true $list = [System.Collections.Generic.List[object]]::new() $dict = $dataForPresenter foreach ($key in $dict.Keys) { $displayValue = ConvertTo-DisplayValue -Value $dict[$key] $list.Add([PSCustomObject]@{ Key = $key Value = $displayValue }) } $dataGrid.ItemsSource = [System.Windows.Data.CollectionViewSource]::GetDefaultView($list) $resultsBorder.Child = $dataGrid } else { # Multi-type view with sub-TabControl if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Checking multi-type - outputDataByType.Count = " + $outputDataByType.Count) } foreach ($typeKey in $outputDataByType.Keys) { if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Type key: " + $typeKey) } } # Cap to 10 tabs - merge overflow into "Other" bucket $maxTypeTabs = 10 $sortedKeys = $outputDataByType.Keys | Sort-Object { $outputDataByType[$_].Count } -Descending $typesToRender = @($sortedKeys | Select-Object -First $maxTypeTabs) $overflowTypes = @($sortedKeys | Select-Object -Skip $maxTypeTabs) if ($overflowTypes.Count -gt 0) { $otherBucket = [System.Collections.Generic.List[object]]::new() foreach ($overflowKey in $overflowTypes) { foreach ($item in $outputDataByType[$overflowKey]) { $otherBucket.Add($item) } } $outputDataByType['Other'] = $otherBucket $typesToRender += 'Other' if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Merged " + $overflowTypes.Count + " overflow types into 'Other' bucket (" + $otherBucket.Count + " items)") } } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Creating type-bucketed sub-TabControl with " + $typesToRender.Count + " tab(s)") } $subTabControl = [System.Windows.Controls.TabControl]::new() $subTabControl.Background = ConvertTo-UiBrush $colors.WindowBg $subTabControl.BorderThickness = [System.Windows.Thickness]::new(0) $subTabControl.Padding = [System.Windows.Thickness]::new(0) $firstDataGrid = $null foreach ($typeName in $typesToRender) { $groupItems = $outputDataByType[$typeName] if ($groupItems.Count -eq 0) { continue } $subTab = [System.Windows.Controls.TabItem]::new() $subTab.Header = "$typeName ($($groupItems.Count))" Set-TabItemStyle -TabItem $subTab $firstItem = $groupItems[0] $isTextType = ($typeName -eq 'String') -or ($firstItem -is [string]) -or (($firstItem -is [ValueType]) -and !($firstItem -is [System.Collections.DictionaryEntry])) if ($isTextType) { $richTextBox = New-TextDisplayRichTextBox -Colors $colors -Lines $groupItems $subTab.Content = $richTextBox $subTab.Tag = 'TextType' [void]$subTabControl.Items.Add($subTab) if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Added TEXT sub-tab for type '" + $typeName + "' with " + $groupItems.Count + " lines") } continue } if ($firstItem -is [System.Collections.DictionaryEntry]) { $result = New-DictionarySubTab -GroupItems $groupItems -TypeName $typeName -Colors $colors -IsDictionaryEntry [void]$subTabControl.Items.Add($result.Tab) if ($null -eq $firstDataGrid) { $firstDataGrid = $result.DataGrid } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Added DICTIONARYENTRY sub-tab for type '" + $typeName + "'") } continue } if ($firstItem -is [System.Collections.IDictionary]) { $result = New-DictionarySubTab -GroupItems $groupItems -TypeName $typeName -Colors $colors [void]$subTabControl.Items.Add($result.Tab) if ($null -eq $firstDataGrid) { $firstDataGrid = $result.DataGrid } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Added DICTIONARY sub-tab for type '" + $typeName + "'") } continue } # Create object DataGrid sub-tab using helper $includeStatus = ($ResultActions -and $ResultActions.Count -gt 0) $subTabParams = @{ GroupItems = $groupItems TypeName = $typeName Colors = $colors SubTabControl = $subTabControl SingleSelect = $SingleSelect IncludeActionStatus = $includeStatus } $result = New-ObjectSubTab @subTabParams [void]$subTabControl.Items.Add($result.Tab) if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Added sub-tab for type '" + $typeName + "' with " + $groupItems.Count + " items") } if ($null -eq $firstDataGrid) { $firstDataGrid = $result.DataGrid } } if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Multi-type complete - subTabControl has " + $subTabControl.Items.Count + " tabs") } $resultsBorder.Child = $subTabControl $dataGrid = $firstDataGrid # Add SelectionChanged handler to filter ResultAction buttons by ObjectType $capturedToolbar = $toolbar $capturedRightToolbar = $rightToolbar $capturedDropdownMenuStack = $actionDropdownMenuStack $subTabControl.Add_SelectionChanged({ param($sender, $eventArgs) # Ignore bubbled SelectionChanged events from child controls (e.g. DataGrid) # Only process events that originated from the TabControl itself if ($eventArgs.OriginalSource -ne $sender -and $eventArgs.OriginalSource -isnot [System.Windows.Controls.TabItem]) { return } $selectedTab = $sender.SelectedItem if (!$selectedTab) { return } # Clear filter on the previously selected tab's DataGrid if ($eventArgs.RemovedItems.Count -gt 0) { $previousTab = $eventArgs.RemovedItems[0] if ($previousTab.Content -is [System.Windows.Controls.DataGrid]) { $prevGrid = $previousTab.Content $view = [System.Windows.Data.CollectionViewSource]::GetDefaultView($prevGrid.ItemsSource) if ($view -and $view.Filter) { $view.Filter = $null $view.Refresh() } } } # Update filter box enabled state based on tab's indexed status $filterBox = $sender.Tag if ($filterBox) { if (![string]::IsNullOrEmpty($filterBox.Text)) { $filterBox.Text = '' } $isIndexed = ($selectedTab.Tag -in @('Indexed', 'TextType', 'Dictionary')) $filterBox.IsEnabled = $isIndexed $filterBox.ToolTip = if ($isIndexed) { 'Filter results' } else { 'Indexing...' } if ($filterBox.Tag.Watermark) { $filterBox.Tag.Watermark.Text = if ($isIndexed) { 'Filter...' } else { 'Indexing...' } } } # Hide column selector button for non-DataGrid tabs or fixed-column tabs $isDataGrid = $selectedTab.Content -is [System.Windows.Controls.DataGrid] $isDictionary = $selectedTab.Tag -eq 'Dictionary' $showColButton = $isDataGrid -and !$isDictionary foreach ($child in $capturedRightToolbar.Children) { if ($child -is [System.Windows.Controls.Button] -and $child.ToolTip -eq 'Show/Hide Columns') { $child.Visibility = if ($showColButton) { 'Visible' } else { 'Collapsed' } } } # Get the type name from the tab header $selectedTypeName = $selectedTab.Header -replace '\s*\(\d+\)$', '' $matchesObjectType = { param($actionDef) if (!$actionDef.ObjectType) { return $true } $objectTypes = @($actionDef.ObjectType) foreach ($ot in $objectTypes) { if ($selectedTypeName -eq $ot -or $selectedTypeName -like "*.$ot" -or $selectedTypeName -match [regex]::Escape($ot)) { return $true } } return $false } # Filter individual buttons based on ObjectType foreach ($child in $capturedToolbar.Children) { if ($child -is [System.Windows.Controls.Button] -and $child.Tag -is [hashtable]) { $actionDef = $child.Tag $child.Visibility = if (& $matchesObjectType $actionDef) { 'Visible' } else { 'Collapsed' } } } # Filter dropdown menu items based on ObjectType if ($capturedDropdownMenuStack) { $visibleCount = 0 foreach ($menuItem in $capturedDropdownMenuStack.Children) { if ($menuItem -is [System.Windows.Controls.Button] -and $menuItem.Tag -is [hashtable]) { $actionDef = $menuItem.Tag $isVisible = & $matchesObjectType $actionDef $menuItem.Visibility = if ($isVisible) { 'Visible' } else { 'Collapsed' } if ($isVisible) { $visibleCount++ } } } foreach ($child in $capturedToolbar.Children) { if ($child -is [System.Windows.Controls.Button] -and $child.ToolTip -eq 'Available actions for selected items') { $child.Visibility = if ($visibleCount -gt 0) { 'Visible' } else { 'Collapsed' } } } } }.GetNewClosure()) # Trigger initial filter on first tab if ($subTabControl.Items.Count -gt 0) { $subTabControl.SelectedIndex = 0 } # Add filter box and column visibility button $filterControls = Add-MultiTypeFilterControls -SubTabControl $subTabControl -RightToolbar $rightToolbar -FilterPanel $filterPanel -Toolbar2 $toolbar2 } # Wire up action buttons using helper function if ($ResultActions -and $ResultActions.Count -gt 0) { $allActionButtons = [System.Collections.Generic.List[object]]::new() if ($actionDropdownMenuStack) { foreach ($menuItem in $actionDropdownMenuStack.Children) { if ($menuItem -is [System.Windows.Controls.Button] -and $menuItem.Tag -is [hashtable]) { $allActionButtons.Add($menuItem) } } } # Ensure tabs exist before passing to result action handlers # (result actions can produce their own errors/warnings) & $ensureErrorsTab & $ensureWarningsTab $actionCaptures = @{ Window = $window State = $state DataGrid = $dataGrid ResultsBorder = $resultsBorder ConsoleTab = $consoleTab ConsoleParagraph = $consoleParagraph ConsoleColorMap = $consoleColorMap AppendConsoleText = $appendConsoleText AppendState = $appendState ErrorsTab = $errorsTabState.Tab ErrorsList = $errorsTabState.List WarningsTab = $warningsTabState.Tab WarningsTextBox = $warningsTabState.TextBox WarningCount = $warningCount ProgressPanel = $progressPanel ProgressBar = $progressBar ProgressLabel = $defaultProgressUI.Label HeaderTitle = $headerTitle Title = $Title TabControl = $tabControl TabNotifications = $tabNotifications ErrorCount = $errorsTabState.List.Count StatusSpinner = $statusSpinner StatusSuccess = $statusSuccess StatusWarning = $statusWarning StatusIndicator = $statusIndicator VarValues = $varValues FuncDefs = $funcDefs Modules = $capturedModules } Add-ResultActionClickHandlers -Captures $actionCaptures -ActionButtons $allActionButtons -DropdownPopup $dropdownPopup } $copyButton.Add_Click({ $text = $dataGrid.ItemsSource | ConvertTo-Csv -NoTypeInformation | Out-String [System.Windows.Clipboard]::SetText($text) Start-UiButtonFeedback -Button $copyButton -OriginalIconChar ([PsUi.ModuleContext]::GetIcon('Copy')) }.GetNewClosure()) # Show and wire up export button for datagrids $exportButton.Visibility = 'Visible' $exportButton.Add_Click({ $saveDialog = [Microsoft.Win32.SaveFileDialog]::new() $saveDialog.Filter = 'CSV files (*.csv)|*.csv|All files (*.*)|*.*' $saveDialog.DefaultExt = '.csv' $saveDialog.FileName = "export_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv" if ($saveDialog.ShowDialog()) { $dataGrid.ItemsSource | Export-Csv -Path $saveDialog.FileName -NoTypeInformation Start-UiButtonFeedback -Button $exportButton -OriginalIconChar ([PsUi.ModuleContext]::GetIcon('Export')) } }.GetNewClosure()) } else { # Text output with search capability $textLines = if ($dataArray.Count -gt 1) { $dataArray } else { @($dataArray[0].ToString()) } $richTextBox = New-TextDisplayRichTextBox -Colors $colors -Lines $textLines $resultsBorder.Child = $richTextBox Add-TextResultsFilter -RichTextBox $richTextBox -FilterPanel $filterPanel -Toolbar2 $toolbar2 $copyButton.Add_Click({ $textRange = [System.Windows.Documents.TextRange]::new($richTextBox.Document.ContentStart, $richTextBox.Document.ContentEnd) [System.Windows.Clipboard]::SetText($textRange.Text) Start-UiButtonFeedback -Button $copyButton -OriginalIconChar ([PsUi.ModuleContext]::GetIcon('Copy')) }.GetNewClosure()) } $resultsTab.Content = $resultsPanel if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Adding resultsTab to tabControl - resultsPanel has content: " + $resultsPanel.Children.Count) } $tabControl.Items.Insert(0, $resultsTab) $tabControl.SelectedIndex = 0 if ($state.DebugEnabled) { [Console]::WriteLine("[DEBUG] Results tab inserted and selected") } } else { # No results returned - check if we have any console/error/warning output $hasConsoleOutput = $consoleParagraph.Inlines.Count -gt 0 $hasErrors = $errorsTabState.List -and $errorsTabState.List.Count -gt 0 $hasWarnings = $warningCount.Value -gt 0 if ($hasConsoleOutput -or $hasErrors -or $hasWarnings) { if ($consoleTab.Visibility -eq 'Visible') { $tabControl.SelectedItem = $consoleTab } elseif ($errorsTabState.Tab -and $errorsTabState.Tab.Visibility -eq 'Visible') { $tabControl.SelectedItem = $errorsTabState.Tab } elseif ($warningsTabState.Tab -and $warningsTabState.Tab.Visibility -eq 'Visible') { $tabControl.SelectedItem = $warningsTabState.Tab } } else { # No output at all - show success message $loadingSpinner.Visibility = 'Collapsed' $successIcon = [System.Windows.Controls.TextBlock]@{ Text = [PsUi.ModuleContext]::GetIcon('Accept') FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets') FontSize = 48 Foreground = ConvertTo-UiBrush '#107C10' HorizontalAlignment = 'Center' Margin = [System.Windows.Thickness]::new(0, 0, 0, 12) } $loadingStack.Children.Insert(0, $successIcon) $loadingLabel.Text = 'Completed successfully. No output was returned.' $loadingLabel.FontSize = 13 $loadingPanel.Visibility = 'Visible' } } } catch { [Console]::WriteLine("[PsUi] OnComplete error: " + $_.Exception.Message) if ($state.DebugEnabled) { [Console]::WriteLine("DEBUG ERROR StackTrace: " + $_.Exception.StackTrace) } # Surface the error in the console tab so the user isn't staring at a blank window try { if ($appendConsoleText -and $appendState) { & $appendConsoleText "Internal error rendering results: $($_.Exception.Message)" 'Red' $null -State $appendState } } catch { Write-Debug "Failed to surface OnComplete error to console" } } } |