Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
63ff5a6
WIP: Builds but doesn't run
tig Nov 13, 2025
ab76e4a
WIP: Got it basically working.
tig Nov 13, 2025
8eccca8
WIP: Fixing bugs - -Filter is not working right
tig Nov 14, 2025
5795efa
Fixed Select All bug.
tig Nov 14, 2025
9af07aa
Code cleanup
tig Nov 14, 2025
4245236
WIP: Starting to get SOT working.
tig Nov 14, 2025
5fd9efd
Got SOT working + code cleanup
tig Nov 14, 2025
1052c39
Code modernization and cleanup.
tig Nov 14, 2025
9f4bc39
Refactor ConsoleGui to OutConsoleGridView
tig Nov 14, 2025
ee84e8a
Reverted cmdlet to ConsoleGridView
tig Nov 14, 2025
c22ca70
Massive OutConsoleGridView and ShowObjectTree refactor to prepare for…
tig Nov 14, 2025
b0cdb95
Enhance null checks, caching, and code readability
tig Nov 14, 2025
b8dbaa6
Refactor runspace creation in TypeGetter.cs
tig Nov 14, 2025
c5f7673
Refactor and enhance object formatting in OCGV
tig Nov 14, 2025
f8eec62
Refactor and enhance grid view rendering logic
tig Nov 15, 2025
aac40e6
Moved header into ListView's Padding.
tig Nov 15, 2025
f14ce81
WIP: Added -AllProperties option
tig Nov 15, 2025
71cca64
Better use of v2 tech + added -AllProperties
tig Nov 15, 2025
1432452
Ensure explicit List conversion for dataTableColumns
tig Nov 15, 2025
f23c77f
Update to .NET 10 and Terminal.Gui 2.0 API
tig Jan 12, 2026
8ee4bc1
updated to latest TG
tig Jan 16, 2026
55f693f
Merge branch 'terminal_gui_v2' of tig:tig/GraphicalTools into termina…
tig Jan 28, 2026
627ae55
Tons of fixes. Code cleanup. 99% working.
tig Jan 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 48 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,49 @@
module/
out/
bin/
obj/
*.swp
*.*~
project.lock.json
.DS_Store
*.pyc
nupkg/

# Rider
.idea

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
msbuild.log
msbuild.err
msbuild.wrn

publish/
*.sln

#Module build
module/

# Visual Studio 2015
.vs/

# ingore downloaded .NET
.dotnet

# Ignore package
Microsoft.PowerShell.GraphicalTools.zip
Microsoft.PowerShell.ConsoleGuiTools.zip

