From 3e12d04396ea7d33bde802bda29e6974ba3724b8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 8 Jan 2026 16:04:19 -0800 Subject: [PATCH 01/38] feat: MarkX.get_Lexicon ( Fixes #25 ) --- Types/MarkX/get_Lexicon.ps1 | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Types/MarkX/get_Lexicon.ps1 diff --git a/Types/MarkX/get_Lexicon.ps1 b/Types/MarkX/get_Lexicon.ps1 new file mode 100644 index 0000000..4b9fb65 --- /dev/null +++ b/Types/MarkX/get_Lexicon.ps1 @@ -0,0 +1,87 @@ +<# +.SYNOPSIS + Gets Lexicons from Markdown +.DESCRIPTION + Gets At Protocol Lexicons defined in Markdown. + + A lexicon table must have at least three columns: + + * Property + * Type + * Description + + It must also contain a row containing the property `$type`. + + This will be considered the lexicon's type. + + Any bolded or italic fields will be considered required. + + Lexicon tables may also be preceeded by an element containing the description. +#> + +$markdownData = $this.DataSet +:nextTable foreach ($table in $markdownData.Tables) { + $isLexiconTable = $table.Columns['Property'] -and $table.Columns['Type'] -and $table.Columns['Description'] + + if (-not $isLexiconTable) { continue nextTable } + + $hasType = $table.Select("Property='`$type'") + if (-not $hasType) { + Write-Warning "Missing `$type" + continue nextTable + } + + $lexiconType = if ($hasType.Description -match '(?:[^\.\s]+\.){3}[^\.\s]+') { + $matches.0 + } + + if (-not $lexiconType) { + continue nextTable + } + + $lexiconObject = [Ordered]@{ + lexicon = 1 + id = $lexiconType + defs = @{ + main = [Ordered]@{ + type = 'record' + description = + if ($table.ExtendedProperties.Description) { + $table.ExtendedProperties.Description + } else { + $lexiconType + } + required = @() + properties = [Ordered]@{} + } + } + } + + foreach ($row in $table) { + + if ($row.Property -eq '$type') { continue } + $lexProp = [Ordered]@{} + $lexProp.type = + switch -regex ($row.type) { + '\[\]' { 'array' } + 'object' { 'object' } + 'string' { 'string' } + 'bool|switch' { 'boolean' } + 'int|number|float|double' { 'number' } + 'date' { 'datetime' } + 'ur[il]' { 'uri'} + } + + $lexProp.description = $row.Description + if ($row.Property -match '\*') { + $lexiconObject.defs.main.required += $row.Property -replace '\*' + } + elseif ($row.tr.td.outerxml -match '<(?>b|i|strong)>') { + $lexiconObject.defs.main.required += $row.Property -replace '\*' + } + + $lexiconObject.defs.main.properties[$row.Property -replace '\*'] = $lexProp + } + + $lexiconObject +} \ No newline at end of file From fa5d8de621846f74f75f3af52cca686a821ea7a1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 9 Jan 2026 00:04:46 +0000 Subject: [PATCH 02/38] feat: MarkX.get_Lexicon ( Fixes #25 ) --- MarkX.types.ps1xml | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml index 1615723..bcf9672 100644 --- a/MarkX.types.ps1xml +++ b/MarkX.types.ps1xml @@ -259,6 +259,98 @@ return ("$($this.XML.XHTML.InnerXML)" + [Environment]::NewLine) $this.XML.XHTML.InnerText + + Lexicon + + <# +.SYNOPSIS + Gets Lexicons from Markdown +.DESCRIPTION + Gets At Protocol Lexicons defined in Markdown. + + A lexicon table must have at least three columns: + + * Property + * Type + * Description + + It must also contain a row containing the property `$type`. + + This will be considered the lexicon's type. + + Any bolded or italic fields will be considered required. + + Lexicon tables may also be preceeded by an element containing the description. +#> + +$markdownData = $this.DataSet +:nextTable foreach ($table in $markdownData.Tables) { + $isLexiconTable = $table.Columns['Property'] -and $table.Columns['Type'] -and $table.Columns['Description'] + + if (-not $isLexiconTable) { continue nextTable } + + $hasType = $table.Select("Property='`$type'") + if (-not $hasType) { + Write-Warning "Missing `$type" + continue nextTable + } + + $lexiconType = if ($hasType.Description -match '(?:[^\.\s]+\.){3}[^\.\s]+') { + $matches.0 + } + + if (-not $lexiconType) { + continue nextTable + } + + $lexiconObject = [Ordered]@{ + lexicon = 1 + id = $lexiconType + defs = @{ + main = [Ordered]@{ + type = 'record' + description = + if ($table.ExtendedProperties.Description) { + $table.ExtendedProperties.Description + } else { + $lexiconType + } + required = @() + properties = [Ordered]@{} + } + } + } + + foreach ($row in $table) { + + if ($row.Property -eq '$type') { continue } + $lexProp = [Ordered]@{} + $lexProp.type = + switch -regex ($row.type) { + '\[\]' { 'array' } + 'object' { 'object' } + 'string' { 'string' } + 'bool|switch' { 'boolean' } + 'int|number|float|double' { 'number' } + 'date' { 'datetime' } + 'ur[il]' { 'uri'} + } + + $lexProp.description = $row.Description + if ($row.Property -match '\*') { + $lexiconObject.defs.main.required += $row.Property -replace '\*' + } + elseif ($row.tr.td.outerxml -match '<(?>b|i|strong)>') { + $lexiconObject.defs.main.required += $row.Property -replace '\*' + } + + $lexiconObject.defs.main.properties[$row.Property -replace '\*'] = $lexProp + } + + $lexiconObject +} + + Links From b744743eda776de222cf932ae5c906d59fcb7a2a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 8 Jan 2026 16:15:46 -0800 Subject: [PATCH 03/38] feat: MarkX.Input and Help support ( Fixes #27, Fixes #28 ) --- Types/MarkX/get_Input.ps1 | 1 + Types/MarkX/set_Input.ps1 | 120 +++++++++++++++++++++++++++++++++++ Types/MarkX/set_Markdown.ps1 | 48 +------------- 3 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 Types/MarkX/get_Input.ps1 create mode 100644 Types/MarkX/set_Input.ps1 diff --git a/Types/MarkX/get_Input.ps1 b/Types/MarkX/get_Input.ps1 new file mode 100644 index 0000000..890e9e0 --- /dev/null +++ b/Types/MarkX/get_Input.ps1 @@ -0,0 +1 @@ +return $this.'#input' \ No newline at end of file diff --git a/Types/MarkX/set_Input.ps1 b/Types/MarkX/set_Input.ps1 new file mode 100644 index 0000000..4b42b06 --- /dev/null +++ b/Types/MarkX/set_Input.ps1 @@ -0,0 +1,120 @@ +param( +[PSObject[]]$InputObject +) + +$this | Add-Member NoteProperty '#Input' $InputObject -Force + +$currentRows = @() +$allMarkdown = @(:nextInput foreach ($md in $InputObject) { + if ($md -isnot [string]) { + + if ($md -is [Management.Automation.CommandInfo]) { + $cmdHelp = if ( + $md -is [Management.Automation.FunctionInfo] -or + $md -is [Management.Automation.AliasInfo] + ) { + Get-Help -Name $md.Name + } elseif ($md -is [Management.Automation.ExternalScriptInfo]) { + Get-Help -Name $md.Source + } else { + continue nextInput + } + if ($cmdHelp) { + $md = $cmdHelp + } + } + + if ($md.pstypenames -match 'HelpInfo') { + @( + "# $($md.Name)" + if ($md.Synopsis) { + "## $($md.Synopsis)" + } + $description = $md.Description.text -join [Environment]::NewLine + if ($description) { + "### $($description)" + } + + $md.alertset.alert.text -join [Environment]::NewLine + foreach ($example in $md.examples.example) { + $exampleNumber++ + + # Combine the code and remarks + $exampleLines = + @( + $example.Code + foreach ($remark in $example.Remarks.text) { + if (-not $remark) { continue } + $remark + } + ) -join ([Environment]::NewLine) -split '(?>\r\n|\n)' # and split into lines + + # Anything until the first non-comment line is a markdown predicate to the example + $nonCommentLine = $false + $markdownLines = @() + + # Go thru each line in the example as part of a loop + $codeBlock = @(foreach ($exampleLine in $exampleLines) { + # Any comments until the first uncommentedLine are markdown + if ($exampleLine -match '^\#' -and -not $nonCommentLine) { + $markdownLines += $exampleLine -replace '^\#' -replace '^\s+' + } else { + $nonCommentLine = $true + $exampleLine + } + }) -join [Environment]::NewLine + + # Join all of our markdown lines together + $markdownLines -join [Environment]::NewLine + "~~~PowerShell" + $codeBlock + "~~~" + } + ) -join [Environment]::NewLine + continue nextInput + } + if ($md -is [ScriptBlock]) { + "
$(
+                [Web.HttpUtility]::HtmlEncode(
+                    "$md"
+                )
+            )
"
+            continue nextInput
+        } 
+        if ($md -is [Collections.IDictionary] -or 
+            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
+            $currentRows += $md            
+            continue
+        }
+    }
+    
+    if ($currentRows) {    
+        $this.ToTable($currentRows)        
+        $currentRows = @()
+    }
+
+    if ($md -match '(?>\.md|markdown)$' -and
+        (Test-Path $md -ErrorAction Ignore)
+    ) {
+        $md = Get-Content -Raw $md
+    }
+
+    $yamlheader = ''
+    if ($md -match '^---') {
+        $null, $yamlheader, $md = $in -split '---', 3
+    }
+
+    $md
+})
+
+if ($currentRows) {    
+    $allMarkdown += $this.ToTable($currentRows)
+    $currentRows = @()
+}
+
+$markdown = $allMarkdown -join [Environment]::NewLine
+
+$this | 
+    Add-Member NoteProperty '#Markdown' $Markdown -Force
+
+$this.Sync()
\ No newline at end of file
diff --git a/Types/MarkX/set_Markdown.ps1 b/Types/MarkX/set_Markdown.ps1
index f164646..b7d51e7 100644
--- a/Types/MarkX/set_Markdown.ps1
+++ b/Types/MarkX/set_Markdown.ps1
@@ -2,50 +2,4 @@ param(
 [PSObject[]]$Markdown
 )
 
