Private/PresentationExportCodecs.ps1
|
function Get-TerminalExportTableShape { param([AllowNull()][object]$Data) $rows = @($Data) $columns = [Collections.Generic.List[string]]::new() foreach ($row in $rows) { $names = if ($row -is [Collections.IDictionary]) { @(Get-TerminalCanonicalDictionaryKeys -Dictionary $row) } else { @($row.PSObject.Properties.Name) } foreach ($name in $names) { if (-not $columns.Contains($name)) { $columns.Add($name) } } } [pscustomobject]@{ Columns = $columns.ToArray(); Rows = $rows } } function ConvertTo-TerminalMarkdownCell { param([AllowNull()][object]$Value) if ($null -eq $Value) { return '' } $rows = Split-TerminalLogicalRows -Text ([string]$Value) return ($rows | ForEach-Object { $_.Replace('|', '\|') }) -join '<br>' } function ConvertTo-TerminalMarkdownTable { param([AllowNull()][object]$Data) $shape = Get-TerminalExportTableShape -Data $Data if ($shape.Columns.Count -eq 0) { return @('_No data_') } $lines = [Collections.Generic.List[string]]::new() $lines.Add('| ' + (($shape.Columns | ForEach-Object { ConvertTo-TerminalMarkdownCell $_ }) -join ' | ') + ' |') $lines.Add('| ' + (($shape.Columns | ForEach-Object { '---' }) -join ' | ') + ' |') foreach ($row in $shape.Rows) { $values = foreach ($column in $shape.Columns) { ConvertTo-TerminalMarkdownCell (Get-TerminalSemanticProperty $row $column) } $lines.Add('| ' + ($values -join ' | ') + ' |') } return ,$lines.ToArray() } function ConvertTo-TerminalHtmlTable { param([AllowNull()][object]$Data) $encode = { param($Value) ConvertTo-TerminalHtmlEncodedText -Value $Value } $shape = Get-TerminalExportTableShape -Data $Data if ($shape.Columns.Count -eq 0) { return '<p><em>No data</em></p>' } $head = ($shape.Columns | ForEach-Object { '<th>' + (& $encode $_) + '</th>' }) -join '' $body = foreach ($row in $shape.Rows) { $cells = foreach ($column in $shape.Columns) { '<td>' + (& $encode (Get-TerminalSemanticProperty $row $column)) + '</td>' } '<tr>' + ($cells -join '') + '</tr>' } return '<table><thead><tr>' + $head + '</tr></thead><tbody>' + ($body -join '') + '</tbody></table>' } function ConvertTo-TerminalCssColor { param( [AllowNull()][string]$Value, [Parameter(Mandatory)][string]$Fallback ) $color = if ([string]::IsNullOrWhiteSpace($Value)) { $Fallback } else { $Value } $rgb = Convert-HexToRgb -Hex $color return '#{0:X2}{1:X2}{2:X2}' -f $rgb[0], $rgb[1], $rgb[2] } function ConvertTo-TerminalHtmlStyle { param([Parameter(Mandatory)][TerminalSlides.Schema.V1.ThemeDefinition]$Theme) $background = ConvertTo-TerminalCssColor $Theme.Background '#000000' $foreground = ConvertTo-TerminalCssColor $Theme.Foreground '#FFFFFF' $primary = ConvertTo-TerminalCssColor $Theme.Primary $foreground $accent = ConvertTo-TerminalCssColor $Theme.Accent $primary $heading = ConvertTo-TerminalCssColor $Theme.Heading $primary $border = ConvertTo-TerminalCssColor $Theme.Border $primary $codeBackground = ConvertTo-TerminalCssColor $Theme.CodeBackground $background $codeForeground = ConvertTo-TerminalCssColor $Theme.CodeForeground $foreground return @" :root { --ts-background: $background; --ts-foreground: $foreground; --ts-primary: $primary; --ts-accent: $accent; --ts-heading: $heading; --ts-border: $border; --ts-code-background: $codeBackground; --ts-code-foreground: $codeForeground; } body { font-family: system-ui, sans-serif; background: var(--ts-background); color: var(--ts-foreground); margin: 0; padding: 2rem; } .slide { background: var(--ts-code-background); border: 1px solid var(--ts-border); border-radius: 12px; padding: 2rem; margin-bottom: 2rem; } h1, h2, h3, h4, figcaption { color: var(--ts-heading); } pre { background: var(--ts-code-background); color: var(--ts-code-foreground); padding: 1rem; overflow-x: auto; } blockquote { border-left: 4px solid var(--ts-accent); margin: 1rem 0; padding-left: 1rem; } table { border-collapse: collapse; margin: 1rem 0; } th, td { border: 1px solid var(--ts-border); padding: .5rem; text-align: left; } img { display: block; height: auto; max-width: 100%; } .box { border: 1px solid var(--ts-primary); border-radius: .5rem; padding: 1rem; } .slide h2, .slide h3, .slide h4, .slide p, .slide li, .slide th, .slide td, .slide figcaption, .slide footer, .slide .box { white-space: pre-wrap; } "@ } function ConvertTo-TerminalMarkdownElement { param([Parameter(Mandatory)][TerminalSlides.Schema.V1.SlideElement]$Element) $payload = Get-TerminalElementPayload -Element $Element switch ($payload.Kind) { 'Title' { "## $($payload.Raw)" } 'Subtitle' { "### $($payload.Raw)" } 'Text' { [string]$payload.Raw } 'Bullet' { "- $($payload.Raw)" } 'Code' { '```' + $payload.Language + "`n" + $payload.Code + "`n" + '```' } 'Table' { (ConvertTo-TerminalMarkdownTable $payload.Raw) -join "`n" } 'Chart' { $parts = [Collections.Generic.List[string]]::new() if ($payload.Title) { $parts.Add("**$($payload.Title)**") } if ($payload.ChartType) { $parts.Add("_Chart: $($payload.ChartType)_") } $parts.Add((ConvertTo-TerminalMarkdownTable $payload.Raw) -join "`n") $parts -join "`n`n" } 'Diagram' { $lines = [Collections.Generic.List[string]]::new(); $lines.Add('**Diagram**') foreach ($node in $payload.Nodes) { $lines.Add("- [$([string](Get-TerminalSemanticProperty $node Id))] $([string](Get-TerminalSemanticProperty $node Label))") } foreach ($edge in $payload.Edges) { $label = Get-TerminalSemanticProperty $edge Label; $suffix = if ($label) { " ($label)" } else { '' } $lines.Add("- $([string](Get-TerminalSemanticProperty $edge From)) -> $([string](Get-TerminalSemanticProperty $edge To))$suffix") } $lines -join "`n" } 'Image' { $alt = ConvertTo-TerminalMarkdownImageAlt $payload.AltText $destination = ConvertTo-TerminalMarkdownImageDestination $payload.Path "" } 'Quote' { $quoteRows = [string[]](Split-TerminalLogicalRows -Text $payload.Text) $lines = @($quoteRows | ForEach-Object { "> $_" }) if ($payload.Attribution) { $attributionRows = [string[]](Split-TerminalLogicalRows -Text $payload.Attribution) $lines += "> — $($attributionRows[0])" if ($attributionRows.Count -gt 1) { $lines += @($attributionRows[1..($attributionRows.Count - 1)] | ForEach-Object { "> $_" }) } } $lines -join "`n" } 'Box' { "> **Key point:** $($payload.Raw)" } } } function ConvertTo-TerminalHtmlElement { param([Parameter(Mandatory)][TerminalSlides.Schema.V1.SlideElement]$Element) $encode = { param($Value) ConvertTo-TerminalHtmlEncodedText -Value $Value } $payload = Get-TerminalElementPayload -Element $Element switch ($payload.Kind) { 'Title' { '<h3>' + (& $encode $payload.Raw) + '</h3>' } 'Subtitle' { '<h4>' + (& $encode $payload.Raw) + '</h4>' } 'Text' { '<p>' + (& $encode $payload.Raw) + '</p>' } 'Bullet' { '<ul><li>' + (& $encode $payload.Raw) + '</li></ul>' } 'Code' { '<pre><code class="language-' + (& $encode $payload.Language) + '">' + (& $encode $payload.Code) + '</code></pre>' } 'Table' { ConvertTo-TerminalHtmlTable $payload.Raw } 'Chart' { $caption = if ($payload.Title) { '<figcaption>' + (& $encode $payload.Title) + '</figcaption>' } else { '' } '<figure class="chart" data-chart-type="' + (& $encode $payload.ChartType) + '">' + $caption + (ConvertTo-TerminalHtmlTable $payload.Raw) + '</figure>' } 'Diagram' { $nodes = foreach ($node in $payload.Nodes) { '<li><code>' + (& $encode (Get-TerminalSemanticProperty $node Id)) + '</code> ' + (& $encode (Get-TerminalSemanticProperty $node Label)) + '</li>' } $edges = foreach ($edge in $payload.Edges) { $label = Get-TerminalSemanticProperty $edge Label; $suffix = if ($label) { ' (' + (& $encode $label) + ')' } else { '' } '<li>' + (& $encode (Get-TerminalSemanticProperty $edge From)) + ' → ' + (& $encode (Get-TerminalSemanticProperty $edge To)) + $suffix + '</li>' } '<figure class="diagram"><figcaption>Diagram</figcaption><ul class="nodes">' + ($nodes -join '') + '</ul><ul class="edges">' + ($edges -join '') + '</ul></figure>' } 'Image' { '<figure class="image"><img src="' + (& $encode $payload.Path) + '" alt="' + (& $encode $payload.AltText) + '" /></figure>' } 'Quote' { $footer = if ($payload.Attribution) { '<footer>— ' + (& $encode $payload.Attribution) + '</footer>' } else { '' } '<blockquote><p>' + (& $encode $payload.Text) + '</p>' + $footer + '</blockquote>' } 'Box' { '<aside class="box">' + (& $encode $payload.Raw) + '</aside>' } } } function ConvertTo-TerminalMarkdownDocument { param([Parameter(Mandatory)][TerminalSlides.Schema.V1.TerminalPresentation]$Presentation) $builder = [Text.StringBuilder]::new() $themeName = (Resolve-TerminalPresentationTheme -Presentation $Presentation).Name [void]$builder.Append("---`n") [void]$builder.Append('title: ' + ($Presentation.Title | ConvertTo-Json -Compress) + "`n") if ($Presentation.Author) { [void]$builder.Append('author: ' + ($Presentation.Author | ConvertTo-Json -Compress) + "`n") } [void]$builder.Append('theme: ' + ($themeName | ConvertTo-Json -Compress) + "`n") [void]$builder.Append("---`n`n") foreach ($slide in $Presentation.Slides) { [void]$builder.Append("# $($slide.Title)`n`n") foreach ($element in $slide.Elements) { [void]$builder.Append((ConvertTo-TerminalMarkdownElement $element)) [void]$builder.Append("`n`n") } if ($slide.Notes) { [void]$builder.Append('<!-- Notes: ' + $slide.Notes + " -->`n`n") } [void]$builder.Append("---`n`n") } return ConvertTo-TerminalLfText -Value $builder.ToString() } |