Fix contrast issues with text-muted and bg-dark classes

- Fixed Bootstrap bg-dark class to use better contrasting color
- Added comprehensive text-muted contrast fixes for various contexts
- Improved dark theme colors for better accessibility
- Fixed CSS inheritance issues for code elements in dark contexts
- All color choices meet WCAG AA contrast requirements
This commit is contained in:
William Valentin
2025-09-14 14:58:35 -07:00
commit 860f60591c
37 changed files with 11599 additions and 0 deletions

496
scripts/README.md Normal file
View File

@@ -0,0 +1,496 @@
# UnitForge Scripts & Utilities
This directory contains utility scripts and libraries for the UnitForge project, including centralized color handling for consistent terminal output across all scripts.
## 📁 Files Overview
| File | Purpose | Language |
|------|---------|----------|
| `colors.sh` | Bash color utility library | Bash |
| `colors.py` | Python color utility module | Python |
| `colors.mk` | Makefile color definitions | Make |
| `test-colors.sh` | Color utility test script | Bash |
| `README.md` | This documentation | Markdown |
## 🎨 Color Utilities
### Why Centralized Colors?
Before this refactor, UnitForge had hardcoded ANSI escape codes scattered across multiple shell scripts like:
```bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
```
This approach had several problems:
-**Duplication**: Same color codes repeated in multiple files
-**Inconsistency**: Different scripts used different color palettes
-**Maintenance**: Changes required updating multiple files
-**No fallback**: No graceful degradation for non-color terminals
The centralized approach solves these issues:
-**DRY Principle**: Single source of truth for all colors
-**Consistency**: Uniform color palette across the project
-**Maintainability**: Update colors in one place
-**Smart Detection**: Automatic color support detection
-**Graceful Degradation**: Works in any terminal environment
## 🐚 Bash Color Utility (`colors.sh`)
### Basic Usage
Source the utility in your shell scripts:
```bash
#!/bin/bash
# Load centralized color utility
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/scripts/colors.sh"
# Use color functions
info "Starting process..."
success "Operation completed!"
warning "Check configuration"
error "Something went wrong"
```
### Available Colors
#### Basic Colors
```bash
red "Red text"
green "Green text"
yellow "Yellow text"
blue "Blue text"
purple "Purple text"
cyan "Cyan text"
white "White text"
gray "Gray text"
```
#### Bright Colors
```bash
bright_red "Bright red text"
bright_green "Bright green text"
bright_blue "Bright blue text"
# ... etc
```
#### Status Messages
```bash
info "Information message" # Blue
success "Success message" # ✓ Green
warning "Warning message" # ⚠ Yellow
error "Error message" # ✗ Red
debug "Debug message" # 🐛 Gray (only if DEBUG=1)
```
#### Headers and Structure
```bash
header "Main Section" # Bold blue with underline
subheader "Subsection" # Bold with dash underline
box_header "Title" 50 # Fancy box with title
box_message "Message" "$GREEN" 40 # Colored message box
```
#### Progress and Status
```bash
step 3 10 "Processing files" # [3/10] Processing files
status "ok" "File processed" # [ OK ] File processed
status "fail" "Connection failed" # [ FAIL ] Connection failed
status "warn" "Deprecated feature" # [ WARN ] Deprecated feature
status "info" "Available updates" # [ INFO ] Available updates
status "skip" "Already exists" # [ SKIP ] Already exists
```
### Advanced Features
#### Color Detection
```bash
if supports_color; then
echo "Terminal supports colors"
else
echo "Colors disabled"
fi
```
#### Environment Variables
- `NO_COLOR=1`: Disable all colors
- `DEBUG=1`: Show debug messages
- `TERM=dumb`: Automatically disables colors
#### Progress Indicators
```bash
# Spinner (use with background processes)
long_running_command &
spinner
# Progress bar
for i in {1..10}; do
progress_bar $i 10 40
sleep 0.5
done
```
## 🐍 Python Color Utility (`colors.py`)
### Basic Usage
Import and use the color functions:
```python
#!/usr/bin/env python3
import sys
import os
# Add scripts directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'scripts'))
from colors import info, success, warning, error, header
# Use color functions
info("Starting process...")
success("Operation completed!")
warning("Check configuration")
error("Something went wrong")
```
### Available Functions
#### Text Coloring
```python
from colors import red, green, blue, yellow
print(red("Error text"))
print(green("Success text"))
print(blue("Info text"))
print(yellow("Warning text"))
```
#### Status Messages
```python
from colors import info, success, warning, error, debug
info("Process starting...") # Blue
success("Task completed!") # ✓ Green
warning("Configuration missing") # ⚠ Yellow
error("Connection failed") # ✗ Red
debug("Variable state: active") # 🐛 Gray (if DEBUG env var set)
```
#### Headers and Structure
```python
from colors import header, subheader, box_header, box_message, c
header("Main Processing Phase")
subheader("File Operations")
box_header("UnitForge Setup", width=50)
box_message("Success!", c.GREEN, width=40)
```
#### Progress and Steps
```python
from colors import step, status, progress_bar
step(1, 5, "Initializing...")
step(2, 5, "Loading configuration...")
status("ok", "File loaded successfully")
status("fail", "Network connection failed")
status("warn", "Using default settings")
# Progress bar
for i in range(11):
progress_bar(i, 10, width=40)
time.sleep(0.1)
```
#### Advanced Usage
```python
from colors import ColorizedFormatter, c, supports_color
# Context manager for colored blocks
with ColorizedFormatter(c.GREEN):
print("This entire block is green")
print("Including multiple lines")
# Manual color codes (discouraged - use functions instead)
if supports_color():
print(f"{c.BOLD}Bold text{c.NC}")
```
## 🔧 Makefile Colors (`colors.mk`)
### Usage in Makefiles
Include the color definitions:
```make
# Include centralized colors
include scripts/colors.mk
target:
$(call info,Starting build process...)
@echo -e "$(GREEN)Building project...$(NC)"
$(call success,Build completed!)
```
### Available Functions
```make
$(call info,Information message)
$(call success,Success message)
$(call warning,Warning message)
$(call error,Error message)
$(call header,Section Title)
$(call subheader,Subsection)
$(call box_header,Fancy Title)
$(call step,3/10,Current step)
$(call status,ok,Operation status)
```
### Color Variables
```make
# Use color variables directly
@echo -e "$(BLUE)Processing...$(NC)"
@echo -e "$(GREEN)✓ Complete$(NC)"
@echo -e "$(YELLOW)⚠ Warning$(NC)"
@echo -e "$(RED)✗ Failed$(NC)"
```
## 🧪 Testing
Run the color utility test:
```bash
# Test all color functions
./scripts/test-colors.sh
# Test Python module
python scripts/colors.py
# Test color detection
NO_COLOR=1 ./scripts/test-colors.sh # Should show no colors
DEBUG=1 ./scripts/test-colors.sh # Should show debug messages
```
## 🎯 Migration Guide
### From Hardcoded Colors
**Before:**
```bash
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
echo -e "${GREEN}✓ Success${NC}"
echo -e "${RED}✗ Error${NC}"
```
**After:**
```bash
#!/bin/bash
source "$(dirname "$0")/scripts/colors.sh"
success "Success"
error "Error"
```
### From Individual Color Functions
**Before:**
```bash
echo_green() { echo -e "\033[0;32m$1\033[0m"; }
echo_red() { echo -e "\033[0;31m$1\033[0m"; }
```
**After:**
```bash
source "$(dirname "$0")/scripts/colors.sh"
# Use built-in green() and red() functions
```
## 🛠️ Development Guidelines
### Adding New Colors
1. **Add to color definitions** in all three files:
- `colors.sh`: Bash export variables
- `colors.py`: Python class attributes
- `colors.mk`: Makefile variables
2. **Add convenience functions** if needed:
```bash
# colors.sh
orange() { color_echo "$ORANGE" "$@"; }
```
3. **Test thoroughly** with different terminal types:
```bash
TERM=dumb ./test-colors.sh
NO_COLOR=1 ./test-colors.sh
```
### Best Practices
1. **Always use functions over raw codes**:
```bash
# Good
success "Operation completed"
# Avoid
echo -e "${GREEN}✓ Operation completed${NC}"
```
2. **Test color support**:
```bash
if supports_color; then
# Use colors
else
# Plain text fallback
fi
```
3. **Provide meaningful status messages**:
```bash
# Good
status "ok" "Dependencies installed successfully"
# Too generic
status "ok" "Done"
```
4. **Use appropriate status types**:
- `info`: General information
- `success`/`ok`: Successful operations
- `warning`/`warn`: Potential issues
- `error`/`fail`: Critical failures
- `skip`: Skipped operations
## 🔗 Integration Examples
### Shell Script Template
```bash
#!/bin/bash
set -e
# Load color utility
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/scripts/colors.sh"
# Script header
box_header "My UnitForge Script"
# Main process with status updates
header "Processing Phase"
step 1 3 "Initializing..."
if initialize_system; then
success "System initialized"
else
error "Initialization failed"
exit 1
fi
step 2 3 "Running tasks..."
info "Processing files..."
# ... task logic ...
success "Tasks completed"
step 3 3 "Cleanup..."
warning "Temporary files will be removed"
cleanup_temp_files
success "Cleanup complete"
# Summary
echo
box_message "Script completed successfully!" "$GREEN"
```
### Python Script Template
```python
#!/usr/bin/env python3
"""UnitForge Python script with color support."""
import sys
import os
from pathlib import Path
# Add scripts to path
script_dir = Path(__file__).parent.parent / "scripts"
sys.path.insert(0, str(script_dir))
from colors import header, info, success, warning, error, step, box_header
def main():
"""Main script function."""
box_header("My UnitForge Python Script")
header("Processing Phase")
step(1, 3, "Initializing...")
try:
# ... initialization logic ...
success("System initialized")
except Exception as e:
error(f"Initialization failed: {e}")
sys.exit(1)
step(2, 3, "Running tasks...")
info("Processing data...")
# ... task logic ...
success("Tasks completed")
step(3, 3, "Finalizing...")
warning("Saving results...")
# ... save logic ...
success("Results saved")
if __name__ == "__main__":
main()
```
## 📊 Color Reference
### Standard Palette
| Color | Bash Function | Python Function | Use Case |
|-------|---------------|-----------------|----------|
| 🔴 Red | `red()` | `red()` | Errors, failures |
| 🟢 Green | `green()` | `green()` | Success, completion |
| 🟡 Yellow | `yellow()` | `yellow()` | Warnings, cautions |
| 🔵 Blue | `blue()` | `blue()` | Information, headers |
| 🟣 Purple | `purple()` | `purple()` | Special operations |
| 🔵 Cyan | `cyan()` | `cyan()` | Steps, progress |
| ⚪ White | `white()` | `white()` | Emphasis |
| ⚫ Gray | `gray()` | `gray()` | Debug, minor info |
### Status Indicators
| Status | Symbol | Color | Usage |
|--------|--------|-------|-------|
| `ok`/`success` | ✓ | Green | Successful operations |
| `fail`/`error` | ✗ | Red | Failed operations |
| `warn`/`warning` | ⚠ | Yellow | Warnings, issues |
| `info` | | Blue | Information |
| `skip` | • | Gray | Skipped operations |
## 🚀 Future Enhancements
- [ ] **Rich progress bars** with animated indicators
- [ ] **Color themes** (dark/light mode support)
- [ ] **Log level integration** with Python logging
- [ ] **JSON output mode** for CI/CD pipelines
- [ ] **Color customization** via configuration files
---
**UnitForge Color Utilities** - Making terminal output beautiful and consistent! 🎨