# git artifacts
*.orig
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}
19 changes: 11 additions & 8 deletions ConsoleGuiTools.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ task Build {

Push-Location src/Microsoft.PowerShell.ConsoleGuiTools
Invoke-BuildExec { & dotnet publish --configuration $Configuration --output publish }
$Assets = $(
"./publish/Microsoft.PowerShell.ConsoleGuiTools.dll",
"./publish/Microsoft.PowerShell.ConsoleGuiTools.psd1",
"./publish/Microsoft.PowerShell.OutGridView.Models.dll",
"./publish/Terminal.Gui.dll",
"./publish/NStack.dll")
$Assets | ForEach-Object {
Copy-Item -Force -Path $_ -Destination ../../module

# Copy all DLLs except PowerShell SDK dependencies (those are provided by PowerShell itself)
Get-ChildItem "./publish/*.dll" | Where-Object {
$_.Name -notlike "System.Management.Automation.dll" -and
$_.Name -notlike "Microsoft.PowerShell.Commands.Diagnostics.dll" -and
$_.Name -notlike "Microsoft.Management.Infrastructure.CimCmdlets.dll"
} | ForEach-Object {
Copy-Item -Force -Path $_.FullName -Destination ../../module
}

# Copy the module manifest
Copy-Item -Force -Path "./publish/Microsoft.PowerShell.ConsoleGuiTools.psd1" -Destination ../../module
Pop-Location

$Assets = $(
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> -->
</PropertyGroup>
</Project>
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ItemGroup>
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.7" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Nstack.Core" Version="1.1.1" />
<PackageVersion Include="Terminal.Gui" Version="1.17.1" />
<PackageVersion Include="System.Management.Automation" Version="7.4.7" />
<!--<PackageVersion Include="Terminal.Gui" Version="2.0.0-develop.4947" />-->
</ItemGroup>
</Project>
</Project>
36 changes: 36 additions & 0 deletions GraphicalTools.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.3.11206.111 d18.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.ConsoleGuiTools", "src\Microsoft.PowerShell.ConsoleGuiTools\Microsoft.PowerShell.ConsoleGuiTools.csproj", "{C0749375-3F76-9F36-9A4D-6857B5504C9A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.OutGridView.Models", "src\Microsoft.PowerShell.OutGridView.Models\Microsoft.PowerShell.OutGridView.Models.csproj", "{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C0749375-3F76-9F36-9A4D-6857B5504C9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0749375-3F76-9F36-9A4D-6857B5504C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0749375-3F76-9F36-9A4D-6857B5504C9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0749375-3F76-9F36-9A4D-6857B5504C9A}.Release|Any CPU.Build.0 = Release|Any CPU
{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C0749375-3F76-9F36-9A4D-6857B5504C9A} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{D5EDF10B-A646-FC2C-FF3C-B7F2C1814863} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7EA28E35-5572-47D2-B03E-5DB79D82BEBC}
EndGlobalSection
EndGlobal
7 changes: 7 additions & 0 deletions GraphicalTools.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PS/@EntryIndexedValue">PS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=BUGBUG/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ocgv/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.405",
"version": "10.0.101",
"rollForward": "latestFeature",
"allowPrerelease": false
}
Expand Down
3 changes: 2 additions & 1 deletion nuget.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<configuration>
<packageSources>
<clear />
<add key="PowerShellCore_PublicPackages" value="https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- <add key="PowerShellCore_PublicPackages" value="https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v3/index.json" /> -->
</packageSources>
</configuration>
152 changes: 152 additions & 0 deletions src/Microsoft.PowerShell.ConsoleGuiTools/CachedMemberResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Microsoft.PowerShell.ConsoleGuiTools;

/// <summary>
/// Represents a cached reflection result for a property or field member, including its value and collection details.
/// </summary>
internal sealed class CachedMemberResult
{
#region Fields

private readonly string? _representation;
private List<CachedMemberResultElement>? _valueAsList;

#endregion

#region Properties

/// <summary>
/// Gets or sets the member information (property or field) that was accessed.
/// </summary>
public MemberInfo Member { get; set; }

/// <summary>
/// Gets or sets the value retrieved from the member.
/// </summary>
public object? Value { get; set; }

/// <summary>
/// Gets or sets the parent object that contains this member.
/// </summary>
public object Parent { get; set; }

/// <summary>
/// Gets a value indicating whether this member's value is a collection.
/// </summary>
public bool IsCollection => _valueAsList != null;

/// <summary>
/// Gets the collection elements if this member's value is a collection; otherwise, <see langword="null" />.
/// </summary>
public IReadOnlyCollection<CachedMemberResultElement>? Elements => _valueAsList?.AsReadOnly();

#endregion

#region Constructor

/// <summary>
/// Initializes a new instance of the <see cref="CachedMemberResult" /> class by reflecting on the specified member.
/// </summary>
/// <param name="parent">The parent object containing the member.</param>
/// <param name="mem">The member information to retrieve the value from.</param>
public CachedMemberResult(object parent, MemberInfo mem)
{
Parent = parent;
Member = mem;

try
{
if (mem is PropertyInfo p)
Value = p.GetValue(parent);
else if (mem is FieldInfo f)
Value = f.GetValue(parent);
else
throw new NotSupportedException($"Unknown {nameof(MemberInfo)} Type");

_representation = ValueToString();
}
catch (Exception)
{
Value = _representation = "Unavailable";
}
}

#endregion

#region Overrides

/// <summary>
/// Returns a string representation of this member in the format "MemberName: value".
/// </summary>
/// <returns>A formatted string showing the member name and value.</returns>
public override string ToString() => Member.Name + ": " + _representation;

#endregion

#region Private Methods

/// <summary>
/// Converts the member's value to a string representation, detecting collections and formatting them appropriately.
/// </summary>
/// <returns>A string representation of the value.</returns>
private string? ValueToString()
{
if (Value == null)
return "Null";

try
{
if (IsCollectionOfKnownTypeAndSize(out var elementType, out var size))
return $"{elementType!.Name}[{size}]";
}
catch (Exception)
{
return Value?.ToString();
}

return Value?.ToString();
}

/// <summary>
/// Determines whether the value is a collection of a known type and caches the collection elements.
/// </summary>
/// <param name="elementType">When this method returns, contains the element type if the value is a homogeneous collection; otherwise, <see langword="null" />.</param>
/// <param name="size">When this method returns, contains the size of the collection if applicable; otherwise, 0.</param>
/// <returns><see langword="true" /> if the value is a collection of a single known type; otherwise, <see langword="false" />.</returns>
private bool IsCollectionOfKnownTypeAndSize(out Type? elementType, out int size)
{
elementType = null;
size = 0;

if (Value is null or string)
return false;

if (Value is IEnumerable enumerable)
{
var list = enumerable.Cast<object>().ToList();

var types = list.Where(v => v != null).Select(v => v!.GetType()).Distinct().ToArray();

if (types.Length == 1)
{
elementType = types[0];
size = list.Count;

_valueAsList = list.Select((e, i) => new CachedMemberResultElement(e, i)).ToList();
return true;
}
}

return false;
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;

namespace Microsoft.PowerShell.ConsoleGuiTools;

/// <summary>
/// Represents an element within a collection member result, providing indexed access to collection items.
/// </summary>
internal sealed class CachedMemberResultElement
{
#region Fields

/// <summary>
/// The index of this element within the collection.
/// </summary>
public int Index;

/// <summary>
/// The value of this collection element.
/// </summary>
public object? Value;

private readonly string _representation;

#endregion

#region Constructor

/// <summary>
/// Initializes a new instance of the <see cref="CachedMemberResultElement" /> class with the specified value and index.
/// </summary>
/// <param name="value">The value of the collection element.</param>
/// <param name="index">The zero-based index of this element within the collection.</param>
public CachedMemberResultElement(object? value, int index)
{
Index = index;
Value = value;

try
{
_representation = Value?.ToString() ?? "Null";
}
catch (Exception)
{
Value = _representation = "Unavailable";
}
}

#endregion

#region Overrides

/// <summary>
/// Returns a string representation of this collection element in the format "[index]: value".
/// </summary>
/// <returns>A formatted string showing the index and value.</returns>
public override string ToString() => $"[{Index}]: {_representation}]";

#endregion
}
Loading