├── .gitignore ├── LICENSE ├── README.md ├── docs ├── example1.gif └── example2.gif ├── ps-menu.psd1 └── ps-menu.psm1 /.gitignore: -------------------------------------------------------------------------------- 1 | *.*~ 2 | *.swp 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 chrisseroka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PS-Menu 2 | Simple module to generate interactive console menus (like yeoman) 3 | 4 | # Examples: 5 | 6 | ```powershell 7 | menu @("option 1", "option 2", "option 3") 8 | ``` 9 | ![Example](https://github.com/chrisseroka/ps-menu/raw/master/docs/example1.gif) 10 | 11 | More useful example: 12 | 13 | ![Example](https://github.com/chrisseroka/ps-menu/raw/master/docs/example2.gif) 14 | 15 | # Installation 16 | 17 | You can install it from the PowerShellGallery using PowerShellGet 18 | 19 | ```powershell 20 | Install-Module PS-Menu 21 | ``` 22 | # Features 23 | 24 | * Returns value of selected menu item 25 | * Returns index of selected menu item (using `-ReturnIndex` switch) 26 | * Navigation with `up/down` arrows 27 | * Navigation with `j/k` (vim style) 28 | * Esc key quits the menu (`null` value returned) 29 | 30 | # Contributing 31 | 32 | * Source hosted at [GitHub][repo] 33 | * Report issues/questions/feature requests on [GitHub Issues][issues] 34 | 35 | Pull requests are very welcome! 36 | -------------------------------------------------------------------------------- /docs/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisseroka/ps-menu/bd0fcfe61dc1950447a74a62dbbd597fd69e1f44/docs/example1.gif -------------------------------------------------------------------------------- /docs/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisseroka/ps-menu/bd0fcfe61dc1950447a74a62dbbd597fd69e1f44/docs/example2.gif -------------------------------------------------------------------------------- /ps-menu.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PS-Menu' 3 | # 4 | # Generated by: @chrisseroka 5 | # 6 | # Generated on: 03/03/2024 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'ps-menu.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '1.0.9' 16 | 17 | # ID used to uniquely identify this module 18 | GUID = '652b2326-2d29-4e86-8149-03828d75503e' 19 | 20 | # Author of this module 21 | Author = '@chrisseroka' 22 | 23 | # Company or vendor of this module 24 | CompanyName = 'Unknown' 25 | 26 | # Copyright statement for this module 27 | Copyright = '(c) 2024 @chrisseroka. All rights reserved.' 28 | 29 | # Description of the functionality provided by this module 30 | Description = 'Powershell module to generate interactive console menu' 31 | 32 | # Minimum version of the Windows PowerShell engine required by this module 33 | PowerShellVersion = '3.0' 34 | 35 | # Name of the Windows PowerShell host required by this module 36 | # PowerShellHostName = '' 37 | 38 | # Minimum version of the Windows PowerShell host required by this module 39 | # PowerShellHostVersion = '' 40 | 41 | # Minimum version of Microsoft .NET Framework required by this module 42 | # DotNetFrameworkVersion = '' 43 | 44 | # Minimum version of the common language runtime (CLR) required by this module 45 | # CLRVersion = '' 46 | 47 | # Processor architecture (None, X86, Amd64) required by this module 48 | # ProcessorArchitecture = '' 49 | 50 | # Modules that must be imported into the global environment prior to importing this module 51 | # RequiredModules = @() 52 | 53 | # Assemblies that must be loaded prior to importing this module 54 | # RequiredAssemblies = @() 55 | 56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 57 | # ScriptsToProcess = @() 58 | 59 | # Type files (.ps1xml) to be loaded when importing this module 60 | # TypesToProcess = @() 61 | 62 | # Format files (.ps1xml) to be loaded when importing this module 63 | # FormatsToProcess = 'ServerOpsMenu.Format.ps1xml' 64 | 65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 66 | # NestedModules = @() 67 | 68 | # Functions to export from this module 69 | FunctionsToExport = 'Menu' 70 | 71 | # Cmdlets to export from this module 72 | # CmdletsToExport = '*' 73 | 74 | # Variables to export from this module 75 | # VariablesToExport = '*' 76 | 77 | # Aliases to export from this module 78 | # AliasesToExport = '*' 79 | 80 | # DSC resources to export from this module 81 | # DscResourcesToExport = @() 82 | 83 | # List of all modules packaged with this module 84 | # ModuleList = @() 85 | 86 | # List of all files packaged with this module 87 | # FileList = @() 88 | 89 | # 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. 90 | PrivateData = @{ 91 | 92 | PSData = @{ 93 | 94 | # Tags applied to this module. These help with module discovery in online galleries. 95 | Tags = @("Menu", "Console", "Interactive") 96 | 97 | # A URL to the license for this module. 98 | # LicenseUri = '' 99 | 100 | # A URL to the main website for this project. 101 | ProjectUri = 'https://github.com/chrisseroka/ps-menu' 102 | 103 | # A URL to an icon representing this module. 104 | # IconUri = '' 105 | 106 | # ReleaseNotes of this module 107 | # ReleaseNotes = '' 108 | 109 | } # End of PSData hashtable 110 | 111 | } # End of PrivateData hashtable 112 | 113 | # HelpInfo URI of this module 114 | # HelpInfoURI = '' 115 | 116 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 117 | # DefaultCommandPrefix = '' 118 | 119 | } 120 | 121 | -------------------------------------------------------------------------------- /ps-menu.psm1: -------------------------------------------------------------------------------- 1 | function DrawMenu { 2 | param ($menuItems, $menuPosition, $Multiselect, $selection) 3 | $l = $menuItems.length 4 | for ($i = 0; $i -le $l;$i++) { 5 | if ($menuItems[$i] -ne $null){ 6 | $item = $menuItems[$i] 7 | if ($Multiselect) 8 | { 9 | if ($selection -contains $i){ 10 | $item = '[x] ' + $item 11 | } 12 | else { 13 | $item = '[ ] ' + $item 14 | } 15 | } 16 | if ($i -eq $menuPosition) { 17 | Write-Host "> $($item)" -ForegroundColor Green 18 | } else { 19 | Write-Host " $($item)" 20 | } 21 | } 22 | } 23 | } 24 | 25 | function Toggle-Selection { 26 | param ($pos, [array]$selection) 27 | if ($selection -contains $pos){ 28 | $result = $selection | where {$_ -ne $pos} 29 | } 30 | else { 31 | $selection += $pos 32 | $result = $selection 33 | } 34 | $result 35 | } 36 | 37 | function Menu { 38 | param ([array]$menuItems, [switch]$ReturnIndex=$false, [switch]$Multiselect) 39 | $vkeycode = 0 40 | $pos = 0 41 | $selection = @() 42 | if ($menuItems.Length -gt 0) 43 | { 44 | try { 45 | $startPos = [System.Console]::CursorTop 46 | [console]::CursorVisible=$false #prevents cursor flickering 47 | DrawMenu $menuItems $pos $Multiselect $selection 48 | While ($vkeycode -ne 13 -and $vkeycode -ne 27) { 49 | $press = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown") 50 | $vkeycode = $press.virtualkeycode 51 | If ($vkeycode -eq 38 -or $press.Character -eq 'k') {$pos--} 52 | If ($vkeycode -eq 40 -or $press.Character -eq 'j') {$pos++} 53 | If ($vkeycode -eq 36) { $pos = 0 } 54 | If ($vkeycode -eq 35) { $pos = $menuItems.length - 1 } 55 | If ($press.Character -eq ' ') { $selection = Toggle-Selection $pos $selection } 56 | if ($pos -lt 0) {$pos = 0} 57 | If ($vkeycode -eq 27) {$pos = $null } 58 | if ($pos -ge $menuItems.length) {$pos = $menuItems.length -1} 59 | if ($vkeycode -ne 27) 60 | { 61 | $startPos = [System.Console]::CursorTop - $menuItems.Length 62 | [System.Console]::SetCursorPosition(0, $startPos) 63 | DrawMenu $menuItems $pos $Multiselect $selection 64 | } 65 | } 66 | } 67 | finally { 68 | [System.Console]::SetCursorPosition(0, $startPos + $menuItems.Length) 69 | [console]::CursorVisible = $true 70 | } 71 | } 72 | else { 73 | $pos = $null 74 | } 75 | 76 | if ($ReturnIndex -eq $false -and $pos -ne $null) 77 | { 78 | if ($Multiselect){ 79 | return $menuItems[$selection] 80 | } 81 | else { 82 | return $menuItems[$pos] 83 | } 84 | } 85 | else 86 | { 87 | if ($Multiselect){ 88 | return $selection 89 | } 90 | else { 91 | return $pos 92 | } 93 | } 94 | } 95 | 96 | --------------------------------------------------------------------------------