Last active
February 22, 2026 09:05
-
-
Save NotNahid/14e55b707b99d50649223da7a60916f4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <#PSScriptInfo | |
| .VERSION 2.2.0 | |
| .GUID 8f696a63-fc1a-416a-bc32-6b4e52898647 | |
| .AUTHOR NotNahid | |
| .DESCRIPTION | |
| Dynamic utility launcher that downloads and runs various tools. | |
| #> | |
| Param() | |
| # ╔════════════════════════════════════════════════════════════════════════════╗ | |
| # ║ ║ | |
| # ║ NAHID POWER UTILITIES - MAIN SCRIPT ║ | |
| # ║ ║ | |
| # ║ Sections: ║ | |
| # ║ 1. IMPORTS & SETUP ║ | |
| # ║ 2. DATA LOADING ║ | |
| # ║ 3. DATA VALIDATION ║ | |
| # ║ 4. THEME CONFIGURATION ║ | |
| # ║ 5. HELPER FUNCTIONS ║ | |
| # ║ 6. DIALOG FUNCTIONS ║ | |
| # ║ 7. UTILITY LAUNCHER (WITH REAL-TIME STATUS) ║ | |
| # ║ 8. THEME FUNCTIONS ║ | |
| # ║ 9. UI CARD BUILDER ║ | |
| # ║ 10. MAIN FORM SETUP ║ | |
| # ║ 11. HEADER SECTION ║ | |
| # ║ 12. UTILITIES PANEL ║ | |
| # ║ 13. STATUS BAR ║ | |
| # ║ 14. DISPLAY UPDATE FUNCTION ║ | |
| # ║ 15. EVENT HANDLERS ║ | |
| # ║ 16. APPLICATION LAUNCH ║ | |
| # ║ ║ | |
| # ╚════════════════════════════════════════════════════════════════════════════╝ | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 1: IMPORTS & SETUP | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Load required .NET assemblies for Windows Forms GUI | |
| Add-Type -AssemblyName System.Windows.Forms | |
| Add-Type -AssemblyName System.Drawing | |
| # Enable visual styles for modern look | |
| [System.Windows.Forms.Application]::EnableVisualStyles() | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 2: DATA LOADING | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Fetch utilities database from remote JSON or use fallback | |
| $configUrl = "https://raw.githubusercontent.com/NotNahid/powershell/refs/heads/main/Main%20Links%20File%20Json/utilities.json" | |
| try { | |
| $Utilities = Invoke-RestMethod -Uri $configUrl -UseBasicParsing | |
| Write-Host "Loaded $($Utilities.Count) utilities from online database." -ForegroundColor Green | |
| } | |
| catch { | |
| Write-Warning "Failed to load online utility database. Using fallback." | |
| $Utilities = @( | |
| [PSCustomObject]@{ | |
| Name = "Ghost Typer" | |
| Link = "https://gist.githubusercontent.com/NotNahid/70de29086ac0bbeab57a9a75a4f04a89/raw/copy-script.ps1" | |
| Desc = "Auto-type text anywhere" | |
| Category = "Automation" | |
| Tags = @("typing", "automation", "productivity") | |
| } | |
| ) | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 3: DATA VALIDATION | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Validate each utility has required fields before displaying | |
| function Test-Utilities { | |
| param($RawUtilities) | |
| $validatedUtilities = @() | |
| $requiredFields = @("Name", "Link", "Desc", "Category", "Tags") | |
| foreach ($util in $RawUtilities) { | |
| $isValid = $true | |
| $missingFields = @() | |
| foreach ($field in $requiredFields) { | |
| $prop = $util.PSObject.Properties[$field] | |
| $value = if ($null -ne $prop) { $prop.Value } else { $null } | |
| if ($null -eq $prop -or $null -eq $value -or | |
| ($value -is [string] -and [string]::IsNullOrWhiteSpace($value))) { | |
| $isValid = $false | |
| $missingFields += $field | |
| } | |
| } | |
| if (-not $isValid) { | |
| Write-Warning "Skipping utility: Missing fields [$($missingFields -join ', ')]" | |
| continue | |
| } | |
| $validatedUtilities += $util | |
| } | |
| return $validatedUtilities | |
| } | |
| $script:ValidatedUtilities = Test-Utilities -RawUtilities $Utilities | |
| if ($script:ValidatedUtilities.Count -eq 0) { | |
| Write-Error "No valid utilities found. Exiting." | |
| exit 1 | |
| } | |
| # ╔══════════════════════════════════════════════════════════╗ | |
| # ║ 🎨 PREMIUM DARK THEME - Modern Fusion UI ║ | |
| # ║ Inspired by: Catppuccin + Fluent + Discord + VS Code ║ | |
| # ╚══════════════════════════════════════════════════════════╝ | |
| $DarkTheme = @{ | |
| # --- Base Layers --- | |
| Background = [System.Drawing.Color]::FromArgb(17, 17, 27) # #11111B Deep crust (Catppuccin) | |
| Surface = [System.Drawing.Color]::FromArgb(24, 24, 37) # #181825 Mantle layer | |
| SurfaceLight = [System.Drawing.Color]::FromArgb(30, 30, 46) # #1E1E2E Base layer | |
| SurfaceHover = [System.Drawing.Color]::FromArgb(49, 50, 68) # #313244 Hover overlay | |
| # --- Accent Colors --- | |
| Primary = [System.Drawing.Color]::FromArgb(137, 180, 250) # #89B4FA Sapphire blue (Catppuccin) | |
| PrimaryHover = [System.Drawing.Color]::FromArgb(116, 199, 236) # #74C7EC Sky blue glow | |
| # --- Status Colors --- | |
| Success = [System.Drawing.Color]::FromArgb(166, 227, 161) # #A6E3A1 Mint green | |
| SuccessHover = [System.Drawing.Color]::FromArgb(148, 226, 213) # #94E2D5 Teal glow | |
| Warning = [System.Drawing.Color]::FromArgb(249, 226, 175) # #F9E2AF Warm peach | |
| Danger = [System.Drawing.Color]::FromArgb(243, 139, 168) # #F38BA8 Soft red/pink | |
| DangerHover = [System.Drawing.Color]::FromArgb(235, 160, 172) # #EBA0AC Flamingo | |
| # --- Typography --- | |
| Text = [System.Drawing.Color]::FromArgb(205, 214, 244) # #CDD6F4 Crisp white-lavender | |
| TextSecondary = [System.Drawing.Color]::FromArgb(166, 173, 200) # #A6ADC8 Subtle overlay | |
| TextMuted = [System.Drawing.Color]::FromArgb(108, 112, 134) # #6C7086 Muted overlay | |
| # --- UI Elements --- | |
| Border = [System.Drawing.Color]::FromArgb(69, 71, 90) # #45475A Subtle separator | |
| SearchBox = [System.Drawing.Color]::FromArgb(24, 24, 37) # #181825 Input field | |
| # --- BONUS: Extra Accent Colors (use if needed) --- | |
| Accent1 = [System.Drawing.Color]::FromArgb(203, 166, 247) # #CBA6F7 Mauve/Purple | |
| Accent2 = [System.Drawing.Color]::FromArgb(250, 179, 135) # #FAB387 Peach/Orange | |
| Accent3 = [System.Drawing.Color]::FromArgb(137, 220, 235) # #89DCEB Cyan | |
| Accent4 = [System.Drawing.Color]::FromArgb(245, 194, 231) # #F5C2E7 Pink | |
| GradientStart = [System.Drawing.Color]::FromArgb(137, 180, 250) # Blue gradient start | |
| GradientEnd = [System.Drawing.Color]::FromArgb(203, 166, 247) # Purple gradient end | |
| } | |
| # ╔══════════════════════════════════════════════════════════╗ | |
| # ║ ☀️ PREMIUM LIGHT THEME - Catppuccin Latte ║ | |
| # ╚══════════════════════════════════════════════════════════╝ | |
| $LightTheme = @{ | |
| # --- Base Layers --- | |
| Background = [System.Drawing.Color]::FromArgb(239, 241, 245) # #EFF1F5 Latte base | |
| Surface = [System.Drawing.Color]::FromArgb(230, 233, 239) # #E6E9EF Mantle | |
| SurfaceLight = [System.Drawing.Color]::FromArgb(220, 224, 232) # #DCE0E8 Crust | |
| SurfaceHover = [System.Drawing.Color]::FromArgb(204, 208, 218) # #CCD0DA Hover | |
| # --- Accent Colors --- | |
| Primary = [System.Drawing.Color]::FromArgb(30, 102, 245) # #1E66F5 Bold blue | |
| PrimaryHover = [System.Drawing.Color]::FromArgb(4, 165, 229) # #04A5E5 Sky blue | |
| # --- Status Colors --- | |
| Success = [System.Drawing.Color]::FromArgb(64, 160, 43) # #40A02B Rich green | |
| SuccessHover = [System.Drawing.Color]::FromArgb(23, 146, 153) # #179299 Teal | |
| Warning = [System.Drawing.Color]::FromArgb(223, 142, 29) # #DF8E1D Golden amber | |
| Danger = [System.Drawing.Color]::FromArgb(210, 15, 57) # #D20F39 Bold red | |
| DangerHover = [System.Drawing.Color]::FromArgb(230, 69, 83) # #E64553 Maroon | |
| # --- Typography --- | |
| Text = [System.Drawing.Color]::FromArgb(76, 79, 105) # #4C4F69 Rich dark text | |
| TextSecondary = [System.Drawing.Color]::FromArgb(92, 95, 119) # #5C5F77 Subtle text | |
| TextMuted = [System.Drawing.Color]::FromArgb(124, 127, 147) # #7C7F93 Muted text | |
| # --- UI Elements --- | |
| Border = [System.Drawing.Color]::FromArgb(188, 192, 204) # #BCC0CC Clean border | |
| SearchBox = [System.Drawing.Color]::FromArgb(230, 233, 239) # #E6E9EF Input field | |
| # --- BONUS: Extra Accent Colors --- | |
| Accent1 = [System.Drawing.Color]::FromArgb(136, 57, 239) # #8839EF Purple | |
| Accent2 = [System.Drawing.Color]::FromArgb(254, 100, 11) # #FE640B Orange | |
| Accent3 = [System.Drawing.Color]::FromArgb(4, 165, 229) # #04A5E5 Cyan | |
| Accent4 = [System.Drawing.Color]::FromArgb(234, 118, 203) # #EA76CB Pink | |
| GradientStart = [System.Drawing.Color]::FromArgb(30, 102, 245) # Blue gradient | |
| GradientEnd = [System.Drawing.Color]::FromArgb(136, 57, 239) # Purple gradient | |
| } | |
| # --- Set Active Theme --- | |
| $script:Colors = $DarkTheme | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 5: HELPER FUNCTIONS | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Small utility functions used throughout the application | |
| # --- Global Tooltip Object --- | |
| $script:tooltip = New-Object System.Windows.Forms.ToolTip | |
| $script:tooltip.AutoPopDelay = 5000 | |
| $script:tooltip.InitialDelay = 300 | |
| $script:tooltip.ReshowDelay = 100 | |
| $script:tooltip.ShowAlways = $true | |
| $script:tooltip.IsBalloon = $false | |
| # --- Add Tooltip to Control --- | |
| function Add-Tooltip { | |
| param( | |
| $Control, | |
| [string]$Text | |
| ) | |
| try { | |
| if ($null -ne $Control -and $null -ne $script:tooltip) { | |
| $script:tooltip.SetToolTip($Control, $Text) | |
| } | |
| } | |
| catch { | |
| Write-Warning "Tooltip error: $_" | |
| } | |
| } | |
| # --- Update Status Bar Text --- | |
| function Update-StatusBar { | |
| param([string]$Message) | |
| try { | |
| if ($null -ne $script:statusLabel) { | |
| $script:statusLabel.Text = $Message | |
| [System.Windows.Forms.Application]::DoEvents() | |
| } | |
| } | |
| catch { } | |
| } | |
| # --- Update Button State (Thread-Safe) --- | |
| function Set-ButtonState { | |
| param( | |
| $Button, | |
| [string]$Text, | |
| [System.Drawing.Color]$BackColor, | |
| [System.Drawing.Color]$ForeColor = [System.Drawing.Color]::White, | |
| [bool]$Enabled = $false | |
| ) | |
| if ($null -eq $Button) { return } | |
| try { | |
| $Button.Text = $Text | |
| $Button.BackColor = $BackColor | |
| $Button.ForeColor = $ForeColor | |
| $Button.Enabled = $Enabled | |
| [System.Windows.Forms.Application]::DoEvents() | |
| } | |
| catch { | |
| Write-Warning "Button state error: $_" | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 6: DIALOG FUNCTIONS | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Custom dialog boxes for confirmations and utility details | |
| # --- Generic Custom Dialog --- | |
| function Show-CustomDialog { | |
| param( | |
| [string]$Title, | |
| [string]$Message, | |
| [array]$Buttons = @("OK"), | |
| [string]$DefaultButton = "OK", | |
| $ParentForm | |
| ) | |
| try { | |
| $dialog = New-Object System.Windows.Forms.Form | |
| $dialog.Text = $Title | |
| $dialog.Size = New-Object System.Drawing.Size(480, 220) | |
| $dialog.StartPosition = "CenterScreen" | |
| $dialog.FormBorderStyle = "FixedDialog" | |
| $dialog.BackColor = $script:Colors.Surface | |
| $dialog.ShowInTaskbar = $false | |
| $dialog.MaximizeBox = $false | |
| $dialog.MinimizeBox = $false | |
| $dialog.KeyPreview = $true | |
| # --- Message Panel --- | |
| $messagePanel = New-Object System.Windows.Forms.Panel | |
| $messagePanel.Location = New-Object System.Drawing.Point(20, 20) | |
| $messagePanel.Size = New-Object System.Drawing.Size(440, 110) | |
| $messagePanel.BackColor = $script:Colors.Surface | |
| $messageLabel = New-Object System.Windows.Forms.Label | |
| $messageLabel.Text = $Message | |
| $messageLabel.Dock = "Fill" | |
| $messageLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10) | |
| $messageLabel.ForeColor = $script:Colors.Text | |
| $messageLabel.TextAlign = "MiddleLeft" | |
| $messageLabel.Padding = New-Object System.Windows.Forms.Padding(10) | |
| $messagePanel.Controls.Add($messageLabel) | |
| # --- Button Panel --- | |
| $buttonPanel = New-Object System.Windows.Forms.Panel | |
| $buttonPanel.Dock = "Bottom" | |
| $buttonPanel.Height = 60 | |
| $buttonPanel.BackColor = $script:Colors.Surface | |
| $buttonWidth = 100 | |
| $buttonSpacing = 10 | |
| $totalButtonWidth = ($Buttons.Count * $buttonWidth) + (($Buttons.Count - 1) * $buttonSpacing) | |
| $startX = ($dialog.Width - $totalButtonWidth) / 2 | |
| $firstButton = $null | |
| for ($i = 0; $i -lt $Buttons.Count; $i++) { | |
| $btn = New-Object System.Windows.Forms.Button | |
| $btn.Text = $Buttons[$i] | |
| $btn.Size = New-Object System.Drawing.Size($buttonWidth, 36) | |
| $btn.Location = New-Object System.Drawing.Point(($startX + ($i * ($buttonWidth + $buttonSpacing))), 12) | |
| $btn.FlatStyle = "Flat" | |
| $btn.FlatAppearance.BorderSize = 1 | |
| $btn.Font = New-Object System.Drawing.Font("Segoe UI", 10) | |
| $btn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $btn.Tag = $Buttons[$i] | |
| $btn.TabIndex = $i | |
| if ($Buttons[$i] -match "Yes|OK|Confirm|Launch|Run") { | |
| $btn.BackColor = $script:Colors.Primary | |
| $btn.ForeColor = [System.Drawing.Color]::White | |
| $btn.FlatAppearance.BorderColor = $script:Colors.Primary | |
| } | |
| else { | |
| $btn.BackColor = $script:Colors.SurfaceLight | |
| $btn.ForeColor = $script:Colors.Text | |
| $btn.FlatAppearance.BorderColor = $script:Colors.Border | |
| } | |
| $capturedDialog = $dialog | |
| $btn.Add_Click({ | |
| param($sender, $e) | |
| $capturedDialog.Tag = $sender.Tag | |
| $capturedDialog.Close() | |
| }) | |
| $btn.Add_MouseEnter({ | |
| if ($this.Text -match "Yes|OK|Confirm|Launch|Run") { | |
| $this.BackColor = $script:Colors.PrimaryHover | |
| } | |
| else { | |
| $this.BackColor = $script:Colors.SurfaceHover | |
| } | |
| }) | |
| $btn.Add_MouseLeave({ | |
| if ($this.Text -match "Yes|OK|Confirm|Launch|Run") { | |
| $this.BackColor = $script:Colors.Primary | |
| } | |
| else { | |
| $this.BackColor = $script:Colors.SurfaceLight | |
| } | |
| }) | |
| $buttonPanel.Controls.Add($btn) | |
| if ($i -eq 0) { $firstButton = $btn } | |
| if ($Buttons[$i] -eq $DefaultButton) { $dialog.AcceptButton = $btn } | |
| if ($Buttons[$i] -match "Cancel|No") { $dialog.CancelButton = $btn } | |
| } | |
| $dialog.Add_KeyDown({ | |
| param($sender, $e) | |
| if ($e.KeyCode -eq "Escape" -and $null -eq $dialog.CancelButton) { | |
| $dialog.Tag = "Cancel" | |
| $dialog.Close() | |
| $e.Handled = $true | |
| } | |
| }) | |
| $dialog.Controls.AddRange(@($buttonPanel, $messagePanel)) | |
| $dialog.Add_Shown({ | |
| if ($null -ne $firstButton) { $firstButton.Focus() } | |
| }) | |
| [void]$dialog.ShowDialog($ParentForm) | |
| return $dialog.Tag | |
| } | |
| catch { | |
| Write-Error "Dialog error: $_" | |
| return $null | |
| } | |
| } | |
| # --- Utility Details Dialog --- | |
| function Show-UtilityDetailsDialog { | |
| param($Util, $ParentForm) | |
| try { | |
| if ($null -eq $Util) { return $null } | |
| $dialog = New-Object System.Windows.Forms.Form | |
| $dialog.Text = $Util.Name | |
| $dialog.Size = New-Object System.Drawing.Size(550, 400) | |
| $dialog.StartPosition = "CenterScreen" | |
| $dialog.FormBorderStyle = "FixedDialog" | |
| $dialog.BackColor = $script:Colors.Surface | |
| $dialog.ShowInTaskbar = $false | |
| $dialog.MaximizeBox = $false | |
| $dialog.MinimizeBox = $false | |
| $dialog.KeyPreview = $true | |
| # --- Content Panel --- | |
| $contentPanel = New-Object System.Windows.Forms.Panel | |
| $contentPanel.Location = New-Object System.Drawing.Point(30, 20) | |
| $contentPanel.Size = New-Object System.Drawing.Size(490, 280) | |
| $contentPanel.BackColor = $script:Colors.Surface | |
| $yPos = 0 | |
| # Utility Name | |
| $nameLabel = New-Object System.Windows.Forms.Label | |
| $nameLabel.Text = $Util.Name | |
| $nameLabel.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $nameLabel.Size = New-Object System.Drawing.Size(490, 35) | |
| $nameLabel.Font = $script:Fonts.Heading1 | |
| $nameLabel.ForeColor = $script:Colors.Text | |
| $yPos += 45 | |
| # Category | |
| $categoryLabel = New-Object System.Windows.Forms.Label | |
| $categoryLabel.Text = "Category: " + $Util.Category | |
| $categoryLabel.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $categoryLabel.AutoSize = $true | |
| $categoryLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $categoryLabel.ForeColor = $script:Colors.Primary | |
| $yPos += 35 | |
| # Description Header | |
| $descHeader = New-Object System.Windows.Forms.Label | |
| $descHeader.Text = "Description" | |
| $descHeader.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $descHeader.Size = New-Object System.Drawing.Size(490, 20) | |
| $descHeader.Font = $script:Fonts.Heading3 | |
| $descHeader.ForeColor = $script:Colors.Text | |
| $descText = New-Object System.Windows.Forms.Label | |
| $descText.Text = $Util.Desc | |
| $descText.Location = New-Object System.Drawing.Point(0, ($yPos + 25)) | |
| $descText.Size = New-Object System.Drawing.Size(490, 40) | |
| $descText.Font = $script:Fonts.BodySmall | |
| $descText.ForeColor = $script:Colors.TextSecondary | |
| $yPos += 75 | |
| # Tags | |
| $tagsHeader = New-Object System.Windows.Forms.Label | |
| $tagsHeader.Text = "Tags" | |
| $tagsHeader.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $tagsHeader.Size = New-Object System.Drawing.Size(490, 20) | |
| $tagsHeader.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) | |
| $tagsHeader.ForeColor = $script:Colors.Text | |
| $tagsValue = $Util.Tags | |
| $tagsString = if ($tagsValue -is [array]) { $tagsValue -join ', ' } else { "$tagsValue" } | |
| $tagsText = New-Object System.Windows.Forms.Label | |
| $tagsText.Text = $tagsString | |
| $tagsText.Location = New-Object System.Drawing.Point(0, ($yPos + 25)) | |
| $tagsText.Size = New-Object System.Drawing.Size(490, 30) | |
| $tagsText.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $tagsText.ForeColor = $script:Colors.Primary | |
| $yPos += 65 | |
| # Source | |
| $linkHeader = New-Object System.Windows.Forms.Label | |
| $linkHeader.Text = "Source" | |
| $linkHeader.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $linkHeader.Size = New-Object System.Drawing.Size(490, 20) | |
| $linkHeader.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) | |
| $linkHeader.ForeColor = $script:Colors.Text | |
| $linkText = New-Object System.Windows.Forms.Label | |
| $linkText.Text = $Util.Link | |
| $linkText.Location = New-Object System.Drawing.Point(0, ($yPos + 25)) | |
| $linkText.Size = New-Object System.Drawing.Size(490, 30) | |
| $linkText.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $linkText.ForeColor = $script:Colors.TextSecondary | |
| $contentPanel.Controls.AddRange(@( | |
| $nameLabel, $categoryLabel, | |
| $descHeader, $descText, | |
| $tagsHeader, $tagsText, | |
| $linkHeader, $linkText | |
| )) | |
| # --- Button Panel --- | |
| $buttonPanel = New-Object System.Windows.Forms.Panel | |
| $buttonPanel.Dock = "Bottom" | |
| $buttonPanel.Height = 70 | |
| $buttonPanel.BackColor = $script:Colors.SurfaceLight | |
| $runBtn = New-Object System.Windows.Forms.Button | |
| $runBtn.Text = "Launch" | |
| $runBtn.Size = New-Object System.Drawing.Size(120, 40) | |
| $runBtn.Location = New-Object System.Drawing.Point(215, 15) | |
| $runBtn.BackColor = $script:Colors.Success | |
| $runBtn.ForeColor = [System.Drawing.Color]::White | |
| $runBtn.FlatStyle = "Flat" | |
| $runBtn.FlatAppearance.BorderSize = 0 | |
| $runBtn.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) | |
| $runBtn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $runBtn.Add_Click({ | |
| $dialog.Tag = "Run" | |
| $dialog.Close() | |
| }) | |
| $runBtn.Add_MouseEnter({ $this.BackColor = $script:Colors.SuccessHover }) | |
| $runBtn.Add_MouseLeave({ $this.BackColor = $script:Colors.Success }) | |
| $buttonPanel.Controls.Add($runBtn) | |
| $dialog.Controls.AddRange(@($buttonPanel, $contentPanel)) | |
| $dialog.Add_KeyDown({ | |
| param($sender, $e) | |
| if ($e.KeyCode -eq "Enter") { | |
| $dialog.Tag = "Run" | |
| $dialog.Close() | |
| $e.Handled = $true | |
| } | |
| elseif ($e.KeyCode -eq "Escape") { | |
| $dialog.Close() | |
| $e.Handled = $true | |
| } | |
| }) | |
| [void]$dialog.ShowDialog($ParentForm) | |
| return $dialog.Tag | |
| } | |
| catch { | |
| Write-Error "Details dialog error: $_" | |
| return $null | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 7: UTILITY LAUNCHER (WITH REAL-TIME STATUS) | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Core function to download and execute utilities with live progress updates | |
| # | |
| # Button States: | |
| # 1. "Downloading..." (Yellow) - Fetching script from URL | |
| # 2. "Starting..." (Orange) - Creating background job | |
| # 3. "Launching..." (Blue) - Waiting for job to initialize | |
| # 4. "Running" (Green) - Job is actively running | |
| # 5. "Done ✓" (Green) - Job completed successfully | |
| # 6. "Run" (Primary) - Ready for next launch | |
| function Invoke-Utility { | |
| param( | |
| $UtilData, | |
| $SenderButton | |
| ) | |
| # --- Color Definitions for Button States --- | |
| $ColorDownloading = [System.Drawing.Color]::FromArgb(255, 204, 0) # Yellow | |
| $ColorStarting = [System.Drawing.Color]::FromArgb(255, 149, 0) # Orange | |
| $ColorLaunching = [System.Drawing.Color]::FromArgb(0, 122, 255) # Blue | |
| $ColorRunning = [System.Drawing.Color]::FromArgb(52, 199, 89) # Green | |
| $ColorSuccess = [System.Drawing.Color]::FromArgb(52, 199, 89) # Green | |
| $ColorError = [System.Drawing.Color]::FromArgb(255, 69, 58) # Red | |
| $ColorText = [System.Drawing.Color]::FromArgb(0, 0, 0) # Black (for yellow bg) | |
| $ColorWhite = [System.Drawing.Color]::White | |
| try { | |
| if ($null -eq $UtilData) { return } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 1: DOWNLOADING | |
| # ═══════════════════════════════════════════════════════════════════ | |
| Write-Host "" | |
| Write-Host "=====================================" -ForegroundColor Cyan | |
| Write-Host "LAUNCHING: $($UtilData.Name)" -ForegroundColor Yellow | |
| Write-Host "=====================================" -ForegroundColor Cyan | |
| Write-Host "Source: $($UtilData.Link)" -ForegroundColor Gray | |
| # Update button: Downloading | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Downloading..." ` | |
| -BackColor $ColorDownloading ` | |
| -ForeColor $ColorText ` | |
| -Enabled $false | |
| Update-StatusBar "Downloading $($UtilData.Name)..." | |
| Write-Host "[1/5] Downloading script..." -ForegroundColor Yellow | |
| # Download the script | |
| $downloadStart = Get-Date | |
| $scriptContent = Invoke-RestMethod -Uri $UtilData.Link -UseBasicParsing | |
| $downloadTime = ((Get-Date) - $downloadStart).TotalSeconds | |
| Write-Host "[1/5] Downloaded ($([Math]::Round($downloadTime, 2))s)" -ForegroundColor Green | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 2: STARTING | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Update button: Starting | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Starting..." ` | |
| -BackColor $ColorStarting ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Starting $($UtilData.Name)..." | |
| Write-Host "[2/5] Creating background job..." -ForegroundColor Yellow | |
| # Create the background job | |
| $job = Start-Job -ScriptBlock { | |
| param($script) | |
| Invoke-Expression $script | |
| } -ArgumentList $scriptContent | |
| Write-Host "[2/5] Job created (ID: $($job.Id))" -ForegroundColor Green | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 3: LAUNCHING (WAIT FOR JOB TO START) | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Update button: Launching | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Launching..." ` | |
| -BackColor $ColorLaunching ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Launching $($UtilData.Name)..." | |
| Write-Host "[3/5] Waiting for job to start..." -ForegroundColor Yellow | |
| # Wait for job to leave "NotStarted" state (max 10 seconds) | |
| $launchTimeout = 10 | |
| $launchStart = Get-Date | |
| while ($job.State -eq "NotStarted") { | |
| Start-Sleep -Milliseconds 100 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| $elapsed = ((Get-Date) - $launchStart).TotalSeconds | |
| if ($elapsed -gt $launchTimeout) { | |
| Write-Host "[3/5] Launch timeout - proceeding anyway" -ForegroundColor Yellow | |
| break | |
| } | |
| } | |
| Write-Host "[3/5] Job state: $($job.State)" -ForegroundColor Green | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 4: RUNNING (MONITOR JOB) | |
| # ═══════════════════════════════════════════════════════════════════ | |
| if ($job.State -eq "Running") { | |
| # Update button: Running | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Running" ` | |
| -BackColor $ColorRunning ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Running: $($UtilData.Name) [Job $($job.Id)]" | |
| Write-Host "[4/5] Job is running..." -ForegroundColor Green | |
| # Monitor job for a few seconds to catch quick completions/failures | |
| $monitorTime = 3 # seconds to monitor | |
| $monitorStart = Get-Date | |
| while ($job.State -eq "Running") { | |
| Start-Sleep -Milliseconds 200 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| $elapsed = ((Get-Date) - $monitorStart).TotalSeconds | |
| if ($elapsed -gt $monitorTime) { | |
| # Job still running after monitor period - it's a long-running utility | |
| Write-Host "[4/5] Job continues in background" -ForegroundColor Green | |
| break | |
| } | |
| } | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 5: COMPLETION | |
| # ═══════════════════════════════════════════════════════════════════ | |
| Write-Host "[5/5] Final state: $($job.State)" -ForegroundColor Cyan | |
| # Check final job state | |
| switch ($job.State) { | |
| "Running" { | |
| # Still running - show success and reset | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Done ✓" ` | |
| -BackColor $ColorSuccess ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Launched: $($UtilData.Name) [Job $($job.Id) running]" | |
| Write-Host "Status: LAUNCHED SUCCESSFULLY" -ForegroundColor Green | |
| } | |
| "Completed" { | |
| # Job finished quickly | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Done ✓" ` | |
| -BackColor $ColorSuccess ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Completed: $($UtilData.Name)" | |
| Write-Host "Status: COMPLETED" -ForegroundColor Green | |
| # Check for any output | |
| $output = Receive-Job -Job $job -ErrorAction SilentlyContinue | |
| if ($output) { | |
| Write-Host "Output: $output" -ForegroundColor Gray | |
| } | |
| } | |
| "Failed" { | |
| # Job failed | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Failed ✗" ` | |
| -BackColor $ColorError ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| $errorMsg = $job.ChildJobs[0].JobStateInfo.Reason.Message | |
| Update-StatusBar "Failed: $($UtilData.Name)" | |
| Write-Host "Status: FAILED - $errorMsg" -ForegroundColor Red | |
| # Show error dialog | |
| Show-CustomDialog ` | |
| -Title "Launch Failed" ` | |
| -Message "The utility '$($UtilData.Name)' failed to run:`n`n$errorMsg" ` | |
| -Buttons @("OK") ` | |
| -ParentForm $script:mainForm | |
| } | |
| default { | |
| # Unknown state | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Done" ` | |
| -BackColor $ColorSuccess ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| Update-StatusBar "Launched: $($UtilData.Name) [$($job.State)]" | |
| Write-Host "Status: $($job.State)" -ForegroundColor Yellow | |
| } | |
| } | |
| Write-Host "=====================================" -ForegroundColor Cyan | |
| Write-Host "" | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PHASE 6: RESET BUTTON (AFTER DELAY) | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Keep "Done ✓" visible for 2 seconds, then reset | |
| Start-Sleep -Milliseconds 500 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| Start-Sleep -Milliseconds 500 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| Start-Sleep -Milliseconds 500 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| Start-Sleep -Milliseconds 500 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| # Reset button to default state | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Run" ` | |
| -BackColor $script:Colors.Primary ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $true | |
| } | |
| catch { | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # ERROR HANDLING | |
| # ═══════════════════════════════════════════════════════════════════ | |
| Write-Host "Status: FAILED - $($_.Exception.Message)" -ForegroundColor Red | |
| Write-Host "=====================================" -ForegroundColor Cyan | |
| Update-StatusBar "Error: $($UtilData.Name)" | |
| # Show error state | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Error ✗" ` | |
| -BackColor $ColorError ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $false | |
| # Show error dialog | |
| Show-CustomDialog ` | |
| -Title "Launch Error" ` | |
| -Message "Failed to launch '$($UtilData.Name)':`n`n$($_.Exception.Message)" ` | |
| -Buttons @("OK") ` | |
| -ParentForm $script:mainForm | |
| # Wait then reset | |
| Start-Sleep -Milliseconds 1500 | |
| [System.Windows.Forms.Application]::DoEvents() | |
| # Reset button | |
| Set-ButtonState -Button $SenderButton ` | |
| -Text "Run" ` | |
| -BackColor $script:Colors.Primary ` | |
| -ForeColor $ColorWhite ` | |
| -Enabled $true | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 8: THEME FUNCTIONS | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Toggle and apply themes to all UI elements | |
| function Switch-Theme { | |
| if ($script:CurrentTheme -eq "Dark") { | |
| $script:CurrentTheme = "Light" | |
| $script:Colors = $LightTheme | |
| } | |
| else { | |
| $script:CurrentTheme = "Dark" | |
| $script:Colors = $DarkTheme | |
| } | |
| Set-Theme | |
| } | |
| function Set-Theme { | |
| try { | |
| $script:mainForm.BackColor = $script:Colors.Background | |
| $script:headerPanel.BackColor = $script:Colors.Surface | |
| $script:titleLabel.ForeColor = $script:Colors.Text | |
| $script:subtitleLabel.ForeColor = $script:Colors.TextSecondary | |
| $script:searchContainer.BackColor = $script:Colors.SearchBox | |
| $script:searchBox.BackColor = $script:Colors.SearchBox | |
| $script:searchBox.ForeColor = $script:Colors.Text | |
| $script:clearBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:clearBtn.ForeColor = $script:Colors.TextSecondary | |
| $script:refreshBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:refreshBtn.ForeColor = $script:Colors.Primary | |
| $script:themeBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:themeBtn.ForeColor = $script:Colors.Text | |
| $script:utilitiesPanel.BackColor = $script:Colors.Background | |
| $script:statusBar.BackColor = $script:Colors.Surface | |
| $script:statusLabel.ForeColor = $script:Colors.Text | |
| $script:versionLabel.ForeColor = $script:Colors.Primary | |
| Update-UtilityDisplay | |
| } | |
| catch { | |
| Write-Warning "Theme apply error: $_" | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 9: UI CARD BUILDER | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Creates individual utility cards for the main panel | |
| function New-UtilityCard { | |
| param( | |
| $Util, | |
| [int]$ContainerWidth | |
| ) | |
| try { | |
| if ($null -eq $Util) { return $null } | |
| # --- Card Dimensions --- | |
| $cardWidth = [Math]::Max(400, $ContainerWidth - 60) | |
| $cardHeight = 90 | |
| # --- Card Container --- | |
| $card = New-Object System.Windows.Forms.Panel | |
| $card.Size = New-Object System.Drawing.Size($cardWidth, $cardHeight) | |
| $card.BackColor = $script:Colors.Surface | |
| $card.Tag = $Util | |
| $card.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $card.Anchor = "Top,Left,Right" | |
| $card.BorderStyle = "FixedSingle" | |
| # --- Title Label --- | |
| $titleLbl = New-Object System.Windows.Forms.Label | |
| $titleLbl.Text = $Util.Name | |
| $titleLbl.Location = New-Object System.Drawing.Point(20, 15) | |
| $titleLbl.Size = New-Object System.Drawing.Size(($cardWidth - 180), 24) | |
| $titleLbl.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold) | |
| $titleLbl.ForeColor = $script:Colors.Text | |
| $titleLbl.BackColor = [System.Drawing.Color]::Transparent | |
| $titleLbl.Anchor = "Top,Left,Right" | |
| Add-Tooltip -Control $titleLbl -Text "Click to view details" | |
| # --- Description Label --- | |
| $descLbl = New-Object System.Windows.Forms.Label | |
| $descLbl.Text = $Util.Desc | |
| $descLbl.Location = New-Object System.Drawing.Point(20, 42) | |
| $descLbl.Size = New-Object System.Drawing.Size(($cardWidth - 180), 18) | |
| $descLbl.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $descLbl.ForeColor = $script:Colors.TextSecondary | |
| $descLbl.BackColor = [System.Drawing.Color]::Transparent | |
| $descLbl.Anchor = "Top,Left,Right" | |
| # --- Category Label --- | |
| $catLbl = New-Object System.Windows.Forms.Label | |
| $catLbl.Text = $Util.Category | |
| $catLbl.Location = New-Object System.Drawing.Point(20, 63) | |
| $catLbl.AutoSize = $true | |
| $catLbl.Font = New-Object System.Drawing.Font("Segoe UI", 8) | |
| $catLbl.ForeColor = $script:Colors.Primary | |
| $catLbl.BackColor = [System.Drawing.Color]::Transparent | |
| # --- Run Button --- | |
| $runBtn = New-Object System.Windows.Forms.Button | |
| $runBtn.Text = "Run" | |
| $runBtn.Size = New-Object System.Drawing.Size(100, 35) | |
| $runBtn.Location = New-Object System.Drawing.Point(($cardWidth - 120), 28) | |
| $runBtn.BackColor = $script:Colors.Primary | |
| $runBtn.ForeColor = [System.Drawing.Color]::White | |
| $runBtn.FlatStyle = "Flat" | |
| $runBtn.FlatAppearance.BorderSize = 0 | |
| $runBtn.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) | |
| $runBtn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $runBtn.Anchor = "Top,Right" | |
| Add-Tooltip -Control $runBtn -Text "Launch $($Util.Name)" | |
| # --- Run Button Click --- | |
| $runBtn.Add_Click({ | |
| param($sender, $e) | |
| try { | |
| # Don't process if button is already in a loading state | |
| if ($sender.Text -ne "Run") { return } | |
| $utilData = $sender.Parent.Tag | |
| if ($null -eq $utilData) { return } | |
| $confirm = Show-CustomDialog ` | |
| -Title "Launch Utility" ` | |
| -Message "Launch '$($utilData.Name)'?`n`n$($utilData.Desc)`n`nSource: $($utilData.Link)" ` | |
| -Buttons @("Launch", "Cancel") ` | |
| -DefaultButton "Launch" ` | |
| -ParentForm $script:mainForm | |
| if ($confirm -eq "Launch") { | |
| Invoke-Utility -UtilData $utilData -SenderButton $sender | |
| } | |
| } | |
| catch { | |
| Write-Error "Run button error: $_" | |
| } | |
| }) | |
| # --- Card Click (Show Details) --- | |
| $card.Add_Click({ | |
| try { | |
| $utilData = $this.Tag | |
| if ($null -eq $utilData) { return } | |
| $result = Show-UtilityDetailsDialog -Util $utilData -ParentForm $script:mainForm | |
| if ($result -eq "Run") { | |
| foreach ($ctrl in $this.Controls) { | |
| if ($ctrl -is [System.Windows.Forms.Button] -and $ctrl.Text -eq "Run") { | |
| $ctrl.PerformClick() | |
| break | |
| } | |
| } | |
| } | |
| } | |
| catch { | |
| Write-Error "Card click error: $_" | |
| } | |
| }) | |
| # --- Hover Effects --- | |
| $card.Add_MouseEnter({ $this.BackColor = $script:Colors.SurfaceHover }) | |
| $card.Add_MouseLeave({ $this.BackColor = $script:Colors.Surface }) | |
| $runBtn.Add_MouseEnter({ | |
| # Only show hover effect if button is in default "Run" state | |
| if ($this.Enabled -and $this.Text -eq "Run") { | |
| $this.BackColor = $script:Colors.PrimaryHover | |
| } | |
| }) | |
| $runBtn.Add_MouseLeave({ | |
| # Only reset if button is in default "Run" state | |
| if ($this.Enabled -and $this.Text -eq "Run") { | |
| $this.BackColor = $script:Colors.Primary | |
| } | |
| }) | |
| # --- Add Controls to Card --- | |
| $card.Controls.AddRange(@($titleLbl, $descLbl, $catLbl, $runBtn)) | |
| return $card | |
| } | |
| catch { | |
| Write-Error "Card creation error: $_" | |
| return $null | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 10: MAIN FORM SETUP | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Create and configure the main application window | |
| $script:mainForm = New-Object System.Windows.Forms.Form | |
| $script:mainForm.Text = "Nahid Power Utilities" | |
| $script:mainForm.StartPosition = "CenterScreen" | |
| $script:mainForm.WindowState = "Maximized" | |
| $script:mainForm.BackColor = $script:Colors.Background | |
| $script:mainForm.FormBorderStyle = "Sizable" | |
| $script:mainForm.MinimumSize = New-Object System.Drawing.Size(700, 550) | |
| $script:mainForm.KeyPreview = $true | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 11: HEADER SECTION | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Top section with title, search bar, and control buttons | |
| # --- Header Panel --- | |
| $script:headerPanel = New-Object System.Windows.Forms.Panel | |
| $script:headerPanel.Dock = "Top" | |
| $script:headerPanel.Height = 130 | |
| $script:headerPanel.BackColor = $script:Colors.Surface | |
| # --- Title --- | |
| $script:titleLabel = New-Object System.Windows.Forms.Label | |
| $script:titleLabel.Text = "Nahid Power Utilities" | |
| $script:titleLabel.Location = New-Object System.Drawing.Point(30, 20) | |
| $script:titleLabel.AutoSize = $true | |
| $script:titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 20, [System.Drawing.FontStyle]::Bold) | |
| $script:titleLabel.ForeColor = $script:Colors.Text | |
| Add-Tooltip -Control $script:titleLabel -Text "Dashboard v2.2" | |
| # --- Subtitle --- | |
| $script:subtitleLabel = New-Object System.Windows.Forms.Label | |
| $script:subtitleLabel.Text = "Your productivity toolkit" | |
| $script:subtitleLabel.Location = New-Object System.Drawing.Point(30, 55) | |
| $script:subtitleLabel.AutoSize = $true | |
| $script:subtitleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10) | |
| $script:subtitleLabel.ForeColor = $script:Colors.TextSecondary | |
| # --- Search Container --- | |
| $script:searchContainer = New-Object System.Windows.Forms.Panel | |
| $script:searchContainer.Location = New-Object System.Drawing.Point(25, 85) | |
| $script:searchContainer.Size = New-Object System.Drawing.Size(400, 35) | |
| $script:searchContainer.BackColor = $script:Colors.SearchBox | |
| $script:searchContainer.Anchor = "Top,Left,Right" | |
| $script:searchContainer.BorderStyle = "FixedSingle" | |
| # --- Search Icon --- | |
| $searchIcon = New-Object System.Windows.Forms.Label | |
| $searchIcon.Text = "Search" | |
| $searchIcon.Location = New-Object System.Drawing.Point(10, 8) | |
| $searchIcon.Size = New-Object System.Drawing.Size(45, 20) | |
| $searchIcon.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $searchIcon.ForeColor = $script:Colors.TextSecondary | |
| # --- Search TextBox --- | |
| $script:searchBox = New-Object System.Windows.Forms.TextBox | |
| $script:searchBox.Location = New-Object System.Drawing.Point(60, 7) | |
| $script:searchBox.Size = New-Object System.Drawing.Size(330, 22) | |
| $script:searchBox.Font = New-Object System.Drawing.Font("Segoe UI", 10) | |
| $script:searchBox.BackColor = $script:Colors.SearchBox | |
| $script:searchBox.ForeColor = $script:Colors.Text | |
| $script:searchBox.BorderStyle = "None" | |
| $script:searchBox.Anchor = "Top,Left,Right" | |
| Add-Tooltip -Control $script:searchBox -Text "Search utilities (Press / to focus)" | |
| $script:searchContainer.Controls.AddRange(@($searchIcon, $script:searchBox)) | |
| # --- Theme Button --- | |
| $script:themeBtn = New-Object System.Windows.Forms.Button | |
| $script:themeBtn.Text = "Theme" | |
| $script:themeBtn.Size = New-Object System.Drawing.Size(60, 35) | |
| $script:themeBtn.Location = New-Object System.Drawing.Point(440, 85) | |
| $script:themeBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:themeBtn.ForeColor = $script:Colors.Text | |
| $script:themeBtn.FlatStyle = "Flat" | |
| $script:themeBtn.FlatAppearance.BorderSize = 0 | |
| $script:themeBtn.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $script:themeBtn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $script:themeBtn.Anchor = "Top,Right" | |
| Add-Tooltip -Control $script:themeBtn -Text "Toggle dark/light mode (Ctrl+T)" | |
| $script:themeBtn.Add_Click({ Switch-Theme }) | |
| $script:themeBtn.Add_MouseEnter({ $this.BackColor = $script:Colors.SurfaceHover }) | |
| $script:themeBtn.Add_MouseLeave({ $this.BackColor = $script:Colors.SurfaceLight }) | |
| # --- Clear Button --- | |
| $script:clearBtn = New-Object System.Windows.Forms.Button | |
| $script:clearBtn.Text = "Clear" | |
| $script:clearBtn.Size = New-Object System.Drawing.Size(60, 35) | |
| $script:clearBtn.Location = New-Object System.Drawing.Point(510, 85) | |
| $script:clearBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:clearBtn.ForeColor = $script:Colors.TextSecondary | |
| $script:clearBtn.FlatStyle = "Flat" | |
| $script:clearBtn.FlatAppearance.BorderSize = 0 | |
| $script:clearBtn.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $script:clearBtn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $script:clearBtn.Anchor = "Top,Right" | |
| Add-Tooltip -Control $script:clearBtn -Text "Clear search (Esc)" | |
| $script:clearBtn.Add_Click({ | |
| $script:searchBox.Text = "" | |
| $script:searchBox.Focus() | |
| Update-UtilityDisplay | |
| }) | |
| $script:clearBtn.Add_MouseEnter({ $this.BackColor = $script:Colors.Danger; $this.ForeColor = [System.Drawing.Color]::White }) | |
| $script:clearBtn.Add_MouseLeave({ $this.BackColor = $script:Colors.SurfaceLight; $this.ForeColor = $script:Colors.TextSecondary }) | |
| # --- Refresh Button --- | |
| $script:refreshBtn = New-Object System.Windows.Forms.Button | |
| $script:refreshBtn.Text = "Refresh" | |
| $script:refreshBtn.Size = New-Object System.Drawing.Size(65, 35) | |
| $script:refreshBtn.Location = New-Object System.Drawing.Point(580, 85) | |
| $script:refreshBtn.BackColor = $script:Colors.SurfaceLight | |
| $script:refreshBtn.ForeColor = $script:Colors.Primary | |
| $script:refreshBtn.FlatStyle = "Flat" | |
| $script:refreshBtn.FlatAppearance.BorderSize = 0 | |
| $script:refreshBtn.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $script:refreshBtn.Cursor = [System.Windows.Forms.Cursors]::Hand | |
| $script:refreshBtn.Anchor = "Top,Right" | |
| Add-Tooltip -Control $script:refreshBtn -Text "Refresh (F5)" | |
| $script:refreshBtn.Add_Click({ | |
| Update-UtilityDisplay | |
| Update-StatusBar "Refreshed" | |
| }) | |
| $script:refreshBtn.Add_MouseEnter({ $this.BackColor = $script:Colors.Primary; $this.ForeColor = [System.Drawing.Color]::White }) | |
| $script:refreshBtn.Add_MouseLeave({ $this.BackColor = $script:Colors.SurfaceLight; $this.ForeColor = $script:Colors.Primary }) | |
| # --- Add Controls to Header --- | |
| $script:headerPanel.Controls.AddRange(@( | |
| $script:titleLabel, | |
| $script:subtitleLabel, | |
| $script:searchContainer, | |
| $script:themeBtn, | |
| $script:clearBtn, | |
| $script:refreshBtn | |
| )) | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 12: UTILITIES PANEL | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Scrollable panel that contains all utility cards | |
| $script:utilitiesPanel = New-Object System.Windows.Forms.Panel | |
| $script:utilitiesPanel.Dock = "Fill" | |
| $script:utilitiesPanel.AutoScroll = $true | |
| $script:utilitiesPanel.BackColor = $script:Colors.Background | |
| $script:utilitiesPanel.Padding = New-Object System.Windows.Forms.Padding(25, 25, 25, 100) | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 13: STATUS BAR | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Bottom status bar showing current state and version | |
| $script:statusBar = New-Object System.Windows.Forms.StatusStrip | |
| $script:statusBar.BackColor = $script:Colors.Surface | |
| $script:statusBar.Dock = "Bottom" | |
| $script:statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel | |
| $script:statusLabel.Text = "Ready | Total: $($script:ValidatedUtilities.Count) utilities" | |
| $script:statusLabel.ForeColor = $script:Colors.Text | |
| $script:statusLabel.Spring = $true | |
| $script:statusLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9) | |
| $script:versionLabel = New-Object System.Windows.Forms.ToolStripStatusLabel | |
| $script:versionLabel.Text = "v2.2" | |
| $script:versionLabel.ForeColor = $script:Colors.Primary | |
| $script:versionLabel.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) | |
| $script:statusBar.Items.AddRange(@($script:statusLabel, $script:versionLabel)) | Out-Null | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 14: DISPLAY UPDATE FUNCTION | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Rebuilds the utility cards based on current search filter | |
| function Update-UtilityDisplay { | |
| param([string]$SearchText = "") | |
| try { | |
| $script:utilitiesPanel.SuspendLayout() | |
| $script:utilitiesPanel.Controls.Clear() | |
| $filtered = $script:ValidatedUtilities | |
| if ($SearchText -eq "") { | |
| $SearchText = $script:searchBox.Text | |
| } | |
| if ($SearchText -ne "") { | |
| $filtered = $filtered | Where-Object { | |
| $_.Name -like "*$SearchText*" -or | |
| $_.Desc -like "*$SearchText*" -or | |
| $_.Category -like "*$SearchText*" -or | |
| ($_.Tags | Where-Object { $_ -like "*$SearchText*" }).Count -gt 0 | |
| } | |
| } | |
| # --- Count Header --- | |
| $countPanel = New-Object System.Windows.Forms.Panel | |
| $countPanel.Height = 40 | |
| $countPanel.Width = $script:utilitiesPanel.Width - 50 | |
| $countPanel.BackColor = [System.Drawing.Color]::Transparent | |
| $countPanel.Anchor = "Top,Left,Right" | |
| $countLabel = New-Object System.Windows.Forms.Label | |
| $countLabel.Text = "Showing $($filtered.Count) of $($script:ValidatedUtilities.Count) utilities" | |
| $countLabel.Location = New-Object System.Drawing.Point(10, 10) | |
| $countLabel.AutoSize = $true | |
| $countLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) | |
| $countLabel.ForeColor = $script:Colors.Text | |
| $countPanel.Controls.Add($countLabel) | |
| $script:utilitiesPanel.Controls.Add($countPanel) | |
| # --- Build Cards --- | |
| $yPos = 50 | |
| $cardSpacing = 15 | |
| foreach ($util in $filtered) { | |
| $card = New-UtilityCard -Util $util -ContainerWidth $script:utilitiesPanel.ClientSize.Width | |
| if ($null -ne $card) { | |
| $card.Location = New-Object System.Drawing.Point(15, $yPos) | |
| $script:utilitiesPanel.Controls.Add($card) | |
| $yPos += $card.Height + $cardSpacing | |
| } | |
| } | |
| # --- Bottom Spacer --- | |
| $bottomSpacer = New-Object System.Windows.Forms.Panel | |
| $bottomSpacer.Size = New-Object System.Drawing.Size(10, 120) | |
| $bottomSpacer.Location = New-Object System.Drawing.Point(0, $yPos) | |
| $bottomSpacer.BackColor = [System.Drawing.Color]::Transparent | |
| $script:utilitiesPanel.Controls.Add($bottomSpacer) | |
| # --- No Results --- | |
| if ($filtered.Count -eq 0) { | |
| $noResultsPanel = New-Object System.Windows.Forms.Panel | |
| $noResultsPanel.Size = New-Object System.Drawing.Size(($script:utilitiesPanel.Width - 50), 180) | |
| $noResultsPanel.Location = New-Object System.Drawing.Point(25, 60) | |
| $noResultsPanel.BackColor = $script:Colors.Surface | |
| $noResultsPanel.Anchor = "Top,Left,Right" | |
| $noResultsLabel = New-Object System.Windows.Forms.Label | |
| $noResultsLabel.Text = "No utilities found" | |
| $noResultsLabel.Size = New-Object System.Drawing.Size($noResultsPanel.Width, 35) | |
| $noResultsLabel.Location = New-Object System.Drawing.Point(0, 60) | |
| $noResultsLabel.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold) | |
| $noResultsLabel.ForeColor = $script:Colors.Text | |
| $noResultsLabel.TextAlign = "MiddleCenter" | |
| $noResultsDesc = New-Object System.Windows.Forms.Label | |
| $noResultsDesc.Text = "Try different search terms" | |
| $noResultsDesc.Size = New-Object System.Drawing.Size($noResultsPanel.Width, 25) | |
| $noResultsDesc.Location = New-Object System.Drawing.Point(0, 100) | |
| $noResultsDesc.Font = New-Object System.Drawing.Font("Segoe UI", 10) | |
| $noResultsDesc.ForeColor = $script:Colors.TextSecondary | |
| $noResultsDesc.TextAlign = "MiddleCenter" | |
| $noResultsPanel.Controls.AddRange(@($noResultsLabel, $noResultsDesc)) | |
| $script:utilitiesPanel.Controls.Add($noResultsPanel) | |
| } | |
| Update-StatusBar "Showing $($filtered.Count) of $($script:ValidatedUtilities.Count) utilities" | |
| } | |
| catch { | |
| Write-Error "Display update error: $_" | |
| } | |
| finally { | |
| $script:utilitiesPanel.ResumeLayout($true) | |
| } | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 15: EVENT HANDLERS | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Handle form resize, search, keyboard shortcuts, and cleanup | |
| # --- Form Resize --- | |
| $script:mainForm.Add_Resize({ | |
| try { | |
| $rightEdge = $script:headerPanel.ClientSize.Width | |
| $script:refreshBtn.Left = $rightEdge - 75 | |
| $script:clearBtn.Left = $rightEdge - 145 | |
| $script:themeBtn.Left = $rightEdge - 215 | |
| $script:searchContainer.Width = $script:themeBtn.Left - 50 | |
| $script:searchBox.Width = $script:searchContainer.Width - 70 | |
| Update-UtilityDisplay | |
| } | |
| catch { | |
| Write-Warning "Resize error: $_" | |
| } | |
| }) | |
| # --- Search Text Changed --- | |
| $script:searchBox.Add_TextChanged({ | |
| try { | |
| Update-UtilityDisplay | |
| } | |
| catch { | |
| Write-Warning "Search error: $_" | |
| } | |
| }) | |
| # --- Global Keyboard Shortcuts --- | |
| $script:mainForm.Add_KeyDown({ | |
| param($sender, $e) | |
| if ($e.KeyCode -eq "OemQuestion" -or $e.KeyCode -eq "Divide" -or | |
| ($e.Control -and $e.KeyCode -eq "F")) { | |
| $script:searchBox.Focus() | |
| $script:searchBox.SelectAll() | |
| $e.Handled = $true | |
| } | |
| if ($e.KeyCode -eq "Escape") { | |
| if ($script:searchBox.Text -ne "") { | |
| $script:searchBox.Text = "" | |
| } | |
| $e.Handled = $true | |
| } | |
| if ($e.KeyCode -eq "F5") { | |
| Update-UtilityDisplay | |
| Update-StatusBar "Refreshed" | |
| $e.Handled = $true | |
| } | |
| if ($e.Control -and $e.KeyCode -eq "T") { | |
| Switch-Theme | |
| $e.Handled = $true | |
| } | |
| }) | |
| # --- Form Closing (Cleanup) --- | |
| $script:mainForm.Add_FormClosing({ | |
| try { | |
| Get-Job | Remove-Job -Force -ErrorAction SilentlyContinue | |
| if ($null -ne $script:tooltip) { | |
| $script:tooltip.Dispose() | |
| } | |
| } | |
| catch { | |
| Write-Warning "Cleanup error: $_" | |
| } | |
| }) | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # SECTION 16: APPLICATION LAUNCH | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Assemble the form and show it to the user | |
| $script:mainForm.Controls.AddRange(@( | |
| $script:statusBar, | |
| $script:utilitiesPanel, | |
| $script:headerPanel | |
| )) | |
| Update-UtilityDisplay | |
| # --- Console Welcome Message --- | |
| Write-Host "" | |
| Write-Host "================================================" -ForegroundColor Cyan | |
| Write-Host " Nahid Power Utilities Dashboard v2.2 " -ForegroundColor Cyan | |
| Write-Host "================================================" -ForegroundColor Cyan | |
| Write-Host "Loaded : $($script:ValidatedUtilities.Count) utilities" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Button States:" -ForegroundColor Yellow | |
| Write-Host " Downloading... : Fetching script" -ForegroundColor DarkYellow | |
| Write-Host " Starting... : Creating job" -ForegroundColor DarkYellow | |
| Write-Host " Launching... : Initializing" -ForegroundColor Blue | |
| Write-Host " Running : Actively running" -ForegroundColor Green | |
| Write-Host " Done : Completed" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Shortcuts:" -ForegroundColor Yellow | |
| Write-Host " / or Ctrl+F : Focus search" -ForegroundColor Gray | |
| Write-Host " Esc : Clear search" -ForegroundColor Gray | |
| Write-Host " F5 : Refresh" -ForegroundColor Gray | |
| Write-Host " Ctrl+T : Toggle theme" -ForegroundColor Gray | |
| Write-Host "" | |
| try { | |
| [void]$script:mainForm.ShowDialog() | |
| } | |
| catch { | |
| Write-Error "Application error: $_" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment