From 3da9c7266fdb52046ba6742152aed02e2c6026bf Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 10:20:26 +0200 Subject: [PATCH 01/11] refact: Converted to Sampler framework --- .gitattributes | 15 + .gitignore | 23 +- .vscode/tasks.json | 125 ++ CHANGELOG.md | 31 + OrionDesign.psd1 | 58 - OrionDesign.psm1 | 124 -- README.md | 339 +----- RequiredModules.psd1 | 25 + Resolve-Dependency.ps1 | 1075 +++++++++++++++++ Resolve-Dependency.psd1 | 76 ++ build.ps1 | 542 +++++++++ build.yaml | 180 +++ source/OrionDesign.psd1 | 137 +++ source/OrionDesign.psm1 | 5 + .../Private/Convert-ToColoredSegments.ps1 | 0 source/Private/Get-PrivateFunction.ps1 | 31 + .../Private/Write-Colored.ps1 | 0 .../Public/Export-OrionHelpers.ps1 | 0 .../Public/Get-OrionMaxWidth.ps1 | 0 .../Public/Get-OrionTheme.ps1 | 0 source/Public/Get-Something.ps1 | 41 + .../Public/Set-OrionMaxWidth.ps1 | 0 .../Public/Set-OrionTheme.ps1 | 0 .../Public/Show-OrionDemo.ps1 | 0 .../Public/Show-OrionSmartMenu.ps1 | 0 {Functions => source}/Public/Write-Action.ps1 | 0 .../Public/Write-ActionResult.ps1 | 0 {Functions => source}/Public/Write-Alert.ps1 | 0 {Functions => source}/Public/Write-Banner.ps1 | 0 {Functions => source}/Public/Write-Chart.ps1 | 0 {Functions => source}/Public/Write-Header.ps1 | 0 .../Public/Write-InfoBox.ps1 | 0 {Functions => source}/Public/Write-Menu.ps1 | 0 .../Public/Write-MenuLine.ps1 | 0 {Functions => source}/Public/Write-Panel.ps1 | 0 .../Public/Write-ProgressBar.ps1 | 0 .../Public/Write-Question.ps1 | 0 .../Public/Write-Separator.ps1 | 0 {Functions => source}/Public/Write-Steps.ps1 | 0 source/en-US/about_OrionDesign.help.txt | 24 + tests/QA/module.tests.ps1 | 251 ++++ .../Private/Get-PrivateFunction.tests.ps1 | 31 + tests/Unit/Public/Get-Something.tests.ps1 | 91 ++ 43 files changed, 2701 insertions(+), 523 deletions(-) create mode 100644 .gitattributes create mode 100644 .vscode/tasks.json create mode 100644 CHANGELOG.md delete mode 100644 OrionDesign.psd1 delete mode 100644 OrionDesign.psm1 create mode 100644 RequiredModules.psd1 create mode 100644 Resolve-Dependency.ps1 create mode 100644 Resolve-Dependency.psd1 create mode 100644 build.ps1 create mode 100644 build.yaml create mode 100644 source/OrionDesign.psd1 create mode 100644 source/OrionDesign.psm1 rename {Functions => source}/Private/Convert-ToColoredSegments.ps1 (100%) create mode 100644 source/Private/Get-PrivateFunction.ps1 rename {Functions => source}/Private/Write-Colored.ps1 (100%) rename {Functions => source}/Public/Export-OrionHelpers.ps1 (100%) rename {Functions => source}/Public/Get-OrionMaxWidth.ps1 (100%) rename {Functions => source}/Public/Get-OrionTheme.ps1 (100%) create mode 100644 source/Public/Get-Something.ps1 rename {Functions => source}/Public/Set-OrionMaxWidth.ps1 (100%) rename {Functions => source}/Public/Set-OrionTheme.ps1 (100%) rename {Functions => source}/Public/Show-OrionDemo.ps1 (100%) rename {Functions => source}/Public/Show-OrionSmartMenu.ps1 (100%) rename {Functions => source}/Public/Write-Action.ps1 (100%) rename {Functions => source}/Public/Write-ActionResult.ps1 (100%) rename {Functions => source}/Public/Write-Alert.ps1 (100%) rename {Functions => source}/Public/Write-Banner.ps1 (100%) rename {Functions => source}/Public/Write-Chart.ps1 (100%) rename {Functions => source}/Public/Write-Header.ps1 (100%) rename {Functions => source}/Public/Write-InfoBox.ps1 (100%) rename {Functions => source}/Public/Write-Menu.ps1 (100%) rename {Functions => source}/Public/Write-MenuLine.ps1 (100%) rename {Functions => source}/Public/Write-Panel.ps1 (100%) rename {Functions => source}/Public/Write-ProgressBar.ps1 (100%) rename {Functions => source}/Public/Write-Question.ps1 (100%) rename {Functions => source}/Public/Write-Separator.ps1 (100%) rename {Functions => source}/Public/Write-Steps.ps1 (100%) create mode 100644 source/en-US/about_OrionDesign.help.txt create mode 100644 tests/QA/module.tests.ps1 create mode 100644 tests/Unit/Private/Get-PrivateFunction.tests.ps1 create mode 100644 tests/Unit/Public/Get-Something.tests.ps1 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..96c2e0d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Needed for publishing of examples, build worker defaults to core.autocrlf=input. +* text eol=autocrlf + +*.mof text eol=crlf +*.sh text eol=lf +*.svg eol=lf + +# Ensure any exe files are treated as binary +*.exe binary +*.jpg binary +*.xl* binary +*.pfx binary +*.png binary +*.dll binary +*.so binary diff --git a/.gitignore b/.gitignore index 4e313b7..17c483d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,17 @@ -# VS Code workspace files (local editor config, not part of the module) -*.code-workspace -.vscode/ +output/ -# PowerShell test output -TestResults/ +**.bak +*.local.* +!**/README.md +.kitchen/ -# Temp files -*.tmp -*.bak +*.nupkg +*.suo +*.user +*.coverage +.vs +.psproj +.sln +markdownissues.txt +node_modules +package-lock.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..2991140 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "_runner": "terminal", + "windows": { + "options": { + "shell": { + "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": [], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "new", + "clear": false + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + }, + { + "label": "test", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore","-Tasks","test"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ffa1e64 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog for OrionDesign + +The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- For new features. + +### Changed + +- For changes in existing functionality. + +### Deprecated + +- For soon-to-be removed features. + +### Removed + +- For now removed features. + +### Fixed + +- For any bug fix. + +### Security + +- In case of vulnerabilities. + diff --git a/OrionDesign.psd1 b/OrionDesign.psd1 deleted file mode 100644 index aadd640..0000000 --- a/OrionDesign.psd1 +++ /dev/null @@ -1,58 +0,0 @@ -@{ - RootModule = 'OrionDesign.psm1' - ModuleVersion = '3.1.0' - GUID = 'f827a4ef-a736-449b-b684-dfeba0fdadc3' - Author = 'Sune Alexandersen Narud' - Copyright = '(c) 2026 Sune Alexandersen Narud. All rights reserved.' - Description = 'Orion Design Framework – Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter.' - PowerShellVersion = '5.1' - - # Functions to export from this module - FunctionsToExport = @( - 'Export-OrionHelpers', - 'Get-OrionMaxWidth', - 'Get-OrionTheme', - 'Set-OrionMaxWidth', - 'Set-OrionTheme', - 'Show-OrionDemo', - 'Show-OrionSmartMenu', - 'Write-Action', - 'Write-ActionResult', - 'Write-Alert', - 'Write-Banner', - 'Write-Chart', - 'Write-Header', - 'Write-InfoBox', - 'Write-Menu', - 'Write-MenuLine', - 'Write-Panel', - 'Write-ProgressBar', - 'Write-Question', - 'Write-Separator', - 'Write-Steps' - ) - - PrivateData = @{ - PSData = @{ - Tags = @('UI', 'Terminal', 'Console', 'Output', 'Formatting', 'Theme', 'Menu', 'Banner', 'Chart', 'ProgressBar', 'Colors', 'TUI', 'PowerShell5', 'PSCore') - ProjectUri = 'https://github.com/suneworld/OrionDesign' - LicenseUri = 'https://github.com/suneworld/OrionDesign/blob/master/LICENSE' - ReleaseNotes = @' -## v3.1.0 -- Added -Demo parameter to all 21 public functions -- Each demo shows live output with exact code that produced it -- Dynamic code box width (auto-sizes to content) -- Added Show-OrionSmartMenu with arrow-key and numeric navigation modes - -## v3.0.0 -- Restructured module to flat layout -- Renamed Write-MenuLine parameter MenuTitle to Text -- Fixed theme color pairings throughout - -## v2.1.4 -- Added Write-MenuLine with -Muted parameter -- Enhanced Write-Action/Write-ActionResult alignment -'@ - } - } -} \ No newline at end of file diff --git a/OrionDesign.psm1 b/OrionDesign.psm1 deleted file mode 100644 index 355391b..0000000 --- a/OrionDesign.psm1 +++ /dev/null @@ -1,124 +0,0 @@ -<# -================================================================================ -ORION DESIGN - POWERSHELL UI FRAMEWORK -================================================================================ - -Author: Sune Alexandersen Narud -Date: February 5, 2026 -Version: 3.0.0 - -HIGH LEVEL DESIGN (HLD): -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -PURPOSE: -OrionDesign is a comprehensive PowerShell UI framework that provides beautiful, -consistent, and professional terminal user interfaces. It enables developers -and system administrators to create visually appealing command-line applications -with minimal effort. - -ARCHITECTURE: -┌─────────────────┬─────────────────┬─────────────────┬─────────────────┐ -│ INFORMATION │ STATUS & │ DATA │ INTERACTIVE │ -│ DISPLAY │ RESULTS │ PRESENTATION │ ELEMENTS │ -├─────────────────┼─────────────────┼─────────────────┼─────────────────┤ -│ Write-Banner │ Write-ActionRes │ Write-Table │ Write-Menu │ -│ Write-Header │ Write-Progress │ Write-Chart │ Write-Question │ -│ Write-InfoBox │ Write-Steps │ Write-Comparison│ │ -│ Write-Alert │ Write-Timeline │ Write-Dashboard │ │ -└─────────────────┴─────────────────┴─────────────────┴─────────────────┘ -┌─────────────────┬─────────────────┬─────────────────────────────────────┐ -│ LAYOUT & │ GLOBAL │ DEMONSTRATION & UTILITIES │ -│ FORMATTING │ CONFIGURATION │ │ -├─────────────────┼─────────────────┼─────────────────────────────────────┤ -│ Write-Separator │ Get-OrionMax │ Show-OrionDemo │ -│ Write-Panel │ Set-OrionMax │ -Demo Basic/Themes/Interactive/All│ -│ Write-CodeBlock │ $script:Theme │ Export-OrionHelpers (Portability) │ -└─────────────────┴─────────────────┴─────────────────────────────────────┘ - -CORE FEATURES: -• 🎨 20 Beautiful UI Functions organized in logical categories -• 📏 Global Max Width Configuration (50-200 chars, default 100) -• 🎯 Consistent ANSI Styling with automatic ISE compatibility -• 📚 Complete Comment-Based Help documentation -• 🔧 Flexible Parameter Options with validation -• 💻 PowerShell ISE fallback support -• 🚀 Production-ready with comprehensive error handling - -DESIGN PRINCIPLES: -1. CONSISTENCY - Unified styling and parameter patterns -2. FLEXIBILITY - Customizable through parameters and global config -3. COMPATIBILITY - Works in PowerShell 5.1+, ISE, and modern terminals -4. USABILITY - Intuitive function names and comprehensive help -5. PERFORMANCE - Optimized for speed with minimal dependencies -6. EXTENSIBILITY - Modular design for easy function additions - -GLOBAL CONFIGURATION: -• $script:OrionMaxWidth (50-200): Controls output width for all functions -• $script:Theme: Color scheme and styling preferences -• ANSI Support: Automatic detection and graceful fallback - -USAGE PATTERNS: -Import-Module OrionDesign -Write-Banner -ScriptName "MyApp" -Author "User" -Design Modern -Write-ActionResult -Action "Deploy" -Status Success -Details "Complete" -Set-OrionMaxWidth -Width 80 # Configure global width -Show-OrionDemo # See all functions in action - -================================================================================ -#> - -Write-Verbose "DEBUG: OrionDesign.psm1 loaded. PSScriptRoot = $PSScriptRoot" - -$root = $PSScriptRoot - -# --- Global OrionDesign Configuration --- -# Maximum width/length for functions to prevent overly long output -$script:OrionMaxWidth = 100 - -# Default theme configuration -$script:Theme = @{ - Accent = 'Cyan' - Success = 'Green' - Warning = 'Yellow' - Error = 'Red' - Text = 'White' - Muted = 'DarkGray' - Action = 'White' - Result = 'Cyan' - Divider = '─' - UseAnsi = $true -} - -# Detect PowerShell ISE and disable ANSI if needed -if ($psISE) { - $script:Theme.UseAnsi = $false -} - -# --- Load Private functions (internal only) --- -$privatePath = Join-Path $root 'Functions\Private' -if (Test-Path $privatePath) { - Get-ChildItem -Path $privatePath -Filter *.ps1 | ForEach-Object { - Write-Verbose "Loading private function: $($_.BaseName)" - . $_.FullName - } -} - -# --- Load Public functions (to be exported) --- -$publicPath = Join-Path $root 'Functions\Public' -$publicFunctions = @() -if (Test-Path $publicPath) { - Get-ChildItem -Path $publicPath -Filter *.ps1 | ForEach-Object { - Write-Verbose "Loading public function: $($_.BaseName)" - . $_.FullName - $publicFunctions += $_.BaseName - } -} - -# --- Export only public functions --- -if ($publicFunctions.Count -gt 0) { - Write-Verbose "Exporting functions: $($publicFunctions -join ', ')" - Export-ModuleMember -Function $publicFunctions -} -else { - Write-Warning "No public functions found to export from OrionDesign" -} diff --git a/README.md b/README.md index 07d95bf..618e36d 100644 --- a/README.md +++ b/README.md @@ -1,340 +1,13 @@ -# OrionDesign Module - Quick Reference Guide +# OrionDesign -## 🎨 OrionDesign PowerShell UI Framework +Orion Design Framework – Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter. -**Version 3.1.0** | A comprehensive collection of PowerShell functions for creating beautiful terminal user interfaces with consistent styling and configurable global settings. - -Created and maintained by Sune Alexandersen Narud - -[![PSGallery](https://img.shields.io/powershellgallery/v/OrionDesign?label=PSGallery&color=blue)](https://www.powershellgallery.com/packages/OrionDesign) -[![Downloads](https://img.shields.io/powershellgallery/dt/OrionDesign)](https://www.powershellgallery.com/packages/OrionDesign) -[![GitHub](https://img.shields.io/badge/GitHub-suneworld%2FOrionDesign-lightgrey?logo=github)](https://github.com/suneworld/OrionDesign) +## Make it yours --- +Generated with Plaster and the SampleModule template -## 🆕 What's New in v3.1.0 - -Every public function now includes a **`-Demo`** parameter. Run any function with `-Demo` to see a live, self-contained demonstration of all its modes and options — including the exact code that produced each example. - -```powershell -# Examples -Write-Alert -Demo # Shows all 4 alert types -Write-Header -Demo # Shows all underline/numbering modes -Write-Chart -Demo # Bar chart and Pie chart examples -Write-Steps -Demo # All 4 step list styles -Set-OrionTheme -Demo # All 13 available presets -# ... same for every public function -``` - -### 📦 Installation - -**From PowerShell Gallery** (recommended): -```powershell -Install-Module -Name OrionDesign -# or with PSResourceGet: -Install-PSResource -Name OrionDesign -``` - -**From source** (clone the [GitHub repo](https://github.com/suneworld/OrionDesign)): -```powershell -Import-Module .\OrionDesign.psd1 -``` - -### ⚙️ Global Configuration - -#### Max Width Control -```powershell -# Check current max width -Get-OrionMaxWidth - -# Set custom max width (50-200 characters) -Set-OrionMaxWidth -Width 80 - -# Reset to default (100 characters) -Set-OrionMaxWidth -Reset -``` - -### 🎯 Core UI Functions - -#### Information Display -| Function | Purpose | Example | -|-----------------|----------------------------------------|----------------------------------------------------------| -| `Write-Banner` | Script headers with decorative borders | `Write-Banner -ScriptName "MyScript" -Author "User"` | -| `Write-Header` | Section headers with underlines | `Write-Header "Configuration"` | -| `Write-InfoBox` | Highlighted information panels | `Write-InfoBox "Title" "Content goes here" -Style Modern` | -| `Write-Alert` | Attention-grabbing alerts | `Write-Alert "Warning!" -Type Warning` | -| `Write-Panel` | Content containers with border styles | `Write-Panel "Details here" -Type Info -Style Box` | - -#### Status & Results -| Function | Purpose | Example | -|--------------------------|--------------------------------------|-----------------------------------------------------------------| -| `Write-Action` | Action text (pairs with Result) | `Write-Action "Processing files"` | -| `Write-Action -Complete` | Standalone result with icon/details | `Write-Action "Deploy" -Complete -Status Success -ShowIcon` | -| `Write-ActionResult` | Right-aligned result (pairs w/ Action) | `Write-ActionResult "Done" -Status Success` | -| `Write-ProgressBar` | Progress bar in various styles | `Write-ProgressBar 75 100 -Style Bar` | -| `Write-Steps` | Step-by-step process lists | `Write-Steps @("Step 1", "Step 2") -CurrentStep 1` | - -#### Data Presentation -| Function | Purpose | Example | -|---------------|----------------------|-------------------------------| -| `Write-Chart` | Bar and pie charts | `Write-Chart @{CPU=75; RAM=60}`| - -#### Interactive Elements -| Function | Purpose | Example | -|-----------------------|-------------------------------------|---------------------------------------------------| -| `Show-OrionSmartMenu` | Arrow-key or numeric selection menu | `Show-OrionSmartMenu "Title" "Pick:" @("A","B")` | -| `Write-Menu` | Numeric selection menus | `Write-Menu "Options" @("A", "B")` | -| `Write-MenuLine` | Single custom menu line | `Write-MenuLine 1 "Option Name"` | -| `Write-Question` | User prompts (text/YesNo/Choice) | `Write-Question "Continue?" -Type YesNo` | - -> **Note:** `Write-Menu` automatically includes an Exit option (X) as the last item in every menu. - -#### Layout & Formatting -| Function | Purpose | Example | -|-------------------|------------------|-------------------------------------------| -| `Write-Separator` | Section dividers | `Write-Separator "Section" -Style Double` | - -#### Utility Functions -| Function | Purpose | Example | -|-----------------------|----------------------------------|----------------------------------------------------| -| `Export-OrionHelpers` | Bundle functions for portability | `Export-OrionHelpers -ScriptPath ".\MyScript.ps1"` | - -### 🎨 Design Themes - -#### Theme System -OrionDesign includes a comprehensive theme system with 13 preset themes across 6 categories: - -```powershell -# Apply a theme -Set-OrionTheme -Preset HighContrast - -# View current theme -Get-OrionTheme - -# See all themes in action -Show-OrionDemo - -# List all 13 presets as a table -Set-OrionTheme -Demo -``` - -#### Available Themes - -**Standard Themes:** -- `Default` - Standard cyan/green/yellow/red theme -- `Dark` - Dark theme with muted colors -- `Light` - Light theme with darker colors - -**Nature Themes:** -- `Ocean` - Blue-based aquatic theme with wave dividers -- `Forest` - Green-based nature theme with tree-like elements - -**Retro/Vintage Themes:** -- `OldSchool` - Classic amber terminal (DOS/Unix era) -- `Vintage` - Warm sepia/amber nostalgic feel -- `Retro80s` - Synthwave magenta/cyan neon pastels - -**Tech/Futuristic Themes:** -- `Matrix` - Green-on-black digital rain aesthetic -- `Cyberpunk` - Futuristic cyan/purple tech aesthetic - -**Artistic Themes:** -- `Sunset` - Orange/magenta warm evening colors -- `Monochrome` - Grayscale theme for high contrast - -**Accessibility Themes:** -- `HighContrast` - Maximum contrast theme for accessibility (white on black) - -### 📏 Width-Aware Functions - -The following functions automatically respect the global max width setting: -- `Write-Action` / `Write-ActionResult` — Auto right-alignment to max width -- `Write-Action -Complete` — Truncates long details -- `Write-Separator` — Adjusts separator length -- `Write-Banner` — Adjusts border width -- `Write-Panel` — Wraps content appropriately - -### ⚡ Real-Time Status Pattern - -The `Write-Action` / `Write-ActionResult` pair provides elegant real-time status reporting with automatic alignment: - -```powershell -# Basic usage - result auto right-aligns to OrionMaxWidth -Write-Action "Connecting to database" -Write-ActionResult "Connected" -Status Success -# Output: Connecting to database Connected - -# Multiple steps with consistent alignment -Write-Action "Loading configuration" -Write-ActionResult "OK" -Status Success -Write-Action "Validating credentials" -Write-ActionResult "Valid" -Status Success -Write-Action "Fetching user data" -Write-ActionResult "125 users" -Status Success - -# Auto-detection of status (no -Status needed) -Write-Action "Processing items" -Write-ActionResult "42 items processed" # Auto-detects as Success - -# Standalone complete action with details (replaces old Write-ActionResult) -Write-Action "Deploy Database" -Complete -Status Success -Duration "00:02:15" -ShowIcon -# Output: ✅ Deploy Database in 00:02:15 - -Write-Action "142" -Complete -Status Success -Subtext "tables updated" -ShowIcon -# Output: ✅ 142 tables updated - -# Overflow handling - when text is too long, result moves to new line -Write-Action "This is a very long action description" -Write-ActionResult "This is also a long result" -Status Success -# Output: -# This is a very long action description -# This is also a long result -``` - -**Key Features:** -- `Write-Action` + `Write-ActionResult` for real-time paired output -- `Write-Action -Complete` for standalone rich results with icons, duration, details -- Result automatically right-aligns to OrionMaxWidth -- Intelligent overflow handling when combined text exceeds width -- Auto-detection of Success/Failed/Warning patterns - -### 🔧 Advanced Features - -#### ANSI Support -- Automatic detection of PowerShell ISE vs terminal -- Full color and formatting support in compatible terminals -- Graceful fallback for limited environments - -#### Validation -- Input validation on all parameters -- Range checking (e.g., max width 50-200) -- Type validation for enums and objects - -#### Error Handling -- Comprehensive error messages -- Parameter validation -- Fallback rendering for compatibility - -### 📚 Getting Help - -```powershell -# Get help for any function -Get-Help Write-Banner -Full -Get-Help Set-OrionMaxWidth -Examples - -# Show all available functions -Get-Command -Module OrionDesign - -# Run the built-in module showcase -Show-OrionDemo - -# Run per-function demos (available on every public function) -Write-Alert -Demo -Write-Banner -Demo -Write-Chart -Demo -Write-Header -Demo -Write-InfoBox -Demo -Write-Menu -Demo -Write-MenuLine -Demo -Write-Panel -Demo -Write-ProgressBar -Demo -Write-Question -Demo -Write-Separator -Demo -Write-Steps -Demo -Write-Action -Demo -Write-ActionResult -Demo -Set-OrionTheme -Demo # Lists all 13 theme presets -Get-OrionTheme -Demo -Set-OrionMaxWidth -Demo -Get-OrionMaxWidth -Demo -Export-OrionHelpers -Demo -Show-OrionSmartMenu -Demo -``` - -### 🚀 Quick Start Examples - -```powershell -# Script header -Write-Banner -ScriptName "Data Migration" -Author "IT Team" -Design Modern - -# Section dividers -Write-Separator "Configuration" -Style Double -Write-Separator "Results" -Style Single - -# Real-time action/result pattern -Write-Action "Connecting to database" -Write-ActionResult "Connected" -Status Success - -Write-Action "Processing files" -Write-ActionResult "42 files processed" # auto-detects Success - -# Standalone complete action -Write-Action "Backup Database" -Complete -Status Success -Duration "00:02:15" -ShowIcon - -# Step-by-step process -Write-Steps @("Connect", "Validate", "Deploy", "Verify") -CurrentStep 3 -CompletedSteps @(1,2) -Style Checklist - -# Progress bar -Write-ProgressBar 45 100 -Style Bar -Label "Processing Files" - -# Interactive smart menu (supports arrow-key navigation) -$choice = Show-OrionSmartMenu "Select Environment" "Choose:" @("Development", "Staging", "Production") - -# Numeric menu (with automatic Exit option) -$choice = Write-Menu "Select Environment" @("Development", "Staging", "Production") -if ($choice.Exit) { - Write-Host "User chose to exit" -} else { - Write-Host "Selected: $($choice.Value)" -} - -# Highlighted info panel -Write-InfoBox "Deployment Info" @{Server="prod-01"; Version="2.4.1"; Region="EU-West"} -Style Modern - -# Alert messages -Write-Alert "Configuration updated successfully" -Type Success -Write-Alert "Low disk space on C:" -Type Warning - -# Chart -Write-Chart @{CPU=75; Memory=60; Disk=90} -Style Bar -Title "Resource Usage" - -# Custom width for specific output -Set-OrionMaxWidth -Width 80 -Write-Separator "Narrow Layout" -Style Double -Set-OrionMaxWidth -Reset -``` - -### 📦 Making Scripts Portable (v2.0.0) - -The `Export-OrionHelpers` function allows you to bundle OrionDesign functions directly into your script's project folder, making scripts portable without requiring the module to be installed. - -```powershell -# Analyze a script and create a self-contained helper file -Export-OrionHelpers -ScriptPath "C:\Scripts\MyScript.ps1" - -# Output to a specific directory -Export-OrionHelpers -ScriptPath ".\Deploy.ps1" -OutputPath ".\lib" - -# Preview what would be exported without making changes -Export-OrionHelpers -ScriptPath ".\MyScript.ps1" -WhatIf - -# Keep the Import-Module line (don't comment it out) -Export-OrionHelpers -ScriptPath ".\MyScript.ps1" -CommentOutImport:$false -``` - -**What it does:** -1. Analyzes your script to find all OrionDesign function calls -2. Resolves dependencies (including private helper functions) -3. Creates `OrionDesign-Helpers.ps1` with all required functions -4. Comments out the `Import-Module OrionDesign` line in your script - -**After export, add this line to your script:** -```powershell -. "$PSScriptRoot\OrionDesign-Helpers.ps1" -``` - ---- -**OrionDesign v3.1.0** | PowerShell UI Framework | 21 Functions | 13 Themes | Global Configuration | ANSI Support | -Demo on every function +This is a sample Readme -🔗 [PSGallery](https://www.powershellgallery.com/packages/OrionDesign) · [GitHub](https://github.com/suneworld/OrionDesign) +## Make it yours diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 new file mode 100644 index 0000000..6e19ba0 --- /dev/null +++ b/RequiredModules.psd1 @@ -0,0 +1,25 @@ +@{ + <# + This is only required if you need to use the method PowerShellGet & PSDepend + It is not required for PSResourceGet or ModuleFast (and will be ignored). + See Resolve-Dependency.psd1 on how to enable methods. + #> + #PSDependOptions = @{ + # AddToPath = $true + # Target = 'output\RequiredModules' + # Parameters = @{ + # Repository = 'PSGallery' + # } + #} + + InvokeBuild = 'latest' + PSScriptAnalyzer = 'latest' + Pester = 'latest' + ModuleBuilder = 'latest' + ChangelogManagement = 'latest' + Sampler = 'latest' + + + +} + diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 new file mode 100644 index 0000000..f677fee --- /dev/null +++ b/Resolve-Dependency.ps1 @@ -0,0 +1,1075 @@ +<# + .DESCRIPTION + Bootstrap script for PSDepend. + + .PARAMETER DependencyFile + Specifies the configuration file for the this script. The default value is + 'RequiredModules.psd1' relative to this script's path. + + .PARAMETER PSDependTarget + Path for PSDepend to be bootstrapped and save other dependencies. + Can also be CurrentUser or AllUsers if you wish to install the modules in + such scope. The default value is 'output/RequiredModules' relative to + this script's path. + + .PARAMETER Proxy + Specifies the URI to use for Proxy when attempting to bootstrap + PackageProvider and PowerShellGet. + + .PARAMETER ProxyCredential + Specifies the credential to contact the Proxy when provided. + + .PARAMETER Scope + Specifies the scope to bootstrap the PackageProvider and PSGet if not available. + THe default value is 'CurrentUser'. + + .PARAMETER Gallery + Specifies the gallery to use when bootstrapping PackageProvider, PSGet and + when calling PSDepend (can be overridden in Dependency files). The default + value is 'PSGallery'. + + .PARAMETER GalleryCredential + Specifies the credentials to use with the Gallery specified above. + + .PARAMETER AllowOldPowerShellGetModule + Allow you to use a locally installed version of PowerShellGet older than + 1.6.0 (not recommended). Default it will install the latest PowerShellGet + if an older version than 2.0 is detected. + + .PARAMETER MinimumPSDependVersion + Allow you to specify a minimum version fo PSDepend, if you're after specific + features. + + .PARAMETER AllowPrerelease + Not yet written. + + .PARAMETER WithYAML + Not yet written. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER ModuleFastBleedingEdge + Specifies to use ModuleFast code that is in the ModuleFast's main branch + in its GitHub repository. The parameter UseModuleFast must also be set to + true. + + .PARAMETER UsePSResourceGet + Specifies to use the new PSResourceGet module instead of the (now legacy) PowerShellGet module. + + .PARAMETER PSResourceGetVersion + String specifying the module version for PSResourceGet if the `UsePSResourceGet` switch is utilized. + + .NOTES + Load defaults for parameters values from Resolve-Dependency.psd1 if not + provided as parameter. +#> +[CmdletBinding()] +param +( + [Parameter()] + [System.String] + $DependencyFile = 'RequiredModules.psd1', + + [Parameter()] + [System.String] + $PSDependTarget = (Join-Path -Path $PSScriptRoot -ChildPath 'output/RequiredModules'), + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [ValidateSet('CurrentUser', 'AllUsers')] + [System.String] + $Scope = 'CurrentUser', + + [Parameter()] + [System.String] + $Gallery = 'PSGallery', + + [Parameter()] + [System.Management.Automation.PSCredential] + $GalleryCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowOldPowerShellGetModule, + + [Parameter()] + [System.String] + $MinimumPSDependVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowPrerelease, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $WithYAML, + + [Parameter()] + [System.Collections.Hashtable] + $RegisterGallery, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ModuleFastBleedingEdge, + + [Parameter()] + [System.String] + $ModuleFastVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.String] + $PSResourceGetVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePowerShellGetCompatibilityModule, + + [Parameter()] + [System.String] + $UsePowerShellGetCompatibilityModuleVersion +) + +try +{ + if ($PSVersionTable.PSVersion.Major -le 5) + { + if (-not (Get-Command -Name 'Import-PowerShellDataFile' -ErrorAction 'SilentlyContinue')) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion '3.1.0.0' + } + } + + Write-Verbose -Message 'Importing Bootstrap default parameters from ''$PSScriptRoot/Resolve-Dependency.psd1''.' + + $resolveDependencyConfigPath = Join-Path -Path $PSScriptRoot -ChildPath '.\Resolve-Dependency.psd1' -Resolve -ErrorAction 'Stop' + + $resolveDependencyDefaults = Import-PowerShellDataFile -Path $resolveDependencyConfigPath + + $parameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + + if ($parameterToDefault.Count -eq 0) + { + $parameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option. + foreach ($parameterName in $parameterToDefault) + { + if (-not $PSBoundParameters.Keys.Contains($parameterName) -and $resolveDependencyDefaults.ContainsKey($parameterName)) + { + Write-Verbose -Message "Setting parameter '$parameterName' to value '$($resolveDependencyDefaults[$parameterName])'." + + try + { + $variableValue = $resolveDependencyDefaults[$parameterName] + + if ($variableValue -is [System.String]) + { + $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) + } + + $PSBoundParameters.Add($parameterName, $variableValue) + + Set-Variable -Name $parameterName -Value $variableValue -Force -ErrorAction 'SilentlyContinue' + } + catch + { + Write-Verbose -Message "Error adding default for $parameterName : $($_.Exception.Message)." + } + } + } +} +catch +{ + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from '$resolveDependencyConfigPath': $($_.Exception.Message)." +} + +# Handle when both ModuleFast and PSResourceGet is configured or/and passed as parameter. +if ($UseModuleFast -and $UsePSResourceGet) +{ + Write-Information -MessageData 'Both ModuleFast and PSResourceGet is configured or/and passed as parameter.' -InformationAction 'Continue' + + if ($PSVersionTable.PSVersion -ge '7.2') + { + $UsePSResourceGet = $false + + Write-Information -MessageData 'PowerShell 7.2 or higher being used, prefer ModuleFast over PSResourceGet.' -InformationAction 'Continue' + } + else + { + $UseModuleFast = $false + + Write-Information -MessageData 'Windows PowerShell or PowerShell <=7.1 is being used, prefer PSResourceGet since ModuleFast is not supported on this version of PowerShell.' -InformationAction 'Continue' + } +} + +# Only bootstrap ModuleFast if it is not already imported. +if ($UseModuleFast -and -not (Get-Module -Name 'ModuleFast')) +{ + try + { + $moduleFastBootstrapScriptBlockParameters = @{} + + if ($ModuleFastBleedingEdge) + { + Write-Information -MessageData 'ModuleFast is configured to use Bleeding Edge (directly from ModuleFast''s main branch).' -InformationAction 'Continue' + + $moduleFastBootstrapScriptBlockParameters.UseMain = $true + } + elseif ($ModuleFastVersion) + { + if ($ModuleFastVersion -notmatch 'v') + { + $ModuleFastVersion = 'v{0}' -f $ModuleFastVersion + } + + Write-Information -MessageData ('ModuleFast is configured to use version {0}.' -f $ModuleFastVersion) -InformationAction 'Continue' + + $moduleFastBootstrapScriptBlockParameters.Release = $ModuleFastVersion + } + else + { + Write-Information -MessageData 'ModuleFast is configured to use latest released version.' -InformationAction 'Continue' + } + + $moduleFastBootstrapUri = 'bit.ly/modulefast' # cSpell: disable-line + + Write-Debug -Message ('Using bootstrap script at {0}' -f $moduleFastBootstrapUri) + + $invokeWebRequestParameters = @{ + Uri = $moduleFastBootstrapUri + ErrorAction = 'Stop' + } + + $moduleFastBootstrapScript = Invoke-WebRequest @invokeWebRequestParameters + + $moduleFastBootstrapScriptBlock = [ScriptBlock]::Create($moduleFastBootstrapScript) + + & $moduleFastBootstrapScriptBlock @moduleFastBootstrapScriptBlockParameters + } + catch + { + Write-Warning -Message ('ModuleFast could not be bootstrapped. Reverting to PSResourceGet. Error: {0}' -f $_.Exception.Message) + + $UseModuleFast = $false + $UsePSResourceGet = $true + } +} + +if ($UsePSResourceGet) +{ + $psResourceGetModuleName = 'Microsoft.PowerShell.PSResourceGet' + + # If PSResourceGet was used prior it will be locked and we can't replace it. + if ((Test-Path -Path "$PSDependTarget/$psResourceGetModuleName" -PathType 'Container') -and (Get-Module -Name $psResourceGetModuleName)) + { + Write-Information -MessageData ('{0} is already bootstrapped and imported into the session. If there is a need to refresh the module, open a new session and resolve dependencies again.' -f $psResourceGetModuleName) -InformationAction 'Continue' + } + else + { + Write-Debug -Message ('{0} do not exist, saving the module to RequiredModules.' -f $psResourceGetModuleName) + + $psResourceGetDownloaded = $false + + try + { + if (-not $PSResourceGetVersion) + { + # Default to latest version if no version is passed in parameter or specified in configuration. + $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$psResourceGetModuleName" + } + else + { + $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$psResourceGetModuleName/$PSResourceGetVersion" + } + + $invokeWebRequestParameters = @{ + # TODO: Should support proxy parameters passed to the script. + Uri = $psResourceGetUri + OutFile = "$PSDependTarget/$psResourceGetModuleName.nupkg" # cSpell: ignore nupkg + ErrorAction = 'Stop' + } + + $previousProgressPreference = $ProgressPreference + $ProgressPreference = 'SilentlyContinue' + + # Bootstrapping Microsoft.PowerShell.PSResourceGet. + Invoke-WebRequest @invokeWebRequestParameters + + $ProgressPreference = $previousProgressPreference + + $psResourceGetDownloaded = $true + } + catch + { + Write-Warning -Message ('{0} could not be bootstrapped. Reverting to PowerShellGet. Error: {1}' -f $psResourceGetModuleName, $_.Exception.Message) + } + + $UsePSResourceGet = $false + + if ($psResourceGetDownloaded) + { + # On Windows PowerShell the command Expand-Archive do not like .nupkg as a zip archive extension. + $zipFileName = ((Split-Path -Path $invokeWebRequestParameters.OutFile -Leaf) -replace 'nupkg', 'zip') + + $renameItemParameters = @{ + Path = $invokeWebRequestParameters.OutFile + NewName = $zipFileName + Force = $true + } + + Rename-Item @renameItemParameters + + $psResourceGetZipArchivePath = Join-Path -Path (Split-Path -Path $invokeWebRequestParameters.OutFile -Parent) -ChildPath $zipFileName + + $expandArchiveParameters = @{ + Path = $psResourceGetZipArchivePath + DestinationPath = "$PSDependTarget/$psResourceGetModuleName" + Force = $true + } + + Microsoft.PowerShell.Archive\Expand-Archive @expandArchiveParameters + + Remove-Item -Path $psResourceGetZipArchivePath + + Import-Module -Name $expandArchiveParameters.DestinationPath -Force + + # Successfully bootstrapped PSResourceGet, so let's use it. + $UsePSResourceGet = $true + } + } + + if ($UsePSResourceGet) + { + $psResourceGetModule = Get-Module -Name $psResourceGetModuleName + + $psResourceGetModuleVersion = $psResourceGetModule.Version.ToString() + + if ($psResourceGetModule.PrivateData.PSData.Prerelease) + { + $psResourceGetModuleVersion += '-{0}' -f $psResourceGetModule.PrivateData.PSData.Prerelease + } + + Write-Information -MessageData ('Using {0} v{1}.' -f $psResourceGetModuleName, $psResourceGetModuleVersion) -InformationAction 'Continue' + + if ($UsePowerShellGetCompatibilityModule) + { + $savePowerShellGetParameters = @{ + Name = 'PowerShellGet' + Path = $PSDependTarget + Repository = 'PSGallery' + TrustRepository = $true + } + + if ($UsePowerShellGetCompatibilityModuleVersion) + { + $savePowerShellGetParameters.Version = $UsePowerShellGetCompatibilityModuleVersion + + # Check if the version is a prerelease. + if ($UsePowerShellGetCompatibilityModuleVersion -match '\d+\.\d+\.\d+-.*') + { + $savePowerShellGetParameters.Prerelease = $true + } + } + + Save-PSResource @savePowerShellGetParameters + + Import-Module -Name "$PSDependTarget/PowerShellGet" + } + } +} + +# Check if legacy PowerShellGet and PSDepend must be bootstrapped. +if (-not ($UseModuleFast -or $UsePSResourceGet)) +{ + if ($PSVersionTable.PSVersion.Major -le 5) + { + <# + Making sure the imported PackageManagement module is not from PS7 module + path. The VSCode PS extension is changing the $env:PSModulePath and + prioritize the PS7 path. This is an issue with PowerShellGet because + it loads an old version if available (or fail to load latest). + #> + Get-Module -ListAvailable PackageManagement | + Where-Object -Property 'ModuleBase' -NotMatch 'powershell.7' | + Select-Object -First 1 | + Import-Module -Force + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 0 -CurrentOperation 'NuGet Bootstrap' + + $importModuleParameters = @{ + Name = 'PowerShellGet' + MinimumVersion = '2.0' + MaximumVersion = '2.8.999' + ErrorAction = 'SilentlyContinue' + PassThru = $true + } + + if ($AllowOldPowerShellGetModule) + { + $importModuleParameters.Remove('MinimumVersion') + } + + $powerShellGetModule = Import-Module @importModuleParameters + + # Install the package provider if it is not available. + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable -ErrorAction 'SilentlyContinue' | + Select-Object -First 1 + + if (-not $powerShellGetModule -and -not $nuGetProvider) + { + $providerBootstrapParameters = @{ + Name = 'NuGet' + Force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + Scope = $Scope + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $providerBootstrapParameters.Add('Proxy', $Proxy) + } + + 'ProxyCredential' + { + $providerBootstrapParameters.Add('ProxyCredential', $ProxyCredential) + } + + 'AllowPrerelease' + { + $providerBootstrapParameters.Add('AllowPrerelease', $AllowPrerelease) + } + } + + Write-Information -MessageData 'Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed).' + + $null = Install-PackageProvider @providerBootstrapParameters + + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + + $nuGetProviderVersion = $nuGetProvider.Version.ToString() + + Write-Information -MessageData "Bootstrap: Importing NuGet Package Provider version $nuGetProviderVersion to current session." + + $Null = Import-PackageProvider -Name 'NuGet' -RequiredVersion $nuGetProviderVersion -Force + } + + if ($RegisterGallery) + { + if ($RegisterGallery.ContainsKey('Name') -and -not [System.String]::IsNullOrEmpty($RegisterGallery.Name)) + { + $Gallery = $RegisterGallery.Name + } + else + { + $RegisterGallery.Name = $Gallery + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 7 -CurrentOperation "Verifying private package repository '$Gallery'" -Completed + + $previousRegisteredRepository = Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue' + + if ($previousRegisteredRepository.SourceLocation -ne $RegisterGallery.SourceLocation) + { + if ($previousRegisteredRepository) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Re-registrering private package repository '$Gallery'" -Completed + + Unregister-PSRepository -Name $Gallery + + $unregisteredPreviousRepository = $true + } + else + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Registering private package repository '$Gallery'" -Completed + } + + Register-PSRepository @RegisterGallery + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + + # Fail if the given PSGallery is not registered. + $previousGalleryInstallationPolicy = (Get-PSRepository -Name $Gallery -ErrorAction 'Stop').Trusted + + $updatedGalleryInstallationPolicy = $false + + if ($previousGalleryInstallationPolicy -ne $true) + { + $updatedGalleryInstallationPolicy = $true + + # Only change policy if the repository is not trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Trusted' -ErrorAction 'Ignore' + } +} + +try +{ + # Check if legacy PowerShellGet and PSDepend must be used. + if (-not ($UseModuleFast -or $UsePSResourceGet)) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 25 -CurrentOperation 'Checking PowerShellGet' + + # Ensure the module is loaded and retrieve the version you have. + $powerShellGetVersion = (Import-Module -Name 'PowerShellGet' -PassThru -ErrorAction 'SilentlyContinue').Version + + Write-Verbose -Message "Bootstrap: The PowerShellGet version is $powerShellGetVersion" + + # Versions below 2.0 are considered old, unreliable & not recommended + if (-not $powerShellGetVersion -or ($powerShellGetVersion -lt [System.Version] '2.0' -and -not $AllowOldPowerShellGetModule)) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 40 -CurrentOperation 'Fetching newer version of PowerShellGet' + + # PowerShellGet module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "PowerShellGet module not found. Attempting to install from Gallery $Gallery." + + Write-Warning -Message "Installing PowerShellGet in $PSDependTarget Scope." + + $installPowerShellGetParameters = @{ + Name = 'PowerShellGet' + Force = $true + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + MaximumVersion = '2.8.999' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $installPowerShellGetParameters.Add('Proxy', $Proxy) + } + + 'ProxyCredential' + { + $installPowerShellGetParameters.Add('ProxyCredential', $ProxyCredential) + } + + 'GalleryCredential' + { + $installPowerShellGetParameters.Add('Credential', $GalleryCredential) + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation 'Installing newer version of PowerShellGet' + + Install-Module @installPowerShellGetParameters + } + else + { + Write-Debug -Message "PowerShellGet module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PowerShellGet' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + MaximumVersion = '2.8.999' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation "Saving PowerShellGet from $Gallery to $Scope" + + Save-Module @saveModuleParameters + } + + Write-Debug -Message 'Removing previous versions of PowerShellGet and PackageManagement from session' + + Get-Module -Name 'PowerShellGet' -All | Remove-Module -Force -ErrorAction 'SilentlyContinue' + Get-Module -Name 'PackageManagement' -All | Remove-Module -Force + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 65 -CurrentOperation 'Loading latest version of PowerShellGet' + + Write-Debug -Message 'Importing latest PowerShellGet and PackageManagement versions into session' + + if ($AllowOldPowerShellGetModule) + { + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -Force -PassThru + } + else + { + Import-Module -Name 'PackageManagement' -MinimumVersion '1.4.8.1' -Force + + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -MinimumVersion '2.2.5' -Force -PassThru + } + + $powerShellGetVersion = $powerShellGetModule.Version.ToString() + + Write-Information -MessageData "Bootstrap: PowerShellGet version loaded is $powerShellGetVersion" + } + + # Try to import the PSDepend module from the available modules. + $getModuleParameters = @{ + Name = 'PSDepend' + ListAvailable = $true + } + + $psDependModule = Get-Module @getModuleParameters + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + try + { + $psDependModule = $psDependModule | Where-Object -FilterScript { $_.Version -ge $MinimumPSDependVersion } + } + catch + { + throw ('There was a problem finding the minimum version of PSDepend. Error: {0}' -f $_) + } + } + + if (-not $psDependModule) + { + Write-Debug -Message 'PSDepend module not found.' + + # PSDepend module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "Attempting to install from Gallery '$Gallery'." + + Write-Warning -Message "Installing PSDepend in $PSDependTarget Scope." + + $installPSDependParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $installPSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + + Install-Module @installPSDependParameters + } + else + { + Write-Debug -Message "Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + if ($MinimumPSDependVersion) + { + $saveModuleParameters.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Saving PSDepend from $Gallery to $PSDependTarget" + + Save-Module @saveModuleParameters + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 80 -CurrentOperation 'Importing PSDepend' + + $importModulePSDependParameters = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + $importModulePSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + # We should have successfully bootstrapped PSDepend. Fail if not available. + $null = Import-Module @importModulePSDependParameters + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 81 -CurrentOperation 'Invoke PSDepend' + + if ($WithYAML) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 82 -CurrentOperation 'Verifying PowerShell module PowerShell-Yaml' + + if (-not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 85 -CurrentOperation 'Installing PowerShell module PowerShell-Yaml' + + Write-Verbose -Message "PowerShell-Yaml module not found. Attempting to Save from Gallery '$Gallery' to '$PSDependTarget'." + + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + Save-Module @SaveModuleParam + } + else + { + Write-Verbose -Message 'PowerShell-Yaml is already available' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 88 -CurrentOperation 'Importing PowerShell module PowerShell-Yaml' + } + } + + if (Test-Path -Path $DependencyFile) + { + if ($UseModuleFast -or $UsePSResourceGet) + { + $requiredModules = Import-PowerShellDataFile -Path $DependencyFile + + $requiredModules = $requiredModules.GetEnumerator() | + Where-Object -FilterScript { $_.Name -ne 'PSDependOptions' } + + if ($UseModuleFast) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking ModuleFast' + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $modulesToSave = @( + 'PSDepend' # Always include PSDepend for backward compatibility. + ) + + if ($WithYAML) + { + $modulesToSave += 'PowerShell-Yaml' + } + + if ($UsePowerShellGetCompatibilityModule) + { + Write-Debug -Message 'PowerShellGet compatibility module is configured to be used.' + + # This is needed to ensure that the PowerShellGet compatibility module works. + $psResourceGetModuleName = 'Microsoft.PowerShell.PSResourceGet' + + if ($PSResourceGetVersion) + { + $modulesToSave += ('{0}:[{1}]' -f $psResourceGetModuleName, $PSResourceGetVersion) + } + else + { + $modulesToSave += $psResourceGetModuleName + } + + $powerShellGetCompatibilityModuleName = 'PowerShellGet' + + if ($UsePowerShellGetCompatibilityModuleVersion) + { + $modulesToSave += ('{0}:[{1}]' -f $powerShellGetCompatibilityModuleName, $UsePowerShellGetCompatibilityModuleVersion) + } + else + { + $modulesToSave += $powerShellGetCompatibilityModuleName + } + } + + foreach ($requiredModule in $requiredModules) + { + # If the RequiredModules.psd1 entry is an Hashtable then special handling is needed. + if ($requiredModule.Value -is [System.Collections.Hashtable]) + { + if (-not $requiredModule.Value.Version) + { + $requiredModuleVersion = 'latest' + } + else + { + $requiredModuleVersion = $requiredModule.Value.Version + } + + if ($requiredModuleVersion -eq 'latest') + { + $moduleNameSuffix = '' + + if ($requiredModule.Value.Parameters.AllowPrerelease -eq $true) + { + <# + Adding '!' to the module name indicate to ModuleFast + that is should also evaluate pre-releases. + #> + $moduleNameSuffix = '!' + } + + $modulesToSave += ('{0}{1}' -f $requiredModule.Name, $moduleNameSuffix) + } + else + { + $modulesToSave += ('{0}:[{1}]' -f $requiredModule.Name, $requiredModuleVersion) + } + } + else + { + if ($requiredModule.Value -eq 'latest') + { + $modulesToSave += $requiredModule.Name + } + else + { + # Handle different nuget version operators already present. + if ($requiredModule.Value -match '[!|:|[|(|,|>|<|=]') + { + $modulesToSave += ('{0}{1}' -f $requiredModule.Name, $requiredModule.Value) + } + else + { + # Assuming the version is a fixed version. + $modulesToSave += ('{0}:[{1}]' -f $requiredModule.Name, $requiredModule.Value) + } + } + } + } + + Write-Debug -Message ("Required modules to retrieve plan for:`n{0}" -f ($modulesToSave | Out-String)) + + $installModuleFastParameters = @{ + Destination = $PSDependTarget + DestinationOnly = $true + NoPSModulePathUpdate = $true + NoProfileUpdate = $true + Update = $true + Confirm = $false + } + + $moduleFastPlan = Install-ModuleFast -Specification $modulesToSave -Plan @installModuleFastParameters + + Write-Debug -Message ("Missing modules that need to be saved:`n{0}" -f ($moduleFastPlan | Out-String)) + + if ($moduleFastPlan) + { + # Clear all modules in plan from the current session so they can be fetched again. + try + { + $moduleFastPlan.Name | Get-Module | Remove-Module -Force + $moduleFastPlan | Install-ModuleFast @installModuleFastParameters + } + catch + { + Write-Warning -Message 'ModuleFast could not save one or more dependencies. Retrying...' + try + { + $moduleFastPlan.Name | Get-Module | Remove-Module -Force + $moduleFastPlan | Install-ModuleFast @installModuleFastParameters + } + catch + { + Write-Error 'ModuleFast could not save one or more dependencies even after a retry.' + } + } + } + else + { + Write-Verbose -Message 'All required modules were already up to date' + } + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + + if ($UsePSResourceGet) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSResourceGet' + + $modulesToSave = @( + @{ + Name = 'PSDepend' # Always include PSDepend for backward compatibility. + } + ) + + if ($WithYAML) + { + $modulesToSave += @{ + Name = 'PowerShell-Yaml' + } + } + + # Prepare hashtable that can be concatenated to the Save-PSResource parameters. + foreach ($requiredModule in $requiredModules) + { + # If the RequiredModules.psd1 entry is an Hashtable then special handling is needed. + if ($requiredModule.Value -is [System.Collections.Hashtable]) + { + $saveModuleHashtable = @{ + Name = $requiredModule.Name + } + + if ($requiredModule.Value.Version -and $requiredModule.Value.Version -ne 'latest') + { + $saveModuleHashtable.Version = $requiredModule.Value.Version + } + + if ($requiredModule.Value.Parameters.AllowPrerelease -eq $true) + { + $saveModuleHashtable.Prerelease = $true + } + + $modulesToSave += $saveModuleHashtable + } + else + { + if ($requiredModule.Value -eq 'latest') + { + $modulesToSave += @{ + Name = $requiredModule.Name + } + } + else + { + $modulesToSave += @{ + Name = $requiredModule.Name + Version = $requiredModule.Value + } + } + } + } + + $percentagePerModule = [System.Math]::Floor(100 / $modulesToSave.Length) + + $progressPercentage = 0 + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' + + foreach ($currentModule in $modulesToSave) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Saving module {0}' -f $savePSResourceParameters.Name) + + $savePSResourceParameters = @{ + Path = $PSDependTarget + TrustRepository = $true + Confirm = $false + } + + # Concatenate the module parameters to the Save-PSResource parameters. + $savePSResourceParameters += $currentModule + + # Modules that Sampler depend on that cannot be refreshed without a new session. + $skipModule = @('PowerShell-Yaml') + + if ($savePSResourceParameters.Name -in $skipModule -and (Get-Module -Name $savePSResourceParameters.Name)) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Skipping module {0}' -f $savePSResourceParameters.Name) + + Write-Information -MessageData ('Skipping the module {0} since it cannot be refresh while loaded into the session. To refresh the module open a new session and resolve dependencies again.' -f $savePSResourceParameters.Name) -InformationAction 'Continue' + } + else + { + # Clear all module from the current session so any new version fetched will be re-imported. + Get-Module -Name $savePSResourceParameters.Name | Remove-Module -Force + + Save-PSResource @savePSResourceParameters -ErrorVariable 'savePSResourceError' + + if ($savePSResourceError) + { + Write-Warning -Message 'Save-PSResource could not save (replace) one or more dependencies. This can be due to the module is loaded into the session (and referencing assemblies). Close the current session and open a new session and try again.' + } + } + + $progressPercentage += $percentagePerModule + } + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + } + else + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSDepend' + + Write-Progress -Activity 'PSDepend:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $psDependParameters = @{ + Force = $true + Path = $DependencyFile + } + + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified. + Invoke-PSDepend @psDependParameters + + Write-Progress -Activity 'PSDepend:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + } + else + { + Write-Warning -Message "The dependency file '$DependencyFile' could not be found." + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 100 -CurrentOperation 'Bootstrap complete' -Completed +} +finally +{ + if ($RegisterGallery) + { + Write-Verbose -Message "Removing private package repository '$Gallery'." + Unregister-PSRepository -Name $Gallery + } + + if ($unregisteredPreviousRepository) + { + Write-Verbose -Message "Reverting private package repository '$Gallery' to previous location URI:s." + + $registerPSRepositoryParameters = @{ + Name = $previousRegisteredRepository.Name + InstallationPolicy = $previousRegisteredRepository.InstallationPolicy + } + + if ($previousRegisteredRepository.SourceLocation) + { + $registerPSRepositoryParameters.SourceLocation = $previousRegisteredRepository.SourceLocation + } + + if ($previousRegisteredRepository.PublishLocation) + { + $registerPSRepositoryParameters.PublishLocation = $previousRegisteredRepository.PublishLocation + } + + if ($previousRegisteredRepository.ScriptSourceLocation) + { + $registerPSRepositoryParameters.ScriptSourceLocation = $previousRegisteredRepository.ScriptSourceLocation + } + + if ($previousRegisteredRepository.ScriptPublishLocation) + { + $registerPSRepositoryParameters.ScriptPublishLocation = $previousRegisteredRepository.ScriptPublishLocation + } + + Register-PSRepository @registerPSRepositoryParameters + } + + if ($updatedGalleryInstallationPolicy -eq $true -and $previousGalleryInstallationPolicy -ne $true) + { + # Only try to revert installation policy if the repository exist + if ((Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue')) + { + # Reverting the Installation Policy for the given gallery if it was not already trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Untrusted' + } + } + + Write-Verbose -Message 'Project Bootstrapped, returning to Invoke-Build.' +} diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 new file mode 100644 index 0000000..ae3315c --- /dev/null +++ b/Resolve-Dependency.psd1 @@ -0,0 +1,76 @@ +@{ + <# + Default parameter values to be loaded by the Resolve-Dependency.ps1 script (unless set in bound parameters + when calling the script). + #> + + #PSDependTarget = './output/modules' + #Proxy = '' + #ProxyCredential = '$MyCredentialVariable' #TODO: find a way to support credentials in build (resolve variable) + + Gallery = 'PSGallery' + + # To use a private nuget repository change the following to your own feed. The locations must be a Nuget v2 feed due + # to limitation in PowerShellGet v2.x. Example below is for a Azure DevOps Server project-scoped feed. While resolving + # dependencies it will be registered as a trusted repository with the name specified in the property 'Gallery' above, + # unless property 'Name' is provided in the hashtable below, if so it will override the property 'Gallery' above. The + # registered repository will be removed when dependencies has been resolved, unless it was already registered to begin + # with. If repository is registered already but with different URL:s the repository will be re-registered and reverted + # after dependencies has been resolved. Currently only Windows integrated security works with private Nuget v2 feeds + # (or if it is a public feed with no security), it is not possible yet to securely provide other credentials for the feed. + # Private repositories will currently only work using PowerShellGet. + #RegisterGallery = @{ + # #Name = 'MyPrivateFeedName' + # GallerySourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryScriptSourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryScriptPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # #InstallationPolicy = 'Trusted' + #} + + #AllowOldPowerShellGetModule = $true + #MinimumPSDependVersion = '0.3.0' + AllowPrerelease = $false + WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files + + <# + Enable ModuleFast to be the default method of resolving dependencies by setting + UseModuleFast to the value $true. ModuleFast requires PowerShell 7.2 or higher. + If UseModuleFast is not configured or set to $false then PowerShellGet (or + PSResourceGet if enabled) will be used to as the default method of resolving + dependencies. You can always use the parameter `-UseModuleFast` of the + Resolve-Dependency.ps1 or build.ps1 script even when this is not configured + or set to $false. + + You can use ModuleFastVersion to specify a specific version of ModuleFast to use. + This will also affect the use of parameter `-UseModuleFast` of the Resolve-Dependency.ps1 + or build.ps1 script. If ModuleFastVersion is not configured then the latest + (non-preview) released version is used. + + ModuleFastBleedingEdge will override ModuleFastVersion and use the absolute latest + code from the ModuleFast repository. This is useful if you want to test the absolute + latest changes in ModuleFast repository. This is not recommended for production use. + By enabling ModuleFastBleedingEdge the pipeline can encounter breaking changes or + problems by code that is merged in the ModuleFast repository, this could affect the + pipeline negatively. Make sure to use a clean PowerShell session after changing + the value of ModuleFastBleedingEdge so that ModuleFast uses the correct bootstrap + script and correct parameter values. This will also affect the use of parameter + `-UseModuleFast` of the Resolve-Dependency.ps1 or build.ps1 script. + #> + #UseModuleFast = $true + #ModuleFastVersion = '0.1.2' + #ModuleFastBleedingEdge = $true + + <# + Enable PSResourceGet to be the default method of resolving dependencies by setting + UsePSResourceGet to the value $true. If UsePSResourceGet is not configured or + set to $false then PowerShellGet will be used to resolve dependencies. + #> + UsePSResourceGet = $true + # PSResourceGetVersion = '1.2.0' + + # PowerShellGet compatibility module only works when using PSResourceGet or ModuleFast. + # UsePowerShellGetCompatibilityModule = $true + # UsePowerShellGetCompatibilityModuleVersion = '3.0.23-beta23' +} + diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..f005a4e --- /dev/null +++ b/build.ps1 @@ -0,0 +1,542 @@ +<# + .DESCRIPTION + Bootstrap and build script for PowerShell module CI/CD pipeline. + + .PARAMETER Tasks + The task or tasks to run. The default value is '.' (runs the default task). + + .PARAMETER CodeCoverageThreshold + The code coverage target threshold to uphold. Set to 0 to disable. + The default value is '' (empty string). + + .PARAMETER BuildConfig + Path to a file with configuration. Supported extensions : psd1, yaml, yml, json, jsonc. + + .PARAMETER OutputDirectory + Specifies the folder to build the artefact into. The default value is 'output'. + + .PARAMETER BuiltModuleSubdirectory + Subdirectory name to build the module (under $OutputDirectory). The default + value is '' (empty string). + + .PARAMETER RequiredModulesDirectory + Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency + and PSDepend where to save the required modules. It is also possible to use + 'CurrentUser' or 'AllUsers' to install missing dependencies. You can override + the value for PSDepend in the Build.psd1 build manifest. The default value is + 'output/RequiredModules'. + + .PARAMETER PesterScript + One or more paths that will override the Pester configuration in build + configuration file when running the build task Invoke_Pester_Tests. + + If running Pester 5 test, use the alias PesterPath to be future-proof. + + .PARAMETER PesterTag + Filter which tags to run when invoking Pester tests. This is used in the + Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER PesterExcludeTag + Filter which tags to exclude when invoking Pester tests. This is used in + the Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER DscTestTag + Filter which tags to run when invoking DSC Resource tests. This is used + in the DscResource.Test.build.ps1 tasks. + + .PARAMETER DscTestExcludeTag + Filter which tags to exclude when invoking DSC Resource tests. This is + used in the DscResource.Test.build.ps1 tasks. + + .PARAMETER ResolveDependency + Resolve missing dependencies. + + .PARAMETER BuildInfo + The build info object from ModuleBuilder. Defaults to an empty hashtable. + + .PARAMETER AutoRestore + Specifies to restore the required modules by running build.ps1 with ResolveDependency switch and empty task `noop`. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER UsePSResourceGet + Specifies to use PSResourceGet instead of PowerShellGet to resolve dependencies + faster. This can also be configured in Resolve-Dependency.psd1. + + .PARAMETER UsePowerShellGetCompatibilityModule + Specifies to use the compatibility module PowerShellGet. This parameter + only works then the method of downloading dependencies is PSResourceGet. + This can also be configured in Resolve-Dependency.psd1. +#> +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because how $PSDependTarget is assigned to splatting variable $resolveDependencyParams.')] +[CmdletBinding()] +param +( + [Parameter(Position = 0)] + [System.String[]] + $Tasks = '.', + + [Parameter()] + [System.String] + $CodeCoverageThreshold = '', + + [Parameter()] + [System.String] + [ValidateScript( + { Test-Path -Path $_ } + )] + $BuildConfig, + + [Parameter()] + [System.String] + $OutputDirectory = 'output', + + [Parameter()] + [System.String] + $BuiltModuleSubdirectory = '', + + [Parameter()] + [System.String] + $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), + + [Parameter()] + # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed + [Alias('PesterPath')] + [System.Object[]] + $PesterScript, + + [Parameter()] + [System.String[]] + $PesterTag, + + [Parameter()] + [System.String[]] + $PesterExcludeTag, + + [Parameter()] + [System.String[]] + $DscTestTag, + + [Parameter()] + [System.String[]] + $DscTestExcludeTag, + + [Parameter()] + [Alias('bootstrap')] + [System.Management.Automation.SwitchParameter] + $ResolveDependency, + + [Parameter(DontShow)] + [AllowNull()] + [System.Collections.Hashtable] + $BuildInfo, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AutoRestore, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePowerShellGetCompatibilityModule +) + +<# + The BEGIN block (at the end of this file) handles the Bootstrap of the Environment + before Invoke-Build can run the tasks if the parameter ResolveDependency (or + parameter alias Bootstrap) is specified. +#> + +process +{ + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). + return + } + + # Execute the Build process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' + + try + { + Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta + + # Load the default BuildInfo if the parameter BuildInfo is not set. + if (-not $PSBoundParameters.ContainsKey('BuildInfo')) + { + try + { + if (Test-Path -Path $BuildConfig) + { + $configFile = Get-Item -Path $BuildConfig + + Write-Host -Object "[build] Loading Configuration from $configFile" + + $BuildInfo = switch -Regex ($configFile.Extension) + { + # Native Support for PSD1 + '\.psd1' + { + if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 + } + + Import-PowerShellDataFile -Path $BuildConfig + } + + # Support for yaml when module PowerShell-Yaml is available + '\.[yaml|yml]' + { + Import-Module -Name 'powershell-yaml' -ErrorAction Stop + + ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) + } + + # Support for JSON and JSONC (by Removing comments) when module PowerShell-Yaml is available + '\.[json|jsonc]' + { + $jsonFile = Get-Content -Raw -Path $configFile + + $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + + # Yaml is superset of JSON. + ConvertFrom-Yaml -Yaml $jsonContent + } + + # Unknown extension, return empty hashtable. + default + { + Write-Error -Message "Extension '$_' not supported. using @{}" + + @{ } + } + } + } + else + { + Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red + + # No config file was found, return empty hashtable. + $BuildInfo = @{ } + } + } + catch + { + $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." + + Write-Host -Object $logMessage -ForegroundColor Yellow + + $BuildInfo = @{ } + + Write-Error -Message $_.Exception.Message + } + } + + # If the Invoke-Build Task Header is specified in the Build Info, set it. + if ($BuildInfo.TaskHeader) + { + Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + <# + Add BuildModuleOutput to PSModule Path environment variable. + Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. + #> + if ($BuiltModuleSubdirectory) + { + if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } # test if BuiltModuleSubDirectory set in build config file + elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. + if ($powerShellModulePaths -notcontains $BuildModuleOutput) + { + Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + <# + Import Tasks from modules via their exported aliases when defined in Build Manifest. + https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + #> + if ($BuildInfo.ContainsKey('ModuleBuildTasks')) + { + foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) + { + try + { + Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray + + $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop + + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) + { + $aliasTasks = $loadedModule.ExportedAliases.GetEnumerator().Where{ + # Using -like to support wildcard. + $_.Key -like $TaskToExport + } + + foreach ($aliasTask in $aliasTasks) + { + Write-Host -Object "`t Loading $($aliasTask.Key)..." -ForegroundColor DarkGray + + # Dot-sourcing the Tasks via their exported aliases. + . (Get-Alias $aliasTask.Key) + } + } + } + catch + { + Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red + + Write-Error -Message $_ + } + } + } + + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). + $taskFiles = Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore + foreach ($taskFile in $taskFiles) + { + "Importing file $($taskFile.BaseName)" | Write-Verbose + + . $taskFile.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process. + task noop { } + + # Define default task sequence ("."), can be overridden in the $BuildInfo. + task . { + Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow + } + + Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray + + # Load Invoke-Build task sequences/workflows from $BuildInfo. + foreach ($workflow in $BuildInfo.BuildWorkflow.keys) + { + Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." + + $workflowItem = $BuildInfo.BuildWorkflow.($workflow) + + if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + { + $workflowItem = [ScriptBlock]::Create($Matches['sb']) + } + + Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray + + task $workflow $workflowItem + } + + Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta + } + finally + { + Pop-Location -StackName 'BeforeBuild' + } +} + +begin +{ + # Find build config if not specified. + if (-not $BuildConfig) + { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore + + if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) + { + throw 'No build configuration found. Specify path via parameter BuildConfig.' + } + elseif ($config -is [System.Array]) + { + if ($config.Length -gt 1) + { + throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' + } + + $BuildConfig = $config[0] + } + else + { + $BuildConfig = $config + } + } + + # Bootstrapping the environment before using Invoke-Build as task runner + + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') + { + Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green + + Push-Location $PSScriptRoot -StackName 'BuildModule' + } + + if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) + { + # Installing modules instead of saving them. + Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $RequiredModulesDirectory + } + else + { + if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) + { + $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory + } + + # Resolving the absolute path to save the required modules to. + if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + { + $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory + } + + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. + if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + { + Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + + $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory + } + else + { + Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green + + $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName + } + + $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator + + # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. + if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and + ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) + { + Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable + $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable + $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable + + # Checking if the user should -ResolveDependency. + if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) + { + if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') + { + Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow + + $ResolveDependency = $true + } + else + { + Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." + } + } + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $requiredModulesPath + } + + if ($ResolveDependency) + { + Write-Host -Object "[pre-build] Resolving dependencies using preferred method." -ForegroundColor Green + + $resolveDependencyParams = @{ } + + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. + if ($BuildConfig -match '\.[yaml|yml]$') + { + $resolveDependencyParams.Add('WithYaml', $true) + } + + $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys + + foreach ($cmdParameter in $resolveDependencyAvailableParams) + { + # The parameter has been explicitly used for calling the .build.ps1 + if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) + { + $paramValue = $MyInvocation.BoundParameters.Item($cmdParameter) + + Write-Debug -Message " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) + } + # Use defaults parameter value from Build.ps1, if any + else + { + $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore + + if ($paramValue) + { + Write-Debug -Message " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) + } + } + } + + Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green + + .\Resolve-Dependency.ps1 @resolveDependencyParams + } + + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') + { + Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." + + if ($PSBoundParameters.ContainsKey('ResolveDependency')) + { + Write-Verbose -Message "Dependency already resolved. Removing task." + + $null = $PSBoundParameters.Remove('ResolveDependency') + } + + Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green + + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path + + Pop-Location -StackName 'BuildModule' + + return + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..adfdbf7 --- /dev/null +++ b/build.yaml @@ -0,0 +1,180 @@ +--- +#################################################### +# ModuleBuilder Configuration # +#################################################### + +BuiltModuleSubdirectory: module +CopyPaths: + - en-US +# - DSCResources + # - Modules +Encoding: UTF8 +# Can be used to manually specify module's semantic version if the preferred method of +# using GitVersion is not available, and it is not possible to set the session environment +# variable `$env:ModuleVersion`, nor setting the variable `$ModuleVersion`, in the +# PowerShell session (parent scope) before running the task `build`. +#SemVer: '99.0.0-preview1' + +# Suffix to add to Root module PSM1 after merge (here, the Set-Alias exporting IB tasks) +# suffix: suffix.ps1 +# prefix: prefix.ps1 +VersionedOutputDirectory: true + +#################################################### +# ModuleBuilder Submodules Configuration # +#################################################### + +NestedModule: +# HelperSubmodule: # This is the first submodule to build into the output +# Path: ./*/Modules/HelperSubmodule/HelperSubmodule.psd1 +# # is trimmed (remove metadata & Prerelease tag) and OutputDirectory expanded (the only one) +# OutputDirectory: ///Modules/HelperSubmodule +# VersionedOutputDirectory: false +# AddToManifest: false +# SemVer: +# # suffix: +# # prefix: + +#################################################### +# Sampler Pipeline Configuration # +#################################################### +# Defining 'Workflows' (suite of InvokeBuild tasks) to be run using their alias +BuildWorkflow: + '.': # "." is the default Invoke-Build workflow. It is called when no -Tasks is specified to the build.ps1 + - build + - test + + build: + - Clean + - Build_Module_ModuleBuilder + - Build_NestedModules_ModuleBuilder + - Create_changelog_release_output + + + pack: + - build + + - package_module_nupkg + + + + # Defining test task to be run when invoking `./build.ps1 -Tasks test` + test: + # Uncomment to modify the PSModulePath in the test pipeline (also requires the build configuration section SetPSModulePath). + #- Set_PSModulePath + - Pester_Tests_Stop_On_Fail + # Use this task if pipeline uses code coverage and the module is using the + # pattern of Public, Private, Enum, Classes. + #- Convert_Pester_Coverage + - Pester_if_Code_Coverage_Under_Threshold + + # Use this task when you have multiple parallel tests, which produce multiple + # code coverage files and needs to get merged into one file. + #merge: + #- Merge_CodeCoverage_Files + + publish: + + + - publish_module_to_gallery + +#################################################### +# PESTER Configuration # +#################################################### + +Pester: + # Pester Advanced configuration. + # If a key is not set it will be using Sampler pipeline default value. + Configuration: + Run: + # If no scripts are defined the default is to use all the tests under the project's + # tests folder or source folder (if present). Test script paths can be defined to + # only run tests in certain folders, or run specific test files, or can be use to + # specify the order tests are run. + Path: + #- tests/QA/module.tests.ps1 + #- tests/QA + #- tests/Unit + #- tests/Integration + Output: + Verbosity: Detailed + StackTraceVerbosity: Full + CIFormat: Auto + Filter: + Tag: + ExcludeTag: + #- helpQuality + #- FunctionalQuality + #- TestQuality + CodeCoverage: + CoveragePercentTarget: 0 # Set to 0 to bypass + #OutputPath: JaCoCo_$OsShortName.xml + #OutputEncoding: ascii + UseBreakpoints: false + TestResult: + OutputFormat: NUnitXML + OutputEncoding: ascii + # Will look at every *.ps1 & *.psm1 under ModulePath, excepts when $_.FullName -match (Join-Path $ProjectPath $ExcludeFromCodeCoverageItem) + ExcludeFromCodeCoverage: + + +# Use this if code coverage should be merged from several pipeline test jobs. +# Any existing keys above should be replaced. See also CodeCoverage below. +# Pester.Configuration.CodeCoverage.OutputPath is the file that is created for +# each pipeline test job. +#OutputPath: JaCoCo_Merge.xml + +# Use this to merged code coverage from several pipeline test jobs. +# CodeCoverageFilePattern - the pattern used to search all pipeline test job artifacts +# after the file specified in CodeCoverage.OutputPath above. +# CodeCoverageMergedOutputFile - the file that is created by the merge build task and +# is the file that should be uploaded to code coverage services. +#CodeCoverage: + #CodeCoverageFilePattern: JaCoCo_Merge.xml # the pattern used to search all pipeline test job artifacts + #CodeCoverageMergedOutputFile: JaCoCo_coverage.xml # the file that is created for the merged code coverage + + +# Import ModuleBuilder tasks from a specific PowerShell module using the build +# task's alias. Wildcard * can be used to specify all tasks that has a similar +# prefix and or suffix. The module contain the task must be added as a required +# module in the file RequiredModules.psd1. +ModuleBuildTasks: + Sampler: + - '*.build.Sampler.ib.tasks' + + + +# Invoke-Build Header to be used to 'decorate' the terminal output of the tasks. +TaskHeader: | + param($Path) + "" + "=" * 79 + Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" + Write-Build DarkGray "$(Get-BuildSynopsis $Task)" + "-" * 79 + Write-Build DarkGray " $Path" + Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + "" + + +#################################################### +# Git Configuration # +#################################################### +GitConfig: + # UserName: dscbot + # UserEmail: dsccommunity@outlook.com + MainGitBranch: main + +#################################################### +# Changelog Configuration # +#################################################### +# Used by Create_Changelog_Branch task. Defaults are shown below. +# ChangelogConfig: +# FilesToAdd: +# - 'CHANGELOG.md' +# UpdateChangelogOnPrerelease: false # Set to true to update changelog on pre-releases too + + + + + diff --git a/source/OrionDesign.psd1 b/source/OrionDesign.psd1 new file mode 100644 index 0000000..86d9742 --- /dev/null +++ b/source/OrionDesign.psd1 @@ -0,0 +1,137 @@ +# +# Module manifest for module 'OrionDesign' +# +# Generated by: Sune Alexandersen Narud +# +# Generated on: 2026-08-26 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'OrionDesign.psm1' + +# Version number of this module. +ModuleVersion = '0.0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '981576c1-f22d-473f-8b3e-06acf22c59d5' + +# Author of this module +Author = 'Sune Alexandersen Narud' + +# Company or vendor of this module +CompanyName = 'Sune Alexandersen Narud' + +# Copyright statement for this module +Copyright = '(c) 2026 Sune Alexandersen Narud. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'Orion Design Framework - Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter.' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '5.1' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = @() + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + Tags = @('UI', 'Terminal', 'Console', 'Output', 'Formatting', 'Theme', 'Menu', 'Banner', 'Chart', 'ProgressBar', 'Colors', 'TUI', 'PowerShell5', 'PSCore') + ProjectUri = 'https://github.com/suneworld/OrionDesign' + LicenseUri = 'https://github.com/suneworld/OrionDesign/blob/master/LICENSE' + ReleaseNotes = @' +## v3.1.0 +- Added -Demo parameter to all 21 public functions +- Each demo shows live output with exact code that produced it +- Dynamic code box width (auto-sizes to content) +- Added Show-OrionSmartMenu with arrow-key and numeric navigation modes + +## v3.0.0 +- Restructured module to flat layout +- Renamed Write-MenuLine parameter MenuTitle to Text +- Fixed theme color pairings throughout + +## v2.1.4 +- Added Write-MenuLine with -Muted parameter +- Enhanced Write-Action/Write-ActionResult alignment +'@ + } + } + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + + + + + + + + + + + + + diff --git a/source/OrionDesign.psm1 b/source/OrionDesign.psm1 new file mode 100644 index 0000000..92d7cfb --- /dev/null +++ b/source/OrionDesign.psm1 @@ -0,0 +1,5 @@ +<# + This file is intentionally left empty. It is must be left here for the module + manifest to refer to. It is recreated during the build process. +#> + diff --git a/Functions/Private/Convert-ToColoredSegments.ps1 b/source/Private/Convert-ToColoredSegments.ps1 similarity index 100% rename from Functions/Private/Convert-ToColoredSegments.ps1 rename to source/Private/Convert-ToColoredSegments.ps1 diff --git a/source/Private/Get-PrivateFunction.ps1 b/source/Private/Get-PrivateFunction.ps1 new file mode 100644 index 0000000..b4ef195 --- /dev/null +++ b/source/Private/Get-PrivateFunction.ps1 @@ -0,0 +1,31 @@ +function Get-PrivateFunction +{ + <# + .SYNOPSIS + This is a sample Private function only visible within the module. + + .DESCRIPTION + This sample function is not exported to the module and only return the data passed as parameter. + + .EXAMPLE + $null = Get-PrivateFunction -PrivateData 'NOTHING TO SEE HERE' + + .PARAMETER PrivateData + The PrivateData parameter is what will be returned without transformation. + + #> + [cmdletBinding()] + [OutputType([string])] + param + ( + [Parameter()] + [String] + $PrivateData + ) + + process + { + Write-Output $PrivateData + } + +} diff --git a/Functions/Private/Write-Colored.ps1 b/source/Private/Write-Colored.ps1 similarity index 100% rename from Functions/Private/Write-Colored.ps1 rename to source/Private/Write-Colored.ps1 diff --git a/Functions/Public/Export-OrionHelpers.ps1 b/source/Public/Export-OrionHelpers.ps1 similarity index 100% rename from Functions/Public/Export-OrionHelpers.ps1 rename to source/Public/Export-OrionHelpers.ps1 diff --git a/Functions/Public/Get-OrionMaxWidth.ps1 b/source/Public/Get-OrionMaxWidth.ps1 similarity index 100% rename from Functions/Public/Get-OrionMaxWidth.ps1 rename to source/Public/Get-OrionMaxWidth.ps1 diff --git a/Functions/Public/Get-OrionTheme.ps1 b/source/Public/Get-OrionTheme.ps1 similarity index 100% rename from Functions/Public/Get-OrionTheme.ps1 rename to source/Public/Get-OrionTheme.ps1 diff --git a/source/Public/Get-Something.ps1 b/source/Public/Get-Something.ps1 new file mode 100644 index 0000000..9f32477 --- /dev/null +++ b/source/Public/Get-Something.ps1 @@ -0,0 +1,41 @@ +function Get-Something +{ + <# + .SYNOPSIS + Sample Function to return input string. + + .DESCRIPTION + This function is only a sample Advanced function that returns the Data given via parameter Data. + + .EXAMPLE + Get-Something -Data 'Get me this text' + + + .PARAMETER Data + The Data parameter is the data that will be returned without transformation. + + #> + [cmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'Low' + )] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [String] + $Data + ) + + process + { + if ($pscmdlet.ShouldProcess($Data)) + { + Write-Verbose -Message ('Returning the data: {0}' -f $Data) + Get-PrivateFunction -PrivateData $Data + } + else + { + Write-Verbose -Message 'oh dear' + } + } +} diff --git a/Functions/Public/Set-OrionMaxWidth.ps1 b/source/Public/Set-OrionMaxWidth.ps1 similarity index 100% rename from Functions/Public/Set-OrionMaxWidth.ps1 rename to source/Public/Set-OrionMaxWidth.ps1 diff --git a/Functions/Public/Set-OrionTheme.ps1 b/source/Public/Set-OrionTheme.ps1 similarity index 100% rename from Functions/Public/Set-OrionTheme.ps1 rename to source/Public/Set-OrionTheme.ps1 diff --git a/Functions/Public/Show-OrionDemo.ps1 b/source/Public/Show-OrionDemo.ps1 similarity index 100% rename from Functions/Public/Show-OrionDemo.ps1 rename to source/Public/Show-OrionDemo.ps1 diff --git a/Functions/Public/Show-OrionSmartMenu.ps1 b/source/Public/Show-OrionSmartMenu.ps1 similarity index 100% rename from Functions/Public/Show-OrionSmartMenu.ps1 rename to source/Public/Show-OrionSmartMenu.ps1 diff --git a/Functions/Public/Write-Action.ps1 b/source/Public/Write-Action.ps1 similarity index 100% rename from Functions/Public/Write-Action.ps1 rename to source/Public/Write-Action.ps1 diff --git a/Functions/Public/Write-ActionResult.ps1 b/source/Public/Write-ActionResult.ps1 similarity index 100% rename from Functions/Public/Write-ActionResult.ps1 rename to source/Public/Write-ActionResult.ps1 diff --git a/Functions/Public/Write-Alert.ps1 b/source/Public/Write-Alert.ps1 similarity index 100% rename from Functions/Public/Write-Alert.ps1 rename to source/Public/Write-Alert.ps1 diff --git a/Functions/Public/Write-Banner.ps1 b/source/Public/Write-Banner.ps1 similarity index 100% rename from Functions/Public/Write-Banner.ps1 rename to source/Public/Write-Banner.ps1 diff --git a/Functions/Public/Write-Chart.ps1 b/source/Public/Write-Chart.ps1 similarity index 100% rename from Functions/Public/Write-Chart.ps1 rename to source/Public/Write-Chart.ps1 diff --git a/Functions/Public/Write-Header.ps1 b/source/Public/Write-Header.ps1 similarity index 100% rename from Functions/Public/Write-Header.ps1 rename to source/Public/Write-Header.ps1 diff --git a/Functions/Public/Write-InfoBox.ps1 b/source/Public/Write-InfoBox.ps1 similarity index 100% rename from Functions/Public/Write-InfoBox.ps1 rename to source/Public/Write-InfoBox.ps1 diff --git a/Functions/Public/Write-Menu.ps1 b/source/Public/Write-Menu.ps1 similarity index 100% rename from Functions/Public/Write-Menu.ps1 rename to source/Public/Write-Menu.ps1 diff --git a/Functions/Public/Write-MenuLine.ps1 b/source/Public/Write-MenuLine.ps1 similarity index 100% rename from Functions/Public/Write-MenuLine.ps1 rename to source/Public/Write-MenuLine.ps1 diff --git a/Functions/Public/Write-Panel.ps1 b/source/Public/Write-Panel.ps1 similarity index 100% rename from Functions/Public/Write-Panel.ps1 rename to source/Public/Write-Panel.ps1 diff --git a/Functions/Public/Write-ProgressBar.ps1 b/source/Public/Write-ProgressBar.ps1 similarity index 100% rename from Functions/Public/Write-ProgressBar.ps1 rename to source/Public/Write-ProgressBar.ps1 diff --git a/Functions/Public/Write-Question.ps1 b/source/Public/Write-Question.ps1 similarity index 100% rename from Functions/Public/Write-Question.ps1 rename to source/Public/Write-Question.ps1 diff --git a/Functions/Public/Write-Separator.ps1 b/source/Public/Write-Separator.ps1 similarity index 100% rename from Functions/Public/Write-Separator.ps1 rename to source/Public/Write-Separator.ps1 diff --git a/Functions/Public/Write-Steps.ps1 b/source/Public/Write-Steps.ps1 similarity index 100% rename from Functions/Public/Write-Steps.ps1 rename to source/Public/Write-Steps.ps1 diff --git a/source/en-US/about_OrionDesign.help.txt b/source/en-US/about_OrionDesign.help.txt new file mode 100644 index 0000000..0006c01 --- /dev/null +++ b/source/en-US/about_OrionDesign.help.txt @@ -0,0 +1,24 @@ +TOPIC + about_OrionDesign + +SHORT DESCRIPTION + Orion Design Framework – Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter. + +LONG DESCRIPTION + Orion Design Framework – Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter. + +EXAMPLES + PS C:\> {{ add examples here }} + +NOTE: + Thank you to all those who contributed to this module, by writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Look out on the Github repository for issues and new releases. + +SEE ALSO + - {{ Please add Project URI such as github }}} + +KEYWORDS + {{ Add comma separated keywords here }} + diff --git a/tests/QA/module.tests.ps1 b/tests/QA/module.tests.ps1 new file mode 100644 index 0000000..4aacd4a --- /dev/null +++ b/tests/QA/module.tests.ps1 @@ -0,0 +1,251 @@ +BeforeDiscovery { + $projectPath = "$($PSScriptRoot)\..\.." | Convert-Path + + <# + If the QA tests are run outside of the build script (e.g with Invoke-Pester) + the parent scope has not set the variable $ProjectName. + #> + if (-not $ProjectName) + { + # Assuming project folder name is project name. + $ProjectName = Get-SamplerProjectName -BuildRoot $projectPath + } + + $script:moduleName = $ProjectName + + Remove-Module -Name $script:moduleName -Force -ErrorAction SilentlyContinue + + $mut = Get-Module -Name $script:moduleName -ListAvailable | + Select-Object -First 1 | + Import-Module -Force -ErrorAction Stop -PassThru +} + +BeforeAll { + # Convert-Path required for PS7 or Join-Path fails + $projectPath = "$($PSScriptRoot)\..\.." | Convert-Path + # Get git-related project path. This is relevant for modules that will not be deployed in the root folder of Git. + $gitTopLevelPath = (&git rev-parse --show-toplevel) + $gitRelatedModulePath = (($projectPath -replace [regex]::Escape([IO.Path]::DirectorySeparatorChar), '/') -replace $gitTopLevelPath, '') + if (-not [string]::IsNullOrEmpty($gitRelatedModulePath)) { $gitRelatedModulePath = $gitRelatedModulePath.Trim('/') + '/' } + $escapedGitRelatedModulePath = [regex]::Escape($gitRelatedModulePath) + + <# + If the QA tests are run outside of the build script (e.g with Invoke-Pester) + the parent scope has not set the variable $ProjectName. + #> + if (-not $ProjectName) + { + # Assuming project folder name is project name. + $ProjectName = Get-SamplerProjectName -BuildRoot $projectPath + } + + $script:moduleName = $ProjectName + + $sourcePath = ( + Get-ChildItem -Path $projectPath\*\*.psd1 | + Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) ` + -and $( + try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + } + ) + } + ).Directory.FullName +} + +Describe 'Changelog Management' -Tag 'Changelog' { + It 'Changelog has been updated' -Skip:( + -not ([bool](Get-Command git -ErrorAction SilentlyContinue) -and + [bool](&(Get-Process -Id $PID).Path -NoProfile -Command 'git rev-parse --is-inside-work-tree 2>$null')) + ) { + <# + Get the list of changed files compared with branch main to verify + that required files are changed. + #> + + $filesChanged = @() + # Only run if there is a remote called origin + if (((git remote) -match 'origin')) + { + $headCommit = &git rev-parse HEAD + $defaultBranchCommit = &git rev-parse origin/main + $filesChanged += (&git @('diff', "$defaultBranchCommit...$headCommit", '--name-only') | + Where-Object { $_ -match "^$escapedGitRelatedModulePath" }) -replace "^$escapedGitRelatedModulePath", "" + } + + $filesStagedAndUnstaged = (&git @('diff', 'HEAD', '--name-only') 2>&1 | + Where-Object { $_ -match "^$escapedGitRelatedModulePath" }) -replace "^$escapedGitRelatedModulePath", "" + + $filesChanged += $filesStagedAndUnstaged + + # Only check if there are any changed files. + if ($filesChanged) + { + $filesChanged | Should -Contain 'CHANGELOG.md' -Because 'the CHANGELOG.md must be updated with at least one entry in the Unreleased section for each PR' + } + } + + It 'Changelog format compliant with keepachangelog format' -Skip:(![bool](Get-Command git -EA SilentlyContinue)) { + { Get-ChangelogData -Path (Join-Path $ProjectPath 'CHANGELOG.md') -ErrorAction Stop } | Should -Not -Throw + } + + It 'Changelog should have an Unreleased header' -Skip:$skipTest { + (Get-ChangelogData -Path (Join-Path -Path $ProjectPath -ChildPath 'CHANGELOG.md') -ErrorAction Stop).Unreleased | Should -Not -BeNullOrEmpty + } +} + +Describe 'General module control' -Tags 'FunctionalQuality' { + It 'Should import without errors' { + { Import-Module -Name $script:moduleName -Force -ErrorAction Stop } | Should -Not -Throw + + Get-Module -Name $script:moduleName | Should -Not -BeNullOrEmpty + } + + It 'Should remove without error' { + { Remove-Module -Name $script:moduleName -ErrorAction Stop } | Should -Not -Throw + + Get-Module $script:moduleName | Should -BeNullOrEmpty + } +} + +BeforeDiscovery { + # Must use the imported module to build test cases. + $allModuleFunctions = & $mut { Get-Command -Module $args[0] -CommandType Function } $script:moduleName + + # Build test cases. + $testCases = @() + + foreach ($function in $allModuleFunctions) + { + $testCases += @{ + Name = $function.Name + } + } +} + +Describe 'Quality for module' -Tags 'TestQuality' { + BeforeDiscovery { + if (Get-Command -Name Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue) + { + $scriptAnalyzerRules = Get-ScriptAnalyzerRule + } + else + { + if ($ErrorActionPreference -ne 'Stop') + { + Write-Warning -Message 'ScriptAnalyzer not found!' + } + else + { + throw 'ScriptAnalyzer not found!' + } + } + } + + It 'Should have a unit test for ' -ForEach $testCases { + Get-ChildItem -Path 'tests\' -Recurse -Include "$Name.Tests.ps1" | Should -Not -BeNullOrEmpty + } + + It 'Should pass Script Analyzer for ' -ForEach $testCases -Skip:(-not $scriptAnalyzerRules) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $pssaResult = (Invoke-ScriptAnalyzer -Path $functionFile.FullName) + $report = $pssaResult | Format-Table -AutoSize | Out-String -Width 110 + $pssaResult | Should -BeNullOrEmpty -Because ` + "some rule triggered.`r`n`r`n $report" + } +} + +Describe 'Help for module' -Tags 'helpQuality' { + It 'Should have .SYNOPSIS for ' -ForEach $testCases { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Synopsis | Should -Not -BeNullOrEmpty + } + + It 'Should have a .DESCRIPTION with length greater than 40 characters for ' -ForEach $testCases { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll($astSearchDelegate, $true) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Description.Length | Should -BeGreaterThan 40 + } + + It 'Should have at least one (1) example for ' -ForEach $testCases { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Examples.Count | Should -BeGreaterThan 0 + $functionHelp.Examples[0] | Should -Match ([regex]::Escape($function.Name)) + $functionHelp.Examples[0].Length | Should -BeGreaterThan ($function.Name.Length + 10) + + } + + It 'Should have described all parameters for ' -ForEach $testCases { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $parameters = $parsedFunction.Body.ParamBlock.Parameters.Name.VariablePath.ForEach({ $_.ToString() }) + + foreach ($parameter in $parameters) + { + $functionHelp.Parameters.($parameter.ToUpper()) | Should -Not -BeNullOrEmpty -Because ('the parameter {0} must have a description' -f $parameter) + $functionHelp.Parameters.($parameter.ToUpper()).Length | Should -BeGreaterThan 25 -Because ('the parameter {0} must have descriptive description' -f $parameter) + } + } +} + diff --git a/tests/Unit/Private/Get-PrivateFunction.tests.ps1 b/tests/Unit/Private/Get-PrivateFunction.tests.ps1 new file mode 100644 index 0000000..1e985a8 --- /dev/null +++ b/tests/Unit/Private/Get-PrivateFunction.tests.ps1 @@ -0,0 +1,31 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-PrivateFunction { + Context 'When calling the function with string value' { + It 'Should return a single object' { + InModuleScope -ModuleName $dscModuleName { + $return = Get-PrivateFunction -PrivateData 'string' + + ($return | Measure-Object).Count | Should -Be 1 + } + } + + It 'Should return a string based on the parameter PrivateData' { + InModuleScope -ModuleName $dscModuleName { + $return = Get-PrivateFunction -PrivateData 'string' + + $return | Should -Be 'string' + } + } + } +} + diff --git a/tests/Unit/Public/Get-Something.tests.ps1 b/tests/Unit/Public/Get-Something.tests.ps1 new file mode 100644 index 0000000..3561dfe --- /dev/null +++ b/tests/Unit/Public/Get-Something.tests.ps1 @@ -0,0 +1,91 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When passing values using named parameters' { + It 'Should call the private function once' { + { Get-Something -Data 'value' } | Should -Not -Throw + + Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 1 -Scope It -ModuleName $dscModuleName + } + + It 'Should return a single object' { + $return = Get-Something -Data 'value' + + ($return | Measure-Object).Count | Should -Be 1 + } + + It 'Should return the correct string value' { + $return = Get-Something -Data 'value' + + $return | Should -Be 'value' + } + } + + Context 'When passing values over the pipeline' { + It 'Should call the private function two times' { + { 'value1', 'value2' | Get-Something } | Should -Not -Throw + + Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 2 -Scope It -ModuleName $dscModuleName + } + + It 'Should return an array with two items' { + $return = 'value1', 'value2' | Get-Something + + $return.Count | Should -Be 2 + } + + It 'Should return an array with the correct string values' { + $return = 'value1', 'value2' | Get-Something + + $return[0] | Should -Be 'value1' + $return[1] | Should -Be 'value2' + } + + It 'Should accept values from the pipeline by property name' { + $return = 'value1', 'value2' | ForEach-Object { + [PSCustomObject]@{ + Data = $_ + OtherProperty = 'other' + } + } | Get-Something + + $return[0] | Should -Be 'value1' + $return[1] | Should -Be 'value2' + } + } + + Context 'When passing WhatIf' { + It 'Should support the parameter WhatIf' { + (Get-Command -Name 'Get-Something').Parameters.ContainsKey('WhatIf') | Should -Be $true + } + + It 'Should not call the private function' { + { Get-Something -Data 'value' -WhatIf } | Should -Not -Throw + + Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 0 -Scope It -ModuleName $dscModuleName + } + + It 'Should return $null' { + $return = Get-Something -Data 'value' -WhatIf + + $return | Should -BeNullOrEmpty + } + } +} + From b2fb404d91fbbce891ecc041f33994e07863d01e Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 10:30:08 +0200 Subject: [PATCH 02/11] chore: Fixed PSD-data --- source/OrionDesign.psd1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/OrionDesign.psd1 b/source/OrionDesign.psd1 index 86d9742..099bc33 100644 --- a/source/OrionDesign.psd1 +++ b/source/OrionDesign.psd1 @@ -12,13 +12,13 @@ RootModule = 'OrionDesign.psm1' # Version number of this module. -ModuleVersion = '0.0.1' +ModuleVersion = '3.1.1' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module -GUID = '981576c1-f22d-473f-8b3e-06acf22c59d5' +GUID = 'f827a4ef-a736-449b-b684-dfeba0fdadc3' # Author of this module Author = 'Sune Alexandersen Narud' @@ -97,6 +97,10 @@ PrivateData = @{ ProjectUri = 'https://github.com/suneworld/OrionDesign' LicenseUri = 'https://github.com/suneworld/OrionDesign/blob/master/LICENSE' ReleaseNotes = @' +## v3.1.1 +- Fixed not all functions beein available by implementing modules + using the Sampler Framework. + ## v3.1.0 - Added -Demo parameter to all 21 public functions - Each demo shows live output with exact code that produced it From 549342c8be47ff5177681ba1a7d271a8269e91bb Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 11:09:04 +0200 Subject: [PATCH 03/11] chore: Changed file format to UTF8wBOM --- build.yaml | 2 +- source/OrionDesign.psd1 | 19 ++++--------------- source/Private/Get-PrivateFunction.ps1 | 2 +- source/Public/Export-OrionHelpers.ps1 | 2 +- source/Public/Get-OrionMaxWidth.ps1 | 2 +- source/Public/Get-OrionTheme.ps1 | 2 +- source/Public/Get-Something.ps1 | 2 +- source/Public/Set-OrionMaxWidth.ps1 | 2 +- source/Public/Set-OrionTheme.ps1 | 2 +- source/Public/Show-OrionDemo.ps1 | 2 +- source/Public/Show-OrionSmartMenu.ps1 | 2 +- source/Public/Write-Action.ps1 | 2 +- source/Public/Write-ActionResult.ps1 | 2 +- source/Public/Write-Alert.ps1 | 2 +- source/Public/Write-Banner.ps1 | 2 +- source/Public/Write-Chart.ps1 | 2 +- source/Public/Write-InfoBox.ps1 | 6 +++--- source/Public/Write-Menu.ps1 | 2 +- source/Public/Write-MenuLine.ps1 | 10 +++++----- source/Public/Write-Panel.ps1 | 2 +- source/Public/Write-ProgressBar.ps1 | 12 ++++++------ source/Public/Write-Question.ps1 | 2 +- source/Public/Write-Separator.ps1 | 2 +- source/Public/Write-Steps.ps1 | 2 +- 24 files changed, 38 insertions(+), 49 deletions(-) diff --git a/build.yaml b/build.yaml index adfdbf7..f20b5c7 100644 --- a/build.yaml +++ b/build.yaml @@ -8,7 +8,7 @@ CopyPaths: - en-US # - DSCResources # - Modules -Encoding: UTF8 +#Encoding: UTF8 # Can be used to manually specify module's semantic version if the preferred method of # using GitVersion is not available, and it is not possible to set the session environment # variable `$env:ModuleVersion`, nor setting the variable `$ModuleVersion`, in the diff --git a/source/OrionDesign.psd1 b/source/OrionDesign.psd1 index 099bc33..4536e82 100644 --- a/source/OrionDesign.psd1 +++ b/source/OrionDesign.psd1 @@ -33,7 +33,7 @@ Copyright = '(c) 2026 Sune Alexandersen Narud. All rights reserved.' Description = 'Orion Design Framework - Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter.' # Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '5.1' +PowerShellVersion = '5.0' # Name of the Windows PowerShell host required by this module # PowerShellHostName = '' @@ -93,6 +93,7 @@ DscResourcesToExport = @() PrivateData = @{ PSData = @{ + Tags = @('UI', 'Terminal', 'Console', 'Output', 'Formatting', 'Theme', 'Menu', 'Banner', 'Chart', 'ProgressBar', 'Colors', 'TUI', 'PowerShell5', 'PSCore') ProjectUri = 'https://github.com/suneworld/OrionDesign' LicenseUri = 'https://github.com/suneworld/OrionDesign/blob/master/LICENSE' @@ -117,7 +118,8 @@ PrivateData = @{ - Enhanced Write-Action/Write-ActionResult alignment '@ } - } + + } # End of PrivateData hashtable # HelpInfo URI of this module # HelpInfoURI = '' @@ -126,16 +128,3 @@ PrivateData = @{ # DefaultCommandPrefix = '' } - - - - - - - - - - - - - diff --git a/source/Private/Get-PrivateFunction.ps1 b/source/Private/Get-PrivateFunction.ps1 index b4ef195..79d75aa 100644 --- a/source/Private/Get-PrivateFunction.ps1 +++ b/source/Private/Get-PrivateFunction.ps1 @@ -1,4 +1,4 @@ -function Get-PrivateFunction +function Get-PrivateFunction { <# .SYNOPSIS diff --git a/source/Public/Export-OrionHelpers.ps1 b/source/Public/Export-OrionHelpers.ps1 index f914258..24caa0a 100644 --- a/source/Public/Export-OrionHelpers.ps1 +++ b/source/Public/Export-OrionHelpers.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Export-OrionHelpers Function ================================================================================ diff --git a/source/Public/Get-OrionMaxWidth.ps1 b/source/Public/Get-OrionMaxWidth.ps1 index 9c48a41..800a64a 100644 --- a/source/Public/Get-OrionMaxWidth.ps1 +++ b/source/Public/Get-OrionMaxWidth.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Get-OrionMaxWidth Function ================================================================================ diff --git a/source/Public/Get-OrionTheme.ps1 b/source/Public/Get-OrionTheme.ps1 index cf7310e..d7878c5 100644 --- a/source/Public/Get-OrionTheme.ps1 +++ b/source/Public/Get-OrionTheme.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Get-OrionTheme Function ================================================================================ diff --git a/source/Public/Get-Something.ps1 b/source/Public/Get-Something.ps1 index 9f32477..d8e33db 100644 --- a/source/Public/Get-Something.ps1 +++ b/source/Public/Get-Something.ps1 @@ -1,4 +1,4 @@ -function Get-Something +function Get-Something { <# .SYNOPSIS diff --git a/source/Public/Set-OrionMaxWidth.ps1 b/source/Public/Set-OrionMaxWidth.ps1 index f673d7d..e811e7f 100644 --- a/source/Public/Set-OrionMaxWidth.ps1 +++ b/source/Public/Set-OrionMaxWidth.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Set-OrionMaxWidth Function ================================================================================ diff --git a/source/Public/Set-OrionTheme.ps1 b/source/Public/Set-OrionTheme.ps1 index 3453fc2..bc85960 100644 --- a/source/Public/Set-OrionTheme.ps1 +++ b/source/Public/Set-OrionTheme.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Set-OrionTheme Function ================================================================================ diff --git a/source/Public/Show-OrionDemo.ps1 b/source/Public/Show-OrionDemo.ps1 index d85f8de..2399bb3 100644 --- a/source/Public/Show-OrionDemo.ps1 +++ b/source/Public/Show-OrionDemo.ps1 @@ -1,4 +1,4 @@ -function Show-OrionDemo { +function Show-OrionDemo { <# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Unified Demo Function diff --git a/source/Public/Show-OrionSmartMenu.ps1 b/source/Public/Show-OrionSmartMenu.ps1 index 54eba18..6abffc9 100644 --- a/source/Public/Show-OrionSmartMenu.ps1 +++ b/source/Public/Show-OrionSmartMenu.ps1 @@ -1,4 +1,4 @@ -function Show-OrionSmartMenu { +function Show-OrionSmartMenu { <# .SYNOPSIS Displays a reusable interactive menu with smart input mode selection. diff --git a/source/Public/Write-Action.ps1 b/source/Public/Write-Action.ps1 index 1a5fcf8..d7be0b7 100644 --- a/source/Public/Write-Action.ps1 +++ b/source/Public/Write-Action.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Action Function ================================================================================ diff --git a/source/Public/Write-ActionResult.ps1 b/source/Public/Write-ActionResult.ps1 index 8142cb2..c339c74 100644 --- a/source/Public/Write-ActionResult.ps1 +++ b/source/Public/Write-ActionResult.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-ActionResult Function ================================================================================ diff --git a/source/Public/Write-Alert.ps1 b/source/Public/Write-Alert.ps1 index 078ef18..3c49d64 100644 --- a/source/Public/Write-Alert.ps1 +++ b/source/Public/Write-Alert.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Alert Function ================================================================================ diff --git a/source/Public/Write-Banner.ps1 b/source/Public/Write-Banner.ps1 index 6c8de45..acb9333 100644 --- a/source/Public/Write-Banner.ps1 +++ b/source/Public/Write-Banner.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Banner Function ================================================================================ diff --git a/source/Public/Write-Chart.ps1 b/source/Public/Write-Chart.ps1 index aa69da3..1d3418d 100644 --- a/source/Public/Write-Chart.ps1 +++ b/source/Public/Write-Chart.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Chart Function ================================================================================ diff --git a/source/Public/Write-InfoBox.ps1 b/source/Public/Write-InfoBox.ps1 index 520d06b..30baa07 100644 --- a/source/Public/Write-InfoBox.ps1 +++ b/source/Public/Write-InfoBox.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-InfoBox Function ================================================================================ @@ -14,8 +14,8 @@ Versatile information display component providing highlighted content areas with automatic width calculation and various visual styles. HLD INTEGRATION: -┌─ INFORMATION ─┐ ┌─ BOX STYLES ─┐ ┌─ OUTPUT ─┐ -│ Write-InfoBox │◄──►│ Classic/Modern│───►│ Bordered │ +┌─ INFORMATION ─┐ ┌─ BOX STYLES ─┐ ┌─ OUTPUT ─┐ +│ Write-InfoBox │◄──►│ Classic/Modern│───►│ Bordered │ │ • Title/Content│ │ Simple/Accent │ │ Info │ │ • Auto Width │ │ Icon Support │ │ Content │ │ • Safety Checks│ │ Width Control │ │ Safe │ diff --git a/source/Public/Write-Menu.ps1 b/source/Public/Write-Menu.ps1 index 6195597..d39314b 100644 --- a/source/Public/Write-Menu.ps1 +++ b/source/Public/Write-Menu.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Menu Function ================================================================================ diff --git a/source/Public/Write-MenuLine.ps1 b/source/Public/Write-MenuLine.ps1 index 791decb..206f7f1 100644 --- a/source/Public/Write-MenuLine.ps1 +++ b/source/Public/Write-MenuLine.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-MenuLine Function ================================================================================ @@ -21,10 +21,10 @@ USE CASES: LAYOUT STRUCTURE: ┌────────────────────────────────────────────────────────────────────────────┐ -│ [Indent] [Number]. [Title] [SuffixNumber] [Suffix] │ -│ ↑ ↑ ↑ ↑ ↑ │ -│ Margin Accent Text Accent Text │ -│ (1 sp) Color Color Color Color │ +│ [Indent] [Number]. [Title] [SuffixNumber] [Suffix] │ +│ ↑ ↑ ↑ ↑ ↑ │ +│ Margin Accent Text Accent Text │ +│ (1 sp) Color Color Color Color │ └────────────────────────────────────────────────────────────────────────────┘ HLD INTEGRATION: diff --git a/source/Public/Write-Panel.ps1 b/source/Public/Write-Panel.ps1 index eaa572d..380bf35 100644 --- a/source/Public/Write-Panel.ps1 +++ b/source/Public/Write-Panel.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Panel Function ================================================================================ diff --git a/source/Public/Write-ProgressBar.ps1 b/source/Public/Write-ProgressBar.ps1 index d6b1aec..318103b 100644 --- a/source/Public/Write-ProgressBar.ps1 +++ b/source/Public/Write-ProgressBar.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-ProgressBar Function ================================================================================ @@ -15,11 +15,11 @@ with bar charts, blocks, and percentage displays. HLD INTEGRATION: ┌─ STATUS & RESULTS ─┐ ┌─ PROGRESS TYPES ─┐ ┌─ OUTPUT ─┐ -│ Write-ProgressBar │◄──►│ Bar/Blocks/Dots │───►│ Visual │ -│ • Current/Max │ │ Percentage Show │ │ Progress │ -│ • Visual Styles │ │ Text Labels │ │ Real-time│ -│ • Customizable │ │ Color Coding │ │ Updates │ -└───────────────────┘ └───────────────────┘ └──────────┘ +│ Write-ProgressBar │◄──►│ Bar/Blocks/Dots │───►│ Visual │ +│ • Current/Max │ │ Percentage Show │ │ Progress │ +│ • Visual Styles │ │ Text Labels │ │ Real-time│ +│ • Customizable │ │ Color Coding │ │ Updates │ +└────────────────────┘ └──────────────────┘ └──────────┘ ================================================================================ #> diff --git a/source/Public/Write-Question.ps1 b/source/Public/Write-Question.ps1 index 30760b0..179b8b6 100644 --- a/source/Public/Write-Question.ps1 +++ b/source/Public/Write-Question.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Question Function ================================================================================ diff --git a/source/Public/Write-Separator.ps1 b/source/Public/Write-Separator.ps1 index 6d609b7..5186e5b 100644 --- a/source/Public/Write-Separator.ps1 +++ b/source/Public/Write-Separator.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Separator Function ================================================================================ diff --git a/source/Public/Write-Steps.ps1 b/source/Public/Write-Steps.ps1 index 06f64b0..2bf691c 100644 --- a/source/Public/Write-Steps.ps1 +++ b/source/Public/Write-Steps.ps1 @@ -1,4 +1,4 @@ -<# +<# ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Steps Function ================================================================================ From 67a4fadb3ed5a9a413946fa42aa53f0e288a1805 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 11:19:36 +0200 Subject: [PATCH 04/11] fix: Changed the PS/ the null-coalescing operator for PS 5.1 syntax --- source/Public/Set-OrionTheme.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/Public/Set-OrionTheme.ps1 b/source/Public/Set-OrionTheme.ps1 index bc85960..268a8d6 100644 --- a/source/Public/Set-OrionTheme.ps1 +++ b/source/Public/Set-OrionTheme.ps1 @@ -158,7 +158,8 @@ function Set-OrionTheme { Write-Host '' $t = Get-OrionTheme Write-Host ' Current theme: ' -NoNewline -ForegroundColor DarkGray - Write-Host ($t.Name ?? 'Default') -ForegroundColor Cyan + $name = if ($null -eq $t.Name) { 'Default' } else { $t.Name } + Write-Host $name -ForegroundColor Cyan Write-Host '' return } From 2637b488e800fef414e3dd98f19517d1ee93e6d2 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 11:47:11 +0200 Subject: [PATCH 05/11] test: Added mock unit tests --- source/Private/Get-PrivateFunction.ps1 | 31 ------- source/Public/Get-Something.ps1 | 41 --------- .../Convert-ToColoredSegments.tests.ps1 | 27 ++++++ .../Private/Get-PrivateFunction.tests.ps1 | 31 ------- tests/Unit/Private/Write-Colored.tests.ps1 | 27 ++++++ .../Unit/Public/Export-OrionHelpers.tests.ps1 | 27 ++++++ tests/Unit/Public/Get-OrionMaxWidth.tests.ps1 | 27 ++++++ tests/Unit/Public/Get-OrionTheme.tests.ps1 | 27 ++++++ tests/Unit/Public/Get-Something.tests.ps1 | 91 ------------------- tests/Unit/Public/Set-OrionMaxWidth.tests.ps1 | 27 ++++++ tests/Unit/Public/Set-OrionTheme.tests.ps1 | 27 ++++++ tests/Unit/Public/Show-OrionDemo.tests.ps1 | 27 ++++++ .../Unit/Public/Show-OrionSmartMenu.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Action.tests.ps1 | 27 ++++++ .../Unit/Public/Write-ActionResult.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Alert.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Banner.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Chart.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Header.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-InfoBox.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Menu.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-MenuLine.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Panel.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-ProgressBar.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Question.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Separator.tests.ps1 | 27 ++++++ tests/Unit/Public/Write-Steps.tests.ps1 | 27 ++++++ 27 files changed, 621 insertions(+), 194 deletions(-) delete mode 100644 source/Private/Get-PrivateFunction.ps1 delete mode 100644 source/Public/Get-Something.ps1 create mode 100644 tests/Unit/Private/Convert-ToColoredSegments.tests.ps1 delete mode 100644 tests/Unit/Private/Get-PrivateFunction.tests.ps1 create mode 100644 tests/Unit/Private/Write-Colored.tests.ps1 create mode 100644 tests/Unit/Public/Export-OrionHelpers.tests.ps1 create mode 100644 tests/Unit/Public/Get-OrionMaxWidth.tests.ps1 create mode 100644 tests/Unit/Public/Get-OrionTheme.tests.ps1 delete mode 100644 tests/Unit/Public/Get-Something.tests.ps1 create mode 100644 tests/Unit/Public/Set-OrionMaxWidth.tests.ps1 create mode 100644 tests/Unit/Public/Set-OrionTheme.tests.ps1 create mode 100644 tests/Unit/Public/Show-OrionDemo.tests.ps1 create mode 100644 tests/Unit/Public/Show-OrionSmartMenu.tests.ps1 create mode 100644 tests/Unit/Public/Write-Action.tests.ps1 create mode 100644 tests/Unit/Public/Write-ActionResult.tests.ps1 create mode 100644 tests/Unit/Public/Write-Alert.tests.ps1 create mode 100644 tests/Unit/Public/Write-Banner.tests.ps1 create mode 100644 tests/Unit/Public/Write-Chart.tests.ps1 create mode 100644 tests/Unit/Public/Write-Header.tests.ps1 create mode 100644 tests/Unit/Public/Write-InfoBox.tests.ps1 create mode 100644 tests/Unit/Public/Write-Menu.tests.ps1 create mode 100644 tests/Unit/Public/Write-MenuLine.tests.ps1 create mode 100644 tests/Unit/Public/Write-Panel.tests.ps1 create mode 100644 tests/Unit/Public/Write-ProgressBar.tests.ps1 create mode 100644 tests/Unit/Public/Write-Question.tests.ps1 create mode 100644 tests/Unit/Public/Write-Separator.tests.ps1 create mode 100644 tests/Unit/Public/Write-Steps.tests.ps1 diff --git a/source/Private/Get-PrivateFunction.ps1 b/source/Private/Get-PrivateFunction.ps1 deleted file mode 100644 index 79d75aa..0000000 --- a/source/Private/Get-PrivateFunction.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -function Get-PrivateFunction -{ - <# - .SYNOPSIS - This is a sample Private function only visible within the module. - - .DESCRIPTION - This sample function is not exported to the module and only return the data passed as parameter. - - .EXAMPLE - $null = Get-PrivateFunction -PrivateData 'NOTHING TO SEE HERE' - - .PARAMETER PrivateData - The PrivateData parameter is what will be returned without transformation. - - #> - [cmdletBinding()] - [OutputType([string])] - param - ( - [Parameter()] - [String] - $PrivateData - ) - - process - { - Write-Output $PrivateData - } - -} diff --git a/source/Public/Get-Something.ps1 b/source/Public/Get-Something.ps1 deleted file mode 100644 index d8e33db..0000000 --- a/source/Public/Get-Something.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -function Get-Something -{ - <# - .SYNOPSIS - Sample Function to return input string. - - .DESCRIPTION - This function is only a sample Advanced function that returns the Data given via parameter Data. - - .EXAMPLE - Get-Something -Data 'Get me this text' - - - .PARAMETER Data - The Data parameter is the data that will be returned without transformation. - - #> - [cmdletBinding( - SupportsShouldProcess = $true, - ConfirmImpact = 'Low' - )] - param - ( - [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] - [String] - $Data - ) - - process - { - if ($pscmdlet.ShouldProcess($Data)) - { - Write-Verbose -Message ('Returning the data: {0}' -f $Data) - Get-PrivateFunction -PrivateData $Data - } - else - { - Write-Verbose -Message 'oh dear' - } - } -} diff --git a/tests/Unit/Private/Convert-ToColoredSegments.tests.ps1 b/tests/Unit/Private/Convert-ToColoredSegments.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Private/Convert-ToColoredSegments.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Private/Get-PrivateFunction.tests.ps1 b/tests/Unit/Private/Get-PrivateFunction.tests.ps1 deleted file mode 100644 index 1e985a8..0000000 --- a/tests/Unit/Private/Get-PrivateFunction.tests.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -BeforeAll { - $script:dscModuleName = 'OrionDesign' - - Import-Module -Name $script:dscModuleName -} - -AfterAll { - # Unload the module being tested so that it doesn't impact any other tests. - Get-Module -Name $script:dscModuleName -All | Remove-Module -Force -} - -Describe Get-PrivateFunction { - Context 'When calling the function with string value' { - It 'Should return a single object' { - InModuleScope -ModuleName $dscModuleName { - $return = Get-PrivateFunction -PrivateData 'string' - - ($return | Measure-Object).Count | Should -Be 1 - } - } - - It 'Should return a string based on the parameter PrivateData' { - InModuleScope -ModuleName $dscModuleName { - $return = Get-PrivateFunction -PrivateData 'string' - - $return | Should -Be 'string' - } - } - } -} - diff --git a/tests/Unit/Private/Write-Colored.tests.ps1 b/tests/Unit/Private/Write-Colored.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Private/Write-Colored.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Export-OrionHelpers.tests.ps1 b/tests/Unit/Public/Export-OrionHelpers.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Export-OrionHelpers.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Get-OrionMaxWidth.tests.ps1 b/tests/Unit/Public/Get-OrionMaxWidth.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Get-OrionMaxWidth.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Get-OrionTheme.tests.ps1 b/tests/Unit/Public/Get-OrionTheme.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Get-OrionTheme.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Get-Something.tests.ps1 b/tests/Unit/Public/Get-Something.tests.ps1 deleted file mode 100644 index 3561dfe..0000000 --- a/tests/Unit/Public/Get-Something.tests.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -BeforeAll { - $script:dscModuleName = 'OrionDesign' - - Import-Module -Name $script:dscModuleName -} - -AfterAll { - # Unload the module being tested so that it doesn't impact any other tests. - Get-Module -Name $script:dscModuleName -All | Remove-Module -Force -} - -Describe Get-Something { - BeforeAll { - Mock -CommandName Get-PrivateFunction -MockWith { - # This return the value passed to the Get-PrivateFunction parameter $PrivateData. - $PrivateData - } -ModuleName $dscModuleName - } - - Context 'When passing values using named parameters' { - It 'Should call the private function once' { - { Get-Something -Data 'value' } | Should -Not -Throw - - Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 1 -Scope It -ModuleName $dscModuleName - } - - It 'Should return a single object' { - $return = Get-Something -Data 'value' - - ($return | Measure-Object).Count | Should -Be 1 - } - - It 'Should return the correct string value' { - $return = Get-Something -Data 'value' - - $return | Should -Be 'value' - } - } - - Context 'When passing values over the pipeline' { - It 'Should call the private function two times' { - { 'value1', 'value2' | Get-Something } | Should -Not -Throw - - Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 2 -Scope It -ModuleName $dscModuleName - } - - It 'Should return an array with two items' { - $return = 'value1', 'value2' | Get-Something - - $return.Count | Should -Be 2 - } - - It 'Should return an array with the correct string values' { - $return = 'value1', 'value2' | Get-Something - - $return[0] | Should -Be 'value1' - $return[1] | Should -Be 'value2' - } - - It 'Should accept values from the pipeline by property name' { - $return = 'value1', 'value2' | ForEach-Object { - [PSCustomObject]@{ - Data = $_ - OtherProperty = 'other' - } - } | Get-Something - - $return[0] | Should -Be 'value1' - $return[1] | Should -Be 'value2' - } - } - - Context 'When passing WhatIf' { - It 'Should support the parameter WhatIf' { - (Get-Command -Name 'Get-Something').Parameters.ContainsKey('WhatIf') | Should -Be $true - } - - It 'Should not call the private function' { - { Get-Something -Data 'value' -WhatIf } | Should -Not -Throw - - Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 0 -Scope It -ModuleName $dscModuleName - } - - It 'Should return $null' { - $return = Get-Something -Data 'value' -WhatIf - - $return | Should -BeNullOrEmpty - } - } -} - diff --git a/tests/Unit/Public/Set-OrionMaxWidth.tests.ps1 b/tests/Unit/Public/Set-OrionMaxWidth.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Set-OrionMaxWidth.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Set-OrionTheme.tests.ps1 b/tests/Unit/Public/Set-OrionTheme.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Set-OrionTheme.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Show-OrionDemo.tests.ps1 b/tests/Unit/Public/Show-OrionDemo.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Show-OrionDemo.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Show-OrionSmartMenu.tests.ps1 b/tests/Unit/Public/Show-OrionSmartMenu.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Show-OrionSmartMenu.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Action.tests.ps1 b/tests/Unit/Public/Write-Action.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Action.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-ActionResult.tests.ps1 b/tests/Unit/Public/Write-ActionResult.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-ActionResult.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Alert.tests.ps1 b/tests/Unit/Public/Write-Alert.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Alert.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Banner.tests.ps1 b/tests/Unit/Public/Write-Banner.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Banner.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Chart.tests.ps1 b/tests/Unit/Public/Write-Chart.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Chart.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Header.tests.ps1 b/tests/Unit/Public/Write-Header.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Header.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-InfoBox.tests.ps1 b/tests/Unit/Public/Write-InfoBox.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-InfoBox.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Menu.tests.ps1 b/tests/Unit/Public/Write-Menu.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Menu.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-MenuLine.tests.ps1 b/tests/Unit/Public/Write-MenuLine.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-MenuLine.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Panel.tests.ps1 b/tests/Unit/Public/Write-Panel.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Panel.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-ProgressBar.tests.ps1 b/tests/Unit/Public/Write-ProgressBar.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-ProgressBar.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Question.tests.ps1 b/tests/Unit/Public/Write-Question.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Question.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Separator.tests.ps1 b/tests/Unit/Public/Write-Separator.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Separator.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + diff --git a/tests/Unit/Public/Write-Steps.tests.ps1 b/tests/Unit/Public/Write-Steps.tests.ps1 new file mode 100644 index 0000000..51ac49c --- /dev/null +++ b/tests/Unit/Public/Write-Steps.tests.ps1 @@ -0,0 +1,27 @@ +BeforeAll { + $script:dscModuleName = 'OrionDesign' + + Import-Module -Name $script:dscModuleName +} + +AfterAll { + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe Get-Something { + BeforeAll { + Mock -CommandName Get-PrivateFunction -MockWith { + # This return the value passed to the Get-PrivateFunction parameter $PrivateData. + $PrivateData + } -ModuleName $dscModuleName + } + + Context 'When tested, skip for now' { + It 'Should skip the test' -Skip { + #No test yet + } + } + +} + From 70f1eceaafe6d84235324513c2b6377daa57dab2 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 13:01:16 +0200 Subject: [PATCH 06/11] test: Added a PSSA-file --- PSScriptAnalyzerSettings.psd1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 PSScriptAnalyzerSettings.psd1 diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..05a268b --- /dev/null +++ b/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,14 @@ +@{ + ExcludeRules = @( + 'PSAvoidUsingWriteHost' + ) + + Rules = @{ + PSAvoidUsingCmdletAliases = @{ + AllowList = @( + 'select', 'where' + ) + } + } + +} From e21c6c9099d73da3ebbe9a5a3e82795de5fa7406 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 13:23:20 +0200 Subject: [PATCH 07/11] chore: Removed withe spaces --- source/OrionDesign.psd1 | 2 +- source/Private/Convert-ToColoredSegments.ps1 | 4 +- source/Private/Write-Colored.ps1 | 2 +- source/Public/Export-OrionHelpers.ps1 | 24 +++---- source/Public/Get-OrionMaxWidth.ps1 | 6 +- source/Public/Get-OrionTheme.ps1 | 2 +- source/Public/Set-OrionMaxWidth.ps1 | 4 +- source/Public/Set-OrionTheme.ps1 | 8 +-- source/Public/Show-OrionDemo.ps1 | 70 +++++++++--------- source/Public/Write-Action.ps1 | 14 ++-- source/Public/Write-ActionResult.ps1 | 22 +++--- source/Public/Write-Alert.ps1 | 14 ++-- source/Public/Write-Banner.ps1 | 74 ++++++++++---------- source/Public/Write-Chart.ps1 | 22 +++--- source/Public/Write-Header.ps1 | 6 +- source/Public/Write-InfoBox.ps1 | 34 ++++----- source/Public/Write-Menu.ps1 | 18 ++--- source/Public/Write-MenuLine.ps1 | 14 ++-- source/Public/Write-Panel.ps1 | 22 +++--- source/Public/Write-ProgressBar.ps1 | 36 +++++----- source/Public/Write-Question.ps1 | 12 ++-- source/Public/Write-Separator.ps1 | 12 ++-- source/Public/Write-Steps.ps1 | 18 ++--- 23 files changed, 220 insertions(+), 220 deletions(-) diff --git a/source/OrionDesign.psd1 b/source/OrionDesign.psd1 index 4536e82..fb4faf5 100644 --- a/source/OrionDesign.psd1 +++ b/source/OrionDesign.psd1 @@ -33,7 +33,7 @@ Copyright = '(c) 2026 Sune Alexandersen Narud. All rights reserved.' Description = 'Orion Design Framework - Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter.' # Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '5.0' +PowerShellVersion = '5.1' # Name of the Windows PowerShell host required by this module # PowerShellHostName = '' diff --git a/source/Private/Convert-ToColoredSegments.ps1 b/source/Private/Convert-ToColoredSegments.ps1 index a4c2bf9..c9ea6ab 100644 --- a/source/Private/Convert-ToColoredSegments.ps1 +++ b/source/Private/Convert-ToColoredSegments.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Convert-ToColoredSegments Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Private Helper Function @@ -30,7 +30,7 @@ function Convert-ToColoredSegments { $pattern = '<(/?)([a-zA-Z]+)>' $tagMatches = [regex]::Matches($Text, $pattern) - $segments = @(); + $segments = @(); $colorStack = New-Object System.Collections.Stack $styleStack = New-Object System.Collections.Stack $pos = 0 diff --git a/source/Private/Write-Colored.ps1 b/source/Private/Write-Colored.ps1 index 68e5b53..d0d2ea1 100644 --- a/source/Private/Write-Colored.ps1 +++ b/source/Private/Write-Colored.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Colored Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Private Helper Function diff --git a/source/Public/Export-OrionHelpers.ps1 b/source/Public/Export-OrionHelpers.ps1 index 24caa0a..a86bb7d 100644 --- a/source/Public/Export-OrionHelpers.ps1 +++ b/source/Public/Export-OrionHelpers.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Export-OrionHelpers Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: January 27, 2026 Module: OrionDesign v2.0.0 Category: Utility / Bundling @@ -14,12 +14,12 @@ helper file with all required function definitions. Makes scripts portable without requiring the OrionDesign module to be installed. HLD INTEGRATION: -┌─ SCRIPT ANALYSIS ─┐ ┌─ DEPENDENCY RESOLUTION ─┐ ┌─ OUTPUT ─┐ -│ Export-OrionHelpers│◄──►│ Parse Script │───►│ Helper │ -│ • Script Path │ │ Find Function Calls │ │ File │ -│ • Output Path │ │ Resolve Dependencies │ │ Bundled │ -│ • Auto-Comment │ │ Include Private Funcs │ │ Portable │ -└────────────────────┘ └────────────────────────┘ └──────────┘ +┌─ SCRIPT ANALYSIS ─┐ ┌─ DEPENDENCY RESOLUTION ─┐ ┌─ OUTPUT ─┐ +│ Export-OrionHelpers│◄──►│ Parse Script │───►│ Helper │ +│ • Script Path │ │ Find Function Calls │ │ File │ +│ • Output Path │ │ Resolve Dependencies │ │ Bundled │ +│ • Auto-Comment │ │ Include Private Funcs │ │ Portable │ +└────────────────────┘ └─────────────────────────┘ └──────────┘ ================================================================================ #> @@ -133,9 +133,9 @@ function Export-OrionHelpers { # If no ScriptPath provided, show interactive file selection if (-not $ScriptPath) { $currentDir = Get-Location - $ps1Files = Get-ChildItem -Path $currentDir -Filter "*.ps1" -File -ErrorAction SilentlyContinue | + $ps1Files = Get-ChildItem -Path $currentDir -Filter "*.ps1" -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne "OrionDesign-Helpers.ps1" } - + if ($ps1Files.Count -eq 0) { Write-Host "" Write-Host " ⚠️ No PowerShell scripts (.ps1) found in current directory" -ForegroundColor Yellow @@ -162,7 +162,7 @@ function Export-OrionHelpers { Write-Host " ($sizeKB KB)" -ForegroundColor DarkGray $index++ } - + Write-Host "" Write-Host " [0] " -NoNewline -ForegroundColor Yellow Write-Host "Cancel" -ForegroundColor Red @@ -170,7 +170,7 @@ function Export-OrionHelpers { Write-Host " ─────────────────────────────────────────────────────────────" -ForegroundColor DarkGray $selection = Read-Host " Enter selection (1-$($ps1Files.Count))" - + if ($selection -eq "0" -or [string]::IsNullOrWhiteSpace($selection)) { Write-Host "" Write-Host " ❌ Operation cancelled." -ForegroundColor Red @@ -222,7 +222,7 @@ function Export-OrionHelpers { $privatePath = Join-Path $modulePath "Functions\Private" # Get all available OrionDesign functions - $publicFunctions = Get-ChildItem -Path $publicPath -Filter "*.ps1" -ErrorAction SilentlyContinue | + $publicFunctions = Get-ChildItem -Path $publicPath -Filter "*.ps1" -ErrorAction SilentlyContinue | Where-Object { $_.BaseName -ne "Export-OrionHelpers" } $privateFunctions = Get-ChildItem -Path $privatePath -Filter "*.ps1" -ErrorAction SilentlyContinue diff --git a/source/Public/Get-OrionMaxWidth.ps1 b/source/Public/Get-OrionMaxWidth.ps1 index 800a64a..5e5636e 100644 --- a/source/Public/Get-OrionMaxWidth.ps1 +++ b/source/Public/Get-OrionMaxWidth.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Get-OrionMaxWidth Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Global Configuration @@ -14,7 +14,7 @@ Core configuration function that enables width management and consistency across all UI components in the framework. HLD INTEGRATION: -┌─ GLOBAL CONFIG ─┐ ┌─ SCRIPT SCOPE ─┐ ┌─ OUTPUT ─┐ +┌─ GLOBAL CONFIG ─┐ ┌─ SCRIPT SCOPE ─┐ ┌─ OUTPUT ─┐ │ Get-OrionMaxWdth│◄──►│ $script:OrionMax│───►│ Current │ │ • Read Setting │ │ Width Variable │ │ Width │ │ • Validation │ │ • Default: 100 │ │ Value │ @@ -63,6 +63,6 @@ function Get-OrionMaxWidth { if (-not $script:OrionMaxWidth) { return 100 # Default value } - + return $script:OrionMaxWidth } diff --git a/source/Public/Get-OrionTheme.ps1 b/source/Public/Get-OrionTheme.ps1 index d7878c5..ae5f4db 100644 --- a/source/Public/Get-OrionTheme.ps1 +++ b/source/Public/Get-OrionTheme.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Get-OrionTheme Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: February 4, 2026 Module: OrionDesign v2.1.2 Category: Configuration diff --git a/source/Public/Set-OrionMaxWidth.ps1 b/source/Public/Set-OrionMaxWidth.ps1 index e811e7f..742d619 100644 --- a/source/Public/Set-OrionMaxWidth.ps1 +++ b/source/Public/Set-OrionMaxWidth.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Set-OrionMaxWidth Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Global Configuration @@ -14,7 +14,7 @@ Critical configuration function that provides consistent width control and prevents overly long output across the entire framework. HLD INTEGRATION: -┌─ GLOBAL CONFIG ─┐ ┌─ SCRIPT SCOPE ─┐ ┌─ FUNCTIONS ─┐ +┌─ GLOBAL CONFIG ─┐ ┌─ SCRIPT SCOPE ─┐ ┌─ FUNCTIONS ─┐ │ Set-OrionMaxWdth│◄──►│ $script:OrionMax│───►│ All UI │ │ • Width Setting │ │ Width Variable │ │ Functions │ │ • Validation │ │ • Range: 50-200 │ │ Respect │ diff --git a/source/Public/Set-OrionTheme.ps1 b/source/Public/Set-OrionTheme.ps1 index 268a8d6..c37d692 100644 --- a/source/Public/Set-OrionTheme.ps1 +++ b/source/Public/Set-OrionTheme.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Set-OrionTheme Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Configuration @@ -34,7 +34,7 @@ You can either provide a custom theme hashtable or use one of the predefined the A hashtable containing theme configuration. Should include: - Accent: Color for highlights and accents - Success: Color for explicit success states (e.g., -Status Success) -- Warning: Color for warning states +- Warning: Color for warning states - Error: Color for error states - Text: Default text color - Muted: Color for secondary text @@ -49,7 +49,7 @@ Use a predefined theme. Valid values: STANDARD THEMES: - 'Default' - Standard cyan/green/yellow/red theme -- 'Dark' - Dark theme with muted colors +- 'Dark' - Dark theme with muted colors - 'Light' - Light theme with darker colors NATURE THEMES: @@ -82,7 +82,7 @@ Set-OrionTheme -Preset Matrix Applies the Matrix theme with green-on-black hacker aesthetic. -.EXAMPLE +.EXAMPLE Set-OrionTheme -Preset Retro80s Applies the 80s synthwave theme with magenta/cyan neon colors. diff --git a/source/Public/Show-OrionDemo.ps1 b/source/Public/Show-OrionDemo.ps1 index 2399bb3..e01bfc7 100644 --- a/source/Public/Show-OrionDemo.ps1 +++ b/source/Public/Show-OrionDemo.ps1 @@ -3,7 +3,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Unified Demo Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: January 26, 2026 Module: OrionDesign v1.6.0 Category: Demonstration @@ -32,7 +32,7 @@ HLD INTEGRATION: .DESCRIPTION Show-OrionDemo provides demonstrations of the OrionDesign module functions. Use the -Demo parameter to select which type of demo to run: - + - Basic: Quick overview of all OrionDesign functions with examples - Themes: Comprehensive showcase of all 13 color theme presets - Interactive: Hands-on demonstration of Write-Menu and Write-Question @@ -47,12 +47,12 @@ HLD INTEGRATION: .PARAMETER Section For 'Basic' and 'All' demos, display only a specific section. - Valid values: All, Banners, Headers, InfoBoxes, Alerts, Actions, Progress, + Valid values: All, Banners, Headers, InfoBoxes, Alerts, Actions, Progress, Steps, Charts, Panels, Separators, Themes, Width .PARAMETER Theme For 'Themes' demo, show only a specific theme instead of all themes. - Valid values: Default, Dark, Light, Ocean, Forest, Sunset, Monochrome, + Valid values: Default, Dark, Light, Ocean, Forest, Sunset, Monochrome, HighContrast, OldSchool, Matrix, Retro80s, Cyberpunk, Vintage .PARAMETER Pause @@ -102,15 +102,15 @@ HLD INTEGRATION: param( [ValidateSet("Basic", "Themes", "Interactive", "All")] [string]$Demo = "Basic", - - [ValidateSet("All", "Banners", "Headers", "InfoBoxes", "Alerts", "Actions", "Progress", + + [ValidateSet("All", "Banners", "Headers", "InfoBoxes", "Alerts", "Actions", "Progress", "Steps", "Charts", "Panels", "Separators", "Themes", "Width")] [string]$Section = "All", - + [ValidateSet('Default', 'Dark', 'Light', 'Ocean', 'Forest', 'Sunset', 'Monochrome', 'HighContrast', 'OldSchool', 'Matrix', 'Retro80s', 'Cyberpunk', 'Vintage', 'All')] [string]$Theme = 'All', - + [switch]$Pause, [switch]$SkipClear ) @@ -181,7 +181,7 @@ HLD INTEGRATION: [string]$Description, [string]$Category ) - + Write-Host Write-Host "═══════════════════════════════════════════════════════════════════════════════════════" -ForegroundColor DarkGray Write-Host "THEME: $ThemeName - $Description" -ForegroundColor DarkGray @@ -191,28 +191,28 @@ HLD INTEGRATION: Set-OrionTheme -Preset $ThemeName $currentTheme = Get-OrionTheme - + Write-Host "Color Palette:" -ForegroundColor $currentTheme.Text Write-Host " Accent: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Accent Write-Host "($($currentTheme.Accent))" -ForegroundColor $currentTheme.Muted - + Write-Host " Success: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Success Write-Host "($($currentTheme.Success))" -ForegroundColor $currentTheme.Muted - + Write-Host " Warning: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Warning Write-Host "($($currentTheme.Warning))" -ForegroundColor $currentTheme.Muted - + Write-Host " Error: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Error Write-Host "($($currentTheme.Error))" -ForegroundColor $currentTheme.Muted - + Write-Host " Text: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "($($currentTheme.Text))" -ForegroundColor $currentTheme.Muted - + Write-Host " Muted: " -NoNewline -ForegroundColor $currentTheme.Text Write-Host "████ " -NoNewline -ForegroundColor $currentTheme.Muted Write-Host "($($currentTheme.Muted))" -ForegroundColor $currentTheme.Muted @@ -221,21 +221,21 @@ HLD INTEGRATION: Write-Header -Text "$ThemeName Theme Components" -Underline Full Write-Banner -ScriptName "OrionDesign $ThemeName" -Author "Theme Demo" -Design Minimal -Description "Showcasing the $ThemeName color scheme" - + Write-Alert -Message "$ThemeName Success Alert" -Type Success Write-Alert -Message "$ThemeName Warning Alert" -Type Warning Write-Alert -Message "$ThemeName Error Alert" -Type Error Write-Alert -Message "$ThemeName Info Alert" -Type Info - + Write-InfoBox -Title "$ThemeName Theme" -Content "This InfoBox demonstrates how the $ThemeName theme affects content presentation." -Style Modern - + Write-ProgressBar -CurrentValue 75 -MaxValue 100 -Text "$ThemeName Progress" -Style Bar -ShowPercentage - + Write-ActionResult -Action "$ThemeName Theme Test" -Status Success -Details "Theme applied successfully" - + $steps = @("Load config", "Apply scheme", "Update components", "Verify") Write-Steps -Steps $steps -CurrentStep 3 -CompletedSteps @(1,2) -Style Numbered - + Write-Panel -Title "$ThemeName Panel" -Content @( "Theme: $ThemeName", "Category: $Category" @@ -251,7 +251,7 @@ HLD INTEGRATION: #endregion #region Basic Demo - + function Show-BasicDemo { if (-not $SkipClear) { Clear-Host } @@ -280,13 +280,13 @@ HLD INTEGRATION: Write-Header -Text "Header with No Underline" -Underline None Write-Header -Text "Advanced Options" -Number 3 Write-Header -Text "Important: System Ready" - + Demo-Separator "Write-InfoBox Styles" Write-InfoBox -Title "Classic Style" -Content "System backup completed successfully." -Style Classic Write-InfoBox -Title "Modern Style" -Content "New security patches available." -Style Modern Write-InfoBox -Title "Accent Style" -Content "Database connection established." -Style Accent Write-InfoBox -Title "Simple Style" -Content "Quick informational note." -Style Simple - + Demo-Separator "Write-Alert Types" Write-Alert -Message "This is a success message!" -Type Success Write-Alert -Message "This is a warning message!" -Type Warning @@ -318,7 +318,7 @@ HLD INTEGRATION: Write-ActionResult -Action "Deploy Application" -Status Success -Details "Deployed in 2.3 seconds" Write-ActionResult -Action "Deploy Application" -Status Warning -Details "Deployed with warnings" Write-ActionResult -Action "Deploy Application" -Status Failed -Details "Deployment failed" - + Demo-Separator "Write-ProgressBar Styles" Write-ProgressBar -CurrentValue 75 -MaxValue 100 -Text "Bar Style" -Style Bar -ShowPercentage Write-ProgressBar -CurrentValue 60 -MaxValue 100 -Text "Dots Style" -Style Dots -ShowPercentage @@ -346,10 +346,10 @@ HLD INTEGRATION: Demo-Separator "Write-Separator Styles" Write-Separator -Text "Single" -Style Single - Write-Separator -Text "Double" -Style Double + Write-Separator -Text "Double" -Style Double Write-Separator -Text "Thick" -Style Thick Write-Separator -Text "Dotted" -Style Dotted - + Demo-Separator "Write-Panel Styles" $panelContent = @("OS: Windows Server 2022", "CPU: Intel Xeon", "Memory: 16 GB") Write-Panel -Title "Box Style" -Content $panelContent -Style Box @@ -371,10 +371,10 @@ HLD INTEGRATION: #endregion #region Themes Demo - + function Show-ThemesDemo { if (-not $SkipClear) { Clear-Host } - + Write-Banner -ScriptName "OrionDesign Theme Gallery" -Author "Sune A Narud" -Design Modern -Description "Complete showcase of all available theme presets" Write-Host "Welcome to the OrionDesign Theme Showcase!" -ForegroundColor White @@ -407,13 +407,13 @@ HLD INTEGRATION: } } else { $categories = 'Standard', 'Nature', 'Retro/Vintage', 'Tech/Futuristic', 'Artistic', 'Accessibility' - + foreach ($category in $categories) { Write-Host Write-Host ("═" * 80) -ForegroundColor Cyan Write-Host " $category THEMES" -ForegroundColor Cyan Write-Host ("═" * 80) -ForegroundColor Cyan - + $categoryThemes = $allThemes | Where-Object { $_.Category -eq $category } foreach ($themeInfo in $categoryThemes) { Show-ThemeDemo -ThemeName $themeInfo.Name -Description $themeInfo.Description -Category $themeInfo.Category @@ -431,10 +431,10 @@ HLD INTEGRATION: #endregion #region Interactive Demo - + function Show-InteractiveDemo { if (-not $SkipClear) { Clear-Host } - + Write-Banner -ScriptName "OrionDesign Interactive Demo" -Author "Sune A Narud" -Design Modern -Description "Interactive demonstration of Write-Menu and Write-Question" Write-Host "Welcome to the Interactive Functions Demo!" -ForegroundColor White @@ -489,7 +489,7 @@ HLD INTEGRATION: #endregion #region Comprehensive Demo (All) - + function Show-ComprehensiveDemo { if (-not $SkipClear) { Clear-Host } @@ -590,7 +590,7 @@ HLD INTEGRATION: Show-DemoSectionHeader -Title "ACTIONS - Write-Action + Write-ActionStatus + Write-ActionResult" -Subtitle "Real-time status reporting" Show-ExampleHeader "Write-Action + Write-ActionStatus Pattern" - + Write-Action "Connecting to Azure" Start-Sleep -Milliseconds 200 Write-ActionStatus "Connected" -Status Success diff --git a/source/Public/Write-Action.ps1 b/source/Public/Write-Action.ps1 index d7be0b7..e88a161 100644 --- a/source/Public/Write-Action.ps1 +++ b/source/Public/Write-Action.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Action Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: February 4, 2026 Module: OrionDesign v2.1.2 Category: Interactive Display @@ -48,7 +48,7 @@ The text length and right margin are stored for Write-ActionResult to calculate Uses theme 'Action' color (or falls back to 'Text' color). COMPLETE MODE (-Complete): -Displays a standalone result with optional icons, status colors, duration, +Displays a standalone result with optional icons, status colors, duration, subtext, and additional details. Use this for summarized action outcomes. RIGHT-ALIGNED MODE (-RightAlign): @@ -112,7 +112,7 @@ Write-Action "Connecting to database" Write-ActionResult "Connected" Standard mode - displays with automatic right-alignment and margins: - Connecting to database Connected + Connecting to database Connected .EXAMPLE Write-Action "Loading configuration" -Indent 0 @@ -152,7 +152,7 @@ Standard mode with 4-space indentation: Write-Action "Completed successfully" -RightAlign Right-aligned mode (implies Complete): - Completed successfully + Completed successfully .NOTES Standard mode: Pairs with Write-ActionResult for real-time status reporting. @@ -174,7 +174,7 @@ function Write-Action { [int]$Indent = 1, [switch]$RightAlign, [switch]$Complete, - + # Complete mode parameters [ValidateSet('Success', 'Failed', 'Warning', 'Info', 'Running', 'Pending')] [string]$Status = "", @@ -262,7 +262,7 @@ function Write-Action { if ($Complete) { # ===== COMPLETE MODE ===== # Standalone output with optional icons, status, duration, details - + $maxContentWidth = $script:OrionMaxWidth - $indentString.Length - 10 # Status icons and colors @@ -353,7 +353,7 @@ function Write-Action { else { # ===== STANDARD MODE ===== # No newline, pairs with Write-ActionResult - + # Use Action color for descriptions (falls back to Text if Action not defined) $actionColor = if ($script:Theme.Action) { $script:Theme.Action } else { $script:Theme.Text } $textColor = if ($Color) { $Color } else { $actionColor } diff --git a/source/Public/Write-ActionResult.ps1 b/source/Public/Write-ActionResult.ps1 index c339c74..d8dd52a 100644 --- a/source/Public/Write-ActionResult.ps1 +++ b/source/Public/Write-ActionResult.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-ActionResult Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: February 4, 2026 Module: OrionDesign v2.1.2 Category: Interactive Display @@ -65,7 +65,7 @@ The result text to display. The status type for color coding. Valid values: - 'Success' - Uses theme Result color (Accent-based) - this is the auto-detect default - 'Failed' - Uses theme Error color (Red) -- 'Warning' - Uses theme Warning color (Yellow) +- 'Warning' - Uses theme Warning color (Yellow) - 'Info' - Uses theme Accent color - 'Running' - Uses theme Accent color - 'Pending' - Uses theme Warning color @@ -78,28 +78,28 @@ Write-Action "Connecting to database" Write-ActionResult "Connected" Displays with automatic right-alignment and margins (auto-detects as Success): - Connecting to database Connected + Connecting to database Connected .EXAMPLE Write-Action "Processing file" Write-ActionResult "File not found" Auto-detects "not found" as Failed, shows in Error color: - Processing file File not found + Processing file File not found .EXAMPLE Write-Action "Checking service" -Write-ActionResult "125 users" +Write-ActionResult "125 users" No failure pattern detected, defaults to Success (Result/Accent color): - Checking service 125 users + Checking service 125 users .EXAMPLE Write-Action "Syncing data" Write-ActionResult "Done" Any text without failure/warning patterns shows as Success: - Syncing data Done + Syncing data Done .EXAMPLE Write-Action "This is a very long action description that takes most of the line" @@ -107,7 +107,7 @@ Write-ActionResult "This is a very long result that causes overflow" When combined text exceeds effective width, automatically outputs on new line: This is a very long action description that takes most of the line - This is a very long result that causes overflow + This is a very long result that causes overflow .NOTES Auto-detection philosophy: "Success unless proven otherwise" @@ -239,13 +239,13 @@ function Write-ActionResult { $maxWidth = if ($script:OrionMaxWidth) { $script:OrionMaxWidth } else { 100 } $actionLength = if ($script:LastActionTextLength) { $script:LastActionTextLength } else { 0 } $rightMargin = if ($script:LastActionRightMargin) { $script:LastActionRightMargin } else { 1 } - + # Effective width accounts for right margin $effectiveWidth = $maxWidth - $rightMargin - + # Calculate if we need a new line (action + space + status > effectiveWidth) $combinedLength = $actionLength + 1 + $Text.Length - + if ($combinedLength -gt $effectiveWidth -and $actionLength -gt 0) { # Not enough room - go to new line and right-align Write-Host "" diff --git a/source/Public/Write-Alert.ps1 b/source/Public/Write-Alert.ps1 index 3c49d64..6a6ae05 100644 --- a/source/Public/Write-Alert.ps1 +++ b/source/Public/Write-Alert.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Alert Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Information Display @@ -50,7 +50,7 @@ Optional action question (e.g., "Continue anyway?"). .PARAMETER ActionType Type of action if Action is specified. Valid values: - 'YesNo' - Yes/No question -- 'Continue' - Continue/Cancel question +- 'Continue' - Continue/Cancel question - 'Custom' - Custom action text .PARAMETER Details @@ -172,7 +172,7 @@ function Write-Alert { $padding = [Math]::Max(0, $maxWidth - $titleLineLength) $titleLine += (" " * $padding) + $alertInfo.Border Write-Host $titleLine -ForegroundColor $alertInfo.Color - + # Separator line Write-Host $border -ForegroundColor $alertInfo.Color } @@ -180,26 +180,26 @@ function Write-Alert { # Message line - clean design without icons $messageLine = $alertInfo.Border + " " + $alertInfo.Label + ": " + $Message + " " $messageLength = $messageLine.Length + 1 # +1 for ending border - + if ($messageLength -gt $maxWidth) { # Multi-line message $words = $Message -split ' ' $currentLine = $alertInfo.Border + " " + $alertInfo.Label + ": " - + foreach ($word in $words) { if (($currentLine + $word + " ").Length -gt $maxWidth - 1) { # Finish current line $padding = [Math]::Max(0, $maxWidth - $currentLine.Length - 1) $currentLine += (" " * $padding) + $alertInfo.Border Write-Host $currentLine -ForegroundColor $alertInfo.Color - + # Start new line $currentLine = $alertInfo.Border + " " + $word + " " } else { $currentLine += $word + " " } } - + # Finish last line $padding = [Math]::Max(0, $maxWidth - $currentLine.Length - 1) $currentLine += (" " * $padding) + $alertInfo.Border diff --git a/source/Public/Write-Banner.ps1 b/source/Public/Write-Banner.ps1 index acb9333..1d5413f 100644 --- a/source/Public/Write-Banner.ps1 +++ b/source/Public/Write-Banner.ps1 @@ -225,27 +225,27 @@ function Write-Banner { # Wings Design - Beautiful wing-shaped pattern function Write-WingsDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + $bannerWidth = $MaxWidth $centerPos = [Math]::Floor($bannerWidth / 2) $paddingSymbol = '█' - + # Wing pattern - creates expanding then contracting pattern $wingPattern = @(2, 4, 6, 8, 10, 12, 14, 16, 18, 16, 14, 12, 10, 8, 6, 4, 2) - + # Top wing foreach ($width in $wingPattern[0..8]) { $spaces = ' ' * ($centerPos - $width) $wing = $paddingSymbol * $width Write-Host ($spaces + $wing + (' ' * ($bannerWidth - ($centerPos - $width) - $width * 2)) + $wing) -ForegroundColor $Theme.Accent } - + # Script name line $nameWidth = $ScriptName.Length $nameSpaces = [Math]::Max(0, [Math]::Floor(($bannerWidth - $nameWidth) / 2)) $nameLine = (' ' * $nameSpaces) + $ScriptName + (' ' * ($bannerWidth - $nameSpaces - $nameWidth)) Write-Host $nameLine -ForegroundColor $Theme.Text -BackgroundColor $Theme.Success - + # Author and date line (if provided) if ($Author -or $AuthorDate) { $authorInfo = if ($Author -and $AuthorDate) { "$Author - $AuthorDate" } elseif ($Author) { $Author } else { $AuthorDate } @@ -254,14 +254,14 @@ function Write-WingsDesign { $authorLine = (' ' * $authorSpaces) + $authorInfo + (' ' * ($bannerWidth - $authorSpaces - $authorWidth)) Write-Host $authorLine -ForegroundColor $Theme.Muted } - + # Bottom wing foreach ($width in $wingPattern[9..16]) { $spaces = ' ' * ($centerPos - $width) $wing = $paddingSymbol * $width Write-Host ($spaces + $wing + (' ' * ($bannerWidth - ($centerPos - $width) - $width * 2)) + $wing) -ForegroundColor $Theme.Accent } - + # Description (if provided) if ($Description) { Write-Host @@ -279,23 +279,23 @@ function Write-WingsDesign { # Classic Design - Traditional rectangular banner function Write-ClassicDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + $bannerWidth = $MaxWidth $border = '=' * $bannerWidth $paddingSymbol = '█' - + # Top border Write-Host $border -ForegroundColor $Theme.Accent - + # Empty line $emptyLine = $paddingSymbol + (' ' * ($bannerWidth - 2)) + $paddingSymbol Write-Host $emptyLine -ForegroundColor $Theme.Accent - + # Script name $nameSpaces = [Math]::Max(0, [Math]::Floor(($bannerWidth - 2 - $ScriptName.Length) / 2)) $nameLine = $paddingSymbol + (' ' * $nameSpaces) + $ScriptName + (' ' * ($bannerWidth - 2 - $nameSpaces - $ScriptName.Length)) + $paddingSymbol Write-Host $nameLine -ForegroundColor $Theme.Text - + # Author info if ($Author -or $AuthorDate) { $authorInfo = if ($Author -and $AuthorDate) { "$Author - $AuthorDate" } elseif ($Author) { $Author } else { $AuthorDate } @@ -303,13 +303,13 @@ function Write-ClassicDesign { $authorLine = $paddingSymbol + (' ' * $authorSpaces) + $authorInfo + (' ' * ($bannerWidth - 2 - $authorSpaces - $authorInfo.Length)) + $paddingSymbol Write-Host $authorLine -ForegroundColor $Theme.Muted } - + # Empty line Write-Host $emptyLine -ForegroundColor $Theme.Accent - + # Bottom border Write-Host $border -ForegroundColor $Theme.Accent - + # Description if ($Description) { Write-Host @@ -326,32 +326,32 @@ function Write-ClassicDesign { # Modern Design - Sleek with gradient effect function Write-ModernDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + $bannerWidth = $MaxWidth - + # Modern header with gradient-like effect $chars = @('▓', '▒', '░') for ($i = 0; $i -lt 3; $i++) { Write-Host ($chars[$i] * $bannerWidth) -ForegroundColor $Theme.Accent } - + # Script name with modern styling Write-Host $segments = Convert-ToColoredSegments -Text " $ScriptName " -Theme $Theme Write-Colored -Segments $segments -Theme $Theme - + # Author info with modern bullets if ($Author -or $AuthorDate) { $authorInfo = if ($Author -and $AuthorDate) { "$Author • $AuthorDate" } elseif ($Author) { $Author } else { $AuthorDate } Write-Host " $authorInfo" -ForegroundColor $Theme.Muted } - + # Modern footer Write-Host for ($i = 2; $i -ge 0; $i--) { Write-Host ($chars[$i] * $bannerWidth) -ForegroundColor $Theme.Accent } - + # Description if ($Description) { Write-Host @@ -368,22 +368,22 @@ function Write-ModernDesign { # Minimal Design - Clean and simple function Write-MinimalDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + # Simple header line Write-Host ('─' * $MaxWidth) -ForegroundColor $Theme.Muted - + # Script name Write-Host $ScriptName.ToUpper() -ForegroundColor $Theme.Text - + # Author info if ($Author -or $AuthorDate) { $authorInfo = if ($Author -and $AuthorDate) { "$Author | $AuthorDate" } elseif ($Author) { $Author } else { $AuthorDate } Write-Host $authorInfo -ForegroundColor $Theme.Muted } - + # Simple footer line Write-Host ('─' * $MaxWidth) -ForegroundColor $Theme.Muted - + # Description if ($Description) { Write-Host @@ -400,9 +400,9 @@ function Write-MinimalDesign { # Geometric Design - Angular and modern function Write-GeometricDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + $bannerWidth = $MaxWidth - + # Geometric top pattern for ($i = 1; $i -le 5; $i++) { $spaces = ' ' * (5 - $i) @@ -416,20 +416,20 @@ function Write-GeometricDesign { Write-Host $simplePattern -ForegroundColor $Theme.Accent } } - + # Author info with geometric styling if ($Author -or $AuthorDate) { $authorInfo = if ($Author -and $AuthorDate) { "◆ $Author ◆ $AuthorDate ◆" } elseif ($Author) { "◆ $Author ◆" } else { "◆ $AuthorDate ◆" } $authorSpaces = [Math]::Max(0, [Math]::Floor(($bannerWidth - $authorInfo.Length) / 2)) Write-Host ((' ' * $authorSpaces) + $authorInfo) -ForegroundColor $Theme.Muted } - + # Geometric bottom pattern for ($i = 5; $i -ge 1; $i--) { $pattern = '▼' * $i + (' ' * ($bannerWidth - $i * 2)) + '▼' * $i Write-Host $pattern -ForegroundColor $Theme.Accent } - + # Description if ($Description) { Write-Host @@ -446,12 +446,12 @@ function Write-GeometricDesign { # Diamond Design - Progressive indentation creating diamond/pyramid shape function Write-DiamondDesign { param($ScriptName, $Author, $AuthorDate, $Description, $Theme, $MaxWidth) - + $bannerWidth = $MaxWidth $paddingSymbol = '═' $indentationIncrement = 4 # How much to indent each level - - # Helper function to calculate padding + + # Helper function to calculate padding function Get-Padding { param ( [int]$TotalWidth, @@ -468,7 +468,7 @@ function Write-DiamondDesign { $RightPadding = $paddingSymbol * ($PaddingEachSide + $PaddingRemainder) return @($LeftPadding, $RightPadding) } - + # Helper function to center text function Center-Text { param([string]$Text, [int]$TotalWidth) @@ -497,7 +497,7 @@ function Write-DiamondDesign { $Content2 = $ScriptName $ContentPadding2 = 4 $Padding2 = Get-Padding -TotalWidth $bannerWidth -LeftIndentLength $Indent2.Length -RightIndentLength $Indent2.Length -Content $Content2 -ContentPadding $ContentPadding2 - + # Left padding Write-Host ($Indent2 + $Padding2[0]) -ForegroundColor $Theme.Accent -NoNewline # Content @@ -512,7 +512,7 @@ function Write-DiamondDesign { $Content3 = if ($Author -and $AuthorDate) { "$Author - $AuthorDate" } elseif ($Author) { $Author } else { $AuthorDate } $ContentPadding3 = 3 $Padding3 = Get-Padding -TotalWidth $bannerWidth -LeftIndentLength $Indent3.Length -RightIndentLength $Indent3.Length -Content $Content3 -ContentPadding $ContentPadding3 - + # Left padding Write-Host ($Indent3 + $Padding3[0]) -ForegroundColor $Theme.Accent -NoNewline # Content diff --git a/source/Public/Write-Chart.ps1 b/source/Public/Write-Chart.ps1 index 1d3418d..f4bd8a4 100644 --- a/source/Public/Write-Chart.ps1 +++ b/source/Public/Write-Chart.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Chart Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Data Presentation @@ -159,20 +159,20 @@ function Write-Chart { foreach ($item in $chartData) { $barLength = [Math]::Round(($item.Value / $maxValue) * $Width) $label = $item.Label.PadRight(15) - + Write-Host $label -ForegroundColor $script:Theme.Text -NoNewline Write-Host " │" -ForegroundColor $script:Theme.Muted -NoNewline - + # Color coding based on value $color = $script:Theme.Accent if ($item.Value -ge $maxValue * 0.8) { $color = $script:Theme.Error } elseif ($item.Value -ge $maxValue * 0.6) { $color = $script:Theme.Warning } elseif ($item.Value -ge $maxValue * 0.3) { $color = $script:Theme.Success } - + for ($i = 0; $i -lt $barLength; $i++) { Write-Host "█" -ForegroundColor $color -NoNewline } - + if ($ShowValues) { Write-Host " $($item.Value)" -ForegroundColor $script:Theme.Text } else { @@ -241,14 +241,14 @@ function Write-Chart { for ($col = 0; $col -lt $scaledData.Count; $col++) { if ($scaledData[$col] -eq $row) { Write-Host "●" -ForegroundColor $script:Theme.Accent -NoNewline - } elseif ($col -gt 0 -and - (($scaledData[$col-1] -gt $row -and $scaledData[$col] -lt $row) -or + } elseif ($col -gt 0 -and + (($scaledData[$col-1] -gt $row -and $scaledData[$col] -lt $row) -or ($scaledData[$col-1] -lt $row -and $scaledData[$col] -gt $row))) { Write-Host "│" -ForegroundColor $script:Theme.Accent -NoNewline } else { Write-Host " " -NoNewline } - + # Connect points with lines if ($col -lt $scaledData.Count - 1) { if ($scaledData[$col] -eq $row -and $scaledData[$col+1] -eq $row) { @@ -280,18 +280,18 @@ function Write-Chart { foreach ($item in $chartData) { $percentage = [Math]::Round(($item.Value / $total) * 100, 1) $barLength = [Math]::Round($percentage / 2) # Scale to reasonable size - + $color = $colors[$colorIndex % $colors.Count] $colorIndex++ Write-Host " " -NoNewline Write-Host "●" -ForegroundColor $color -NoNewline Write-Host " $($item.Label)".PadRight(15) -ForegroundColor $script:Theme.Text -NoNewline - + for ($i = 0; $i -lt $barLength; $i++) { Write-Host "█" -ForegroundColor $color -NoNewline } - + if ($ShowPercentage) { Write-Host " $percentage%" -ForegroundColor $script:Theme.Text } else { diff --git a/source/Public/Write-Header.ps1 b/source/Public/Write-Header.ps1 index 120f195..e46388d 100644 --- a/source/Public/Write-Header.ps1 +++ b/source/Public/Write-Header.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Header Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Information Display @@ -33,7 +33,7 @@ The Write-Header function displays styled text headers with customizable underli .PARAMETER Text The header text to display. Supports markup tags for colored formatting: - text - Displays text in theme accent color (cyan) -- text - Displays text in theme success color (green) +- text - Displays text in theme success color (green) - text - Displays text in theme warning color (yellow) - text - Displays text in theme error color (red) - text - Displays text in theme muted color (dark gray) @@ -174,7 +174,7 @@ function Write-Header { # Add a linebreak Write-Host - + # Default theme if (-not $script:Theme) { $script:Theme = @{ diff --git a/source/Public/Write-InfoBox.ps1 b/source/Public/Write-InfoBox.ps1 index 30baa07..342c8b8 100644 --- a/source/Public/Write-InfoBox.ps1 +++ b/source/Public/Write-InfoBox.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-InfoBox Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Information Display @@ -51,7 +51,7 @@ Width of the information box (default: auto-calculate). .EXAMPLE Write-InfoBox -Title "System Information" -Content @{ "Server" = "SQL-01" - "Database" = "Production" + "Database" = "Production" "Version" = "2019 SP3" } @@ -114,11 +114,11 @@ function Write-InfoBox { function Wrap-Text { param([string]$Text, [int]$MaxWidth) if ($Text.Length -le $MaxWidth -or $MaxWidth -le 0) { return @($Text) } - + $words = $Text -split '\s+' $lines = @() $currentLine = "" - + foreach ($word in $words) { $testLine = if ($currentLine) { "$currentLine $word" } else { $word } if ($testLine.Length -le $MaxWidth) { @@ -175,22 +175,22 @@ function Write-InfoBox { if ($Width -eq 0) { # Use global max width if available $globalWidth = if ($script:OrionMaxWidth) { $script:OrionMaxWidth } else { 80 } - + # Calculate minimum width based on actual content requirements $titleWidthReq = $Title.Length + 10 # Title + decorative elements $contentWidthReq = $maxContentWidth + 4 # Content + border chars (│ + space + content + space + │) $minWidth = [Math]::Max($titleWidthReq, $contentWidthReq) - + # Respect global width while ensuring content fits $Width = [Math]::Max(40, [Math]::Min($globalWidth, $minWidth)) - + # If content still doesn't fit within global limit, expand to accommodate it if ($minWidth -gt $globalWidth) { $Width = $minWidth Write-Verbose "Content width ($minWidth) exceeds global limit ($globalWidth), expanding to fit content" } } - + # Ensure minimum width to prevent negative values $Width = [Math]::Max($Width, $Title.Length + 6) @@ -209,28 +209,28 @@ function Write-InfoBox { if ($Content -is [hashtable] -and $line -match '^([^:]+): (.+)$') { $key = $matches[1] $value = $matches[2] - + if ($WrapContent -and $value.Length -gt ($Width - $maxKeyLength - 8)) { # Wrap long values $maxValueWidth = $Width - $maxKeyLength - 8 $wrappedLines = Wrap-Text -Text $value -MaxWidth $maxValueWidth - + # First line with key Write-Host "│ " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host $key.PadRight($maxKeyLength) -ForegroundColor $script:Theme.Accent -NoNewline Write-Host " : " -ForegroundColor $script:Theme.Muted -NoNewline Write-Host $wrappedLines[0] -ForegroundColor $script:Theme.Text -NoNewline - + $contentLength = 2 + $maxKeyLength + 3 + $wrappedLines[0].Length + 1 $padding = [Math]::Max(0, $Width - $contentLength) Write-Host (" " * $padding + "│") -ForegroundColor $script:Theme.Accent - + # Additional wrapped lines for ($i = 1; $i -lt $wrappedLines.Count; $i++) { Write-Host "│ " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host (" " * ($maxKeyLength + 3)) -NoNewline Write-Host $wrappedLines[$i] -ForegroundColor $script:Theme.Text -NoNewline - + $contentLength = 2 + $maxKeyLength + 3 + $wrappedLines[$i].Length + 1 $padding = [Math]::Max(0, $Width - $contentLength) Write-Host (" " * $padding + "│") -ForegroundColor $script:Theme.Accent @@ -241,7 +241,7 @@ function Write-InfoBox { Write-Host $key.PadRight($maxKeyLength) -ForegroundColor $script:Theme.Accent -NoNewline Write-Host " : " -ForegroundColor $script:Theme.Muted -NoNewline Write-Host $value -ForegroundColor $script:Theme.Text -NoNewline - + $contentLength = 2 + $maxKeyLength + 3 + $value.Length + 1 $padding = [Math]::Max(0, $Width - $contentLength) Write-Host (" " * $padding + "│") -ForegroundColor $script:Theme.Accent @@ -285,7 +285,7 @@ function Write-InfoBox { if ($Content -is [hashtable] -and $line -match '^([^:]+): (.+)$') { $key = $matches[1] $value = $matches[2] - + Write-Host "▌ " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host "● " -ForegroundColor $script:Theme.Success -NoNewline Write-Host $key -ForegroundColor $script:Theme.Accent -NoNewline @@ -309,7 +309,7 @@ function Write-InfoBox { if ($Content -is [hashtable] -and $line -match '^([^:]+): (.+)$') { $key = $matches[1] $value = $matches[2] - + Write-Host " $key" -ForegroundColor $script:Theme.Muted -NoNewline Write-Host ": " -ForegroundColor $script:Theme.Muted -NoNewline Write-Host $value -ForegroundColor $script:Theme.Text @@ -330,7 +330,7 @@ function Write-InfoBox { if ($Content -is [hashtable] -and $line -match '^([^:]+): (.+)$') { $key = $matches[1] $value = $matches[2] - + Write-Host " ◆ " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host $key -ForegroundColor $script:Theme.Accent -NoNewline Write-Host ": " -ForegroundColor $script:Theme.Muted -NoNewline diff --git a/source/Public/Write-Menu.ps1 b/source/Public/Write-Menu.ps1 index d39314b..ca26330 100644 --- a/source/Public/Write-Menu.ps1 +++ b/source/Public/Write-Menu.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Menu Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: January 30, 2026 Module: OrionDesign v2.1.0 Category: Interactive Elements @@ -29,8 +29,8 @@ HLD INTEGRATION: Creates styled interactive menus for user selection with built-in Exit option. .DESCRIPTION -The Write-Menu function displays a formatted menu with options that users can select from. -Supports different styles and keyboard navigation. Every menu automatically includes an +The Write-Menu function displays a formatted menu with options that users can select from. +Supports different styles and keyboard navigation. Every menu automatically includes an Exit option (selectable with 'X') as the last item for consistent user experience. .PARAMETER Title @@ -197,7 +197,7 @@ function Write-Menu { for ($i = 0; $i -lt $Options.Count; $i++) { $optionNumber = $i + 1 $option = $Options[$i] - + switch ($Style) { 'Simple' { Write-Host " $optionNumber. $option" -ForegroundColor $script:Theme.Text @@ -250,13 +250,13 @@ function Write-Menu { } else { Write-Host "Select option (1-$($Options.Count), or X to exit): " -ForegroundColor $script:Theme.Muted -NoNewline } - + $selection = Read-Host - + if ([string]::IsNullOrWhiteSpace($selection) -and $DefaultSelection -and -not $MultiSelect) { $selection = $DefaultSelection } - + # Check for Exit selection if ($selection -eq 'X' -or $selection -eq 'x') { Write-Host "👋 $ExitLabel" -ForegroundColor $script:Theme.Muted @@ -274,7 +274,7 @@ function Write-Menu { $selectedNumbers = @() $validSelection = $true $parts = $selection -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } - + if ($parts.Count -eq 0) { $validSelection = $false } else { @@ -290,7 +290,7 @@ function Write-Menu { } } } - + if ($validSelection -and $selectedNumbers.Count -gt 0) { Write-Host "✅ Selected: " -ForegroundColor $script:Theme.Success -NoNewline $selectedValues = $selectedNumbers | ForEach-Object { $Options[$_ - 1] } diff --git a/source/Public/Write-MenuLine.ps1 b/source/Public/Write-MenuLine.ps1 index 206f7f1..f319970 100644 --- a/source/Public/Write-MenuLine.ps1 +++ b/source/Public/Write-MenuLine.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-MenuLine Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: February 5, 2026 Module: OrionDesign v3.0.0 Category: Interactive Display @@ -75,7 +75,7 @@ OUTPUT FORMAT: THEME COLORS USED: - MenuNumber: Accent color (cyan by default) - Period (.): Muted color (dark gray) - - Text: Text color (white by default) + - Text: Text color (white by default) - SuffixNumber: Accent color (matches MenuNumber) - Suffix: Text color (matches Text) @@ -89,7 +89,7 @@ The menu option number or identifier displayed on the left side. Can be a number (1, 2, 3) or text (A, B, X). Displayed in the theme's Accent color. -.PARAMETER Text +.PARAMETER Text The menu option text or description displayed after the number. This is the main text the user sees for the menu option. Displayed in the theme's Text color. @@ -247,7 +247,7 @@ Write-Menu .LINK Write-Action -.LINK +.LINK Write-ActionResult .LINK @@ -378,14 +378,14 @@ function Write-MenuLine { if ($hasSuffix) { # Build suffix text for length calculation $suffixText = if ($Suffix) { "$SuffixNumber $Suffix" } else { "$SuffixNumber" } - + # Calculate padding for right-alignment $padding = $effectiveWidth - $leftLength - $suffixText.Length - + if ($padding -gt 0) { Write-Host (" " * $padding) -NoNewline } - + # Output SuffixNumber in Accent color (or Muted), Suffix in Text color (or Muted) Write-Host $SuffixNumber -ForegroundColor $suffixNumColor -NoNewline if ($Suffix) { diff --git a/source/Public/Write-Panel.ps1 b/source/Public/Write-Panel.ps1 index 380bf35..180650a 100644 --- a/source/Public/Write-Panel.ps1 +++ b/source/Public/Write-Panel.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Panel Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Layout & Formatting @@ -146,23 +146,23 @@ function Write-Panel { # Set colors and icons based on type switch ($Type) { - 'Info' { + 'Info' { $color = $script:Theme.Accent if (-not $Icon) { $Icon = "ℹ" } } - 'Success' { + 'Success' { $color = $script:Theme.Success if (-not $Icon) { $Icon = "✓" } } - 'Warning' { + 'Warning' { $color = $script:Theme.Warning if (-not $Icon) { $Icon = "⚠" } } - 'Error' { + 'Error' { $color = $script:Theme.Error if (-not $Icon) { $Icon = "✗" } } - 'Default' { + 'Default' { $color = $script:Theme.Text if (-not $Icon) { $Icon = "●" } } @@ -183,7 +183,7 @@ function Write-Panel { } $Width = $maxLength + ($Padding * 2) + 6 # Space for borders, icons, and spacing if ($Width -lt 30) { $Width = 30 } - + # Apply global max width if set if ($script:OrionMaxWidth -and $Width -gt $script:OrionMaxWidth) { $Width = $script:OrionMaxWidth @@ -230,13 +230,13 @@ function Write-Panel { } Write-Host $line -ForegroundColor $script:Theme.Text -NoNewline - + # Calculate exact remaining space to match border width $contentWidth = $Padding + $line.Length if (-not $Title) { $contentWidth += $Icon.Length + 1 } $remainingSpace = $Width - 2 - $contentWidth - $Padding # Width - borders - content - right padding $remainingSpace = [Math]::Max(0, $remainingSpace) - + Write-Host (" " * $Padding) -NoNewline # Right padding Write-Host (" " * $remainingSpace) -NoNewline # Fill to border Write-Host "│" -ForegroundColor $color @@ -300,7 +300,7 @@ function Write-Panel { if ($Title) { $usedSpace = 4 + $Icon.Length + $Title.Length # "│ " + icon + " " + title + "│" $remaining = [Math]::Max(0, $Width - $usedSpace) - + Write-Host "│ " -ForegroundColor $color -NoNewline Write-Host $Icon -ForegroundColor $color -NoNewline Write-Host " $Title" -ForegroundColor $color -NoNewline @@ -320,7 +320,7 @@ function Write-Panel { $iconSpace = if (-not $Title) { $Icon.Length + 1 } else { 0 } $usedSpace = 2 + 1 + $line.Length + $iconSpace # "│" + " " + content + icon + "│" $remaining = [Math]::Max(0, $Width - $usedSpace) - + Write-Host "│" -ForegroundColor $color -NoNewline Write-Host " " -NoNewline diff --git a/source/Public/Write-ProgressBar.ps1 b/source/Public/Write-ProgressBar.ps1 index 318103b..28bb137 100644 --- a/source/Public/Write-ProgressBar.ps1 +++ b/source/Public/Write-ProgressBar.ps1 @@ -154,35 +154,35 @@ function Write-ProgressBar { switch ($Style) { 'Bar' { Write-Host "[" -ForegroundColor $script:Theme.Muted -NoNewline - + # Enhanced with fractional progress from former Blocks style $blockChars = @("▏","▎","▍","▌","▋","▊","▉","█") $fullBlocks = [Math]::Floor($filledWidth) $partialBlock = ($filledWidth - $fullBlocks) * 8 - + # Full filled blocks for ($i = 0; $i -lt $fullBlocks; $i++) { Write-Host "█" -ForegroundColor $Color -NoNewline } - + # Partial block for precise progress if ($partialBlock -gt 0 -and $fullBlocks -lt $Width) { $charIndex = [Math]::Floor($partialBlock) Write-Host $blockChars[$charIndex] -ForegroundColor $Color -NoNewline $fullBlocks++ } - + # Empty portion for ($i = $fullBlocks; $i -lt $Width; $i++) { Write-Host "░" -ForegroundColor $script:Theme.Muted -NoNewline } - + Write-Host "]" -ForegroundColor $script:Theme.Muted -NoNewline - + if ($ShowPercentage) { Write-Host " $percentage%" -ForegroundColor $script:Theme.Text -NoNewline } - + if ($Text) { Write-Host " $Text" -ForegroundColor $script:Theme.Text -NoNewline } @@ -197,14 +197,14 @@ function Write-ProgressBar { $dots += "●" } $dots = $dots.PadRight(4) - + Write-Host "⟳ " -ForegroundColor $Color -NoNewline Write-Host "$dots" -ForegroundColor $script:Theme.Text -NoNewline - + if ($ShowPercentage) { Write-Host " $percentage%" -ForegroundColor $script:Theme.Text -NoNewline } - + if ($Text) { Write-Host " $Text" -ForegroundColor $script:Theme.Text -NoNewline } @@ -213,14 +213,14 @@ function Write-ProgressBar { 'Spinner' { $spinnerChars = @("⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏") $spinnerIndex = $CurrentValue % $spinnerChars.Count - + Write-Host $spinnerChars[$spinnerIndex] -ForegroundColor $Color -NoNewline Write-Host " " -NoNewline - + if ($ShowPercentage) { Write-Host "$percentage% " -ForegroundColor $script:Theme.Text -NoNewline } - + if ($Text) { Write-Host "$Text" -ForegroundColor $script:Theme.Text -NoNewline } @@ -229,23 +229,23 @@ function Write-ProgressBar { 'Modern' { # Modern progress with rounded ends Write-Host "●" -ForegroundColor $Color -NoNewline - + for ($i = 0; $i -lt $filledWidth - 1; $i++) { Write-Host "━" -ForegroundColor $Color -NoNewline } - + if ($filledWidth -gt 0) { Write-Host "●" -ForegroundColor $Color -NoNewline } - + for ($i = $filledWidth; $i -lt $Width; $i++) { Write-Host "┄" -ForegroundColor $script:Theme.Muted -NoNewline } - + if ($ShowPercentage) { Write-Host " $percentage%" -ForegroundColor $script:Theme.Text -NoNewline } - + if ($Text) { Write-Host " $Text" -ForegroundColor $script:Theme.Text -NoNewline } diff --git a/source/Public/Write-Question.ps1 b/source/Public/Write-Question.ps1 index 179b8b6..fe64ad1 100644 --- a/source/Public/Write-Question.ps1 +++ b/source/Public/Write-Question.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Question Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Interactive Elements @@ -246,10 +246,10 @@ function Write-Question { if ([string]::IsNullOrWhiteSpace($userInput)) { $userInput = if ($Default -eq 'Yes') { 'Y' } else { 'N' } } - if ($userInput -match '^[Yy]') { - return $true - } elseif ($userInput -match '^[Nn]') { - return $false + if ($userInput -match '^[Yy]') { + return $true + } elseif ($userInput -match '^[Nn]') { + return $false } else { Write-Host " ❌ Please enter Y for Yes or N for No" -ForegroundColor $script:Theme.Error $validInput = $false @@ -285,7 +285,7 @@ function Write-Question { return $null } } - + # Validate the number (whether from input or default) if (-not [string]::IsNullOrWhiteSpace($userInput)) { $number = 0 diff --git a/source/Public/Write-Separator.ps1 b/source/Public/Write-Separator.ps1 index 5186e5b..534b780 100644 --- a/source/Public/Write-Separator.ps1 +++ b/source/Public/Write-Separator.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Separator Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Layout & Formatting @@ -52,7 +52,7 @@ Custom character for 'Custom' style. .PARAMETER Color Color of the separator. Accepts either: - Semantic theme colors: Accent, Success, Warning, Error, Text, Muted -- Direct console colors: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, +- Direct console colors: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White .PARAMETER Center @@ -155,7 +155,7 @@ function Write-Separator { $Length = try { [Console]::WindowWidth } catch { 80 } } } - + # Ensure length doesn't exceed global max width if ($script:OrionMaxWidth -and $Length -gt $script:OrionMaxWidth) { $Length = $script:OrionMaxWidth @@ -189,11 +189,11 @@ function Write-Separator { if ($Center) { $textLength = $Text.Length + 2 # Add spaces around text $remainingLength = $Length - $textLength - + if ($remainingLength -gt 0) { $leftLength = [Math]::Floor($remainingLength / 2) $rightLength = $remainingLength - $leftLength - + Write-Host ($sepChar * $leftLength) -ForegroundColor $sepColor -NoNewline Write-Host " $Text " -ForegroundColor $script:Theme.Text -NoNewline Write-Host ($sepChar * $rightLength) -ForegroundColor $sepColor @@ -207,7 +207,7 @@ function Write-Separator { $textLength = $Text.Length $spacesCount = 2 # Space before and after text: " Text " $suffixLength = $Length - $prefixLength - $textLength - $spacesCount - + if ($suffixLength -gt 0) { Write-Host ($sepChar * $prefixLength) -ForegroundColor $sepColor -NoNewline Write-Host " $Text " -ForegroundColor $script:Theme.Text -NoNewline diff --git a/source/Public/Write-Steps.ps1 b/source/Public/Write-Steps.ps1 index 2bf691c..10fba4c 100644 --- a/source/Public/Write-Steps.ps1 +++ b/source/Public/Write-Steps.ps1 @@ -2,7 +2,7 @@ ================================================================================ ORION DESIGN - POWERSHELL UI FRAMEWORK | Write-Steps Function ================================================================================ -Author: Sune Alexandersen Narud +Author: Sune Alexandersen Narud Date: August 22, 2025 Module: OrionDesign v1.6.0 Category: Status & Results @@ -52,7 +52,7 @@ Array of completed step numbers. .EXAMPLE Write-Steps @( "Connect to server", - "Backup database", + "Backup database", "Apply updates", "Verify changes" ) -Style Numbered @@ -138,8 +138,8 @@ function Write-Steps { for ($i = 0; $i -lt $Steps.Count; $i++) { $stepNumber = $i + 1 - $step = $Steps[$i] - +$step = $Steps[$i] + # Determine step text and status if ($step -is [hashtable]) { $stepText = $step.Text @@ -165,24 +165,24 @@ function Write-Steps { Write-Host " $stepNumber. " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host $stepText -ForegroundColor $statusInfo.Color } - + 'Arrows' { $arrow = if ($i -eq 0) { "▶️" } else { " ↓" } Write-Host "$arrow " -ForegroundColor $script:Theme.Accent -NoNewline Write-Host $stepText -ForegroundColor $statusInfo.Color - + if ($i -lt $Steps.Count - 1) { Write-Host " │" -ForegroundColor $script:Theme.Muted } } - + 'Progress' { Write-Host $statusInfo.Icon -NoNewline Write-Host " Step $stepNumber" -ForegroundColor $script:Theme.Accent -NoNewline Write-Host ": $stepText " -ForegroundColor $script:Theme.Text -NoNewline Write-Host $statusInfo.Prefix -ForegroundColor $statusInfo.Color } - + 'Checklist' { $checkbox = switch ($stepStatus) { # 'Complete' { "☑️ 🗹 " } @@ -216,7 +216,7 @@ function Write-Steps { $completed = $CompletedSteps.Count $total = $Steps.Count } - + $percentage = if ($total -gt 0) { [Math]::Round(($completed / $total) * 100) } else { 0 } Write-Host "Progress: " -ForegroundColor $script:Theme.Text -NoNewline Write-Host "$completed/$total" -ForegroundColor $script:Theme.Accent -NoNewline From 130997b0ec55b5536820d347d5eb6119969a6526 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 13:37:18 +0200 Subject: [PATCH 08/11] docs: Added information about building the module using Sampler --- README.md | 360 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 354 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 618e36d..8ec7069 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,361 @@ -# OrionDesign +# OrionDesign Module - Quick Reference Guide -Orion Design Framework – Beautiful PowerShell UI functions for creating rich terminal interfaces. Includes themed output, progress bars, menus, banners, charts, alerts, panels and more. Every function includes a built-in -Demo parameter. +## 🎨 OrionDesign PowerShell UI Framework -## Make it yours +**Version 3.1.0** | A comprehensive collection of PowerShell functions for creating beautiful terminal user interfaces with consistent styling and configurable global settings. + +Created and maintained by Sune Alexandersen Narud + +[![PSGallery](https://img.shields.io/powershellgallery/v/OrionDesign?label=PSGallery&color=blue)](https://www.powershellgallery.com/packages/OrionDesign) +[![Downloads](https://img.shields.io/powershellgallery/dt/OrionDesign)](https://www.powershellgallery.com/packages/OrionDesign) +[![GitHub](https://img.shields.io/badge/GitHub-suneworld%2FOrionDesign-lightgrey?logo=github)](https://github.com/suneworld/OrionDesign) --- -Generated with Plaster and the SampleModule template +## 🆕 What's New in v3.1.0 + +Every public function now includes a **`-Demo`** parameter. Run any function with `-Demo` to see a live, self-contained demonstration of all its modes and options — including the exact code that produced each example. + +```powershell +# Examples +Write-Alert -Demo # Shows all 4 alert types +Write-Header -Demo # Shows all underline/numbering modes +Write-Chart -Demo # Bar chart and Pie chart examples +Write-Steps -Demo # All 4 step list styles +Set-OrionTheme -Demo # All 13 available presets +# ... same for every public function +``` + +### 📦 Installation + +**From PowerShell Gallery** (recommended): +```powershell +Install-Module -Name OrionDesign +# or with PSResourceGet: +Install-PSResource -Name OrionDesign +``` + +**From source** (clone the [GitHub repo](https://github.com/suneworld/OrionDesign)): +```powershell +Import-Module .\OrionDesign.psd1 +``` + +### ⚙️ Global Configuration + +#### Max Width Control +```powershell +# Check current max width +Get-OrionMaxWidth + +# Set custom max width (50-200 characters) +Set-OrionMaxWidth -Width 80 + +# Reset to default (100 characters) +Set-OrionMaxWidth -Reset +``` + +### 🎯 Core UI Functions + +#### Information Display +| Function | Purpose | Example | +|-----------------|----------------------------------------|----------------------------------------------------------| +| `Write-Banner` | Script headers with decorative borders | `Write-Banner -ScriptName "MyScript" -Author "User"` | +| `Write-Header` | Section headers with underlines | `Write-Header "Configuration"` | +| `Write-InfoBox` | Highlighted information panels | `Write-InfoBox "Title" "Content goes here" -Style Modern` | +| `Write-Alert` | Attention-grabbing alerts | `Write-Alert "Warning!" -Type Warning` | +| `Write-Panel` | Content containers with border styles | `Write-Panel "Details here" -Type Info -Style Box` | + +#### Status & Results +| Function | Purpose | Example | +|--------------------------|--------------------------------------|-----------------------------------------------------------------| +| `Write-Action` | Action text (pairs with Result) | `Write-Action "Processing files"` | +| `Write-Action -Complete` | Standalone result with icon/details | `Write-Action "Deploy" -Complete -Status Success -ShowIcon` | +| `Write-ActionResult` | Right-aligned result (pairs w/ Action) | `Write-ActionResult "Done" -Status Success` | +| `Write-ProgressBar` | Progress bar in various styles | `Write-ProgressBar 75 100 -Style Bar` | +| `Write-Steps` | Step-by-step process lists | `Write-Steps @("Step 1", "Step 2") -CurrentStep 1` | + +#### Data Presentation +| Function | Purpose | Example | +|---------------|----------------------|-------------------------------| +| `Write-Chart` | Bar and pie charts | `Write-Chart @{CPU=75; RAM=60}`| + +#### Interactive Elements +| Function | Purpose | Example | +|-----------------------|-------------------------------------|---------------------------------------------------| +| `Show-OrionSmartMenu` | Arrow-key or numeric selection menu | `Show-OrionSmartMenu "Title" "Pick:" @("A","B")` | +| `Write-Menu` | Numeric selection menus | `Write-Menu "Options" @("A", "B")` | +| `Write-MenuLine` | Single custom menu line | `Write-MenuLine 1 "Option Name"` | +| `Write-Question` | User prompts (text/YesNo/Choice) | `Write-Question "Continue?" -Type YesNo` | + +> **Note:** `Write-Menu` automatically includes an Exit option (X) as the last item in every menu. + +#### Layout & Formatting +| Function | Purpose | Example | +|-------------------|------------------|-------------------------------------------| +| `Write-Separator` | Section dividers | `Write-Separator "Section" -Style Double` | + +#### Utility Functions +| Function | Purpose | Example | +|-----------------------|----------------------------------|----------------------------------------------------| +| `Export-OrionHelpers` | Bundle functions for portability | `Export-OrionHelpers -ScriptPath ".\MyScript.ps1"` | + +### 🎨 Design Themes + +#### Theme System +OrionDesign includes a comprehensive theme system with 13 preset themes across 6 categories: + +```powershell +# Apply a theme +Set-OrionTheme -Preset HighContrast + +# View current theme +Get-OrionTheme + +# See all themes in action +Show-OrionDemo + +# List all 13 presets as a table +Set-OrionTheme -Demo +``` + +#### Available Themes + +**Standard Themes:** +- `Default` - Standard cyan/green/yellow/red theme +- `Dark` - Dark theme with muted colors +- `Light` - Light theme with darker colors + +**Nature Themes:** +- `Ocean` - Blue-based aquatic theme with wave dividers +- `Forest` - Green-based nature theme with tree-like elements + +**Retro/Vintage Themes:** +- `OldSchool` - Classic amber terminal (DOS/Unix era) +- `Vintage` - Warm sepia/amber nostalgic feel +- `Retro80s` - Synthwave magenta/cyan neon pastels + +**Tech/Futuristic Themes:** +- `Matrix` - Green-on-black digital rain aesthetic +- `Cyberpunk` - Futuristic cyan/purple tech aesthetic + +**Artistic Themes:** +- `Sunset` - Orange/magenta warm evening colors +- `Monochrome` - Grayscale theme for high contrast + +**Accessibility Themes:** +- `HighContrast` - Maximum contrast theme for accessibility (white on black) + +### 📏 Width-Aware Functions + +The following functions automatically respect the global max width setting: +- `Write-Action` / `Write-ActionResult` — Auto right-alignment to max width +- `Write-Action -Complete` — Truncates long details +- `Write-Separator` — Adjusts separator length +- `Write-Banner` — Adjusts border width +- `Write-Panel` — Wraps content appropriately + +### ⚡ Real-Time Status Pattern + +The `Write-Action` / `Write-ActionResult` pair provides elegant real-time status reporting with automatic alignment: + +```powershell +# Basic usage - result auto right-aligns to OrionMaxWidth +Write-Action "Connecting to database" +Write-ActionResult "Connected" -Status Success +# Output: Connecting to database Connected + +# Multiple steps with consistent alignment +Write-Action "Loading configuration" +Write-ActionResult "OK" -Status Success +Write-Action "Validating credentials" +Write-ActionResult "Valid" -Status Success +Write-Action "Fetching user data" +Write-ActionResult "125 users" -Status Success + +# Auto-detection of status (no -Status needed) +Write-Action "Processing items" +Write-ActionResult "42 items processed" # Auto-detects as Success + +# Standalone complete action with details (replaces old Write-ActionResult) +Write-Action "Deploy Database" -Complete -Status Success -Duration "00:02:15" -ShowIcon +# Output: ✅ Deploy Database in 00:02:15 + +Write-Action "142" -Complete -Status Success -Subtext "tables updated" -ShowIcon +# Output: ✅ 142 tables updated + +# Overflow handling - when text is too long, result moves to new line +Write-Action "This is a very long action description" +Write-ActionResult "This is also a long result" -Status Success +# Output: +# This is a very long action description +# This is also a long result +``` + +**Key Features:** +- `Write-Action` + `Write-ActionResult` for real-time paired output +- `Write-Action -Complete` for standalone rich results with icons, duration, details +- Result automatically right-aligns to OrionMaxWidth +- Intelligent overflow handling when combined text exceeds width +- Auto-detection of Success/Failed/Warning patterns + +### 🔧 Advanced Features + +#### ANSI Support +- Automatic detection of PowerShell ISE vs terminal +- Full color and formatting support in compatible terminals +- Graceful fallback for limited environments + +#### Validation +- Input validation on all parameters +- Range checking (e.g., max width 50-200) +- Type validation for enums and objects + +#### Error Handling +- Comprehensive error messages +- Parameter validation +- Fallback rendering for compatibility + +### 📚 Getting Help + +```powershell +# Get help for any function +Get-Help Write-Banner -Full +Get-Help Set-OrionMaxWidth -Examples + +# Show all available functions +Get-Command -Module OrionDesign + +# Run the built-in module showcase +Show-OrionDemo + +# Run per-function demos (available on every public function) +Write-Alert -Demo +Write-Banner -Demo +Write-Chart -Demo +Write-Header -Demo +Write-InfoBox -Demo +Write-Menu -Demo +Write-MenuLine -Demo +Write-Panel -Demo +Write-ProgressBar -Demo +Write-Question -Demo +Write-Separator -Demo +Write-Steps -Demo +Write-Action -Demo +Write-ActionResult -Demo +Set-OrionTheme -Demo # Lists all 13 theme presets +Get-OrionTheme -Demo +Set-OrionMaxWidth -Demo +Get-OrionMaxWidth -Demo +Export-OrionHelpers -Demo +Show-OrionSmartMenu -Demo +``` + +### 🚀 Quick Start Examples + +```powershell +# Script header +Write-Banner -ScriptName "Data Migration" -Author "IT Team" -Design Modern + +# Section dividers +Write-Separator "Configuration" -Style Double +Write-Separator "Results" -Style Single + +# Real-time action/result pattern +Write-Action "Connecting to database" +Write-ActionResult "Connected" -Status Success + +Write-Action "Processing files" +Write-ActionResult "42 files processed" # auto-detects Success + +# Standalone complete action +Write-Action "Backup Database" -Complete -Status Success -Duration "00:02:15" -ShowIcon + +# Step-by-step process +Write-Steps @("Connect", "Validate", "Deploy", "Verify") -CurrentStep 3 -CompletedSteps @(1,2) -Style Checklist + +# Progress bar +Write-ProgressBar 45 100 -Style Bar -Label "Processing Files" + +# Interactive smart menu (supports arrow-key navigation) +$choice = Show-OrionSmartMenu "Select Environment" "Choose:" @("Development", "Staging", "Production") + +# Numeric menu (with automatic Exit option) +$choice = Write-Menu "Select Environment" @("Development", "Staging", "Production") +if ($choice.Exit) { + Write-Host "User chose to exit" +} else { + Write-Host "Selected: $($choice.Value)" +} + +# Highlighted info panel +Write-InfoBox "Deployment Info" @{Server="prod-01"; Version="2.4.1"; Region="EU-West"} -Style Modern + +# Alert messages +Write-Alert "Configuration updated successfully" -Type Success +Write-Alert "Low disk space on C:" -Type Warning + +# Chart +Write-Chart @{CPU=75; Memory=60; Disk=90} -Style Bar -Title "Resource Usage" + +# Custom width for specific output +Set-OrionMaxWidth -Width 80 +Write-Separator "Narrow Layout" -Style Double +Set-OrionMaxWidth -Reset +``` + +### 📦 Making Scripts Portable (v2.0.0) + +The `Export-OrionHelpers` function allows you to bundle OrionDesign functions directly into your script's project folder, making scripts portable without requiring the module to be installed. + +```powershell +# Analyze a script and create a self-contained helper file +Export-OrionHelpers -ScriptPath "C:\Scripts\MyScript.ps1" + +# Output to a specific directory +Export-OrionHelpers -ScriptPath ".\Deploy.ps1" -OutputPath ".\lib" + +# Preview what would be exported without making changes +Export-OrionHelpers -ScriptPath ".\MyScript.ps1" -WhatIf + +# Keep the Import-Module line (don't comment it out) +Export-OrionHelpers -ScriptPath ".\MyScript.ps1" -CommentOutImport:$false +``` + +**What it does:** +1. Analyzes your script to find all OrionDesign function calls +2. Resolves dependencies (including private helper functions) +3. Creates `OrionDesign-Helpers.ps1` with all required functions +4. Comments out the `Import-Module OrionDesign` line in your script + +**After export, add this line to your script:** +```powershell +. "$PSScriptRoot\OrionDesign-Helpers.ps1" +``` + +--- + +**OrionDesign v3.1.0** | PowerShell UI Framework | 21 Functions | 13 Themes | Global Configuration | ANSI Support | -Demo on every function + +🔗 [PSGallery](https://www.powershellgallery.com/packages/OrionDesign) · [GitHub](https://github.com/suneworld/OrionDesign) + +## How do I use this repo? + +This module is generated with Plaster and the SampleModule template. + +To build the module you need to use samplew + +~~~ +Install-Module Sampler + +.\build build +~~~ + +You might need to change the format of `output\module\OrionDesign\3.1.1\OrionDesign.psm1` from UTF8 to UTF8wBOM +to be able to use the generated module. + +## References and links -This is a sample Readme +* [Getting startaed with Sampler (Project ReadMe)][1] -## Make it yours +[1]:https://github.com/gaelcolas/Sampler/blob/main/README.md From 54148c07c3fa67a360481749a700d4a95ed209e4 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 13:46:09 +0200 Subject: [PATCH 09/11] fix: Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ec7069..cf43a94 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,6 @@ to be able to use the generated module. ## References and links -* [Getting startaed with Sampler (Project ReadMe)][1] +* [Getting started with Sampler (Project ReadMe)][1] [1]:https://github.com/gaelcolas/Sampler/blob/main/README.md From 396d2c25335c1de5974eb1509201da1f943a2ba5 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Wed, 26 Aug 2026 13:50:05 +0200 Subject: [PATCH 10/11] docs(ReadMe): Clarification --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf43a94..bda3493 100644 --- a/README.md +++ b/README.md @@ -341,13 +341,17 @@ Export-OrionHelpers -ScriptPath ".\MyScript.ps1" -CommentOutImport:$false ## How do I use this repo? -This module is generated with Plaster and the SampleModule template. +This module is generated with Plaster and the SamplerModule template. -To build the module you need to use samplew +To build the module you need to use Sampler ~~~ Install-Module Sampler +~~~ + +Navigate to the root of a clone of this repo. +~~~ .\build build ~~~ From 0b2248ffae90d83f38a548bb6eb135a972978c71 Mon Sep 17 00:00:00 2001 From: DennisL68 Date: Thu, 27 Aug 2026 12:54:35 +0200 Subject: [PATCH 11/11] chore: Added original settings to .gitignore --- .gitignore | 9 ++++ .vscode/tasks.json | 125 --------------------------------------------- 2 files changed, 9 insertions(+), 125 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.gitignore b/.gitignore index 17c483d..b9c1e83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,14 @@ +# VS Code workspace files (local editor config, not part of the module) +*.code-workspace +.vscode/ + +# PowerShell test output +TestResults/ + +# Sampler build outpits output/ +# Other files to omit (and to not) **.bak *.local.* !**/README.md diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 2991140..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "version": "2.0.0", - "_runner": "terminal", - "windows": { - "options": { - "shell": { - "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - "args": [ - "-NoProfile", - "-ExecutionPolicy", - "Bypass", - "-Command" - ] - } - } - }, - "linux": { - "options": { - "shell": { - "executable": "/usr/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] - } - } - }, - "osx": { - "options": { - "shell": { - "executable": "/usr/local/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] - } - } - }, - "tasks": [ - { - "label": "build", - "type": "shell", - "command": "&${cwd}/build.ps1", - "args": [], - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "new", - "clear": false - }, - "runOptions": { - "runOn": "default" - }, - "problemMatcher": [ - { - "owner": "powershell", - "fileLocation": [ - "absolute" - ], - "severity": "error", - "pattern": [ - { - "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", - "message": 1 - }, - { - "regexp": "(.*)", - "code": 1 - }, - { - "regexp": "" - }, - { - "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", - "file": 1, - "line": 2 - } - ] - } - ] - }, - { - "label": "test", - "type": "shell", - "command": "&${cwd}/build.ps1", - "args": ["-AutoRestore","-Tasks","test"], - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "dedicated", - "showReuseMessage": true, - "clear": false - }, - "problemMatcher": [ - { - "owner": "powershell", - "fileLocation": [ - "absolute" - ], - "severity": "error", - "pattern": [ - { - "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", - "message": 1 - }, - { - "regexp": "(.*)", - "code": 1 - }, - { - "regexp": "" - }, - { - "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", - "file": 1, - "line": 2 - } - ] - } - ] - } - ] -}