-$currentRows = @()
-$allMarkdown = @(foreach ($md in $Markdown) {    
-    if ($md -isnot [string]) {        
-        if ($md -is [ScriptBlock]) {
-            $md = "
$(
-                [Web.HttpUtility]::HtmlEncode(
-                    "$md"
-                )
-            )
"
-        } 
-        if ($md -is [Collections.IDictionary] -or 
-            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
-            $currentRows += $md            
-            continue
-        }
-    }
-    
-    if ($currentRows) {    
-        $this.ToTable($currentRows)        
-        $currentRows = @()
-    }
-
-    if ($md -match '(?>\.md|markdown)$' -and
-        (Test-Path $md -ErrorAction Ignore)
-    ) {
-        $md = Get-Content -Raw $md
-    }
-
-    $yamlheader = ''
-    if ($md -match '^---') {
-        $null, $yamlheader, $md = $in -split '---', 3
-    }
-
-    $md
-})
-
-if ($currentRows) {    
-    $allMarkdown += $this.ToTable($currentRows)
-    $currentRows = @()
-}
-
-$markdown = $allMarkdown -join [Environment]::NewLine
-
-$this | 
-    Add-Member NoteProperty '#Markdown' $Markdown -Force
-
-$this.Sync()
\ No newline at end of file
+$this.Input = $Markdown

From fc4a3843abe28a7d4acce6d42baa3c406b3cb07e Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Fri, 9 Jan 2026 00:16:13 +0000
Subject: [PATCH 04/38] feat: MarkX.Input and Help support ( Fixes #27, Fixes
 #28 )

---
 MarkX.types.ps1xml | 175 +++++++++++++++++++++++++++++++++------------
 1 file changed, 129 insertions(+), 46 deletions(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index bcf9672..2230d25 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -259,6 +259,134 @@ return ("$($this.XML.XHTML.InnerXML)" + [Environment]::NewLine)
                         $this.XML.XHTML.InnerText
                     
       
+      
+        Input
+        
+                        return $this.'#input'
+                    
+        
+                        param(
+[PSObject[]]$InputObject
+)
+
+$this | Add-Member NoteProperty '#Input' $InputObject -Force
+
+$currentRows = @()
+$allMarkdown = @(:nextInput foreach ($md in $InputObject) {    
+    if ($md -isnot [string]) {
+        
+        if ($md -is [Management.Automation.CommandInfo]) {
+            $cmdHelp = if (
+                $md -is [Management.Automation.FunctionInfo] -or
+                $md -is [Management.Automation.AliasInfo]
+            ) {
+                Get-Help -Name $md.Name
+            } elseif ($md -is [Management.Automation.ExternalScriptInfo]) {
+                Get-Help -Name $md.Source
+            } else {
+                continue nextInput
+            }
+            if ($cmdHelp) {
+                $md = $cmdHelp                
+            }
+        }
+
+        if ($md.pstypenames -match 'HelpInfo') {
+            @(
+                "# $($md.Name)"
+                if ($md.Synopsis) {
+                    "## $($md.Synopsis)"
+                }                
+                $description = $md.Description.text -join [Environment]::NewLine
+                if ($description) {
+                    "### $($description)"
+                }
+                
+                $md.alertset.alert.text -join [Environment]::NewLine
+                foreach ($example in $md.examples.example) {
+                    $exampleNumber++
+                    
+                    # Combine the code and remarks
+                    $exampleLines = 
+                        @(
+                            $example.Code
+                            foreach ($remark in $example.Remarks.text) {
+                                if (-not $remark) { continue }
+                                $remark
+                            }
+                        ) -join ([Environment]::NewLine) -split '(?>\r\n|\n)' # and split into lines
+
+                    # Anything until the first non-comment line is a markdown predicate to the example
+                    $nonCommentLine = $false
+                    $markdownLines = @()
+
+                    # Go thru each line in the example as part of a loop
+                    $codeBlock = @(foreach ($exampleLine in $exampleLines) {
+                        # Any comments until the first uncommentedLine are markdown
+                        if ($exampleLine -match '^\#' -and -not $nonCommentLine) {
+                            $markdownLines += $exampleLine -replace '^\#' -replace '^\s+'
+                        } else {
+                            $nonCommentLine = $true
+                            $exampleLine
+                        }
+                    }) -join [Environment]::NewLine
+                    
+                    # Join all of our markdown lines together                        
+                    $markdownLines -join [Environment]::NewLine
+                    "~~~PowerShell"
+                    $codeBlock
+                    "~~~"
+                }
+            ) -join [Environment]::NewLine
+            continue nextInput
+        }        
+        if ($md -is [ScriptBlock]) {
+            "<pre><code class='language-powershell'>$(
+                [Web.HttpUtility]::HtmlEncode(
+                    "$md"
+                )
+            )</code><pre>"
+            continue nextInput
+        } 
+        if ($md -is [Collections.IDictionary] -or 
+            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
+            $currentRows += $md            
+            continue
+        }
+    }
+    
+    if ($currentRows) {    
+        $this.ToTable($currentRows)        
+        $currentRows = @()
+    }
+
+    if ($md -match '(?>\.md|markdown)$' -and
+        (Test-Path $md -ErrorAction Ignore)
+    ) {
+        $md = Get-Content -Raw $md
+    }
+
+    $yamlheader = ''
+    if ($md -match '^---') {
+        $null, $yamlheader, $md = $in -split '---', 3
+    }
+
+    $md
+})
+
+if ($currentRows) {    
+    $allMarkdown += $this.ToTable($currentRows)
+    $currentRows = @()
+}
+
+$markdown = $allMarkdown -join [Environment]::NewLine
+
+$this | 
+    Add-Member NoteProperty '#Markdown' $Markdown -Force
+
+$this.Sync()
+                    
+      
       
         Lexicon
         
@@ -371,53 +499,8 @@ $markdownData = $this.DataSet
 [PSObject[]]$Markdown
 )
 
