Skip to content

dan-metzler/PSYamlTUI

Repository files navigation

PSYamlTUI

Terminal menus that don't make you want to quit. (Unless you press Q.)

PSYamlTUI turns a simple YAML file into a fully navigable terminal UI - recursive submenus, automatic terminal detection, and safe script execution included.

Build Tests Gallery Downloads License PowerShell
build tests psgallery downloads license ps-version

PSYamlTUI Demo



Features

Feature What it gives you
YAML-driven menus Define your menu once in YAML and launch it anywhere with a single command.
Recursive submenus Nest as deep as needed via inline children or external import files.
Three rendering tiers ANSI+Unicode, Write-Host+Unicode, and Write-Host+ASCII for modern and legacy terminals.
Automatic terminal detection PSYamlTUI detects terminal capabilities automatically.
Five border styles Choose Single, Double, Rounded, Heavy, or ASCII.
Fully customizable themes Pass a theme hashtable or point -ThemePath to a YAML/JSON theme file.
Remappable key bindings Map navigation keys to match your workflow.
Token substitution Use {{key}} placeholders with values from vars.yaml, -VarsPath, or -Context.
Before hooks Gate branch access or leaf execution with reusable hook functions.
Status bar Show context such as connected user, environment, or any runtime metadata.
Safe execution Calls scripts/functions via & with path validation, root-jail enforcement, and injection checks.
PowerShell 5.1 and 7+ Works on both Windows PowerShell and PowerShell Core.

Installation

PowerShell 5.1 or higher is required. No other dependencies needed → everything is bundled.

# Install from PowerShell Gallery
Install-Module -Name PSYamlTUI -Scope CurrentUser

# Import
Import-Module PSYamlTUI

# Verify
Get-Command -Module PSYamlTUI

Quick start

Step 1: Create a basic menu file

Create menu.yaml in your current folder:

menu:
  title: "Main Menu"
  items:
    - label: "Show date"
      call: "Get-Date"
    - label: "Exit"
      exit: true

Step 2: Define optional status and key bindings

$statusData = @{
    'Connected As' = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name -split '\\')[-1]
    'Environment'  = 'Production'
}

$keyBindings = @{
    Up     = [System.ConsoleKey]::UpArrow
    Down   = [System.ConsoleKey]::DownArrow
    Select = [System.ConsoleKey]::RightArrow
    Back   = [System.ConsoleKey]::LeftArrow
    Quit   = 'X'
    Home   = 'H'
}

Step 3: Launch the menu

Start-Menu -Path .\menu.yaml -BorderStyle Rounded -StatusData $statusData -KeyBindings $keyBindings

Run the included launchers from repo root

This is optional and intended for testing or demos. It is not a required setup step.

If you cloned the repository and your terminal is at the root of the cloned folder, you can run the example launchers directly with full relative paths:

# From repo root
.\Docs\Examples\MenuLaunchers\Simple-Example.ps1
.\Docs\Examples\MenuLaunchers\Intermediate-Example.ps1
.\Docs\Examples\MenuLaunchers\Advanced-Example.ps1

If you create your own launcher in this folder (for example menus.ps1), run it the same way:

.\Docs\Examples\MenuLaunchers\menus.ps1

VS Code setup

Install the Red Hat YAML extension (redhat.vscode-yaml) from the VS Code marketplace.

Without it the YAML files open as plain text -- no validation, no autocompletion, no hover docs. With it, the extension reads menu.schema.json at the repo root and gives you:

  • Validation -- unknown keys, missing required keys, and wrong value types are flagged inline
  • Autocompletion -- Ctrl+Space offers valid properties at any point in a menu or vars file
  • Hover documentation -- hover over any key to see what it does

The extension is already configured via .vscode/settings.json checked into the repo. No manual setup is needed beyond installing the extension.

What menu.schema.json covers

The schema applies to menu files, submenu import files, and vars files:

File pattern What it validates
*.menu.yaml Root menu files -- requires menu: wrapper with title: and items:
**/import-*.yaml Submenu import files -- top-level items: only, no menu: wrapper
vars.yaml / *.vars.yaml Vars files -- top-level vars: map, scalar values only

Theme files (*.theme.yaml) use a flat key/value structure and are not covered by this schema.

Why single quotes matter

.vscode/settings.json sets yaml.format.singleQuote: true so the YAML formatter always uses single-quoted strings. This is important because YAML processes the two quote styles differently:

Quote style Escape processing Safe for Windows paths?
'single' None -- every character is literal Yes
"double" Yes -- \n, \t, \\, etc. are interpreted No -- bare \ can corrupt silently

Always use single quotes for values in your menu and vars files. Backslashes, curly braces, and other special characters are passed through exactly as written:

# Safe -- single-quoted, backslash is literal
scriptsPath: 'C:\scripts\myapp'

# Unsafe -- double-quoted, \s and \m are undefined escape sequences
scriptsPath: "C:\scripts\myapp"

The formatter enforces this automatically on save, so you generally do not need to think about it.


Start-Menu Parameter reference

Parameter Type Default Description
-Path string .\menu.yaml Root menu YAML path
-VarsPath string auto .\vars.yaml Vars YAML path (vars: map)
-Context hashtable none Runtime token values (wins over vars)
-BorderStyle string Single Single, Double, Rounded, Heavy, ASCII
-KeyBindings hashtable defaults Key map for Up/Down/Select/Back/Quit/Home
-Theme hashtable defaults Theme overrides as ConsoleColor values
-ThemePath string none YAML/JSON theme file path
-StatusData hashtable none Status bar key/value pairs
-Timer switch off Shows elapsed execution time after leaf actions

Guides


YAML schema

PSYamlTUI infers the node type from the keys present -- you never have to declare it explicitly.

menu:
  title: "Main Menu"
  items:
    - label: "Reports"
      description: "Run system reports"
      children:
        - label: "System Info"
          call: "./scripts/Show-SystemInfo.ps1"
          confirm: true
        - label: "Process Report"
          call: "./scripts/Get-ProcessReport.ps1"
    - label: "Settings"
      import: "./menus/settings.yaml"
    - label: "Exit"
      exit: true

Node types

Keys present Node type Behavior
exit: true EXIT Cleanly quits the menu
children or import BRANCH Drills into a submenu
call with .ps1 or path chars SCRIPT Executes a script via safe & call
call with no extension FUNCTION Calls a whitelisted PowerShell function

Valid node properties

Key Required Notes
label yes Display text shown in the menu
exit no Set to true to signal a clean quit
children no Inline list of submenu items
import no Relative path to an external YAML submenu file
call no Script path or function name to execute
params no Hashtable of parameters splatted to call
confirm no If true, prompts Y/N before executing
description no Brief subtitle shown under the item while navigating the menu
details no Longer text shown in the action banner just before the script or function runs. SCRIPT and FUNCTION nodes only. Supports multi-line YAML block scalars (|)
hotkey no Single character shortcut, case-insensitive
before no Pre-execution hook, hook object, or ordered list of hooks

Navigation

All keys are remappable via the -KeyBindings parameter -- see the Quick Start section for an example.

Key Action
Up / Down Navigate items
Enter / Right Arrow Select / drill into submenu
Left Arrow / Escape Go back one level
Q Quit
H Jump to home / root menu
Home / End Jump to first / last item
PageUp / PageDown Scroll long menus

Index Navigation Mode

For users on high-latency connections, slow hardware, or remote/VDI sessions, the default keybinding navigation (using arrow keys to move the selection) can feel sluggish because the UI re-renders on every navigation keypress.

Index Navigation Mode eliminates this by letting you select menu items directly by number:

  • Each menu item is prefixed with its 1-based index (e.g., 1., 2., ...)
  • Just type the number of the item you want and press Enter (or wait a moment)
  • The menu does not re-render as you type; it only updates when you make a selection or use a control key
  • Back, Home, and Quit keys still work as usual
  • Hotkeys and description sub-lines are suppressed in this mode for clarity
  • The selected highlight is not shown—your choice is immediate
  • Only the Back, Quit, and Home key bindings are shown in the footer in this mode. Up, Down, and Select bindings are ignored and not displayed.

When to use it:

  • If you notice too much lag or flicker when navigating with arrow keys
  • If you're on a remote desktop, SSH, or VDI session
  • If your terminal is slow to redraw

How to enable:

Pass the -IndexNavigation switch to Start-Menu:

Start-Menu -Path .\menu.yaml -IndexNavigation

You can combine this with custom key bindings, status data, or themes as usual.

Example:

Start-Menu -Path .\menu.yaml -IndexNavigation -KeyBindings $keyBindings -StatusData $statusData

In index mode, the UI is more responsive on slow systems because it avoids unnecessary TUI refreshing while you are choosing an item.

Themes

PSYamlTUI Demo

PSYamlTUI always starts from its built-in default theme values. You can either pass a hashtable to -Theme or pass a YAML/JSON file to -ThemePath.

The runnable examples in Docs/Examples/README.md use external YAML theme files in:

Theme hashtable shape

Any key you omit falls back to the Default theme value, so partial overrides are totally fine.

@{
    Border          = 'DarkCyan'   # box-drawing characters and frame
    Title           = 'White'      # menu title text
    Breadcrumb      = 'DarkGray'   # breadcrumb trail
    ItemDefault     = ''           # unselected items -- empty string uses terminal default
    ItemSelected    = 'Yellow'     # highlighted item
    ItemHotkey      = 'DarkGray'   # hotkey character
    ItemDescription = 'DarkGray'   # subtitle shown under selected item
    StatusLabel     = 'DarkGray'   # status bar key names
    StatusValue     = 'Cyan'       # status bar values
    FooterText      = 'DarkGray'   # footer separator and label text
}

All values are [System.ConsoleColor] names.

Simplest YAML theme file:

Border: "DarkGreen"
Title: "White"
Breadcrumb: "Gray"
ItemDefault: ""
ItemSelected: "Yellow"
ItemHotkey: "Cyan"
ItemDescription: "Gray"
StatusLabel: "DarkGray"
StatusValue: "Cyan"
FooterText: "Gray"

Also supported:

theme:
  Border: "DarkGreen"
  Title: "White"

Hashtable example:

$theme = @{
    Border       = 'DarkCyan'
    Title        = 'White'
    ItemSelected = 'Yellow'
}

Start-Menu -Path .\menu.yaml -Theme $theme

Theme file example:

Start-Menu -Path .\menu.yaml -ThemePath .\theme.yaml

Contributing

If you found a bug or have an idea, opening an issue first is helpful (but not required) so we can align on scope.

See CONTRIBUTING.md for more details.


License

This project is licensed under the MIT License.


Author

Built by Dan Metzler.

About

No description or website provided.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors