GliderUI
Cross-platform Desktop GUI framework for PowerShell powered by Avalonia
Install / Use
/learn @mdgrs-mei/GliderUIREADME
GliderUI
Cross-platform Desktop GUI framework for PowerShell powered by Avalonia.
https://github.com/user-attachments/assets/aa53a0b8-d478-4897-bc82-d10c804c98b1
</div>[!NOTE] This module is in its prototyping phase. Frequent breaking changes are expected until this notice is removed.
Requirements
- PowerShell 7.4 or newer
- Windows(win-x64/win-arm64), macOS(osx-arm64) or Linux(linux-x64)
Installation
Install-PSResource -Name GliderUI
On macOS or Linux, call Enable-GLIExecution once after the installation or update using the same user account that installed the module:
# Adds execute permission to the server executable file.
Enable-GLIExecution
Quick Start
This code creates a Window that has a clickable button:
using namespace GliderUI.Avalonia.Controls
Import-Module GliderUI -Force
$win = [Window]::new()
$win.Title = 'Hello from PowerShell!'
$win.Width = 400
$win.Height = 200
$button = [Button]::new()
$button.Content = 'Click Me'
$button.HorizontalAlignment = 'Center'
$button.VerticalAlignment = 'Center'
$button.AddClick({
$button.Content = 'Clicked!'
})
$win.Content = $button
# Show() shows the window but does not block the script.
$win.Show()
$win.WaitForClosed()
If you dot-source the script and comment out $win.WaitForClosed(), you can inspect UI objects or even modify them on the terminal:
PS> $button
ClickMode : Release
HotKey :
CommandParameter :
IsDefault : False
IsCancel : False
IsPressed : False
Flyout :
Content : Click Me
ContentTemplate :
Presenter : GliderUI.Avalonia.Controls.Presenters.ContentPresenter
:
Since the API of GliderUI follows the Avalonia's API, you can read the Avalonia documentation to see what should be available. The documentation of the Button class is here for example.
You can also refer to the examples folder for script samples.
How It Works
GliderUI launches a server process GliderUI.Server that provides all the UI functionalities. The GliderUI module communicates with the server through IPC (Inter-Process Communication) to create UI elements and handle events. No Avalonia's dlls are loaded in PowerShell.
This model simplifies the script structure. You can write long-running code in event handlers without blocking GUI. It's also allowed to access properties of UI elements directly on any thread without using Dispatchers.
This works:
$status = [TextBlock]::new()
$progressBar = [ProgressBar]::new()
$button.AddClick({
$button.IsEnabled = $false
$status.Text = 'Downloading...'
1..50 | ForEach-Object {
$progressBar.Value = $_
Start-Sleep -Milliseconds 50
}
$status.Text = 'Installing...'
51..100 | ForEach-Object {
$progressBar.Value = $_
Start-Sleep -Milliseconds 50
}
$status.Text = '🎉Done!'
$button.IsEnabled = $true
})
Supported APIs
All Avalonia controls and types become accessible by adding "GliderUI" as a prefix to their namespaces.
$button = [GliderUI.Avalonia.Controls.Button]::new()
Most methods and properties are automatically generated by the source generator, but those that use the following types are not supported:
- Arrays
- Delegates
- Pointers
- Types with
reforoutmodifiers
Avalonia version
The current supported Avalonia version is 11.3.12.
Event Callback
Event callbacks are script blocks that are invoked when UI events are fired. You can register them with Add{EventName} methods of UI elements. The typical example is the Click event of a button:
$button = [Button]::new()
$argumentList = 1, 2
$button.AddClick({
param ($argumentList, $s, $e)
Write-Host "ArgumentList: $argumentList"
Write-Host "Sender: $s"
Write-Host "EventArgs: $e"
}, $argumentList)
The same code can also be written using the EventCallback class. It allows you to customize the callback behavior:
$button = [Button]::new()
$clickCallback = [EventCallback]::new()
$clickCallback.RunspaceMode = 'RunspacePoolAsyncUI'
$clickCallback.DisabledControlsWhileProcessing = $button
$clickCallback.ArgumentList = 1, 2
$clickCallback.ScriptBlock = {
param ($argumentList, $s, $e)
Write-Host "ArgumentList: $argumentList"
Write-Host "Sender: $s"
Write-Host "EventArgs: $e"
}
$button.AddClick($clickCallback)
The RunspaceMode property controls where and how the script block runs.
$clickCallback.RunspaceMode = 'RunspacePoolAsyncUI'
There are three runspace modes, MainRunspaceAsyncUI, MainRunspaceSyncUI, and RunspacePoolAsyncUI.
MainRunspaceAsyncUI (Default)
The default value of RunspaceMode is MainRunspaceAsyncUI which means that the script block runs in the runspace where the script block is created (Main runspace). The script block can be a bound script block and sees the global or script scope variables in the runspace.
AsyncUI means that the callbacks do not block the UI thread on the server side. Even if a callback takes long time to finish, the UI stays responsive. If a button is pressed while the previous callback is running, the new callback is queued and processed after the previous one completes. If this behavior is not desirable, you can specify controls that are disabled on the server side while the event callback is running:
$clickCallback.DisabledControlsWhileProcessing = $button
It is a good practice to set the DisabledControlsWhileProcessing for long-running callbacks to avoid unintuitive queuing.
There is one callback queue per runspace where GliderUI module is loaded, and callbacks in the queue are typically processed inside Window.WaitForClosed method or {AwaitableType}.WaitForCompleted method. Please see MultipleRunspaces.ps1 for an example of multi-runspace scenario.
MainRunspaceSyncUI
Callbacks in MainRunspaceSyncUI mode run in the main runspace just like those with MainRunspaceAsyncUI, but they block the UI thread on the server side until they complete. Because they block the UI thread, it is guaranteed that no other events are triggered while the callback is running (No need to set DisabledControlsWhileProcessing).
RunspacePoolAsyncUI
Callbacks in RunspacePoolAsyncUI mode are handled in parallel by multiple runspaces in the runspace pool. This mode is ideal for long-running callbacks that must keep other callbacks responsive during execution. You can specify the number of runspaces in the pool and the script block that defines the global variables and functions in the runspaces:
Set-GLIRunspacePoolOption -RunspaceCount 5 -InitializationScript {
param ($ScriptRoot)
$globalVar = 'Global variable in the runspace.'
function GlobalFunction() {
'Global function in the runspace.'
}
} -InitializationScriptArgumentList $PSScriptRoot
Note that the GliderUI module is automatically loaded in each runspace.
Since the callbacks are executed in parallel, you should pass variables via ArgumentList and handle thread safety just as you would with Start-ThreadJob. See CancelLongRunningEventCallback.ps1 as a basic example, and MultipleProgressBars.ps1 as an example of multiple concurrent tasks.
AvaloniaRuntimeXamlLoader
Instead of creating UI elements by code, you can also create them by loading XAML similar to WPF in PowerShell. You can search for an UI element by the FindControl method of Control and add event handlers from PowerShell. Note that you can't use x:Class attributes or code-behind binding in XAML.
using namespace GliderUI.Avalonia.Markup.Xaml
$xamlString = @'
<Window
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button x:Name="button" Content="Click Me" HorizontalAlignment="Center" />
</Window>
'@
$win = [AvaloniaRuntimeXamlLoader]::Parse($xamlString, $null)
$win.Width = 400
$win.Height = 200
$button = $win.FindControl('button')
$button.AddClick({
$button.Content = 'Clicked!'
})
$win.Show()
$win.WaitForClosed()
Contributing
Code of Conduct
Please read our Code of Conduct to foster a welcoming environment. By participating in this project, you are expected to uphold this code.
Have a question or want to showcase something?
Please come to our Discussions page and avoid filing an issue to ask a question.
Want to file an issue or make a PR?
Please see our Contribution Guidelines.
Credits
GliderUI uses:
- Avalonia<br>https://github.com/AvaloniaUI/Avalonia
- vs-StreamJsonRpc<br>https://github.com/microsoft/vs-streamjsonrpc
Related Skills
node-connect
352.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
