Last active
March 20, 2025 00:57
-
-
Save codebykyle/b241e723ddd495aac4eaad9b8aa7c6bc to your computer and use it in GitHub Desktop.
Windows Terminal Split Pane Powershell Script - v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Collections.Generic | |
# Encapsulate an arbitrary command | |
class PaneCommand { | |
[string]$Command | |
PaneCommand() { | |
$this.Command = ""; | |
} | |
PaneCommand([string]$command) { | |
$this.Command = $command | |
} | |
[string]GetCommand() { | |
return $this.Command | |
} | |
[string]ToString() { | |
return $this.GetCommand(); | |
} | |
} | |
# A proxy for Split Pane which takes in a command to run inside the pane | |
class Pane : PaneCommand { | |
[string]$ProfileName; | |
[string]$Orientation | |
[decimal]$Size; | |
Pane([string]$command) : base($command) { | |
$this.Orientation = ''; | |
$this.ProfileName = "Windows Powershell" | |
$this.Size = 0.5; | |
} | |
Pane([string]$command, [string]$orientation) : base($command) { | |
$this.Orientation = $orientation; | |
$this.ProfileName = "Windows Powershell" | |
$this.Size = 0.5; | |
} | |
Pane([string]$command, [string]$orientation, [decimal]$size) : base($command) { | |
$this.Orientation = $orientation; | |
$this.ProfileName = "Windows Powershell" | |
$this.size = $size; | |
} | |
Pane([string]$ProfileName, [string]$command, [string]$orientation, [decimal]$size) : base($command) { | |
$this.Orientation = $orientation; | |
$this.ProfileName = $ProfileName; | |
$this.size = $size; | |
} | |
[string]GetCommand() { | |
return 'split-pane --size {0} {1} -p "{2}" -c {3}' -f $this.Size, $this.Orientation, $this.ProfileName, $this.Command | |
} | |
} | |
class TargetPane : PaneCommand { | |
[int]$SelectedIndex; | |
TargetPane([int]$index) { | |
$this.SelectedIndex = $index; | |
} | |
[string]GetCommand() { | |
return "focus-pane --target={0}" -f $this.SelectedIndex; | |
} | |
} | |
class MoveFocus : PaneCommand { | |
[string]$direction; | |
MoveFocus([string]$direction) { | |
$this.direction = $direction; | |
} | |
[string]GetCommand() { | |
return 'move-focus --direction {0}' -f $this.direction; | |
} | |
} | |
class PaneManager : PaneCommand { | |
[string]$InitialCommand; | |
[List[PaneCommand]]$PaneCommands; | |
[string]$ProfileName; | |
[string]$DefaultOrientation; | |
[double]$DefaultSize; | |
PaneManager() { | |
$this.PaneCommands = [List[PaneCommand]]::new(); | |
$this.ProfileName = "Microsoft Powershell"; | |
$this.DefaultOrientation = '-H'; | |
$this.DefaultSize = 0.5; | |
$this.InitialCommand = "--maximized" | |
} | |
PaneManager([string]$ProfileName) { | |
$this.ProfileName = $ProfileName; | |
$this.DefaultOrientation = '-H'; | |
$this.DefaultSize = 0.5; | |
} | |
[PaneManager]SetInitialCommand([string]$command) { | |
$this.InitialCommand = $command; | |
return $this; | |
} | |
[PaneManager]SetProfileName([string]$name) { | |
$this.ProfileName = $name; | |
return $this; | |
} | |
[PaneManager]SetDefaultOrientation([string]$orientation) { | |
$this.DefaultOrientation = $orientation; | |
return $this; | |
} | |
[PaneManager]SetDefaultSize([double]$size) { | |
$this.DefaultSize = $size; | |
return $this; | |
} | |
[PaneManager]SetOptions([string]$name, [string]$orientation, [double]$size) { | |
return $this.SetProfileName($name) | |
.SetDefaultOrientation($orientation) | |
.SetDefaultSize($size); | |
} | |
[PaneManager]AddPane([PaneManager]$manager) { | |
$manager.SetInitialCommand(''); | |
$this.AddCommand($manager); | |
return $this; | |
} | |
[PaneManager]AddCommand([PaneCommand]$command) { | |
$this.PaneCommands.Add($command); | |
return $this; | |
} | |
[PaneManager]AddPane([string]$command, [string]$orientation, [decimal]$size) { | |
$newPane = $this.MakePane( | |
$this.ProfileName, | |
$command, | |
$orientation, | |
$size | |
); | |
$this.AddCommand($newPane); | |
return $this; | |
} | |
[Pane]MakePane($ProfileName, $command, $orientation, $size) { | |
$newPane = [Pane]::new($ProfileName, $command, $orientation, $size); | |
return $newPane; | |
} | |
[PaneManager]TargetPane([int]$index) { | |
$targetCommand = [TargetPane]::new($index) | |
$this.AddCommand($targetCommand) | |
return $this; | |
} | |
[PaneManager]MoveFocus([string]$direction) { | |
$targetCommand = [MoveFocus]::new($direction) | |
$this.AddCommand($targetCommand) | |
return $this; | |
} | |
[int]GetPaneCount() { | |
$count = 0; | |
foreach ($command in $this.PaneCommands) | |
{ | |
if ($command -is [PaneManager]) { | |
$count += $command.GetPaneCount(); | |
} elseif ($command -is [PaneCommand]) { | |
$count += 1; | |
} | |
} | |
return $count; | |
} | |
[string]GetCommand() { | |
$joinedCommands = $this.PaneCommands -join "; "; | |
if ($joinedCommands -eq "") { | |
return $this.InitialCommand; | |
} | |
$finalCommand = if ($this.InitialCommand -ne "") { "{0}; {1}" -f $this.InitialCommand, $joinedCommands} else { $joinedCommands }; | |
return $finalCommand | |
} | |
} | |
# Your script here; see Gist comments |
For those using non US-EN locales, specifically where the thousands separator is "." instead of ",", use the following code bit as a work around:
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US") $culture.NumberFormat.NumberDecimalSeparator = "." $culture.NumberFormat.NumberGroupSeparator = "," [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
Thanks for your work, now I can make my workflow automatically.
Thanks buddy i use this:
Set-Alias -Name t -Value tabRenameFN
function tabRenameFN {
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$culture.NumberFormat.NumberDecimalSeparator = "."
$culture.NumberFormat.NumberGroupSeparator = ","
[System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
$paneManagerClass = ([PaneManager]::new()).
AddPane("powershell", '-H', 0.3).
AddPane("powershell", '-V', 0.5).
MoveFocus("up");
start wt $paneManagerClass;
}
running t command and enjoy.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello everyone. Thank you for all the stars and the support. I have added some functionality to this. I added a fluent interface, support for different types of consoles, and nested windows.
Usage
Copy the above script into a file. Configure your script at the bottom to layout your panes, and then run the script.
The way this script works is by constructing a command to start Windows Terminal. We have a PaneManager class that makes adding repetitive commands a little easier. You can either use this script to generate a command for you, or run this directly through wt.
The following examples assume you have pasted the above script into a file, and you are editing, the very bottom of the file
Basic Usage
To get started, you need to instantiate a PaneManager class, which you can do so like this:
You also need to give the Pane Manager an initial script to run. This will run in the first window, before any splits are done. For example, to create a simple, single-pane SSH connection, you can run:
This will result in a single pane, running the command you specified:

To split a window, we can add a Pane. We need to tell it the direction to split on, and how big it should be. To add another SSH connection with a 50/50 split:
The second argument, '-V', means that we want to split the current pane, vertically. You can use '-H' if you want to split a pane horizontally.
You don't always have to override the initial command. Not specifying it will (currently) default to
--maximized
, which will maximize the current window, and open a local powershell window in the current directory.If I want one panel for my local computer on top, and two SSH connections, I can use the following:
Because we did not override the initial command, a default powershell window was already made. We split it in half horizontally, then split that in half vertically.
Targeting a specific pane
You will find, that if you follow this pattern long enough, you end up with a really nice Fibonacci sequence. This is because every time you add a new panel, it will cut the current box you are on, with relative size. Because of this, you can target a specific pane to cut either by its position, or its index.
results in:
You can also target a specific panel by using direction, for example, if you want to move to the "left":
Different Profiles and Settings
You can launch WSL or Command Line, or other Windows Terminals profiles by using the settings on the Pane Manager class. Set the Profile, and all windows added after that will use that profile.
Fluent Interface
This now supports a fluent interface. Rather than having to declare each command, line by line, you can chain together commands. Please note the location of the
.
in the following code. Its position is important.Nesting Panels
You can nest panels, although, its a little wonky. You should utilize the targeting for this to provide a decent result. You can use relative positioning with the
MoveFocus
command listed above.Debugging & Generating
You may not want this giant block of functions and classes in your script. The Pane Manager is eventually being converted down to a command. You can use this script to generate a command, and save that to a file, or run it from a terminal. All you need to do is output the PaneManager as a string:
produces:
--maximized; split-pane --size 0.75 -H -p "Microsoft Powershell" -c powershell connect_rbp01; split-pane --size 0.5 -V -p "Microsoft Powershell" -c powershell connect_rbp02; move-focus --direction left; split-pane --size 0.66 -H -p "Microsoft Powershell" -c powershell connect_rbp03; move-focus --direction right; split-pane --size 0.66 -H -p "Microsoft Powershell" -c powershell connect_rbp04; move-focus --direction left; split-pane --size 0.5 -H -p "Microsoft Powershell" -c powershell connect_rbp05; move-focus --direction right; split-pane --size 0.5 -H -p "Microsoft Powershell" -c powershell connect_rbp06
Full Example Usage
Which looks like:

Which generates:
Please let me know if you have any questions