diff --git a/lib/core/base.ps1 b/lib/core/base.ps1 index ab0d73b..d985b22 100644 --- a/lib/core/base.ps1 +++ b/lib/core/base.ps1 @@ -12,41 +12,48 @@ $script:MOLE_BASE_LOADED = $true # ============================================================================ # Color Definitions (ANSI escape codes for modern terminals) # ============================================================================ -$script:ESC = [char]27 -$script:DefaultColors = @{ - Green = "$ESC[0;32m" - Blue = "$ESC[0;34m" - Cyan = "$ESC[0;36m" - Yellow = "$ESC[0;33m" - Purple = "$ESC[0;35m" - PurpleBold = "$ESC[1;35m" - Red = "$ESC[0;31m" - Gray = "$ESC[0;90m" - White = "$ESC[0;37m" - NC = "$ESC[0m" # No Color / Reset +function Get-MoleDefaultColors { + $esc = [char]27 + return @{ + Green = "$esc[0;32m" + Blue = "$esc[0;34m" + Cyan = "$esc[0;36m" + Yellow = "$esc[0;33m" + Purple = "$esc[0;35m" + PurpleBold = "$esc[1;35m" + Red = "$esc[0;31m" + Gray = "$esc[0;90m" + White = "$esc[0;37m" + NC = "$esc[0m" # No Color / Reset + } } # ============================================================================ # Icon Definitions # ============================================================================ -$script:DefaultIcons = @{ - Confirm = [char]0x25CE # ◎ - Admin = [char]0x2699 # ⚙ - Success = [char]0x2713 # ✓ - Error = [char]0x263B # ☻ - Warning = [char]0x25CF # ● - Empty = [char]0x25CB # ○ - Solid = [char]0x25CF # ● - List = [char]0x2022 # • - Arrow = [char]0x27A4 # ➤ - DryRun = [char]0x2192 # → - NavUp = [char]0x2191 # ↑ - NavDown = [char]0x2193 # ↓ - Folder = [char]0x25A0 # ■ (folder substitute) - File = [char]0x25A1 # □ (file substitute) - Trash = [char]0x2718 # ✘ (trash substitute) +function Get-MoleDefaultIcons { + return @{ + Confirm = [char]0x25CE # ◎ + Admin = [char]0x2699 # ⚙ + Success = [char]0x2713 # ✓ + Error = [char]0x263B # ☻ + Warning = [char]0x25CF # ● + Empty = [char]0x25CB # ○ + Solid = [char]0x25CF # ● + List = [char]0x2022 # • + Arrow = [char]0x27A4 # ➤ + DryRun = [char]0x2192 # → + NavUp = [char]0x2191 # ↑ + NavDown = [char]0x2193 # ↓ + Folder = [char]0x25A0 # ■ (folder substitute) + File = [char]0x25A1 # □ (file substitute) + Trash = [char]0x2718 # ✘ (trash substitute) + } } +$script:DefaultColors = Get-MoleDefaultColors +$script:DefaultIcons = Get-MoleDefaultIcons + function Get-OrCreateScriptHashtable { param( [Parameter(Mandatory)] @@ -63,24 +70,79 @@ function Get-OrCreateScriptHashtable { return $table } +function Get-MoleDefaultHashtable { + param( + [Parameter(Mandatory)] + [ValidateSet('Colors', 'Icons')] + [string]$Name + ) + + $defaultVariable = Get-Variable -Name "Default$Name" -Scope Script -ErrorAction SilentlyContinue + if ($defaultVariable -and $defaultVariable.Value -is [hashtable]) { + return $defaultVariable.Value + } + + switch ($Name) { + 'Colors' { return Get-MoleDefaultColors } + 'Icons' { return Get-MoleDefaultIcons } + } +} + function Initialize-MoleVisualDefaults { + $defaultColors = Get-MoleDefaultHashtable -Name "Colors" $colors = Get-OrCreateScriptHashtable -Name "Colors" - foreach ($entry in $script:DefaultColors.GetEnumerator()) { + foreach ($entry in $defaultColors.GetEnumerator()) { if (-not $colors.ContainsKey($entry.Key)) { $colors[$entry.Key] = $entry.Value } } + $defaultIcons = Get-MoleDefaultHashtable -Name "Icons" $icons = Get-OrCreateScriptHashtable -Name "Icons" - foreach ($entry in $script:DefaultIcons.GetEnumerator()) { + foreach ($entry in $defaultIcons.GetEnumerator()) { if (-not $icons.ContainsKey($entry.Key)) { $icons[$entry.Key] = $entry.Value } } } +function Get-MoleVisualValue { + param( + [Parameter(Mandatory)] + [ValidateSet('Colors', 'Icons')] + [string]$TableName, + + [Parameter(Mandatory)] + [string]$Key + ) + + $table = Get-OrCreateScriptHashtable -Name $TableName + if (-not $table.ContainsKey($Key)) { + $defaults = Get-MoleDefaultHashtable -Name $TableName + if ($defaults.ContainsKey($Key)) { + $table[$Key] = $defaults[$Key] + } + } + + if ($table.ContainsKey($Key)) { + return $table[$Key] + } + + return $null +} + +function Get-MoleColor { + param([Parameter(Mandatory)][string]$Name) + return Get-MoleVisualValue -TableName "Colors" -Key $Name +} + +function Get-MoleIcon { + param([Parameter(Mandatory)][string]$Name) + return Get-MoleVisualValue -TableName "Icons" -Key $Name +} + Initialize-MoleVisualDefaults # ============================================================================ diff --git a/lib/core/log.ps1 b/lib/core/log.ps1 index dc291fb..d0db808 100644 --- a/lib/core/log.ps1 +++ b/lib/core/log.ps1 @@ -40,8 +40,8 @@ function Write-LogMessage { ) $timestamp = Get-Date -Format "HH:mm:ss" - $colorCode = $script:Colors[$Color] - $nc = $script:Colors.NC + $colorCode = Get-MoleColor -Name $Color + $nc = Get-MoleColor -Name "NC" $formattedIcon = if ($Icon) { "$Icon " } else { "" } $output = " ${colorCode}${formattedIcon}${nc}${Message}" @@ -60,7 +60,7 @@ function Write-Info { Write an informational message #> param([string]$Message) - Write-LogMessage -Message $Message -Level "INFO" -Color "Cyan" -Icon $script:Icons.List + Write-LogMessage -Message $Message -Level "INFO" -Color "Cyan" -Icon (Get-MoleIcon -Name "List") } function Write-Success { @@ -69,7 +69,7 @@ function Write-Success { Write a success message #> param([string]$Message) - Write-LogMessage -Message $Message -Level "SUCCESS" -Color "Green" -Icon $script:Icons.Success + Write-LogMessage -Message $Message -Level "SUCCESS" -Color "Green" -Icon (Get-MoleIcon -Name "Success") } @@ -79,7 +79,7 @@ function Write-MoleWarning { Write a warning message #> param([string]$Message) - Write-LogMessage -Message $Message -Level "WARN" -Color "Yellow" -Icon $script:Icons.Warning + Write-LogMessage -Message $Message -Level "WARN" -Color "Yellow" -Icon (Get-MoleIcon -Name "Warning") } function Write-MoleError { @@ -88,7 +88,7 @@ function Write-MoleError { Write an error message #> param([string]$Message) - Write-LogMessage -Message $Message -Level "ERROR" -Color "Red" -Icon $script:Icons.Error + Write-LogMessage -Message $Message -Level "ERROR" -Color "Red" -Icon (Get-MoleIcon -Name "Error") } @@ -100,8 +100,8 @@ function Write-Debug { param([string]$Message) if ($script:LogConfig.DebugEnabled) { - $gray = $script:Colors.Gray - $nc = $script:Colors.NC + $gray = Get-MoleColor -Name "Gray" + $nc = Get-MoleColor -Name "NC" Write-Host " ${gray}[DEBUG] $Message${nc}" } } @@ -112,7 +112,7 @@ function Write-DryRun { Write a dry-run message (action that would be taken) #> param([string]$Message) - Write-LogMessage -Message $Message -Level "DRYRUN" -Color "Yellow" -Icon $script:Icons.DryRun + Write-LogMessage -Message $Message -Level "DRYRUN" -Color "Yellow" -Icon (Get-MoleIcon -Name "DryRun") } # ============================================================================ @@ -136,9 +136,9 @@ function Start-Section { $script:CurrentSection.Activity = $false $script:CurrentSection.Name = $Title - $purple = $script:Colors.PurpleBold - $nc = $script:Colors.NC - $arrow = $script:Icons.Arrow + $purple = Get-MoleColor -Name "PurpleBold" + $nc = Get-MoleColor -Name "NC" + $arrow = Get-MoleIcon -Name "Arrow" Write-Host "" Write-Host "${purple}${arrow} ${Title}${nc}" @@ -181,8 +181,8 @@ function Start-Spinner { param([string]$Message = "Working...") $script:SpinnerIndex = 0 - $gray = $script:Colors.Gray - $nc = $script:Colors.NC + $gray = Get-MoleColor -Name "Gray" + $nc = Get-MoleColor -Name "NC" Write-Host -NoNewline " ${gray}$($script:SpinnerFrames[0]) $Message${nc}" } @@ -196,8 +196,8 @@ function Update-Spinner { $script:SpinnerIndex = ($script:SpinnerIndex + 1) % $script:SpinnerFrames.Count $frame = $script:SpinnerFrames[$script:SpinnerIndex] - $gray = $script:Colors.Gray - $nc = $script:Colors.NC + $gray = Get-MoleColor -Name "Gray" + $nc = Get-MoleColor -Name "NC" # Move cursor to beginning of line and clear Write-Host -NoNewline "`r ${gray}$frame $Message${nc} " @@ -232,8 +232,8 @@ function Write-Progress { $empty = $Width - $filled $bar = ("[" + ("=" * $filled) + (" " * $empty) + "]") - $cyan = $script:Colors.Cyan - $nc = $script:Colors.NC + $cyan = Get-MoleColor -Name "Cyan" + $nc = Get-MoleColor -Name "NC" Write-Host -NoNewline "`r ${cyan}$bar${nc} ${percent}% $Message " } diff --git a/tests/Commands.Tests.ps1 b/tests/Commands.Tests.ps1 index 59d391d..a12db6f 100644 --- a/tests/Commands.Tests.ps1 +++ b/tests/Commands.Tests.ps1 @@ -6,7 +6,7 @@ BeforeAll { $script:WindowsDir = Split-Path -Parent $PSScriptRoot $script:BinDir = Join-Path $script:WindowsDir "bin" $script:InstallScript = Join-Path $script:WindowsDir "install.ps1" - $script:VisualDefaultsErrorPattern = 'property ''Solid'' cannot be found|找不到属性.?Solid|VariableIsUndefined|\$script:Colors' + $script:VisualDefaultsErrorPattern = 'property ''(Solid|Error)'' cannot be found|找不到属性.?(Solid|Error)|VariableIsUndefined|\$script:Colors' } Describe "Clean Command" { diff --git a/tests/Core.Tests.ps1 b/tests/Core.Tests.ps1 index e9080f4..d493f40 100644 --- a/tests/Core.Tests.ps1 +++ b/tests/Core.Tests.ps1 @@ -70,6 +70,14 @@ Describe "Base Module" { $script:Icons.Solid | Should -Be "*" $script:Icons.Admin | Should -Not -BeNullOrEmpty } + + It "Should restore missing icon entries on demand" { + $script:Icons = @{ Solid = "*" } + + (Get-MoleIcon -Name "Error") | Should -Not -BeNullOrEmpty + $script:Icons.Error | Should -Not -BeNullOrEmpty + $script:Icons.Solid | Should -Be "*" + } } Context "Test-IsAdmin" { @@ -242,6 +250,19 @@ Describe "Logging Module" { # Note: The actual function is Write-MoleError { Write-MoleError "Test message" } | Should -Not -Throw } + + It "Should log errors even when the icon table is partial" { + $originalIcons = $script:Icons.Clone() + + try { + $script:Icons = @{ Solid = "*" } + { Write-MoleError "Test message" } | Should -Not -Throw + $script:Icons.Error | Should -Not -BeNullOrEmpty + } + finally { + $script:Icons = $originalIcons + } + } } Context "Section Functions" {