From 0fb369e156a4dc27ba648bde53dd47ae9c1d0742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 00:10:18 +0000 Subject: [PATCH 1/3] Initial plan From be03e40b5895d86eedb83107c1406c28995d07e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 00:26:07 +0000 Subject: [PATCH 2/3] Add Avalonia UI GUI launcher for System Resources As Built Report Co-authored-by: rebelinux <1002783+rebelinux@users.noreply.github.com> --- AsBuiltReport.System.Resources.psd1 | 2 +- README.md | 47 +++ Src/GUI/Invoke-AsBuiltReportGui.ps1 | 530 ++++++++++++++++++++++++++ Src/Public/Start-AsBuiltReportGui.ps1 | 63 +++ Todo.md | 6 +- 5 files changed, 646 insertions(+), 2 deletions(-) create mode 100644 Src/GUI/Invoke-AsBuiltReportGui.ps1 create mode 100644 Src/Public/Start-AsBuiltReportGui.ps1 diff --git a/AsBuiltReport.System.Resources.psd1 b/AsBuiltReport.System.Resources.psd1 index db373a9..c58f1bf 100644 --- a/AsBuiltReport.System.Resources.psd1 +++ b/AsBuiltReport.System.Resources.psd1 @@ -83,7 +83,7 @@ # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = @('Invoke-AsBuiltReport.System.Resources') + FunctionsToExport = @('Invoke-AsBuiltReport.System.Resources', 'Start-AsBuiltReportGui') # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. # CmdletsToExport = @() diff --git a/README.md b/README.md index 3ab2a4c..278ccc3 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,53 @@ The **Healthcheck** schema is used to toggle health checks on or off. No health checks are currently available for this report. +## :desktop_computer: Graphical User Interface (GUI) + +A cross-platform graphical launcher is included in the module. It is powered by +[AvaloniaUIShell](https://github.com/mdgrs-mei/AvaloniaUIShell) and requires +**PowerShell 7.4 or newer**. + +### GUI Requirements + +| Requirement | Version | +| ----------- | ------- | +| PowerShell | 7.4+ | +| AvaloniaUIShell | latest | + +### Installing AvaloniaUIShell + +```powershell +# Install the AvaloniaUIShell module +Install-PSResource -Name AvaloniaUIShell + +# macOS / Linux only – add execute permission to the server binary: +Enable-AUIExecution +``` + +### Launching the GUI + +```powershell +# Option A – via the exported module function (recommended): +Import-Module AsBuiltReport.System.Resources +Start-AsBuiltReportGui + +# Option B – run the script directly: +& "$((Get-Module AsBuiltReport.System.Resources).ModuleBase)\Src\GUI\Invoke-AsBuiltReportGui.ps1" +``` + +The GUI window lets you configure every common report parameter through a +point-and-click interface: + +| Section | Controls | +| ------- | -------- | +| **Report Configuration** | Target, Output Folder (with folder browser), Report Name, Language | +| **Output Formats** | HTML · Word · Text · Excel checkboxes | +| **Options** | Timestamp · HealthCheck · Enable Diagrams · Export Diagrams · Diagram Theme | +| **Info Level** | Per-section selectors (0 = Disabled, 1 = Summary, 2 = Detailed) for Date, TimeZone, Uptime, PSHost, ProcessInfo | +| **Generate** | Progress bar · scrollable log showing live output | + +Report generation runs on a background thread so the window stays fully responsive. + ## :computer: Examples The following examples demonstrate how to generate a System Resources As Built Report. diff --git a/Src/GUI/Invoke-AsBuiltReportGui.ps1 b/Src/GUI/Invoke-AsBuiltReportGui.ps1 new file mode 100644 index 0000000..c1ebbf5 --- /dev/null +++ b/Src/GUI/Invoke-AsBuiltReportGui.ps1 @@ -0,0 +1,530 @@ +<# +.SYNOPSIS + Graphical launcher for the System Resources As Built Report. +.DESCRIPTION + Presents an Avalonia UI window (via the AvaloniaUIShell module) that lets the user + configure every common New-AsBuiltReport parameter through a point-and-click + interface and then generate the report without touching the command line. + + Sections of the GUI: + + Report Configuration - Target, Output Folder, Report Name, Language + Output Formats - Html / Word / Text / Excel checkboxes + Options - Timestamp, HealthCheck, Diagrams, Export Diagrams, + Diagram Theme + Info Level - Per-section detail level (0 = Disabled, 1 = Summary, + 2 = Detailed) for Date, TimeZone, Uptime, PSHost, + ProcessInfo + Generate - Progress bar + scrollable log; report runs on a + background thread so the UI stays responsive. + +.NOTES + Requirements: + - PowerShell 7.4 or newer (AvaloniaUIShell requirement) + - AvaloniaUIShell module (Install-PSResource -Name AvaloniaUIShell) + - AsBuiltReport.Core 1.6.2 or newer + - AsBuiltReport.System.Resources module (this module) + + On macOS / Linux run Enable-AUIExecution once after installing AvaloniaUIShell. + +.EXAMPLE + # Launch the GUI from any PowerShell 7 prompt: + & "$PSScriptRoot\Invoke-AsBuiltReportGui.ps1" + +.EXAMPLE + # Or call the exported wrapper function after importing the module: + Import-Module AsBuiltReport.System.Resources + Start-AsBuiltReportGui + +.LINK + https://github.com/AsBuiltReport/AsBuiltReport.System.Resources +.LINK + https://github.com/mdgrs-mei/AvaloniaUIShell +#> + +#Requires -Version 7.4 + +using namespace AvaloniaUIShell +using namespace AvaloniaUIShell.Avalonia +using namespace AvaloniaUIShell.Avalonia.Controls +using namespace AvaloniaUIShell.Avalonia.Media + +# --------------------------------------------------------------------------- +# Guard - AvaloniaUIShell must be available +# --------------------------------------------------------------------------- +if (-not (Get-Module -Name AvaloniaUIShell -ListAvailable)) { + Write-Error @" +The AvaloniaUIShell module is required to run the GUI. +Install it with: + Install-PSResource -Name AvaloniaUIShell +On macOS / Linux also run: + Enable-AUIExecution +"@ + return +} + +if (-not (Get-Module -Name AvaloniaUIShell)) { + Import-Module AvaloniaUIShell -ErrorAction Stop +} + +# --------------------------------------------------------------------------- +# Supported language list (matches AsBuiltReport.System.Resources/Language/) +# --------------------------------------------------------------------------- +$LanguageCodes = @( + 'en-US', 'en-GB', + 'ar-SA', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', + 'es-ES', 'fi-FI', 'fr-FR', 'he-IL', 'hi-IN', + 'hu-HU', 'it-IT', 'ja-JP', 'ko-KR', 'nb-NO', + 'nl-NL', 'pl-PL', 'pt-PT', 'ru-RU', 'sv-SE', + 'th-TH', 'tr-TR', 'vi-VN', 'zh-CN', 'zh-Hans', 'zh-Hant' +) + +# --------------------------------------------------------------------------- +# Shared state accessible from the background runspace +# --------------------------------------------------------------------------- +$syncHash = [Hashtable]::Synchronized(@{ + Log = '' + IsRunning = $false + CancelRequest = $false +}) + +# --------------------------------------------------------------------------- +# Helper: append a line to the log TextBox (thread-safe via AvaloniaUIShell) +# --------------------------------------------------------------------------- +function Add-LogLine { + param ([string]$Message) + $ts = (Get-Date).ToString('HH:mm:ss') + $line = "[$ts] $Message" + $syncHash.LogBox.Text += "$line`n" + # Auto-scroll to bottom + $syncHash.LogScroll.ScrollToEnd() +} + +# =========================================================================== +# -- WINDOW ------------------------------------------------------------------ +# =========================================================================== +$win = [Window]::new() +$win.Title = 'AsBuiltReport - System Resources' +$win.Width = 680 +$win.Height = 740 +$win.WindowStartupLocation = 'CenterScreen' +$win.CanMaximize = $false + +# =========================================================================== +# -- SECTION HEADER FACTORY -------------------------------------------------- +# =========================================================================== +function Get-SectionHeader { + param ([string]$Text) + $tb = [TextBlock]::new() + $tb.Text = $Text + $tb.FontSize = 13 + $tb.FontWeight = 'SemiBold' + $tb.Margin = [Thickness]::new(0, 12, 0, 4) + $tb +} + +# =========================================================================== +# -- REPORT CONFIGURATION ---------------------------------------------------- +# =========================================================================== + +# Target +$lblTarget = [TextBlock]::new(); $lblTarget.Text = 'Target:' +$lblTarget.Width = 130; $lblTarget.VerticalAlignment = 'Center' +$txtTarget = [TextBox]::new() +$txtTarget.Text = [System.Net.Dns]::GetHostName() +$txtTarget.Width = 380 +$syncHash.TxtTarget = $txtTarget + +$rowTarget = [StackPanel]::new() +$rowTarget.Orientation = 'Horizontal'; $rowTarget.Spacing = 8 +$rowTarget.Children.Add($lblTarget) +$rowTarget.Children.Add($txtTarget) + +# Output Folder +$lblFolder = [TextBlock]::new(); $lblFolder.Text = 'Output Folder:' +$lblFolder.Width = 130; $lblFolder.VerticalAlignment = 'Center' +$txtFolder = [TextBox]::new() +$txtFolder.Text = [Environment]::GetFolderPath('Desktop') +$txtFolder.Width = 300 +$syncHash.TxtFolder = $txtFolder + +$btnBrowse = [Button]::new() +$btnBrowse.Content = 'Browse Folder' +$btnBrowse.Width = 90 +$btnBrowse.AddClick({ + # Use PowerShell's native folder-picker dialog if available (Windows only); + # on other platforms fall back to manual text entry. + if ($IsWindows) { + try { + Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop + $dlg = [System.Windows.Forms.FolderBrowserDialog]::new() + $dlg.Description = 'Select the report output folder' + $dlg.SelectedPath = $syncHash.TxtFolder.Text + if ($dlg.ShowDialog() -eq 'OK') { + $syncHash.TxtFolder.Text = $dlg.SelectedPath + } + } catch { + Add-LogLine "Folder browser unavailable: $_. Please type the path directly." + } + } else { + Add-LogLine 'Folder browser not available on this platform - type the path directly.' + } +}) + +$rowFolder = [StackPanel]::new() +$rowFolder.Orientation = 'Horizontal'; $rowFolder.Spacing = 8 +$rowFolder.Children.Add($lblFolder) +$rowFolder.Children.Add($txtFolder) +$rowFolder.Children.Add($btnBrowse) + +# Report Name +$lblName = [TextBlock]::new(); $lblName.Text = 'Report Name:' +$lblName.Width = 130; $lblName.VerticalAlignment = 'Center' +$txtName = [TextBox]::new() +$txtName.Text = 'System Resources As Built Report' +$txtName.Width = 380 +$syncHash.TxtName = $txtName + +$rowName = [StackPanel]::new() +$rowName.Orientation = 'Horizontal'; $rowName.Spacing = 8 +$rowName.Children.Add($lblName) +$rowName.Children.Add($txtName) + +# Language +$lblLang = [TextBlock]::new(); $lblLang.Text = 'Language:' +$lblLang.Width = 130; $lblLang.VerticalAlignment = 'Center' +$cbLang = [ComboBox]::new() +$cbLang.Width = 160 +foreach ($code in $LanguageCodes) { + $item = [ComboBoxItem]::new(); $item.Content = $code + $cbLang.Items.Add($item) | Out-Null +} +$cbLang.SelectedIndex = 0 # en-US +$syncHash.CbLang = $cbLang + +$rowLang = [StackPanel]::new() +$rowLang.Orientation = 'Horizontal'; $rowLang.Spacing = 8 +$rowLang.Children.Add($lblLang) +$rowLang.Children.Add($cbLang) + +$panelConfig = [StackPanel]::new(); $panelConfig.Spacing = 6 +$panelConfig.Children.Add((Get-SectionHeader 'Report Configuration')) +$panelConfig.Children.Add($rowTarget) +$panelConfig.Children.Add($rowFolder) +$panelConfig.Children.Add($rowName) +$panelConfig.Children.Add($rowLang) + +# =========================================================================== +# -- OUTPUT FORMATS ---------------------------------------------------------- +# =========================================================================== +$chkHtml = [CheckBox]::new(); $chkHtml.Content = 'HTML'; $chkHtml.IsChecked = $true +$chkWord = [CheckBox]::new(); $chkWord.Content = 'Word'; $chkWord.IsChecked = $false +$chkText = [CheckBox]::new(); $chkText.Content = 'Text'; $chkText.IsChecked = $false +$chkExcel = [CheckBox]::new(); $chkExcel.Content = 'Excel'; $chkExcel.IsChecked = $false +$syncHash.ChkHtml = $chkHtml +$syncHash.ChkWord = $chkWord +$syncHash.ChkText = $chkText +$syncHash.ChkExcel = $chkExcel + +$rowFormats = [StackPanel]::new() +$rowFormats.Orientation = 'Horizontal'; $rowFormats.Spacing = 18 +$rowFormats.Children.Add($chkHtml) +$rowFormats.Children.Add($chkWord) +$rowFormats.Children.Add($chkText) +$rowFormats.Children.Add($chkExcel) + +$panelFormats = [StackPanel]::new(); $panelFormats.Spacing = 6 +$panelFormats.Children.Add((Get-SectionHeader 'Output Formats')) +$panelFormats.Children.Add($rowFormats) + +# =========================================================================== +# -- OPTIONS ----------------------------------------------------------------- +# =========================================================================== +$chkTimestamp = [CheckBox]::new(); $chkTimestamp.Content = 'Append Timestamp'; $chkTimestamp.IsChecked = $true +$chkHealth = [CheckBox]::new(); $chkHealth.Content = 'Enable HealthCheck'; $chkHealth.IsChecked = $false +$chkDiagrams = [CheckBox]::new(); $chkDiagrams.Content = 'Enable Diagrams'; $chkDiagrams.IsChecked = $true +$chkExportDiag = [CheckBox]::new(); $chkExportDiag.Content = 'Export Diagrams'; $chkExportDiag.IsChecked= $false +$syncHash.ChkTimestamp = $chkTimestamp +$syncHash.ChkHealth = $chkHealth +$syncHash.ChkDiagrams = $chkDiagrams +$syncHash.ChkExportDiag = $chkExportDiag + +$rowOpt1 = [StackPanel]::new() +$rowOpt1.Orientation = 'Horizontal'; $rowOpt1.Spacing = 18 +$rowOpt1.Children.Add($chkTimestamp) +$rowOpt1.Children.Add($chkHealth) + +$rowOpt2 = [StackPanel]::new() +$rowOpt2.Orientation = 'Horizontal'; $rowOpt2.Spacing = 18 +$rowOpt2.Children.Add($chkDiagrams) +$rowOpt2.Children.Add($chkExportDiag) + +# Diagram Theme +$lblTheme = [TextBlock]::new(); $lblTheme.Text = 'Diagram Theme:'; $lblTheme.VerticalAlignment = 'Center' +$cbTheme = [ComboBox]::new(); $cbTheme.Width = 120 +foreach ($t in @('White', 'Black', 'Neon')) { + $item = [ComboBoxItem]::new(); $item.Content = $t + $cbTheme.Items.Add($item) | Out-Null +} +$cbTheme.SelectedIndex = 0 +$syncHash.CbTheme = $cbTheme + +$rowTheme = [StackPanel]::new() +$rowTheme.Orientation = 'Horizontal'; $rowTheme.Spacing = 8 +$rowTheme.Children.Add($lblTheme) +$rowTheme.Children.Add($cbTheme) + +$panelOptions = [StackPanel]::new(); $panelOptions.Spacing = 6 +$panelOptions.Children.Add((Get-SectionHeader 'Options')) +$panelOptions.Children.Add($rowOpt1) +$panelOptions.Children.Add($rowOpt2) +$panelOptions.Children.Add($rowTheme) + +# =========================================================================== +# -- INFO LEVEL -------------------------------------------------------------- +# =========================================================================== +function Get-InfoLevelRow { + param ([string]$Label, [int]$Default = 1) + $lbl = [TextBlock]::new(); $lbl.Text = "${Label}:"; $lbl.Width = 100; $lbl.VerticalAlignment = 'Center' + $cb = [ComboBox]::new(); $cb.Width = 70 + foreach ($n in 0..2) { + $item = [ComboBoxItem]::new(); $item.Content = $n.ToString() + $cb.Items.Add($item) | Out-Null + } + $cb.SelectedIndex = $Default + $row = [StackPanel]::new(); $row.Orientation = 'Horizontal'; $row.Spacing = 6 + $row.Children.Add($lbl); $row.Children.Add($cb) + return $row, $cb +} + +$rowDate, $cbDate = Get-InfoLevelRow 'Date' 1 +$rowTZ, $cbTZ = Get-InfoLevelRow 'TimeZone' 2 +$rowUptime, $cbUptime = Get-InfoLevelRow 'Uptime' 2 +$rowPSHost, $cbPSHost = Get-InfoLevelRow 'PSHost' 2 +$rowProcess, $cbProcess = Get-InfoLevelRow 'ProcessInfo' 1 +$syncHash.CbDate = $cbDate +$syncHash.CbTZ = $cbTZ +$syncHash.CbUptime = $cbUptime +$syncHash.CbPSHost = $cbPSHost +$syncHash.CbProcess = $cbProcess + +$gridInfo = [UniformGrid]::new() +$gridInfo.Columns = 2 +$gridInfo.Children.Add($rowDate) | Out-Null +$gridInfo.Children.Add($rowTZ) | Out-Null +$gridInfo.Children.Add($rowUptime) | Out-Null +$gridInfo.Children.Add($rowPSHost) | Out-Null +$gridInfo.Children.Add($rowProcess) | Out-Null + +$panelInfo = [StackPanel]::new(); $panelInfo.Spacing = 6 +$panelInfo.Children.Add((Get-SectionHeader 'Info Level (0=Disabled, 1=Summary, 2=Detailed)')) +$panelInfo.Children.Add($gridInfo) + +# =========================================================================== +# -- PROGRESS & LOG ---------------------------------------------------------- +# =========================================================================== +$progressBar = [ProgressBar]::new() +$progressBar.IsIndeterminate = $true +$progressBar.IsVisible = $false +$progressBar.Height = 6 +$syncHash.ProgressBar = $progressBar + +$logBox = [TextBox]::new() +$logBox.IsReadOnly = $true +$logBox.AcceptsReturn = $true +$logBox.Height = 120 +$logBox.FontFamily = 'Courier New' +$logBox.FontSize = 11 +$logBox.Text = '' +$syncHash.LogBox = $logBox + +$logScroll = [ScrollViewer]::new() +$logScroll.Height = 120 +$logScroll.Content = $logBox +$syncHash.LogScroll = $logScroll + +# =========================================================================== +# -- GENERATE BUTTON --------------------------------------------------------- +# =========================================================================== +$btnGenerate = [Button]::new() +$btnGenerate.Content = 'Generate Report' +$btnGenerate.HorizontalAlignment = 'Stretch' +$btnGenerate.HorizontalContentAlignment = 'Center' +$btnGenerate.Height = 40 +$btnGenerate.Classes.Add('accent') +$syncHash.BtnGenerate = $btnGenerate + +$lblStatus = [TextBlock]::new() +$lblStatus.Text = 'Ready.' +$lblStatus.Margin = [Thickness]::new(0, 4, 0, 0) +$syncHash.LblStatus = $lblStatus + +# --------------------------------------------------------------------------- +# Generate button click - runs on RunspacePoolAsyncUI so the UI stays alive +# --------------------------------------------------------------------------- +$generateCallback = [EventCallback]::new() +$generateCallback.RunspaceMode = 'RunspacePoolAsyncUI' +$generateCallback.DisabledControlsWhileProcessing = $btnGenerate +$generateCallback.ArgumentList = $syncHash + +$generateCallback.ScriptBlock = { + param ($syncHash) + + # ---- Collect values from UI ---------------------------------------- + $target = $syncHash.TxtTarget.Text.Trim() + $outFolder = $syncHash.TxtFolder.Text.Trim() + $reportName = $syncHash.TxtName.Text.Trim() + $language = ($syncHash.CbLang.SelectedItem).Content + + $formats = @() + if ($syncHash.ChkHtml.IsChecked) { $formats += 'Html' } + if ($syncHash.ChkWord.IsChecked) { $formats += 'Word' } + if ($syncHash.ChkText.IsChecked) { $formats += 'Text' } + if ($syncHash.ChkExcel.IsChecked) { $formats += 'Excel' } + + $useTimestamp = [bool]$syncHash.ChkTimestamp.IsChecked + $useHealth = [bool]$syncHash.ChkHealth.IsChecked + $diagTheme = ($syncHash.CbTheme.SelectedItem).Content + + $infoDate = [int]($syncHash.CbDate.SelectedItem).Content + $infoTZ = [int]($syncHash.CbTZ.SelectedItem).Content + $infoUptime = [int]($syncHash.CbUptime.SelectedItem).Content + $infoPSHost = [int]($syncHash.CbPSHost.SelectedItem).Content + $infoProcess = [int]($syncHash.CbProcess.SelectedItem).Content + $enableDiag = [bool]$syncHash.ChkDiagrams.IsChecked + $exportDiag = [bool]$syncHash.ChkExportDiag.IsChecked + + # ---- Validate ------------------------------------------------------- + if (-not $target) { + $syncHash.LblStatus.Text = '[WARNING] Target cannot be empty.' + return + } + if (-not $outFolder) { + $syncHash.LblStatus.Text = '[WARNING] Output folder cannot be empty.' + return + } + if ($formats.Count -eq 0) { + $syncHash.LblStatus.Text = '[WARNING] Select at least one output format.' + return + } + + # ---- Show progress -------------------------------------------------- + $syncHash.ProgressBar.IsVisible = $true + $syncHash.LblStatus.Text = 'Generating report...' + $syncHash.LogBox.Text = '' + + $ts = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts] Starting report generation for target '$target'...`n" + $syncHash.LogBox.Text += "[$ts] Output folder : $outFolder`n" + $syncHash.LogBox.Text += "[$ts] Format(s) : $($formats -join ', ')`n" + $syncHash.LogBox.Text += "[$ts] Language : $language`n" + $syncHash.LogBox.Text += "[$ts] Diagram theme : $diagTheme`n" + + # ---- Build a temporary JSON config file ---------------------------- + $configObj = [ordered]@{ + Report = [ordered]@{ + Name = $reportName + Version = '1.0' + Status = 'Released' + Language = $language + ShowCoverPageImage = $true + ShowTableOfContents = $true + ShowHeaderFooter = $true + ShowTableCaptions = $true + } + Options = [ordered]@{ + EnableDiagrams = $enableDiag + EnableDiagramDebug = $false + EnableDiagramMainLogo = $false + DiagramTheme = $diagTheme + DiagramWaterMark = '' + ExportDiagrams = $exportDiag + ExportDiagramsFormat = @('png') + EnableDiagramSignature = $false + DiagramColumnSize = 4 + SignatureAuthorName = '' + SignatureCompanyName = '' + } + InfoLevel = [ordered]@{ + Date = $infoDate + TimeZone = $infoTZ + Uptime = $infoUptime + PSHost = $infoPSHost + ProcessInfo = $infoProcess + } + HealthCheck = [ordered]@{} + } + + $tmpConfig = [System.IO.Path]::Combine( + [System.IO.Path]::GetTempPath(), + "AsBuiltReport.System.Resources.$(Get-Date -Format 'yyyyMMddHHmmss').json" + ) + $configObj | ConvertTo-Json -Depth 10 | Set-Content -Path $tmpConfig -Encoding UTF8 + + $ts = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts] Temp config : $tmpConfig`n" + + # ---- Assemble New-AsBuiltReport parameters ------------------------- + $abrParams = @{ + Report = 'System.Resources' + Target = $target + Format = $formats + OutputFolderPath = $outFolder + ReportConfigFilePath = $tmpConfig + ReportLanguage = $language + ErrorAction = 'Stop' + } + if ($useTimestamp) { $abrParams['Timestamp'] = $true } + if ($useHealth) { $abrParams['EnableHealthCheck'] = $true } + + # ---- Run the report ------------------------------------------------- + try { + $ts = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts] Invoking New-AsBuiltReport...`n" + $syncHash.LogScroll.ScrollToEnd() + + New-AsBuiltReport @abrParams 4>&1 | ForEach-Object { + $line = $_.ToString() + $ts2 = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts2] $line`n" + $syncHash.LogScroll.ScrollToEnd() + } + + $ts = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts] Report generation complete.`n" + $syncHash.LblStatus.Text = "Done! Report saved to: $outFolder" + } catch { + $ts = (Get-Date).ToString('HH:mm:ss') + $syncHash.LogBox.Text += "[$ts] ERROR: $_`n" + $syncHash.LblStatus.Text = "ERROR: $($_.Exception.Message)" + } finally { + # Clean up temp config + if (Test-Path $tmpConfig) { Remove-Item $tmpConfig -Force -ErrorAction SilentlyContinue } + $syncHash.ProgressBar.IsVisible = $false + $syncHash.LogScroll.ScrollToEnd() + } +} + +$btnGenerate.AddClick($generateCallback) + +# =========================================================================== +# -- LAYOUT - stack everything into a ScrollViewer --------------------------- +# =========================================================================== +$mainPanel = [StackPanel]::new() +$mainPanel.Margin = [Thickness]::new(20) +$mainPanel.Spacing = 4 +$mainPanel.Children.Add($panelConfig) +$mainPanel.Children.Add($panelFormats) +$mainPanel.Children.Add($panelOptions) +$mainPanel.Children.Add($panelInfo) +$mainPanel.Children.Add($progressBar) +$mainPanel.Children.Add($btnGenerate) +$mainPanel.Children.Add($lblStatus) +$mainPanel.Children.Add($logScroll) + +$scroll = [ScrollViewer]::new() +$scroll.Content = $mainPanel + +$win.Content = $scroll +$win.Show() +$win.WaitForClosed() diff --git a/Src/Public/Start-AsBuiltReportGui.ps1 b/Src/Public/Start-AsBuiltReportGui.ps1 new file mode 100644 index 0000000..0a21e07 --- /dev/null +++ b/Src/Public/Start-AsBuiltReportGui.ps1 @@ -0,0 +1,63 @@ +function Start-AsBuiltReportGui { + <# + .SYNOPSIS + Launches the Avalonia UI graphical interface for generating a System Resources + As Built Report. + .DESCRIPTION + Opens an interactive window (powered by the AvaloniaUIShell module) that lets + the user configure and generate a System Resources As Built Report without + using the command line. + + The GUI exposes the most common New-AsBuiltReport parameters: + + - Target system name + - Output folder and report name + - Report language + - Output format (Html / Word / Text / Excel) + - Timestamp and HealthCheck toggles + - Diagram options (Enable, Export, Theme) + - Per-section InfoLevel (0-2) for Date, TimeZone, Uptime, PSHost, + and ProcessInfo + + Report generation runs on a background thread, so the window stays fully + responsive while the report is being produced. + + .NOTES + Requirements: + - PowerShell 7.4 or newer (required by AvaloniaUIShell) + - AvaloniaUIShell module (Install-PSResource -Name AvaloniaUIShell) + - AsBuiltReport.Core 1.6.2 or newer + - AsBuiltReport.System.Resources module (this module) + + On macOS / Linux run Enable-AUIExecution once after installing AvaloniaUIShell. + + .EXAMPLE + # Import the module then open the GUI: + Import-Module AsBuiltReport.System.Resources + Start-AsBuiltReportGui + + .LINK + https://github.com/AsBuiltReport/AsBuiltReport.System.Resources + .LINK + https://github.com/mdgrs-mei/AvaloniaUIShell + #> + + [CmdletBinding(SupportsShouldProcess)] + param() + + $guiScript = Join-Path $PSScriptRoot '..' 'GUI' 'Invoke-AsBuiltReportGui.ps1' + $guiScript = [System.IO.Path]::GetFullPath($guiScript) + + if (-not (Test-Path $guiScript)) { + throw "GUI script not found at expected path: $guiScript" + } + + if ($PSVersionTable.PSVersion.Major -lt 7 -or + ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -lt 4)) { + throw 'Start-AsBuiltReportGui requires PowerShell 7.4 or newer (AvaloniaUIShell requirement).' + } + + if ($PSCmdlet.ShouldProcess('AsBuiltReport GUI', 'Start')) { + & $guiScript + } +} diff --git a/Todo.md b/Todo.md index 9ba0878..f165955 100644 --- a/Todo.md +++ b/Todo.md @@ -5,4 +5,8 @@ - [X] CPU usage charts - [X] Memory usage charts - [x] Add support for diagrams - - [x] Process diagrams \ No newline at end of file + - [x] Process diagrams +- [x] Design a GUI using Avalonia UI (AvaloniaUIShell) to generate the report + - [x] Avalonia UI window with report configuration, format, options and info-level controls + - [x] Background-thread report generation (UI stays responsive) + - [x] Start-AsBuiltReportGui exported function \ No newline at end of file From 3a4f1e5e2b2eecf2d3cc2732c3f615ab4a32a467 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Tue, 10 Mar 2026 21:07:35 -0400 Subject: [PATCH 3/3] Refactor code formatting and improve readability in Invoke-AsBuiltReportGui.ps1 --- Src/GUI/Invoke-AsBuiltReportGui.ps1 | 336 ++++++++++++++-------------- 1 file changed, 168 insertions(+), 168 deletions(-) diff --git a/Src/GUI/Invoke-AsBuiltReportGui.ps1 b/Src/GUI/Invoke-AsBuiltReportGui.ps1 index c1ebbf5..93748b4 100644 --- a/Src/GUI/Invoke-AsBuiltReportGui.ps1 +++ b/Src/GUI/Invoke-AsBuiltReportGui.ps1 @@ -53,13 +53,13 @@ using namespace AvaloniaUIShell.Avalonia.Media # Guard - AvaloniaUIShell must be available # --------------------------------------------------------------------------- if (-not (Get-Module -Name AvaloniaUIShell -ListAvailable)) { - Write-Error @" + Write-Error @' The AvaloniaUIShell module is required to run the GUI. Install it with: Install-PSResource -Name AvaloniaUIShell On macOS / Linux also run: Enable-AUIExecution -"@ +'@ return } @@ -83,17 +83,17 @@ $LanguageCodes = @( # Shared state accessible from the background runspace # --------------------------------------------------------------------------- $syncHash = [Hashtable]::Synchronized(@{ - Log = '' - IsRunning = $false - CancelRequest = $false -}) + Log = '' + IsRunning = $false + CancelRequest = $false + }) # --------------------------------------------------------------------------- # Helper: append a line to the log TextBox (thread-safe via AvaloniaUIShell) # --------------------------------------------------------------------------- function Add-LogLine { param ([string]$Message) - $ts = (Get-Date).ToString('HH:mm:ss') + $ts = (Get-Date).ToString('HH:mm:ss') $line = "[$ts] $Message" $syncHash.LogBox.Text += "$line`n" # Auto-scroll to bottom @@ -104,22 +104,22 @@ function Add-LogLine { # -- WINDOW ------------------------------------------------------------------ # =========================================================================== $win = [Window]::new() -$win.Title = 'AsBuiltReport - System Resources' -$win.Width = 680 -$win.Height = 740 -$win.WindowStartupLocation = 'CenterScreen' -$win.CanMaximize = $false +$win.Title = 'AsBuiltReport - System Resources' +$win.Width = 680 +$win.Height = 740 +$win.WindowStartupLocation = 'CenterScreen' +$win.CanMaximize = $false # =========================================================================== # -- SECTION HEADER FACTORY -------------------------------------------------- # =========================================================================== function Get-SectionHeader { param ([string]$Text) - $tb = [TextBlock]::new() - $tb.Text = $Text - $tb.FontSize = 13 + $tb = [TextBlock]::new() + $tb.Text = $Text + $tb.FontSize = 13 $tb.FontWeight = 'SemiBold' - $tb.Margin = [Thickness]::new(0, 12, 0, 4) + $tb.Margin = [Thickness]::new(0, 12, 0, 4) $tb } @@ -128,11 +128,11 @@ function Get-SectionHeader { # =========================================================================== # Target -$lblTarget = [TextBlock]::new(); $lblTarget.Text = 'Target:' -$lblTarget.Width = 130; $lblTarget.VerticalAlignment = 'Center' -$txtTarget = [TextBox]::new() -$txtTarget.Text = [System.Net.Dns]::GetHostName() -$txtTarget.Width = 380 +$lblTarget = [TextBlock]::new(); $lblTarget.Text = 'Target:' +$lblTarget.Width = 130; $lblTarget.VerticalAlignment = 'Center' +$txtTarget = [TextBox]::new() +$txtTarget.Text = [System.Net.Dns]::GetHostName() +$txtTarget.Width = 380 $syncHash.TxtTarget = $txtTarget $rowTarget = [StackPanel]::new() @@ -141,35 +141,35 @@ $rowTarget.Children.Add($lblTarget) $rowTarget.Children.Add($txtTarget) # Output Folder -$lblFolder = [TextBlock]::new(); $lblFolder.Text = 'Output Folder:' -$lblFolder.Width = 130; $lblFolder.VerticalAlignment = 'Center' -$txtFolder = [TextBox]::new() -$txtFolder.Text = [Environment]::GetFolderPath('Desktop') -$txtFolder.Width = 300 +$lblFolder = [TextBlock]::new(); $lblFolder.Text = 'Output Folder:' +$lblFolder.Width = 130; $lblFolder.VerticalAlignment = 'Center' +$txtFolder = [TextBox]::new() +$txtFolder.Text = [Environment]::GetFolderPath('Desktop') +$txtFolder.Width = 300 $syncHash.TxtFolder = $txtFolder -$btnBrowse = [Button]::new() +$btnBrowse = [Button]::new() $btnBrowse.Content = 'Browse Folder' -$btnBrowse.Width = 90 +$btnBrowse.Width = 120 $btnBrowse.AddClick({ - # Use PowerShell's native folder-picker dialog if available (Windows only); - # on other platforms fall back to manual text entry. - if ($IsWindows) { - try { - Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop - $dlg = [System.Windows.Forms.FolderBrowserDialog]::new() - $dlg.Description = 'Select the report output folder' - $dlg.SelectedPath = $syncHash.TxtFolder.Text - if ($dlg.ShowDialog() -eq 'OK') { - $syncHash.TxtFolder.Text = $dlg.SelectedPath + # Use PowerShell's native folder-picker dialog if available (Windows only); + # on other platforms fall back to manual text entry. + if ($IsWindows) { + try { + Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop + $dlg = [System.Windows.Forms.FolderBrowserDialog]::new() + $dlg.Description = 'Select the report output folder' + $dlg.SelectedPath = $syncHash.TxtFolder.Text + if ($dlg.ShowDialog() -eq 'OK') { + $syncHash.TxtFolder.Text = $dlg.SelectedPath + } + } catch { + Add-LogLine "Folder browser unavailable: $_. Please type the path directly." } - } catch { - Add-LogLine "Folder browser unavailable: $_. Please type the path directly." + } else { + Add-LogLine 'Folder browser not available on this platform - type the path directly.' } - } else { - Add-LogLine 'Folder browser not available on this platform - type the path directly.' - } -}) + }) $rowFolder = [StackPanel]::new() $rowFolder.Orientation = 'Horizontal'; $rowFolder.Spacing = 8 @@ -178,11 +178,11 @@ $rowFolder.Children.Add($txtFolder) $rowFolder.Children.Add($btnBrowse) # Report Name -$lblName = [TextBlock]::new(); $lblName.Text = 'Report Name:' -$lblName.Width = 130; $lblName.VerticalAlignment = 'Center' -$txtName = [TextBox]::new() -$txtName.Text = 'System Resources As Built Report' -$txtName.Width = 380 +$lblName = [TextBlock]::new(); $lblName.Text = 'Report Name:' +$lblName.Width = 130; $lblName.VerticalAlignment = 'Center' +$txtName = [TextBox]::new() +$txtName.Text = 'System Resources As Built Report' +$txtName.Width = 380 $syncHash.TxtName = $txtName $rowName = [StackPanel]::new() @@ -191,10 +191,10 @@ $rowName.Children.Add($lblName) $rowName.Children.Add($txtName) # Language -$lblLang = [TextBlock]::new(); $lblLang.Text = 'Language:' -$lblLang.Width = 130; $lblLang.VerticalAlignment = 'Center' -$cbLang = [ComboBox]::new() -$cbLang.Width = 160 +$lblLang = [TextBlock]::new(); $lblLang.Text = 'Language:' +$lblLang.Width = 130; $lblLang.VerticalAlignment = 'Center' +$cbLang = [ComboBox]::new() +$cbLang.Width = 160 foreach ($code in $LanguageCodes) { $item = [ComboBoxItem]::new(); $item.Content = $code $cbLang.Items.Add($item) | Out-Null @@ -217,13 +217,13 @@ $panelConfig.Children.Add($rowLang) # =========================================================================== # -- OUTPUT FORMATS ---------------------------------------------------------- # =========================================================================== -$chkHtml = [CheckBox]::new(); $chkHtml.Content = 'HTML'; $chkHtml.IsChecked = $true -$chkWord = [CheckBox]::new(); $chkWord.Content = 'Word'; $chkWord.IsChecked = $false -$chkText = [CheckBox]::new(); $chkText.Content = 'Text'; $chkText.IsChecked = $false +$chkHtml = [CheckBox]::new(); $chkHtml.Content = 'HTML'; $chkHtml.IsChecked = $true +$chkWord = [CheckBox]::new(); $chkWord.Content = 'Word'; $chkWord.IsChecked = $false +$chkText = [CheckBox]::new(); $chkText.Content = 'Text'; $chkText.IsChecked = $false $chkExcel = [CheckBox]::new(); $chkExcel.Content = 'Excel'; $chkExcel.IsChecked = $false -$syncHash.ChkHtml = $chkHtml -$syncHash.ChkWord = $chkWord -$syncHash.ChkText = $chkText +$syncHash.ChkHtml = $chkHtml +$syncHash.ChkWord = $chkWord +$syncHash.ChkText = $chkText $syncHash.ChkExcel = $chkExcel $rowFormats = [StackPanel]::new() @@ -240,13 +240,13 @@ $panelFormats.Children.Add($rowFormats) # =========================================================================== # -- OPTIONS ----------------------------------------------------------------- # =========================================================================== -$chkTimestamp = [CheckBox]::new(); $chkTimestamp.Content = 'Append Timestamp'; $chkTimestamp.IsChecked = $true -$chkHealth = [CheckBox]::new(); $chkHealth.Content = 'Enable HealthCheck'; $chkHealth.IsChecked = $false -$chkDiagrams = [CheckBox]::new(); $chkDiagrams.Content = 'Enable Diagrams'; $chkDiagrams.IsChecked = $true -$chkExportDiag = [CheckBox]::new(); $chkExportDiag.Content = 'Export Diagrams'; $chkExportDiag.IsChecked= $false -$syncHash.ChkTimestamp = $chkTimestamp -$syncHash.ChkHealth = $chkHealth -$syncHash.ChkDiagrams = $chkDiagrams +$chkTimestamp = [CheckBox]::new(); $chkTimestamp.Content = 'Append Timestamp'; $chkTimestamp.IsChecked = $true +$chkHealth = [CheckBox]::new(); $chkHealth.Content = 'Enable HealthCheck'; $chkHealth.IsChecked = $false +$chkDiagrams = [CheckBox]::new(); $chkDiagrams.Content = 'Enable Diagrams'; $chkDiagrams.IsChecked = $true +$chkExportDiag = [CheckBox]::new(); $chkExportDiag.Content = 'Export Diagrams'; $chkExportDiag.IsChecked = $false +$syncHash.ChkTimestamp = $chkTimestamp +$syncHash.ChkHealth = $chkHealth +$syncHash.ChkDiagrams = $chkDiagrams $syncHash.ChkExportDiag = $chkExportDiag $rowOpt1 = [StackPanel]::new() @@ -261,7 +261,7 @@ $rowOpt2.Children.Add($chkExportDiag) # Diagram Theme $lblTheme = [TextBlock]::new(); $lblTheme.Text = 'Diagram Theme:'; $lblTheme.VerticalAlignment = 'Center' -$cbTheme = [ComboBox]::new(); $cbTheme.Width = 120 +$cbTheme = [ComboBox]::new(); $cbTheme.Width = 120 foreach ($t in @('White', 'Black', 'Neon')) { $item = [ComboBoxItem]::new(); $item.Content = $t $cbTheme.Items.Add($item) | Out-Null @@ -285,35 +285,35 @@ $panelOptions.Children.Add($rowTheme) # =========================================================================== function Get-InfoLevelRow { param ([string]$Label, [int]$Default = 1) - $lbl = [TextBlock]::new(); $lbl.Text = "${Label}:"; $lbl.Width = 100; $lbl.VerticalAlignment = 'Center' - $cb = [ComboBox]::new(); $cb.Width = 70 + $lbl = [TextBlock]::new(); $lbl.Text = "${Label}:"; $lbl.Width = 100; $lbl.VerticalAlignment = 'Center' + $cb = [ComboBox]::new(); $cb.Width = 70 foreach ($n in 0..2) { $item = [ComboBoxItem]::new(); $item.Content = $n.ToString() $cb.Items.Add($item) | Out-Null } $cb.SelectedIndex = $Default - $row = [StackPanel]::new(); $row.Orientation = 'Horizontal'; $row.Spacing = 6 + $row = [StackPanel]::new(); $row.Orientation = 'Horizontal'; $row.Spacing = 6 $row.Children.Add($lbl); $row.Children.Add($cb) return $row, $cb } -$rowDate, $cbDate = Get-InfoLevelRow 'Date' 1 -$rowTZ, $cbTZ = Get-InfoLevelRow 'TimeZone' 2 -$rowUptime, $cbUptime = Get-InfoLevelRow 'Uptime' 2 -$rowPSHost, $cbPSHost = Get-InfoLevelRow 'PSHost' 2 +$rowDate, $cbDate = Get-InfoLevelRow 'Date' 1 +$rowTZ, $cbTZ = Get-InfoLevelRow 'TimeZone' 2 +$rowUptime, $cbUptime = Get-InfoLevelRow 'Uptime' 2 +$rowPSHost, $cbPSHost = Get-InfoLevelRow 'PSHost' 2 $rowProcess, $cbProcess = Get-InfoLevelRow 'ProcessInfo' 1 -$syncHash.CbDate = $cbDate -$syncHash.CbTZ = $cbTZ -$syncHash.CbUptime = $cbUptime -$syncHash.CbPSHost = $cbPSHost +$syncHash.CbDate = $cbDate +$syncHash.CbTZ = $cbTZ +$syncHash.CbUptime = $cbUptime +$syncHash.CbPSHost = $cbPSHost $syncHash.CbProcess = $cbProcess -$gridInfo = [UniformGrid]::new() -$gridInfo.Columns = 2 -$gridInfo.Children.Add($rowDate) | Out-Null -$gridInfo.Children.Add($rowTZ) | Out-Null -$gridInfo.Children.Add($rowUptime) | Out-Null -$gridInfo.Children.Add($rowPSHost) | Out-Null +$gridInfo = [StackPanel]::new() +$gridInfo.Spacing = 6 +$gridInfo.Children.Add($rowDate) | Out-Null +$gridInfo.Children.Add($rowTZ) | Out-Null +$gridInfo.Children.Add($rowUptime) | Out-Null +$gridInfo.Children.Add($rowPSHost) | Out-Null $gridInfo.Children.Add($rowProcess) | Out-Null $panelInfo = [StackPanel]::new(); $panelInfo.Spacing = 6 @@ -323,76 +323,76 @@ $panelInfo.Children.Add($gridInfo) # =========================================================================== # -- PROGRESS & LOG ---------------------------------------------------------- # =========================================================================== -$progressBar = [ProgressBar]::new() +$progressBar = [ProgressBar]::new() $progressBar.IsIndeterminate = $true -$progressBar.IsVisible = $false -$progressBar.Height = 6 -$syncHash.ProgressBar = $progressBar +$progressBar.IsVisible = $false +$progressBar.Height = 6 +$syncHash.ProgressBar = $progressBar -$logBox = [TextBox]::new() -$logBox.IsReadOnly = $true +$logBox = [TextBox]::new() +$logBox.IsReadOnly = $true $logBox.AcceptsReturn = $true -$logBox.Height = 120 -$logBox.FontFamily = 'Courier New' -$logBox.FontSize = 11 -$logBox.Text = '' -$syncHash.LogBox = $logBox +$logBox.Height = 120 +$logBox.FontFamily = 'Courier New' +$logBox.FontSize = 11 +$logBox.Text = '' +$syncHash.LogBox = $logBox -$logScroll = [ScrollViewer]::new() -$logScroll.Height = 120 -$logScroll.Content = $logBox -$syncHash.LogScroll = $logScroll +$logScroll = [ScrollViewer]::new() +$logScroll.Height = 120 +$logScroll.Content = $logBox +$syncHash.LogScroll = $logScroll # =========================================================================== # -- GENERATE BUTTON --------------------------------------------------------- # =========================================================================== -$btnGenerate = [Button]::new() -$btnGenerate.Content = 'Generate Report' -$btnGenerate.HorizontalAlignment = 'Stretch' +$btnGenerate = [Button]::new() +$btnGenerate.Content = 'Generate Report' +$btnGenerate.HorizontalAlignment = 'Stretch' $btnGenerate.HorizontalContentAlignment = 'Center' -$btnGenerate.Height = 40 +$btnGenerate.Height = 40 $btnGenerate.Classes.Add('accent') -$syncHash.BtnGenerate = $btnGenerate +$syncHash.BtnGenerate = $btnGenerate -$lblStatus = [TextBlock]::new() -$lblStatus.Text = 'Ready.' +$lblStatus = [TextBlock]::new() +$lblStatus.Text = 'Ready.' $lblStatus.Margin = [Thickness]::new(0, 4, 0, 0) $syncHash.LblStatus = $lblStatus # --------------------------------------------------------------------------- # Generate button click - runs on RunspacePoolAsyncUI so the UI stays alive # --------------------------------------------------------------------------- -$generateCallback = [EventCallback]::new() -$generateCallback.RunspaceMode = 'RunspacePoolAsyncUI' +$generateCallback = [EventCallback]::new() +$generateCallback.RunspaceMode = 'RunspacePoolAsyncUI' $generateCallback.DisabledControlsWhileProcessing = $btnGenerate -$generateCallback.ArgumentList = $syncHash +$generateCallback.ArgumentList = $syncHash $generateCallback.ScriptBlock = { param ($syncHash) # ---- Collect values from UI ---------------------------------------- - $target = $syncHash.TxtTarget.Text.Trim() - $outFolder = $syncHash.TxtFolder.Text.Trim() - $reportName = $syncHash.TxtName.Text.Trim() - $language = ($syncHash.CbLang.SelectedItem).Content + $target = $syncHash.TxtTarget.Text.Trim() + $outFolder = $syncHash.TxtFolder.Text.Trim() + $reportName = $syncHash.TxtName.Text.Trim() + $language = ($syncHash.CbLang.SelectedItem).Content $formats = @() - if ($syncHash.ChkHtml.IsChecked) { $formats += 'Html' } - if ($syncHash.ChkWord.IsChecked) { $formats += 'Word' } - if ($syncHash.ChkText.IsChecked) { $formats += 'Text' } + if ($syncHash.ChkHtml.IsChecked) { $formats += 'Html' } + if ($syncHash.ChkWord.IsChecked) { $formats += 'Word' } + if ($syncHash.ChkText.IsChecked) { $formats += 'Text' } if ($syncHash.ChkExcel.IsChecked) { $formats += 'Excel' } - $useTimestamp = [bool]$syncHash.ChkTimestamp.IsChecked - $useHealth = [bool]$syncHash.ChkHealth.IsChecked - $diagTheme = ($syncHash.CbTheme.SelectedItem).Content + $useTimestamp = [bool]$syncHash.ChkTimestamp.IsChecked + $useHealth = [bool]$syncHash.ChkHealth.IsChecked + $diagTheme = ($syncHash.CbTheme.SelectedItem).Content - $infoDate = [int]($syncHash.CbDate.SelectedItem).Content - $infoTZ = [int]($syncHash.CbTZ.SelectedItem).Content - $infoUptime = [int]($syncHash.CbUptime.SelectedItem).Content - $infoPSHost = [int]($syncHash.CbPSHost.SelectedItem).Content + $infoDate = [int]($syncHash.CbDate.SelectedItem).Content + $infoTZ = [int]($syncHash.CbTZ.SelectedItem).Content + $infoUptime = [int]($syncHash.CbUptime.SelectedItem).Content + $infoPSHost = [int]($syncHash.CbPSHost.SelectedItem).Content $infoProcess = [int]($syncHash.CbProcess.SelectedItem).Content - $enableDiag = [bool]$syncHash.ChkDiagrams.IsChecked - $exportDiag = [bool]$syncHash.ChkExportDiag.IsChecked + $enableDiag = [bool]$syncHash.ChkDiagrams.IsChecked + $exportDiag = [bool]$syncHash.ChkExportDiag.IsChecked # ---- Validate ------------------------------------------------------- if (-not $target) { @@ -410,10 +410,10 @@ $generateCallback.ScriptBlock = { # ---- Show progress -------------------------------------------------- $syncHash.ProgressBar.IsVisible = $true - $syncHash.LblStatus.Text = 'Generating report...' - $syncHash.LogBox.Text = '' + $syncHash.LblStatus.Text = 'Generating report...' + $syncHash.LogBox.Text = '' - $ts = (Get-Date).ToString('HH:mm:ss') + $ts = (Get-Date).ToString('HH:mm:ss') $syncHash.LogBox.Text += "[$ts] Starting report generation for target '$target'...`n" $syncHash.LogBox.Text += "[$ts] Output folder : $outFolder`n" $syncHash.LogBox.Text += "[$ts] Format(s) : $($formats -join ', ')`n" @@ -422,34 +422,34 @@ $generateCallback.ScriptBlock = { # ---- Build a temporary JSON config file ---------------------------- $configObj = [ordered]@{ - Report = [ordered]@{ - Name = $reportName - Version = '1.0' - Status = 'Released' - Language = $language - ShowCoverPageImage = $true + Report = [ordered]@{ + Name = $reportName + Version = '1.0' + Status = 'Released' + Language = $language + ShowCoverPageImage = $true ShowTableOfContents = $true - ShowHeaderFooter = $true - ShowTableCaptions = $true + ShowHeaderFooter = $true + ShowTableCaptions = $true } - Options = [ordered]@{ - EnableDiagrams = $enableDiag - EnableDiagramDebug = $false - EnableDiagramMainLogo = $false - DiagramTheme = $diagTheme - DiagramWaterMark = '' - ExportDiagrams = $exportDiag - ExportDiagramsFormat = @('png') - EnableDiagramSignature = $false - DiagramColumnSize = 4 - SignatureAuthorName = '' - SignatureCompanyName = '' + Options = [ordered]@{ + EnableDiagrams = $enableDiag + EnableDiagramDebug = $false + EnableDiagramMainLogo = $false + DiagramTheme = $diagTheme + DiagramWaterMark = '' + ExportDiagrams = $exportDiag + ExportDiagramsFormat = @('png') + EnableDiagramSignature = $false + DiagramColumnSize = 4 + SignatureAuthorName = '' + SignatureCompanyName = '' } InfoLevel = [ordered]@{ - Date = $infoDate - TimeZone = $infoTZ - Uptime = $infoUptime - PSHost = $infoPSHost + Date = $infoDate + TimeZone = $infoTZ + Uptime = $infoUptime + PSHost = $infoPSHost ProcessInfo = $infoProcess } HealthCheck = [ordered]@{} @@ -466,16 +466,16 @@ $generateCallback.ScriptBlock = { # ---- Assemble New-AsBuiltReport parameters ------------------------- $abrParams = @{ - Report = 'System.Resources' - Target = $target - Format = $formats - OutputFolderPath = $outFolder + Report = 'System.Resources' + Target = $target + Format = $formats + OutputFolderPath = $outFolder ReportConfigFilePath = $tmpConfig - ReportLanguage = $language - ErrorAction = 'Stop' + ReportLanguage = $language + ErrorAction = 'Stop' } - if ($useTimestamp) { $abrParams['Timestamp'] = $true } - if ($useHealth) { $abrParams['EnableHealthCheck'] = $true } + if ($useTimestamp) { $abrParams['Timestamp'] = $true } + if ($useHealth) { $abrParams['EnableHealthCheck'] = $true } # ---- Run the report ------------------------------------------------- try { @@ -485,18 +485,18 @@ $generateCallback.ScriptBlock = { New-AsBuiltReport @abrParams 4>&1 | ForEach-Object { $line = $_.ToString() - $ts2 = (Get-Date).ToString('HH:mm:ss') + $ts2 = (Get-Date).ToString('HH:mm:ss') $syncHash.LogBox.Text += "[$ts2] $line`n" $syncHash.LogScroll.ScrollToEnd() } $ts = (Get-Date).ToString('HH:mm:ss') - $syncHash.LogBox.Text += "[$ts] Report generation complete.`n" - $syncHash.LblStatus.Text = "Done! Report saved to: $outFolder" + $syncHash.LogBox.Text += "[$ts] Report generation complete.`n" + $syncHash.LblStatus.Text = "Done! Report saved to: $outFolder" } catch { $ts = (Get-Date).ToString('HH:mm:ss') - $syncHash.LogBox.Text += "[$ts] ERROR: $_`n" - $syncHash.LblStatus.Text = "ERROR: $($_.Exception.Message)" + $syncHash.LogBox.Text += "[$ts] ERROR: $_`n" + $syncHash.LblStatus.Text = "ERROR: $($_.Exception.Message)" } finally { # Clean up temp config if (Test-Path $tmpConfig) { Remove-Item $tmpConfig -Force -ErrorAction SilentlyContinue } @@ -510,8 +510,8 @@ $btnGenerate.AddClick($generateCallback) # =========================================================================== # -- LAYOUT - stack everything into a ScrollViewer --------------------------- # =========================================================================== -$mainPanel = [StackPanel]::new() -$mainPanel.Margin = [Thickness]::new(20) +$mainPanel = [StackPanel]::new() +$mainPanel.Margin = [Thickness]::new(20) $mainPanel.Spacing = 4 $mainPanel.Children.Add($panelConfig) $mainPanel.Children.Add($panelFormats) @@ -522,7 +522,7 @@ $mainPanel.Children.Add($btnGenerate) $mainPanel.Children.Add($lblStatus) $mainPanel.Children.Add($logScroll) -$scroll = [ScrollViewer]::new() +$scroll = [ScrollViewer]::new() $scroll.Content = $mainPanel $win.Content = $scroll