115
scripts/colors.mk Normal file
View File

@@ -0,0 +1,115 @@
# UnitForge Color Definitions for Makefiles
# Centralized ANSI color codes for consistent terminal output in Make targets
# Basic colors
BLUE := \033[0;34m
GREEN := \033[0;32m
YELLOW := \033[1;33m
RED := \033[0;31m
PURPLE := \033[0;35m
CYAN := \033[0;36m
WHITE := \033[0;37m
GRAY := \033[0;90m
# Bright colors
BRIGHT_RED := \033[1;31m
BRIGHT_GREEN := \033[1;32m
BRIGHT_YELLOW := \033[1;33m
BRIGHT_BLUE := \033[1;34m
BRIGHT_PURPLE := \033[1;35m
BRIGHT_CYAN := \033[1;36m
BRIGHT_WHITE := \033[1;37m
# Background colors
BG_RED := \033[41m
BG_GREEN := \033[42m
BG_YELLOW := \033[43m
BG_BLUE := \033[44m
BG_PURPLE := \033[45m
BG_CYAN := \033[46m
BG_WHITE := \033[47m
# Text formatting
BOLD := \033[1m
DIM := \033[2m
UNDERLINE := \033[4m
REVERSE := \033[7m
# Reset
NC := \033[0m
RESET := \033[0m
# Status symbols
INFO_SYMBOL :=
SUCCESS_SYMBOL :=
WARNING_SYMBOL :=
ERROR_SYMBOL :=
DEBUG_SYMBOL := 🐛
# Makefile utility functions (use with $(call function_name,message))
define info
@printf "$(BLUE)$(INFO_SYMBOL)$(NC) %s\n" "$(1)"
endef
define success
@printf "$(GREEN)$(SUCCESS_SYMBOL)$(NC) %s\n" "$(1)"
endef
define warning
@printf "$(YELLOW)$(WARNING_SYMBOL)$(NC) %s\n" "$(1)"
endef
define error
@printf "$(RED)$(ERROR_SYMBOL)$(NC) %s\n" "$(1)" >&2
endef
define header
@echo ""
@printf "$(BOLD)$(BLUE)%s$(NC)\n" "$(1)"
@printf "$(BLUE)%s$(NC)\n" "$$(printf '%.0s=' $$(seq 1 $$(printf '%s' '$(1)' | wc -c)))"
endef
define subheader
@echo ""
@printf "$(BOLD)%s$(NC)\n" "$(1)"
@printf "%s\n" "$$(printf '%.0s-' $$(seq 1 $$(printf '%s' '$(1)' | wc -c)))"
endef
define step
@printf "$(CYAN)[%s]$(NC) $(BOLD)%s$(NC)\n" "$(1)" "$(2)"
endef
define status
@case "$(1)" in \
"ok"|"success"|"done") \
printf "$(GREEN)[ OK ]$(NC) %s\n" "$(2)" ;; \
"fail"|"error"|"failed") \
printf "$(RED)[ FAIL ]$(NC) %s\n" "$(2)" ;; \
"warn"|"warning") \
printf "$(YELLOW)[ WARN ]$(NC) %s\n" "$(2)" ;; \
"info") \
printf "$(BLUE)[ INFO ]$(NC) %s\n" "$(2)" ;; \
"skip"|"skipped") \
printf "$(GRAY)[ SKIP ]$(NC) %s\n" "$(2)" ;; \
*) \
printf "$(WHITE)[ ]$(NC) %s\n" "$(2)" ;; \
esac
endef
# Box drawing
define box_header
@printf "$(BLUE)╔══════════════════════════════════════════════╗$(NC)\n"
@printf "$(BLUE)║$(NC) %-44s $(BLUE)║$(NC)\n" "$(1)"
@printf "$(BLUE)╚══════════════════════════════════════════════╝$(NC)\n"
endef
# Usage examples:
# $(call info,This is an info message)
# $(call success,Operation completed successfully)
# $(call warning,This is a warning)
# $(call error,Something went wrong)
# $(call header,Main Section)
# $(call subheader,Subsection)
# $(call box_header,UnitForge Setup)
# $(call step,1/5,Installing dependencies)
# $(call status,ok,Package installed)

398
scripts/colors.py Normal file
View File

@@ -0,0 +1,398 @@
"""
UnitForge Color Utility for Python
Centralized ANSI color definitions and utility functions for consistent terminal output
"""
import os
import sys
from typing import Optional
class Colors:
"""ANSI color codes and formatting constants."""
# Basic colors
RED = "\033[0;31m"
GREEN = "\033[0;32m"
YELLOW = "\033[1;33m"
BLUE = "\033[0;34m"
PURPLE = "\033[0;35m"
CYAN = "\033[0;36m"
WHITE = "\033[0;37m"
GRAY = "\033[0;90m"
# Bright colors
BRIGHT_RED = "\033[1;31m"
BRIGHT_GREEN = "\033[1;32m"
BRIGHT_YELLOW = "\033[1;33m"
BRIGHT_BLUE = "\033[1;34m"
BRIGHT_PURPLE = "\033[1;35m"
BRIGHT_CYAN = "\033[1;36m"
BRIGHT_WHITE = "\033[1;37m"
# Background colors
BG_RED = "\033[41m"
BG_GREEN = "\033[42m"
BG_YELLOW = "\033[43m"
BG_BLUE = "\033[44m"
BG_PURPLE = "\033[45m"
BG_CYAN = "\033[46m"
BG_WHITE = "\033[47m"
# Text formatting
BOLD = "\033[1m"
DIM = "\033[2m"
ITALIC = "\033[3m"
UNDERLINE = "\033[4m"
BLINK = "\033[5m"
REVERSE = "\033[7m"
STRIKETHROUGH = "\033[9m"
# Reset
NC = "\033[0m" # No Color
RESET = "\033[0m"
# Status symbols
INFO_SYMBOL = ""
SUCCESS_SYMBOL = ""
WARNING_SYMBOL = ""
ERROR_SYMBOL = ""
DEBUG_SYMBOL = "🐛"
def supports_color() -> bool:
"""
Check if the terminal supports colors.
Returns:
bool: True if colors are supported, False otherwise
"""
# Check if we're in a terminal
if not hasattr(sys.stdout, "isatty") or not sys.stdout.isatty():
return False
# Check environment variables
if os.environ.get("NO_COLOR"):
return False
if os.environ.get("FORCE_COLOR"):
return True
# Check TERM environment variable
term = os.environ.get("TERM", "")
if term == "dumb":
return False
return True
# Initialize colors based on support
if supports_color():
c = Colors()
else:
# Create a class with empty strings for all colors
class NoColors:
def __getattr__(self, name):
return ""
c = NoColors()
def color_text(text: str, color: str) -> str:
"""
Apply color to text.
Args:
text: The text to colorize
color: The ANSI color code
Returns:
str: Colored text with reset at the end
"""
if not supports_color():
return text
return f"{color}{text}{c.NC}"
def red(text: str) -> str:
"""Return red colored text."""
return color_text(text, c.RED)
def green(text: str) -> str:
"""Return green colored text."""
return color_text(text, c.GREEN)
def yellow(text: str) -> str:
"""Return yellow colored text."""
return color_text(text, c.YELLOW)
def blue(text: str) -> str:
"""Return blue colored text."""
return color_text(text, c.BLUE)
def purple(text: str) -> str:
"""Return purple colored text."""
return color_text(text, c.PURPLE)
def cyan(text: str) -> str:
"""Return cyan colored text."""
return color_text(text, c.CYAN)
def white(text: str) -> str:
"""Return white colored text."""
return color_text(text, c.WHITE)
def gray(text: str) -> str:
"""Return gray colored text."""
return color_text(text, c.GRAY)
def bright_red(text: str) -> str:
"""Return bright red colored text."""
return color_text(text, c.BRIGHT_RED)
def bright_green(text: str) -> str:
"""Return bright green colored text."""
return color_text(text, c.BRIGHT_GREEN)
def bright_yellow(text: str) -> str:
"""Return bright yellow colored text."""
return color_text(text, c.BRIGHT_YELLOW)
def bright_blue(text: str) -> str:
"""Return bright blue colored text."""
return color_text(text, c.BRIGHT_BLUE)
def bright_purple(text: str) -> str:
"""Return bright purple colored text."""
return color_text(text, c.BRIGHT_PURPLE)
def bright_cyan(text: str) -> str:
"""Return bright cyan colored text."""
return color_text(text, c.BRIGHT_CYAN)
def bright_white(text: str) -> str:
"""Return bright white colored text."""
return color_text(text, c.BRIGHT_WHITE)
def bold(text: str) -> str:
"""Return bold text."""
return color_text(text, c.BOLD)
def dim(text: str) -> str:
"""Return dim text."""
return color_text(text, c.DIM)
def italic(text: str) -> str:
"""Return italic text."""
return color_text(text, c.ITALIC)
def underline(text: str) -> str:
"""Return underlined text."""
return color_text(text, c.UNDERLINE)
def info(message: str, file=None) -> None:
"""Print an info message."""
output = f"{c.BLUE}{c.INFO_SYMBOL}{c.NC} {message}"
print(output, file=file)
def success(message: str, file=None) -> None:
"""Print a success message."""
output = f"{c.GREEN}{c.SUCCESS_SYMBOL}{c.NC} {message}"
print(output, file=file)
def warning(message: str, file=None) -> None:
"""Print a warning message."""
output = f"{c.YELLOW}{c.WARNING_SYMBOL}{c.NC} {message}"
print(output, file=file)
def error(message: str, file=None) -> None:
"""Print an error message."""
output = f"{c.RED}{c.ERROR_SYMBOL}{c.NC} {message}"
print(output, file=file or sys.stderr)
def debug(message: str, file=None) -> None:
"""Print a debug message (only if DEBUG environment variable is set)."""
if os.environ.get("DEBUG"):
output = f"{c.GRAY}{c.DEBUG_SYMBOL}{c.NC} {message}"
print(output, file=file or sys.stderr)
def header(text: str, file=None) -> None:
"""Print a header with underline."""
print(file=file)
print(f"{c.BOLD}{c.BLUE}{text}{c.NC}", file=file)
print(f"{c.BLUE}{'=' * len(text)}{c.NC}", file=file)
def subheader(text: str, file=None) -> None:
"""Print a subheader with underline."""
print(file=file)
print(f"{c.BOLD}{text}{c.NC}", file=file)
print("-" * len(text), file=file)
def step(current: int, total: int, description: str, file=None) -> None:
"""Print a step indicator."""
output = f"{c.CYAN}[{current}/{total}]{c.NC} {c.BOLD}{description}{c.NC}"
print(output, file=file)
def status(status_type: str, message: str, file=None) -> None:
"""
Print a status message with appropriate formatting.
Args:
status_type: Type of status (ok, fail, warn, info, skip)
message: The status message
file: Output file (defaults to stdout, stderr for errors)
"""
status_map = {
"ok": (f"{c.GREEN}[ OK ]{c.NC}", None),
"success": (f"{c.GREEN}[ OK ]{c.NC}", None),
"done": (f"{c.GREEN}[ OK ]{c.NC}", None),
"fail": (f"{c.RED}[ FAIL ]{c.NC}", sys.stderr),
"error": (f"{c.RED}[ FAIL ]{c.NC}", sys.stderr),
"failed": (f"{c.RED}[ FAIL ]{c.NC}", sys.stderr),
"warn": (f"{c.YELLOW}[ WARN ]{c.NC}", None),
"warning": (f"{c.YELLOW}[ WARN ]{c.NC}", None),
"info": (f"{c.BLUE}[ INFO ]{c.NC}", None),
"skip": (f"{c.GRAY}[ SKIP ]{c.NC}", None),
"skipped": (f"{c.GRAY}[ SKIP ]{c.NC}", None),
}
status_prefix, default_file = status_map.get(
status_type.lower(), (f"{c.WHITE}[ ]{c.NC}", None)
)
output_file = file or default_file
print(f"{status_prefix} {message}", file=output_file)
def box_header(text: str, width: int = 50, file=None) -> None:
"""Print a fancy box header."""
padding = (width - len(text) - 2) // 2
print(f"{c.BLUE}{'' * (width - 2)}{c.NC}", file=file)
print(
f"{c.BLUE}{c.NC}{' ' * padding}{c.BOLD}{text}{c.NC}"
f"{' ' * padding}{c.BLUE}{c.NC}",
file=file,
)
print(f"{c.BLUE}{'' * (width - 2)}{c.NC}", file=file)
def box_message(
text: str, color: Optional[str] = None, width: int = 50, file=None
) -> None:
"""Print a message in a box."""
box_color = color or c.BLUE
padding = (width - len(text) - 2) // 2
print(f"{box_color}{'' * (width - 2)}{c.NC}", file=file)
print(
f"{box_color}{c.NC}{' ' * padding}{text}{' ' * padding}{box_color}{c.NC}",
file=file,
)
print(f"{box_color}{'' * (width - 2)}{c.NC}", file=file)
def progress_bar(current: int, total: int, width: int = 40, file=None) -> None:
"""Print a progress bar."""
percent = current * 100 // total
filled = current * width // total
empty = width - filled
bar = f"{c.BLUE}[{'' * filled}{'' * empty}] {percent}% ({current}/{total}){c.NC}"
print(f"\r{bar}", end="", file=file, flush=True)
if current == total:
print(file=file) # New line when complete
class ColorizedFormatter:
"""A context manager for colorized output."""
def __init__(self, color: str):
self.color = color
def __enter__(self):
if supports_color():
print(self.color, end="")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if supports_color():
print(c.NC, end="")
# Example usage and testing
if __name__ == "__main__":
print("\nUnitForge Color Utility Test")
print("=" * 30)
# Test basic colors
print("\nBasic Colors:")
print(red("Red text"))
print(green("Green text"))
print(yellow("Yellow text"))
print(blue("Blue text"))
print(purple("Purple text"))
print(cyan("Cyan text"))
# Test status functions
print("\nStatus Messages:")
info("This is an info message")
success("This is a success message")
warning("This is a warning message")
error("This is an error message")
debug("This is a debug message")
# Test headers
header("Main Header")
subheader("Sub Header")
# Test status indicators
print("\nStatus Indicators:")
status("ok", "Operation successful")
status("fail", "Operation failed")
status("warn", "Operation completed with warnings")
status("info", "Information message")
status("skip", "Operation skipped")
# Test boxes
print("\nBox Examples:")
box_header("UnitForge Test", 30)
box_message("Success!", c.GREEN, 30)
# Test progress
print("\nProgress Example:")
import time
for i in range(11):
progress_bar(i, 10, 30)
if i < 10:
time.sleep(0.1)
print("\nColor support:", "Yes" if supports_color() else "No")
print("Test completed!")

218
scripts/colors.sh Executable file
View File

@@ -0,0 +1,218 @@
#!/bin/bash
# UnitForge Color Utility Library
# Centralized ANSI color definitions and utility functions for consistent terminal output
# ANSI Color Codes
export RED='\033[0;31m'
export GREEN='\033[0;32m'
export YELLOW='\033[1;33m'
export BLUE='\033[0;34m'
export PURPLE='\033[0;35m'
export CYAN='\033[0;36m'
export WHITE='\033[0;37m'
export GRAY='\033[0;90m'
# Bright colors
export BRIGHT_RED='\033[1;31m'
export BRIGHT_GREEN='\033[1;32m'
export BRIGHT_YELLOW='\033[1;33m'
export BRIGHT_BLUE='\033[1;34m'
export BRIGHT_PURPLE='\033[1;35m'
export BRIGHT_CYAN='\033[1;36m'
export BRIGHT_WHITE='\033[1;37m'
# Background colors
export BG_RED='\033[41m'
export BG_GREEN='\033[42m'
export BG_YELLOW='\033[43m'
export BG_BLUE='\033[44m'
export BG_PURPLE='\033[45m'
export BG_CYAN='\033[46m'
export BG_WHITE='\033[47m'
# Text formatting
export BOLD='\033[1m'
export DIM='\033[2m'
export ITALIC='\033[3m'
export UNDERLINE='\033[4m'
export BLINK='\033[5m'
export REVERSE='\033[7m'
export STRIKETHROUGH='\033[9m'
# Reset
export NC='\033[0m' # No Color
export RESET='\033[0m'
# Utility functions for colored output
color_echo() {
local color="$1"
shift
echo -e "${color}$*${NC}"
}
# Convenience functions
red() { color_echo "$RED" "$@"; }
green() { color_echo "$GREEN" "$@"; }
yellow() { color_echo "$YELLOW" "$@"; }
blue() { color_echo "$BLUE" "$@"; }
purple() { color_echo "$PURPLE" "$@"; }
cyan() { color_echo "$CYAN" "$@"; }
white() { color_echo "$WHITE" "$@"; }
gray() { color_echo "$GRAY" "$@"; }
# Bright variants
bright_red() { color_echo "$BRIGHT_RED" "$@"; }
bright_green() { color_echo "$BRIGHT_GREEN" "$@"; }
bright_yellow() { color_echo "$BRIGHT_YELLOW" "$@"; }
bright_blue() { color_echo "$BRIGHT_BLUE" "$@"; }
bright_purple() { color_echo "$BRIGHT_PURPLE" "$@"; }
bright_cyan() { color_echo "$BRIGHT_CYAN" "$@"; }
bright_white() { color_echo "$BRIGHT_WHITE" "$@"; }
# Formatted output functions
info() {
echo -e "${BLUE}${NC} $*"
}
success() {
echo -e "${GREEN}${NC} $*"
}
warning() {
echo -e "${YELLOW}${NC} $*"
}
error() {
echo -e "${RED}${NC} $*" >&2
}
debug() {
if [[ "${DEBUG:-}" == "1" ]]; then
echo -e "${GRAY}🐛${NC} $*" >&2
fi
}
# Header functions
header() {
echo
echo -e "${BOLD}${BLUE}$*${NC}"
echo -e "${BLUE}$(printf '%.0s=' $(seq 1 ${#1}))${NC}"
}
subheader() {
echo
echo -e "${BOLD}$*${NC}"
echo -e "$(printf '%.0s-' $(seq 1 ${#1}))"
}
# Box drawing functions
box_header() {
local text="$1"
local width=${2:-50}
local padding=$(( (width - ${#text} - 2) / 2 ))
echo -e "${BLUE}$(printf '%.0s═' $(seq 1 $((width-2))))${NC}"
printf "${BLUE}${NC}%*s${BOLD}%s${NC}%*s${BLUE}${NC}\n" $padding "" "$text" $padding ""
echo -e "${BLUE}$(printf '%.0s═' $(seq 1 $((width-2))))${NC}"
}
box_message() {
local text="$1"
local color="${2:-$BLUE}"
local width=${3:-50}
local padding=$(( (width - ${#text} - 2) / 2 ))
echo -e "${color}$(printf '%.0s─' $(seq 1 $((width-2))))${NC}"
printf "${color}${NC}%*s%s%*s${color}${NC}\n" $padding "" "$text" $padding ""
echo -e "${color}$(printf '%.0s─' $(seq 1 $((width-2))))${NC}"
}
# Progress indicators
spinner() {
local pid=$!
local delay=0.1
local spinstr='|/-\'
while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
local temp=${spinstr#?}
printf " [%c] " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# Progress bar
progress_bar() {
local current=$1
local total=$2
local width=${3:-40}
local percent=$((current * 100 / total))
local filled=$((current * width / total))
local empty=$((width - filled))
printf "\r${BLUE}["
printf "%*s" $filled | tr ' ' '█'
printf "%*s" $empty | tr ' ' '░'
printf "] %d%% (%d/%d)${NC}" $percent $current $total
}
# Status functions
step() {
local step_num="$1"
local total_steps="$2"
local description="$3"
echo -e "${CYAN}[${step_num}/${total_steps}]${NC} ${BOLD}${description}${NC}"
}
status() {
local status="$1"
shift
case "$status" in
"ok"|"success"|"done")
echo -e "${GREEN}[ OK ]${NC} $*"
;;
"fail"|"error"|"failed")
echo -e "${RED}[ FAIL ]${NC} $*"
;;
"warn"|"warning")
echo -e "${YELLOW}[ WARN ]${NC} $*"
;;
"info")
echo -e "${BLUE}[ INFO ]${NC} $*"
;;
"skip"|"skipped")
echo -e "${GRAY}[ SKIP ]${NC} $*"
;;
*)
echo -e "${WHITE}[ ]${NC} $*"
;;
esac
}
# Color detection and graceful degradation
supports_color() {
# Check if we're in a terminal that supports colors
[[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]] && [[ "${NO_COLOR:-}" != "1" ]]
}
# Disable colors if not supported
if ! supports_color; then
# Redefine all color variables as empty
RED='' GREEN='' YELLOW='' BLUE='' PURPLE='' CYAN='' WHITE='' GRAY=''
BRIGHT_RED='' BRIGHT_GREEN='' BRIGHT_YELLOW='' BRIGHT_BLUE='' BRIGHT_PURPLE='' BRIGHT_CYAN='' BRIGHT_WHITE=''
BG_RED='' BG_GREEN='' BG_YELLOW='' BG_BLUE='' BG_PURPLE='' BG_CYAN='' BG_WHITE=''
BOLD='' DIM='' ITALIC='' UNDERLINE='' BLINK='' REVERSE='' STRIKETHROUGH=''
NC='' RESET=''
fi
# Usage examples (commented out)
# info "This is an info message"
# success "Operation completed successfully"
# warning "This is a warning"
# error "Something went wrong"
# header "Main Section"
# subheader "Subsection"
# box_header "UnitForge Setup"
# step 1 5 "Installing dependencies"
# status ok "Package installed"

90
scripts/test-colors.sh Executable file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
# Test script for UnitForge color utility
# This script tests all color functions and displays examples
set -e
# Load the color utility
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/colors.sh"
echo
box_header "UnitForge Color Utility Test"
echo
header "Basic Color Functions"
red "This is red text"
green "This is green text"
yellow "This is yellow text"
blue "This is blue text"
purple "This is purple text"
cyan "This is cyan text"
white "This is white text"
gray "This is gray text"
echo
subheader "Bright Colors"
bright_red "This is bright red text"
bright_green "This is bright green text"
bright_yellow "This is bright yellow text"
bright_blue "This is bright blue text"
bright_purple "This is bright purple text"
bright_cyan "This is bright cyan text"
bright_white "This is bright white text"
echo
subheader "Status Messages"
info "This is an info message"
success "This is a success message"
warning "This is a warning message"
error "This is an error message"
debug "This is a debug message (only shown if DEBUG=1)"
echo
DEBUG=1 debug "This debug message should be visible"
echo
subheader "Status Indicators"
status ok "Operation completed successfully"
status fail "Operation failed"
status warn "Operation completed with warnings"
status info "Information about the operation"
status skip "Operation was skipped"
status unknown "Unknown status"
echo
subheader "Progress Steps"
step 1 5 "Initializing project"
step 2 5 "Installing dependencies"
step 3 5 "Running tests"
step 4 5 "Building documentation"
step 5 5 "Deployment complete"
echo
subheader "Box Messages"
box_message "Success: Operation completed!" "$GREEN" 50
box_message "Warning: Check configuration" "$YELLOW" 50
box_message "Error: Something went wrong" "$RED" 50
echo
subheader "Headers and Formatting"
echo "Testing ${BOLD}bold${NC}, ${UNDERLINE}underline${NC}, and ${DIM}dim${NC} text"
echo "Testing ${ITALIC}italic${NC} text (if supported by terminal)"
echo
subheader "Color Support Detection"
if supports_color; then
success "Terminal supports colors"
else
warning "Terminal does not support colors (colors disabled)"
fi
echo
subheader "Environment Info"
echo "TERM: ${TERM:-not set}"
echo "NO_COLOR: ${NO_COLOR:-not set}"
echo "Terminal type: $(tty 2>/dev/null || echo 'not a terminal')"
echo
success "Color utility test completed!"
echo