-$currentRows = @()
-$allMarkdown = @(foreach ($md in $Markdown) {    
-    if ($md -isnot [string]) {        
-        if ($md -is [ScriptBlock]) {
-            $md = "<pre><code class='language-powershell'>$(
-                [Web.HttpUtility]::HtmlEncode(
-                    "$md"
-                )
-            )</code><pre>"
-        } 
-        if ($md -is [Collections.IDictionary] -or 
-            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
-            $currentRows += $md            
-            continue
-        }
-    }
-    
-    if ($currentRows) {    
-        $this.ToTable($currentRows)        
-        $currentRows = @()
-    }
-
-    if ($md -match '(?>\.md|markdown)$' -and
-        (Test-Path $md -ErrorAction Ignore)
-    ) {
-        $md = Get-Content -Raw $md
-    }
-
-    $yamlheader = ''
-    if ($md -match '^---') {
-        $null, $yamlheader, $md = $in -split '---', 3
-    }
-
-    $md
-})
-
-if ($currentRows) {    
-    $allMarkdown += $this.ToTable($currentRows)
-    $currentRows = @()
-}
+$this.Input = $Markdown
 
-$markdown = $allMarkdown -join [Environment]::NewLine
-
-$this | 
-    Add-Member NoteProperty '#Markdown' $Markdown -Force
-
-$this.Sync()
                     
       
       

From 81c70b8bfd3fe33a21b7fb9a045ae529510c83c3 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Thu, 8 Jan 2026 17:28:21 -0800
Subject: [PATCH 05/38] docs: MarkX Help ( Fixes #26 )

---
 Commands/Get-MarkX.ps1 | 41 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/Commands/Get-MarkX.ps1 b/Commands/Get-MarkX.ps1
index ad0973d..b7bfec4 100644
--- a/Commands/Get-MarkX.ps1
+++ b/Commands/Get-MarkX.ps1
@@ -1,4 +1,41 @@
 function Get-MarkX {
+    <#
+    .SYNOPSIS
+        Gets MarkX
+    .DESCRIPTION
+        Gets MarkX - Markdown as XML
+        
+        This allows us to query, extract, and customize markdown.
+    .EXAMPLE
+        # 'Hello World' In Markdown / MarkX
+        '# Hello World' | MarkX
+    .EXAMPLE
+        # MarkX is aliased to Markdown
+        # 'Hello World' as Markdown as XML
+        '# Hello World' | Markdown | Select -Expand XML
+    .EXAMPLE
+        # We can generate tables by piping in objects
+        @{n1=1;n2=2}, @{n1=2;n3=3} | MarkX
+    .EXAMPLE
+        # Make a TimesTable in MarkX
+        @(
+            "#### TimesTable"
+            foreach ($rowN in 1..9) {
+                $row = [Ordered]@{}
+                foreach ($colN in 1..9) {
+                    $row["$colN"] = $colN * $rowN
+                }
+                $row
+            }
+        ) | Get-MarkX
+    .EXAMPLE
+        # We can pipe a command into MarkX
+        # This will get the command help as Markdown
+        Get-Command Get-MarkX | MarkX
+    .EXAMPLE
+        # We can pipe help into MarkX
+        Get-Help Get-MarkX | MarkX
+    #>
     [Alias('MarkX','Markdown','Get-Markdown')]
     param()
 
@@ -6,9 +43,9 @@ function Get-MarkX {
         $args
     })    
     
-    [PSCustomObject]@{
+    New-Object PSObject -Property @{
         PSTypeName = 'MarkX'
-        Markdown = $allInput
+        Input = $allInput
         YamlHeader = $yamlheader
     }    
 }
\ No newline at end of file

From cbb389afb83e482e688a21c05ca931092e4b6170 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Thu, 8 Jan 2026 17:47:35 -0800
Subject: [PATCH 06/38] feat: MarkX.Headings ( Fixes #29 )

---
 Types/MarkX/Alias.psd1       |  1 +
 Types/MarkX/get_Headings.ps1 | 12 ++++++++++++
 2 files changed, 13 insertions(+)
 create mode 100644 Types/MarkX/get_Headings.ps1

diff --git a/Types/MarkX/Alias.psd1 b/Types/MarkX/Alias.psd1
index 3d378e6..77da964 100644
--- a/Types/MarkX/Alias.psd1
+++ b/Types/MarkX/Alias.psd1
@@ -2,4 +2,5 @@
     DB = 'DataSet'
     Tables = 'Table'
     Link = 'Links'
+    Heading = 'Headings'
 }
\ No newline at end of file
diff --git a/Types/MarkX/get_Headings.ps1 b/Types/MarkX/get_Headings.ps1
new file mode 100644
index 0000000..d31ed45
--- /dev/null
+++ b/Types/MarkX/get_Headings.ps1
@@ -0,0 +1,12 @@
+<#
+.SYNOPSIS
+    Gets Markdown headings
+.DESCRIPTION
+    Gets any heading elements in the markdown
+#>
+$this.XML | 
+    Select-Xml -XPath //* |
+    Where-Object {
+        $_.Node.LocalName -match 'h[1-6]'
+    } |
+    Select-Object -ExpandProperty Node
\ No newline at end of file

From a812993752233252b725b5e6b2697cf67c706b4a Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Fri, 9 Jan 2026 01:47:54 +0000
Subject: [PATCH 07/38] feat: MarkX.Headings ( Fixes #29 )

---
 MarkX.types.ps1xml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index 2230d25..59a5b58 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -19,6 +19,10 @@
         DB
         DataSet
       
+      
+        Heading
+        Headings
+      
       
         Link
         Links
@@ -236,6 +240,23 @@ $markdownLines
                         return $this.'#DataSet'
                     
       
+      
+        Headings
+        
+                        <#
+.SYNOPSIS
+    Gets Markdown headings
+.DESCRIPTION
+    Gets any heading elements in the markdown
+#>
+$this.XML | 
+    Select-Xml -XPath //* |
+    Where-Object {
+        $_.Node.LocalName -match 'h[1-6]'
+    } |
+    Select-Object -ExpandProperty Node
+                    
+      
       
         HTML
         

From e14a13ef722fca433a1336ef032851c0810dc2f4 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Fri, 9 Jan 2026 12:10:21 -0800
Subject: [PATCH 08/38] docs: Adding Lexicon example and updating readme (
 Fixes #25 )

---
 MarkX.ps1 | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 README.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)

diff --git a/MarkX.ps1 b/MarkX.ps1
index b85ac9e..4ca443d 100644
--- a/MarkX.ps1
+++ b/MarkX.ps1
@@ -6,6 +6,11 @@
 
 "@ | MarkX
 
+@"
+
+[![MarkX PowerShell Gallery](https://img.shields.io/powershellgallery/dt/MarkX)](https://www.powershellgallery.com/packages/MarkX/)
+
+"@ | MarkX
 
 @'
 
@@ -226,3 +231,69 @@ $(& $GetMarkdownTableData)
 
 "@ | MarkX
 
+
+
+$lexiconMarkdown = @'
+
+
+### Markdown Lexicons
+
+Since we can extra tables and data Markdown, we can also get any data of a particular known shape.
+
+The first special shape MarkX supports is an [at protocol lexicon](https://atproto.com/guides/lexicon)
+
+MarkX current supports lexicon type definitions.  It will support query and procedure definitions in the future.
+
+A type definition consists of a namespace identifier, a description, and a series of properties.
+
+#### com.example.happy.birthday
+> An example lexicon to record birthday messages
+
+|Property|Type|Description|
+|-|-|-|
+|`$type`      | `[string]`   | The type of the object.  Must be `com.example.happy.birthday` |
+|**`message`**| `[string]`   | A birthday message |
+|`forUri`     | `[uri]`      | A link |
+|`birthday`   | `[datetime]` | The birthday |
+|`createdAt`  | `[datetime]` | The time the record was created |
+
+'@ | MarkX
+
+$lexiconMarkdown
+
+
+$lexiconJson = $lexiconMarkdown.Lexicon | ConvertTo-Json -Depth 5
+
+$lexiconMarkdownExample = @'
+
+
+To extract out a lexicon from the text above, we can:
+
+~~~PowerShell
+$lexiconMarkdown.Lexicon | ConvertTo-Json -Depth 5
+~~~
+
+Which gives us:
+
+'@ + @"
+
+~~~json
+$lexiconJson
+~~~
+
+As you can see, we can take rich data within Markdown and process it into lexicons (or anything else we might want)
+"@
+
+$lexiconMarkdownExample | MarkX
+
+$InSummary = @"
+
+## In Summary
+
+MarkX is a simple and powerful tool.
+It allows us to turn many objects into Markdown, and turn Markdown into many objects.
+
+Please pay around with the module
+
+"@
+
diff --git a/README.md b/README.md
index 81233a7..1184594 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,9 @@
 ## Greetings from MarkX!
 
 
+[![MarkX PowerShell Gallery](https://img.shields.io/powershellgallery/dt/MarkX)](https://www.powershellgallery.com/packages/MarkX/)
+
+
 ### What Is MarkX?
 
 MarkX is a useful little tool built around a useful little trick.
@@ -181,3 +184,71 @@ When we run this example, we get:
 

abc

abc
123
456
789

def

def
123
246
369
4812
12 10 + + +### Markdown Lexicons + +Since we can extra tables and data Markdown, we can also get any data of a particular known shape. + +The first special shape MarkX supports is an [at protocol lexicon](https://atproto.com/guides/lexicon) + +MarkX current supports lexicon type definitions. It will support query and procedure definitions in the future. + +A type definition consists of a namespace identifier, a description, and a series of properties. + +#### com.example.happy.birthday +> An example lexicon to record birthday messages + +|Property|Type|Description| +|-|-|-| +|`$type` | `[string]` | The type of the object. Must be `com.example.happy.birthday` | +|**`message`**| `[string]` | A birthday message | +|`forUri` | `[uri]` | A link | +|`birthday` | `[datetime]` | The birthday | +|`createdAt` | `[datetime]` | The time the record was created | + + + +To extract out a lexicon from the text above, we can: + +~~~PowerShell +$lexiconMarkdown.Lexicon | ConvertTo-Json -Depth 5 +~~~ + +Which gives us: + +~~~json +{ + "lexicon": 1, + "id": "com.example.happy.birthday", + "defs": { + "main": { + "type": "record", + "description": "com.example.happy.birthday", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string", + "description": "A birthday message" + }, + "forUri": { + "type": "uri", + "description": "A link" + }, + "birthday": { + "type": "datetime", + "description": "The birthday" + }, + "createdAt": { + "type": "datetime", + "description": "The time the record was created" + } + } + } + } +} +~~~ + +As you can see, we can take rich data within Markdown and process it into lexicons (or anything else we might want) From 172ad115fd87e3f8df310cac9919fac3181ebc0c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 9 Jan 2026 12:18:24 -0800 Subject: [PATCH 09/38] docs: Adding help example and updating readme ( Fixes #27 ) --- MarkX.ps1 | 28 +++++++++++++++++++++++++--- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/MarkX.ps1 b/MarkX.ps1 index 4ca443d..b52b2cd 100644 --- a/MarkX.ps1 +++ b/MarkX.ps1 @@ -232,7 +232,6 @@ $(& $GetMarkdownTableData) "@ | MarkX - $lexiconMarkdown = @' @@ -286,6 +285,28 @@ As you can see, we can take rich data within Markdown and process it into lexico $lexiconMarkdownExample | MarkX + +$selfHelp = {Get-Help Get-MarkX | MarkX} + +$markdownHelp = @" + +### Markdown Help + +PowerShell commands generally contain help. + +We can pipe Get-Help into MarkX to get help as markdown + +~~~PowerShell +$selfHelp +~~~ + +When we run this, we get: + +"@ + +. $selfHelp + + $InSummary = @" ## In Summary @@ -293,7 +314,8 @@ $InSummary = @" MarkX is a simple and powerful tool. It allows us to turn many objects into Markdown, and turn Markdown into many objects. -Please pay around with the module +Please pay around and see what you can do. -"@ +"@ +$InSummary | MarkX diff --git a/README.md b/README.md index 1184594..cea6fcd 100644 --- a/README.md +++ b/README.md @@ -252,3 +252,52 @@ Which gives us: ~~~ As you can see, we can take rich data within Markdown and process it into lexicons (or anything else we might want) +# Get-MarkX +## Gets MarkX +### Gets MarkX - Markdown as XML + +This allows us to query, extract, and customize markdown. + +'Hello World' In Markdown / MarkX +~~~PowerShell +'# Hello World' | MarkX +~~~ +MarkX is aliased to Markdown +'Hello World' as Markdown as XML +~~~PowerShell +'# Hello World' | Markdown | Select -Expand XML +~~~ +We can generate tables by piping in objects +~~~PowerShell +@{n1=1;n2=2}, @{n1=2;n3=3} | MarkX +~~~ +Make a TimesTable in MarkX +~~~PowerShell +@( + "#### TimesTable" + foreach ($rowN in 1..9) { + $row = [Ordered]@{} + foreach ($colN in 1..9) { + $row["$colN"] = $colN * $rowN + } + $row + } +) | Get-MarkX +~~~ +We can pipe a command into MarkX +This will get the command help as Markdown +~~~PowerShell +Get-Command Get-MarkX | MarkX +~~~ +We can pipe help into MarkX +~~~PowerShell +Get-Help Get-MarkX | MarkX +~~~ + +## In Summary + +MarkX is a simple and powerful tool. +It allows us to turn many objects into Markdown, and turn Markdown into many objects. + +Please pay around and see what you can do. + From 554a3a80d899e55698279eb6457b1226af8d6fc5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 9 Jan 2026 12:38:15 -0800 Subject: [PATCH 10/38] feat: MarkX Help Support ( Fixes #27 ) Removing path from external script help --- Types/MarkX/set_Input.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Types/MarkX/set_Input.ps1 b/Types/MarkX/set_Input.ps1 index 4b42b06..a5479f4 100644 --- a/Types/MarkX/set_Input.ps1 +++ b/Types/MarkX/set_Input.ps1 @@ -26,7 +26,12 @@ $allMarkdown = @(:nextInput foreach ($md in $InputObject) { if ($md.pstypenames -match 'HelpInfo') { @( - "# $($md.Name)" + if ($md.Name -match '[\\/]') { + "# $(@($md.Name -split '[\\/]')[-1] -replace '\.ps1')" + } else { + "# $($md.Name)" + } + if ($md.Synopsis) { "## $($md.Synopsis)" } From be4c39853baea93289bcdc0220e65cac9df2651f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 9 Jan 2026 20:38:32 +0000 Subject: [PATCH 11/38] feat: MarkX Help Support ( Fixes #27 ) Removing path from external script help --- MarkX.types.ps1xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml index 59a5b58..3dabcd7 100644 --- a/MarkX.types.ps1xml +++ b/MarkX.types.ps1xml @@ -314,7 +314,12 @@ $allMarkdown = @(:nextInput foreach ($md in $InputObject) { if ($md.pstypenames -match 'HelpInfo') { @( - "# $($md.Name)" + if ($md.Name -match '[\\/]') { + "# $(@($md.Name -split '[\\/]')[-1] -replace '\.ps1')" + } else { + "# $($md.Name)" + } + if ($md.Synopsis) { "## $($md.Synopsis)" } From 754c370774b31f8b9f409f1cb04c0ec4e8497d37 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 11 Jan 2026 12:30:34 -0800 Subject: [PATCH 12/38] feat: MarkX.Input ( Fixes #28 ) Making input light and putting logic in Sync --- Types/MarkX/Sync.ps1 | 119 ++++++++++++++++++++++++++++++++++++++ Types/MarkX/set_Input.ps1 | 1 + 2 files changed, 120 insertions(+) diff --git a/Types/MarkX/Sync.ps1 b/Types/MarkX/Sync.ps1 index 82fa5ee..eefe7e9 100644 --- a/Types/MarkX/Sync.ps1 +++ b/Types/MarkX/Sync.ps1 @@ -1,3 +1,122 @@ +$currentRows = @() +$allMarkdown = @(:nextInput foreach ($md in $this.Input) { + if ($md -isnot [string]) { + + if ($md -is [Management.Automation.CommandInfo]) { + $cmdHelp = if ( + $md -is [Management.Automation.FunctionInfo] -or + $md -is [Management.Automation.AliasInfo] + ) { + Get-Help -Name $md.Name + } elseif ($md -is [Management.Automation.ExternalScriptInfo]) { + Get-Help -Name $md.Source + } else { + continue nextInput + } + if ($cmdHelp) { + $md = $cmdHelp + } + } + + if ($md.pstypenames -match 'HelpInfo') { + @( + if ($md.Name -match '[\\/]') { + "# $(@($md.Name -split '[\\/]')[-1] -replace '\.ps1')" + } else { + "# $($md.Name)" + } + + if ($md.Synopsis) { + "## $($md.Synopsis)" + } + $description = $md.Description.text -join [Environment]::NewLine + if ($description) { + "### $($description)" + } + + $md.alertset.alert.text -join [Environment]::NewLine + + foreach ($example in $md.examples.example) { + $exampleNumber++ + + # Combine the code and remarks + $exampleLines = + @( + $example.Code + foreach ($remark in $example.Remarks.text) { + if (-not $remark) { continue } + $remark + } + ) -join ([Environment]::NewLine) -split '(?>\r\n|\n)' # and split into lines + + # Anything until the first non-comment line is a markdown predicate to the example + $nonCommentLine = $false + $markdownLines = @() + + # Go thru each line in the example as part of a loop + $codeBlock = @(foreach ($exampleLine in $exampleLines) { + # Any comments until the first uncommentedLine are markdown + if ($exampleLine -match '^\#' -and -not $nonCommentLine) { + $markdownLines += $exampleLine -replace '^\#' -replace '^\s+' + } else { + $nonCommentLine = $true + $exampleLine + } + }) -join [Environment]::NewLine + + # Join all of our markdown lines together + $markdownLines -join [Environment]::NewLine + "~~~PowerShell" + $codeBlock + "~~~" + } + ) -join [Environment]::NewLine + continue nextInput + } + if ($md -is [ScriptBlock]) { + "
$(
+                [Web.HttpUtility]::HtmlEncode(
+                    "$md"
+                )
+            )
"
+            continue nextInput
+        } 
+        if ($md -is [Collections.IDictionary] -or 
+            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
+            $currentRows += $md            
+            continue
+        }
+    }
+    
+    if ($currentRows) {    
+        $this.ToTable($currentRows)        
+        $currentRows = @()
+    }
+
+    if ($md -match '(?>\.md|markdown)$' -and
+        (Test-Path $md -ErrorAction Ignore)
+    ) {
+        $md = Get-Content -Raw $md
+    }
+
+    $yamlheader = ''
+    if ($md -match '^---') {
+        $null, $yamlheader, $md = $in -split '---', 3
+    }
+
+    $md
+})
+
+if ($currentRows) {    
+    $allMarkdown += $this.ToTable($currentRows)
+    $currentRows = @()
+}
+
+$markdown = $allMarkdown -join [Environment]::NewLine
+
+$this | 
+    Add-Member NoteProperty '#Markdown' $Markdown -Force
+
 $Markdown = $this.'#Markdown'
 
 if (-not $Markdown) { return }
diff --git a/Types/MarkX/set_Input.ps1 b/Types/MarkX/set_Input.ps1
index a5479f4..f9d6e77 100644
--- a/Types/MarkX/set_Input.ps1
+++ b/Types/MarkX/set_Input.ps1
@@ -41,6 +41,7 @@ $allMarkdown = @(:nextInput foreach ($md in $InputObject) {
                 }
                 
                 $md.alertset.alert.text -join [Environment]::NewLine
+                
                 foreach ($example in $md.examples.example) {
                     $exampleNumber++
                     

From 44d206aee5403ec2f1481ca6d778c58a0703e3b5 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Sun, 11 Jan 2026 20:30:49 +0000
Subject: [PATCH 13/38] feat: MarkX.Input ( Fixes #28 )

Making input light and putting logic in Sync
---
 MarkX.types.ps1xml | 122 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 121 insertions(+), 1 deletion(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index 3dabcd7..df450c9 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -34,7 +34,126 @@
       
         Sync
         
       
+      
+        Code
+        
+                        <#
+.SYNOPSIS
+    Gets code blocks
+.DESCRIPTION
+    Gets code blocks within Markdown / MarkX
+.EXAMPLE
+    "
+    # Hello World in PowerShell
+    ~~~PowerShell
+    'Hello World'
+    ~~~
+    " | markx | Select-Object -ExpandProperty Code 
+#>
+$codeByLanguage = [Ordered]@{}
+
+foreach ($element in $this.XML | Select-Xml -XPath //code) {
+    $language = 'unknown'
+    if ($element.node.class -match 'language-(?<language>\S+)?') {
+        $language = $matches.language        
+    }
+    if (-not $codeByLanguage[$language]) {
+        $codeByLanguage[$language] = @()
+    }
+    $codeByLanguage[$language] += $element.node.InnerText
+}
+
+return $codeByLanguage
+                    
+      
       
         DataSet
         

From e5f9602bd8a82e97aa709c401c500bc9e4e37e09 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Sun, 11 Jan 2026 22:47:20 -0800
Subject: [PATCH 18/38] feat: MarkX.get_Code ( Fixes #30 )

Amending help
---
 Types/MarkX/get_Code.ps1 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Types/MarkX/get_Code.ps1 b/Types/MarkX/get_Code.ps1
index 84d4be8..4f53108 100644
--- a/Types/MarkX/get_Code.ps1
+++ b/Types/MarkX/get_Code.ps1
@@ -1,8 +1,8 @@
 <#
 .SYNOPSIS
-    Gets code blocks
+    Gets code
 .DESCRIPTION
-    Gets code blocks within Markdown / MarkX
+    Gets code within Markdown / MarkX, grouped by language.
 .EXAMPLE
     "
     # Hello World in PowerShell

From cb39b087452ad402b928fe1347d593bad9b3e9b1 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Sun, 11 Jan 2026 22:48:18 -0800
Subject: [PATCH 19/38] feat: MarkX.get_CodeBlock ( Fixes #31 )

---
 Types/MarkX/get_CodeBlock.ps1 | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Types/MarkX/get_CodeBlock.ps1

diff --git a/Types/MarkX/get_CodeBlock.ps1 b/Types/MarkX/get_CodeBlock.ps1
new file mode 100644
index 0000000..1a6f934
--- /dev/null
+++ b/Types/MarkX/get_CodeBlock.ps1
@@ -0,0 +1,27 @@
+<#
+.SYNOPSIS
+    Gets code blocks
+.DESCRIPTION
+    Gets code blocks within Markdown / MarkX
+.EXAMPLE
+    "
+    # Hello World in PowerShell
+    ~~~PowerShell
+    'Hello World'
+    ~~~
+    " | markx | Select-Object -ExpandProperty CodeBlock 
+#>
+$codeByLanguage = [Ordered]@{}
+
+foreach ($element in $this.XML | Select-Xml -XPath //code) {
+    $language = 'unknown'
+    if ($element.node.class -match 'language-(?\S+)?') {
+        $language = $matches.language        
+    }
+    if (-not $codeByLanguage[$language]) {
+        $codeByLanguage[$language] = @()
+    }
+    $codeByLanguage[$language] += $element.node
+}
+
+return $codeByLanguage
\ No newline at end of file

From e66d492daad750f6a89cd3f6e1df772c6b554df0 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 06:48:34 +0000
Subject: [PATCH 20/38] feat: MarkX.get_CodeBlock ( Fixes #31 )

---
 MarkX.types.ps1xml | 36 ++++++++++++++++++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index f18697f..445edff 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -358,9 +358,9 @@ $markdownLines
         
                         <#
 .SYNOPSIS
-    Gets code blocks
+    Gets code
 .DESCRIPTION
-    Gets code blocks within Markdown / MarkX
+    Gets code within Markdown / MarkX, grouped by language.
 .EXAMPLE
     "
     # Hello World in PowerShell
@@ -382,6 +382,38 @@ foreach ($element in $this.XML | Select-Xml -XPath //code) {
     $codeByLanguage[$language] += $element.node.InnerText
 }
 
+return $codeByLanguage
+                    
+      
+      
+        CodeBlock
+        
+                        <#
+.SYNOPSIS
+    Gets code blocks
+.DESCRIPTION
+    Gets code blocks within Markdown / MarkX
+.EXAMPLE
+    "
+    # Hello World in PowerShell
+    ~~~PowerShell
+    'Hello World'
+    ~~~
+    " | markx | Select-Object -ExpandProperty CodeBlock 
+#>
+$codeByLanguage = [Ordered]@{}
+
+foreach ($element in $this.XML | Select-Xml -XPath //code) {
+    $language = 'unknown'
+    if ($element.node.class -match 'language-(?<language>\S+)?') {
+        $language = $matches.language        
+    }
+    if (-not $codeByLanguage[$language]) {
+        $codeByLanguage[$language] = @()
+    }
+    $codeByLanguage[$language] += $element.node
+}
+
 return $codeByLanguage
                     
       

From 92822adcb3967a01873da274cc8da8b178873b32 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Sun, 11 Jan 2026 23:46:18 -0800
Subject: [PATCH 21/38] feat: MarkX.YamlHeader ( Fixes #32, Fixes #33 )

---
 Types/MarkX/Alias.psd1         |  1 +
 Types/MarkX/get_YamlHeader.ps1 |  1 +
 Types/MarkX/set_YamlHeader.ps1 | 23 +++++++++++++++++++++++
 3 files changed, 25 insertions(+)
 create mode 100644 Types/MarkX/get_YamlHeader.ps1
 create mode 100644 Types/MarkX/set_YamlHeader.ps1

diff --git a/Types/MarkX/Alias.psd1 b/Types/MarkX/Alias.psd1
index 77da964..e7a45db 100644
--- a/Types/MarkX/Alias.psd1
+++ b/Types/MarkX/Alias.psd1
@@ -3,4 +3,5 @@
     Tables = 'Table'
     Link = 'Links'
     Heading = 'Headings'
+    Header = 'YamlHeader'
 }
\ No newline at end of file
diff --git a/Types/MarkX/get_YamlHeader.ps1 b/Types/MarkX/get_YamlHeader.ps1
new file mode 100644
index 0000000..bbca11b
--- /dev/null
+++ b/Types/MarkX/get_YamlHeader.ps1
@@ -0,0 +1 @@
+return $this.'#YamlHeader'
\ No newline at end of file
diff --git a/Types/MarkX/set_YamlHeader.ps1 b/Types/MarkX/set_YamlHeader.ps1
new file mode 100644
index 0000000..ab46613
--- /dev/null
+++ b/Types/MarkX/set_YamlHeader.ps1
@@ -0,0 +1,23 @@
+param($header)
+
+if ($header -is [string]) {
+    $this | Add-Member NoteProperty '#YamlHeader' $header -Force
+    return
+}
+
+$convertToYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertTo-Yaml', 'Alias,Cmdlet,Function')
+if (-not $convertToYaml) {
+    Write-Warning "Cannot set yaml header without converter"
+    return
+}
+
+$convertParameters = @{}
+if ($convertToYaml.Parameters['Depth']) {
+    $convertParameters['Depth'] = $FormatEnumerationLimit
+}
+$toYaml = $header | & $convertToYaml @convertParameters
+if ($toYaml -is [string]) {
+    $this | Add-Member NoteProperty '#YamlHeader' $toYaml -Force
+}
+
+

From b879797117456264e48d9b38a45e7149f1a76b29 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 07:46:42 +0000
Subject: [PATCH 22/38] feat: MarkX.YamlHeader ( Fixes #32, Fixes #33 )

---
 MarkX.types.ps1xml | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index 445edff..1683a16 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -19,6 +19,10 @@
         DB
         DataSet
       
+      
+        Header
+        YamlHeader
+      
       
         Heading
         Headings
@@ -606,6 +610,38 @@ $this.Input = $Markdown
                         return $this.'#XMl'
                     
       
+      
+        YamlHeader
+        
+                        return $this.'#YamlHeader'
+                    
+        
+                        param($header)
+
+if ($header -is [string]) {
+    $this | Add-Member NoteProperty '#YamlHeader' $header -Force
+    return
+}
+
+$convertToYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertTo-Yaml', 'Alias,Cmdlet,Function')
+if (-not $convertToYaml) {
+    Write-Warning "Cannot set yaml header without converter"
+    return
+}
+
+$convertParameters = @{}
+if ($convertToYaml.Parameters['Depth']) {
+    $convertParameters['Depth'] = $FormatEnumerationLimit
+}
+$toYaml = $header | & $convertToYaml @convertParameters
+if ($toYaml -is [string]) {
+    $this | Add-Member NoteProperty '#YamlHeader' $toYaml -Force
+}
+
+
+
+                    
+      
       
         DefaultDisplay
         Markdown

From 796dc276bbcbf8cc9ca58489fa490a02a97ebc54 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Sun, 11 Jan 2026 23:47:25 -0800
Subject: [PATCH 23/38] feat: MarkX.YamlHeader ( Fixes #32, Fixes #33 )

Adding to Sync
---
 Types/MarkX/Sync.ps1 | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Types/MarkX/Sync.ps1 b/Types/MarkX/Sync.ps1
index eefe7e9..f57dec1 100644
--- a/Types/MarkX/Sync.ps1
+++ b/Types/MarkX/Sync.ps1
@@ -102,6 +102,9 @@ $allMarkdown = @(:nextInput foreach ($md in $this.Input) {
     $yamlheader = ''
     if ($md -match '^---') {
         $null, $yamlheader, $md = $in -split '---', 3
+        if ($yamlheader) {
+            $this | Add-Member NoteProperty '#YamlHeader' $yamlheader -Force
+        }
     }
 
     $md

From 6b61634ea9c3796eff5be869c226b14c0fdf80b5 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 07:47:54 +0000
Subject: [PATCH 24/38] feat: MarkX.YamlHeader ( Fixes #32, Fixes #33 )

Adding to Sync
---
 MarkX.types.ps1xml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index 1683a16..daa8679 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -142,6 +142,9 @@ $allMarkdown = @(:nextInput foreach ($md in $this.Input) {
     $yamlheader = ''
     if ($md -match '^---') {
         $null, $yamlheader, $md = $in -split '---', 3
+        if ($yamlheader) {
+            $this | Add-Member NoteProperty '#YamlHeader' $yamlheader -Force
+        }
     }
 
     $md

From db60b5286481ed227943f1aad83e7152873c5109 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 11:29:05 -0800
Subject: [PATCH 25/38] feat: Get-MarkX input improvement ( Fixes #28 )

Today I Learned that using -Property does not trigger a script property set (this makes sense, since the object will not be decorated with the right typename quite yet)
---
 Commands/Get-MarkX.ps1 | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/Commands/Get-MarkX.ps1 b/Commands/Get-MarkX.ps1
index b7bfec4..87b25f2 100644
--- a/Commands/Get-MarkX.ps1
+++ b/Commands/Get-MarkX.ps1
@@ -36,16 +36,17 @@ function Get-MarkX {
         # We can pipe help into MarkX
         Get-Help Get-MarkX | MarkX
     #>
-    [Alias('MarkX','Markdown','Get-Markdown')]
+    [Alias('MarkX','Markdown','Get-Markdown')]    
     param()
 
     $allInput = @($input) + $(if ($args) {
         $args
-    })    
+    })
     
-    New-Object PSObject -Property @{
-        PSTypeName = 'MarkX'
-        Input = $allInput
-        YamlHeader = $yamlheader
-    }    
+    
+    $markx = New-Object PSObject -Property @{
+        PSTypeName = 'MarkX'        
+    }
+    $markx.Input = $allInput    
+    $markx
 }
\ No newline at end of file

From ceb47d23f2a65b0faa08106d111ab8f2b2d20cdf Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 11:30:11 -0800
Subject: [PATCH 26/38] feat: MarkX.Sync file input support ( Fixes #34 )

---
 Types/MarkX/Sync.ps1 | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/Types/MarkX/Sync.ps1 b/Types/MarkX/Sync.ps1
index f57dec1..affd14d 100644
--- a/Types/MarkX/Sync.ps1
+++ b/Types/MarkX/Sync.ps1
@@ -1,6 +1,14 @@
 $currentRows = @()
 $allMarkdown = @(:nextInput foreach ($md in $this.Input) {    
     if ($md -isnot [string]) {
+        # If the markdown was a file
+        if ($md -is [IO.FileInfo] -and 
+            # and it had the extension .md or markdown
+            $md.Extension -in '.md', '.markdown') {
+            $md = Get-Content -LiteralPath $md.Fullname -Raw 
+            $md
+            continue
+        }
         
         if ($md -is [Management.Automation.CommandInfo]) {
             $cmdHelp = if (
@@ -82,10 +90,13 @@ $allMarkdown = @(:nextInput foreach ($md in $this.Input) {
             continue nextInput
         } 
         if ($md -is [Collections.IDictionary] -or 
-            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
+            ($md.GetType -and 
+                (-not $md.GetType().IsPrimitive)
+            )  
+        ) {            
             $currentRows += $md            
             continue
-        }
+        }        
     }
     
     if ($currentRows) {    
@@ -115,6 +126,26 @@ if ($currentRows) {
     $currentRows = @()
 }
 
+$yamlHeaders = @()
+$allMarkdown = @(foreach ($md in $allMarkdown) {
+    if ($md -match '^---') {
+        $null, $yamlheader, $restOfMakdown = $md -split '---', 3
+        if ($yamlheader) {
+            $yamlHeaders+= $yamlheader            
+        }
+        $restOfMakdown
+    } else {
+        $md
+    }
+})
+
+if ($yamlHeaders) {
+    $yamlHeader = $yamlHeaders -join (
+        [Environment]::NewLine + '---' + [Environment]::NewLine
+    )
+    $this | Add-Member NoteProperty '#YamlHeader' $yamlHeader -Force
+}
+
 $markdown = $allMarkdown -join [Environment]::NewLine
 
 $this | 
@@ -139,7 +170,6 @@ $this |
 if (-not $this.'#XML') { return }
 
 $tables = $this.'#XML' | Select-Xml //table
-if (-not $tables) { return }
 filter innerText {
     $in = $_
     if ($in -is [string]) { "$in" }

From 81c97938541c852a3d54dc865690d93f9f9beacb Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 11:32:00 -0800
Subject: [PATCH 27/38] feat: MarkX.Header ( Fixes #33 )

Returning object with header, and text with yamlheader.  Allowing object or text set for either.
---
 Types/MarkX/get_Header.ps1 | 9 +++++++++
 Types/MarkX/set_Header.ps1 | 3 +++
 2 files changed, 12 insertions(+)
 create mode 100644 Types/MarkX/get_Header.ps1
 create mode 100644 Types/MarkX/set_Header.ps1

diff --git a/Types/MarkX/get_Header.ps1 b/Types/MarkX/get_Header.ps1
new file mode 100644
index 0000000..782f37f
--- /dev/null
+++ b/Types/MarkX/get_Header.ps1
@@ -0,0 +1,9 @@
+if (-not $this.'#YamlHeader') { return }
+
+$convertFromYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertFrom-Yaml', 'Alias,Cmdlet,Function')
+if (-not $convertFromYaml) {
+    Write-Warning "Cannot get header without ConvertFrom-Yaml"
+    return
+}
+
+return ($this.'#YamlHeader' | & $convertFromYaml)
\ No newline at end of file
diff --git a/Types/MarkX/set_Header.ps1 b/Types/MarkX/set_Header.ps1
new file mode 100644
index 0000000..cea3547
--- /dev/null
+++ b/Types/MarkX/set_Header.ps1
@@ -0,0 +1,3 @@
+param($header)
+
+$this.YamlHeader = $header
\ No newline at end of file

From b4a48ad47173425302bd6078e9340c03b190ccc7 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 19:32:19 +0000
Subject: [PATCH 28/38] feat: MarkX.Header ( Fixes #33 )

Returning object with header, and text with yamlheader.  Allowing object or text set for either.
---
 MarkX.types.ps1xml | 55 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 52 insertions(+), 3 deletions(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index daa8679..f68299d 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -41,6 +41,14 @@
                         $currentRows = @()
 $allMarkdown = @(:nextInput foreach ($md in $this.Input) {    
     if ($md -isnot [string]) {
+        # If the markdown was a file
+        if ($md -is [IO.FileInfo] -and 
+            # and it had the extension .md or markdown
+            $md.Extension -in '.md', '.markdown') {
+            $md = Get-Content -LiteralPath $md.Fullname -Raw 
+            $md
+            continue
+        }
         
         if ($md -is [Management.Automation.CommandInfo]) {
             $cmdHelp = if (
@@ -122,10 +130,13 @@ $allMarkdown = @(:nextInput foreach ($md in $this.Input) {
             continue nextInput
         } 
         if ($md -is [Collections.IDictionary] -or 
-            ($md.GetType -and -not $md.GetType().IsPrimitive))  {            
+            ($md.GetType -and 
+                (-not $md.GetType().IsPrimitive)
+            )  
+        ) {            
             $currentRows += $md            
             continue
-        }
+        }        
     }
     
     if ($currentRows) {    
@@ -155,6 +166,26 @@ if ($currentRows) {
     $currentRows = @()
 }
 
+$yamlHeaders = @()
+$allMarkdown = @(foreach ($md in $allMarkdown) {
+    if ($md -match '^---') {
+        $null, $yamlheader, $restOfMakdown = $md -split '---', 3
+        if ($yamlheader) {
+            $yamlHeaders+= $yamlheader            
+        }
+        $restOfMakdown
+    } else {
+        $md
+    }
+})
+
+if ($yamlHeaders) {
+    $yamlHeader = $yamlHeaders -join (
+        [Environment]::NewLine + '---' + [Environment]::NewLine
+    )
+    $this | Add-Member NoteProperty '#YamlHeader' $yamlHeader -Force
+}
+
 $markdown = $allMarkdown -join [Environment]::NewLine
 
 $this | 
@@ -179,7 +210,6 @@ $this |
 if (-not $this.'#XML') { return }
 
 $tables = $this.'#XML' | Select-Xml //table
-if (-not $tables) { return }
 filter innerText {
     $in = $_
     if ($in -is [string]) { "$in" }
@@ -430,6 +460,25 @@ return $codeByLanguage
                         return $this.'#DataSet'
                     
       
+      
+        Header
+        
+                        if (-not $this.'#YamlHeader') { return }
+
+$convertFromYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertFrom-Yaml', 'Alias,Cmdlet,Function')
+if (-not $convertFromYaml) {
+    Write-Warning "Cannot get header without ConvertFrom-Yaml"
+    return
+}
+
+return ($this.'#YamlHeader' | & $convertFromYaml)
+                    
+        
+                        param($header)
+
+$this.YamlHeader = $header
+                    
+      
       
         Headings
         

From 57c4b79dc34f9cd84fe76d9b82afef4577339b0e Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 11:36:21 -0800
Subject: [PATCH 29/38] feat: MarkX.ToString flexibility ( Fixes #35 )

---
 Types/MarkX/ToString.ps1 | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/Types/MarkX/ToString.ps1 b/Types/MarkX/ToString.ps1
index 12b6c8c..035c9d2 100644
--- a/Types/MarkX/ToString.ps1
+++ b/Types/MarkX/ToString.ps1
@@ -1,2 +1,18 @@
+if ($args) {
+    $anyOutput = foreach ($arg in $args) {
+        $thisArg = $this.$arg
+        if ($thisArg) {
+            if ($thisArg.XHTML.InnerXML) {
+                "$($thisArg.XHTML.InnerXML)" + [Environment]::NewLine
+            } else {
+                "$thisArg"
+            }
+        }
+    }
+    if ($anyOutput) {
+        return $anyOutput -join [Environment]::NewLine
+    }    
+}
+
 if (-not $this.XML.XHTML) { return '' }
 return ("$($this.XML.XHTML.InnerXML)" + [Environment]::NewLine)
\ No newline at end of file

From 854248aade7447166bba0e258ddf9205975c0376 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 19:36:43 +0000
Subject: [PATCH 30/38] feat: MarkX.ToString flexibility ( Fixes #35 )

---
 MarkX.types.ps1xml | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index f68299d..5c2de9d 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -303,7 +303,23 @@ $this | Add-Member NoteProperty '#DataSet' $markdownData -Force
       
         ToString
         
       

From 7207920da92ff897c6277284774d5ce46c7a3dd5 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:16:37 -0800
Subject: [PATCH 31/38] fix: Fixing aliasing and adding code example

---
 Commands/Get-MarkX.ps1 | 5 +++++
 Types/MarkX/Alias.psd1 | 3 +--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/Commands/Get-MarkX.ps1 b/Commands/Get-MarkX.ps1
index 87b25f2..5f64855 100644
--- a/Commands/Get-MarkX.ps1
+++ b/Commands/Get-MarkX.ps1
@@ -35,6 +35,11 @@ function Get-MarkX {
     .EXAMPLE
         # We can pipe help into MarkX
         Get-Help Get-MarkX | MarkX
+    .EXAMPLE
+        # We can get code from markdown
+        Get-Help Get-MarkX | 
+            MarkX | 
+            Select-Object -ExpandProperty Code
     #>
     [Alias('MarkX','Markdown','Get-Markdown')]    
     param()
diff --git a/Types/MarkX/Alias.psd1 b/Types/MarkX/Alias.psd1
index e7a45db..e454738 100644
--- a/Types/MarkX/Alias.psd1
+++ b/Types/MarkX/Alias.psd1
@@ -2,6 +2,5 @@
     DB = 'DataSet'
     Tables = 'Table'
     Link = 'Links'
-    Heading = 'Headings'
-    Header = 'YamlHeader'
+    Heading = 'Headings'    
 }
\ No newline at end of file

From c751c5439927f386bb21f3467130f33cca291ec9 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 20:17:04 +0000
Subject: [PATCH 32/38] fix: Fixing aliasing and adding code example

---
 MarkX.types.ps1xml | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/MarkX.types.ps1xml b/MarkX.types.ps1xml
index 5c2de9d..d8170ec 100644
--- a/MarkX.types.ps1xml
+++ b/MarkX.types.ps1xml
@@ -19,10 +19,6 @@
         DB
         DataSet
       
-      
-        Header
-        YamlHeader
-      
       
         Heading
         Headings

From 077ac026325458e7cd48787f5588e2c682d50838 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:17:39 -0800
Subject: [PATCH 33/38] docs: Updating README and source

---
 README.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/README.md b/README.md
index cea6fcd..f32269d 100644
--- a/README.md
+++ b/README.md
@@ -293,6 +293,12 @@ We can pipe help into MarkX
 ~~~PowerShell
 Get-Help Get-MarkX | MarkX
 ~~~
+We can get code from markdown
+~~~PowerShell
+Get-Help Get-MarkX | 
+    MarkX | 
+    Select-Object -ExpandProperty Code
+~~~
 
 ## In Summary
 

From 8f65226f02724949f8f0eac79a836a65d9ac7af0 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:20:29 -0800
Subject: [PATCH 34/38] feat: MarkX formatting ( Fixes #36 )

---
 Types/MarkX/MarkX.format.ps1 | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 Types/MarkX/MarkX.format.ps1

diff --git a/Types/MarkX/MarkX.format.ps1 b/Types/MarkX/MarkX.format.ps1
new file mode 100644
index 0000000..7dde8b0
--- /dev/null
+++ b/Types/MarkX/MarkX.format.ps1
@@ -0,0 +1 @@
+Write-FormatView -TypeName MarkX -Property Markdown -Wrap

From f61f8f9b8e355a1afc571bd9dcbce284df8cb86b Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 20:20:48 +0000
Subject: [PATCH 35/38] feat: MarkX formatting ( Fixes #36 )

---
 MarkX.format.ps1xml | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 MarkX.format.ps1xml

diff --git a/MarkX.format.ps1xml b/MarkX.format.ps1xml
new file mode 100644
index 0000000..39e06d3
--- /dev/null
+++ b/MarkX.format.ps1xml
@@ -0,0 +1,27 @@
+
+
+  
+    
+      MarkX
+      
+        MarkX
+      
+      
+        
+          
+          
+        
+        
+          
+            
+            
+              
+                Markdown
+              
+            
+          
+        
+      
+    
+  
+
\ No newline at end of file

From 3eaf84a0cf7b9ce5649f6bf4ab003177994c4a05 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:21:22 -0800
Subject: [PATCH 36/38] feat: MarkX formatting ( Fixes #36 )

Adding formatting to manifest
---
 MarkX.psd1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MarkX.psd1 b/MarkX.psd1
index f610d78..9ca17e4 100644
--- a/MarkX.psd1
+++ b/MarkX.psd1
@@ -36,7 +36,7 @@ Description = 'MarkX - Markdown, XML, and PowerShell'
 TypesToProcess = @('MarkX.types.ps1xml')
 
 # Format files (.ps1xml) to be loaded when importing this module
-# FormatsToProcess = @()
+FormatsToProcess = @('MarkX.format.ps1xml')
 
 # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
 FunctionsToExport = 'Get-MarkX'

From 8caab61554eb7709f2143c6ee2b86a98b294e1d2 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:25:10 -0800
Subject: [PATCH 37/38] release: MarkX 0.1.1

Updating Release Notes and CHANGELOG
---
 CHANGELOG.md | 15 +++++++++++++++
 MarkX.psd1   | 41 ++++++++++++++++-------------------------
 2 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1145ddc..6338cc4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+## MarkX 0.1.1:
+
+* MarkX Help (#26)
+* MarkX now accepts commands / help (#27)
+* MarkX.Headings gets heading elements (#29)
+* MarkX.Lexicon gets lexicons in Markdown (#25)
+* MarkX.YamlHeader support (#32, #33)
+* Allowing piped markdown file input (#34)
+* MarkX.Code gets code within markdown (#30)
+* MarkX.CodeBlock gets code blocks within markdown (#31)
+* MarkX.ToString can now stringify any property (#35)
+* MarkX formatting (#36)
+
+---
+
 ## MarkX 0.1:
 
 * `Get-MarkX` ( #1, #4 )
diff --git a/MarkX.psd1 b/MarkX.psd1
index 9ca17e4..ec13d31 100644
--- a/MarkX.psd1
+++ b/MarkX.psd1
@@ -63,31 +63,22 @@ PrivateData = @{
 
         # ReleaseNotes of this module
         ReleaseNotes = @'
-## MarkX 0.1:
-
-* `Get-MarkX` ( #1, #4 )
-* Docs
-  * Sponsorship ( #20 )
-  * Security ( #19 )
-  * Contributing ( #18 )
-  * Code of conduct ( #17 )
-  * README (generated with MarkX) (#16)
-* Extended Types
-  * `MarkX.get_HTML` ( #14 )    
-  * `MarkX.ToString()` ( #12 )
-  * `MarkX.get_DataSet` ( #13 )
-  * `MarkX.get_Images` (#11)
-  * `MarkX.get_Links` (#10)
-  * `MarkX.get_Table` ( #9 )
-  * `MarkX.get_InnerText` ( #8 )
-  * `MarkX.get_XML` ( #7 )
-  * `MarkX.get/set_Markdown` ( #6 )
-  * `MarkX.Sync` ( #5 )
-  * `MarkX.ToTable` ( #22 )
-* MarkX build ( #2, #3 )
-* MarkX Action ( #21 )
-* MarkX Tests ( #15 )
-
+## MarkX 0.1.1:
+
+* MarkX Help (#26)
+* MarkX now accepts commands / help (#27)
+* MarkX.Headings gets heading elements (#29)
+* MarkX.Lexicon gets lexicons in Markdown (#25)
+* MarkX.YamlHeader support (#32, #33)
+* Allowing piped markdown file input (#34)
+* MarkX.Code gets code within markdown (#30)
+* MarkX.CodeBlock gets code blocks within markdown (#31)
+* MarkX.ToString can now stringify any property (#35)
+* MarkX formatting (#36)
+
+---
+
+Additional release notes in [CHANGELOG](https://github.com/StartAutomating/MarkX/blob/main/CHANGELOG.md)
 '@
 
         # Prerelease string of this module

From a0d93d568e804d994d249fd0be232c880a05eb17 Mon Sep 17 00:00:00 2001
From: StartAutomating 
Date: Mon, 12 Jan 2026 12:32:12 -0800
Subject: [PATCH 38/38] release: MarkX 0.1.1

Updating Module Version
---
 MarkX.psd1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MarkX.psd1 b/MarkX.psd1
index ec13d31..cc90e7c 100644
--- a/MarkX.psd1
+++ b/MarkX.psd1
@@ -12,7 +12,7 @@
 RootModule = 'MarkX.psm1'
 
 # Version number of this module.
-ModuleVersion = '0.1'
+ModuleVersion = '0.1.1'
 
 # Supported PSEditions
 # CompatiblePSEditions = @()