├── .github
├── FUNDING.yml
└── workflows
│ ├── BuildShowDemo.yml
│ ├── GitPub.yml
│ └── SendPSA.yml
├── Assets
├── ShowDemo-animated.svg
├── ShowDemo.svg
├── ShowDemo@1080p.png
├── demo.gif
└── demo.mp4
├── Build
├── GitHub
│ ├── Actions
│ │ └── DemoPowerShell.ps1
│ └── Jobs
│ │ ├── BuildShowDemo.psd1
│ │ └── SendPSA.psd1
├── ShowDemo.GitHubAction.PSDevOps.ps1
├── ShowDemo.GitHubWorkflow.PSDevOps.ps1
├── ShowDemo.HelpOut.ps1
├── ShowDemo.PSA.ps1
├── ShowDemo.PSSVG.ps1
└── ShowDemo.ezout.ps1
├── CHANGELOG.md
├── Commands
├── Export-Demo.ps1
├── Get-Demo.ps.ps1
├── Get-Demo.ps1
├── Import-Demo.ps.ps1
├── Import-Demo.ps1
├── Resume-Demo.ps1
└── Show-Demo.ps1
├── Demos
├── Demo.demo.md
├── Demo.demo.ps1
├── Trinity-Of-Discoverability.demo.md
└── Trinity-Of-Discoverability.demo.ps1
├── Dockerfile
├── LICENSE
├── README.md
├── ShowDemo.format.ps1xml
├── ShowDemo.ps.psm1
├── ShowDemo.psd1
├── ShowDemo.psm1
├── ShowDemo.tests.ps1
├── ShowDemo.types.ps1xml
├── Types
├── Demo.Chapter
│ └── get_Steps.ps1
├── Demo.Step
│ ├── HidePrompt.ps1
│ ├── Invoke.ps1
│ ├── ShowPrompt.ps1
│ ├── Silent.ps1
│ ├── get_HiddenStep.ps1
│ └── get_IsComment.ps1
└── Demo
│ ├── Demo.format.ps1
│ ├── Dump.ps1
│ ├── NextChapter.ps1
│ ├── NextStep.ps1
│ ├── ProcessInput.ps1
│ ├── Reset.ps1
│ ├── SetChapter.ps1
│ ├── SetStatus.ps1
│ ├── ShowStep.ps1
│ ├── Start.ps1
│ ├── StartChapter.ps1
│ ├── Stop.ps1
│ ├── ToMarkdown.ps1
│ └── get_TotalSteps.ps1
├── action.yml
└── docs
├── 2022-12-04.md
├── 2022-12.md
├── 2022.md
├── 2023-05-23.md
├── 2023-05.md
├── 2023-06-15.md
├── 2023-06.md
├── 2023-08-02.md
├── 2023-08-18.md
├── 2023-08.md
├── 2023-10-10.md
├── 2023-10.md
├── 2023.md
├── 2024-03-09.md
├── 2024-03.md
├── 2024-04-14.md
├── 2024-04.md
├── 2024.md
├── Assets
├── ShowDemo-animated.svg
├── ShowDemo.svg
├── ShowDemo@1080p.png
├── demo.gif
└── demo.mp4
├── CHANGELOG.md
├── CNAME
├── Demo.demo.md
├── Demo
├── NextChapter.md
├── NextStep.md
├── ProcessInput.md
├── README.md
├── Reset.md
├── SetStatus.md
├── Start.md
├── Step
│ ├── HidePrompt.md
│ ├── Invoke.md
│ ├── README.md
│ ├── ShowPrompt.md
│ └── Silent.md
└── Stop.md
├── Export-Demo.md
├── Get-Demo.md
├── Import-Demo.md
├── README.md
├── Resume-Demo.md
├── Show-Demo.md
├── Start-Demo.md
├── Trinity-Of-Discoverability.demo.md
├── _config.yml
├── _posts
├── 2022-12-04-ShowDemo-0.1.md
├── 2022-12-04-Showing-PowerShell-Demos.md
├── 2023-05-23-ShowDemo-0.1.1.md
├── 2023-06-15-ShowDemo-0.1.2.md
├── 2023-08-02-ShowDemo-0.1.3.md
├── 2023-08-18-ShowDemo-0.1.4.md
├── 2023-10-10-ShowDemo-0.1.5.md
├── 2024-03-09-ShowDemo-0.1.6.md
└── 2024-04-14-ShowDemo-0.1.7.md
└── rss.xml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [StartAutomating]
2 |
--------------------------------------------------------------------------------
/.github/workflows/GitPub.yml:
--------------------------------------------------------------------------------
1 |
2 | name: GitPub
3 | on:
4 | workflow_dispatch:
5 | jobs:
6 | RunGitPub:
7 | runs-on: ubuntu-latest
8 | if: ${{ success() }}
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v2
12 | - name: Use GitPub Action
13 | uses: StartAutomating/GitPub@main
14 | id: GitPub
15 | with:
16 | TargetBranch: edits-$([DateTime]::Now.ToString("r").Replace(":","-").Replace(" ", ""))
17 | CommitMessage: Posting with GitPub [skip ci]
18 | PublishParameters: |
19 | {
20 | "Get-GitPubIssue": {
21 | "Repository": '${{github.repository}}'
22 | },
23 | "Get-GitPubRelease": {
24 | "Repository": '${{github.repository}}'
25 | },
26 | "Publish-GitPubJekyll": {
27 | "OutputPath": "docs/_posts"
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/SendPSA.yml:
--------------------------------------------------------------------------------
1 |
2 | name: show-demo-psa
3 | on:
4 | workflow_dispatch:
5 | jobs:
6 | SendPSA:
7 | runs-on: ubuntu-latest
8 | if: ${{ success() }}
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v3
12 | - name: PSA
13 | uses: StartAutomating/PSA@main
14 | id: PSA
15 | env:
16 | AT_PROTOCOL_APP_PASSWORD: ${{ secrets.AT_PROTOCOL_APP_PASSWORD }}
17 | AT_PROTOCOL_HANDLE: mrpowershell.bsky.social
18 |
--------------------------------------------------------------------------------
/Assets/ShowDemo-animated.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Show
13 |
14 | Demo
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Assets/ShowDemo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Show
12 | Demo
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Assets/ShowDemo@1080p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/Assets/ShowDemo@1080p.png
--------------------------------------------------------------------------------
/Assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/Assets/demo.gif
--------------------------------------------------------------------------------
/Assets/demo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/Assets/demo.mp4
--------------------------------------------------------------------------------
/Build/GitHub/Actions/DemoPowerShell.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | GitHub Action for ShowDemo
4 | .Description
5 | GitHub Action for ShowDemo. This will:
6 |
7 | * Import ShowDemo
8 | * Get all demos in the current directory
9 | * Export each demo to a markdown file.
10 | * Run any .ShowDemo.ps1 scripts
11 | * Run the content of the .ShowDemoScript parameter
12 |
13 | Any files changed can be outputted by the script, and those changes can be checked back into the repo.
14 | Make sure to use the "persistCredentials" option with checkout.
15 | #>
16 |
17 | param(
18 | # A PowerShell Script that uses ShowDemo.
19 | # Any files outputted from the script will be added to the repository.
20 | # If those files have a .Message attached to them, they will be committed with that message.
21 | [string]
22 | $ShowDemoScript,
23 |
24 | # If set, will not process any files named *.ShowDemo.ps1
25 | [switch]
26 | $SkipShowDemoPS1,
27 |
28 | # The name of the module for which types and formats are being generated.
29 | # If not provided, this will be assumed to be the name of the root directory.
30 | [string]
31 | $ModuleName,
32 |
33 | # If provided, will commit any remaining changes made to the workspace with this commit message.
34 | [string]
35 | $CommitMessage,
36 |
37 | # The user email associated with a git commit.
38 | [string]
39 | $UserEmail,
40 |
41 | # The user name associated with a git commit.
42 | [string]
43 | $UserName
44 | )
45 |
46 | #region Initial Logging
47 |
48 | # Output the parameters passed to this script (for debugging)
49 | "::group::Parameters" | Out-Host
50 | [PSCustomObject]$PSBoundParameters | Format-List | Out-Host
51 | "::endgroup::" | Out-Host
52 |
53 | # Get the GitHub Event
54 | $gitHubEvent =
55 | if ($env:GITHUB_EVENT_PATH) {
56 | [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json
57 | } else { $null }
58 |
59 | # Log the GitHub Event
60 | @"
61 | ::group::GitHubEvent
62 | $($gitHubEvent | ConvertTo-Json -Depth 100)
63 | ::endgroup::
64 | "@ | Out-Host
65 |
66 | # Check that there is a workspace (and throw if there is not)
67 | if (-not $env:GITHUB_WORKSPACE) { throw "No GitHub workspace" }
68 |
69 | #endregion Initial Logging
70 |
71 | # Check to ensure we are on a branch
72 | $branchName = git rev-parse --abrev-ref HEAD
73 | # If we were not, return.
74 | if (-not $branchName) {
75 | "::warning::Not on a branch" | Out-Host
76 | return
77 | }
78 |
79 | #region Configure UserName and Email
80 | if (-not $UserName) {
81 | $UserName =
82 | if ($env:GITHUB_TOKEN) {
83 | Invoke-RestMethod -uri "https://api.github.com/user" -Headers @{
84 | Authorization = "token $env:GITHUB_TOKEN"
85 | } |
86 | Select-Object -First 1 -ExpandProperty name
87 | } else {
88 | $env:GITHUB_ACTOR
89 | }
90 | }
91 |
92 | if (-not $UserEmail) {
93 | $GitHubUserEmail =
94 | if ($env:GITHUB_TOKEN) {
95 | Invoke-RestMethod -uri "https://api.github.com/user/emails" -Headers @{
96 | Authorization = "token $env:GITHUB_TOKEN"
97 | } |
98 | Select-Object -First 1 -ExpandProperty email
99 | } else {''}
100 | $UserEmail =
101 | if ($GitHubUserEmail) {
102 | $GitHubUserEmail
103 | } else {
104 | "$UserName@github.com"
105 | }
106 | }
107 | git config --global user.email $UserEmail
108 | git config --global user.name $UserName
109 | #endregion Configure UserName and Email
110 |
111 |
112 | git pull | Out-Host
113 |
114 |
115 | #region Load Action Module
116 | $ActionModuleName = "ShowDemo"
117 | $ActionModuleFileName = "$ActionModuleName.psd1"
118 |
119 | # Try to find a local copy of the action's module.
120 | # This allows the action to use the current branch's code instead of the action's implementation.
121 | $PSD1Found = Get-ChildItem -Recurse -Filter "*.psd1" |
122 | Where-Object Name -eq $ActionModuleFileName |
123 | Select-Object -First 1
124 |
125 | $ActionModulePath, $ActionModule =
126 | # If there was a .PSD1 found
127 | if ($PSD1Found) {
128 | $PSD1Found.FullName # import from there.
129 | Import-Module $PSD1Found.FullName -Force -PassThru
130 | }
131 | # Otherwise, if we have a GITHUB_ACTION_PATH
132 | elseif ($env:GITHUB_ACTION_PATH)
133 | {
134 | $actionModulePath = Join-Path $env:GITHUB_ACTION_PATH $ActionModuleFileName
135 | if (Test-path $actionModulePath) {
136 | $actionModulePath
137 | Import-Module $actionModulePath -Force -PassThru
138 | } else {
139 | throw "$actionModuleName not found"
140 | }
141 | }
142 | elseif (-not (Get-Module $ActionModuleName)) {
143 | throw "$actionModulePath could not be loaded."
144 | }
145 |
146 | "::notice title=ModuleLoaded::$actionModuleName Loaded from Path - $($actionModulePath)" | Out-Host
147 | #endregion Load Action Module
148 |
149 |
150 | #region Install/Import Other Modules
151 | @"
152 | ::group::Installing Modules
153 | $(
154 | "Installing ugit" | Out-Host
155 | Install-Module -Name ugit -Scope CurrentUser -Force
156 | "Importing ugit" | Out-Host
157 | Import-Module ugit -Force -PassThru -Global | Out-Host
158 | )
159 | ::endgroup::
160 | "@ | Out-Host
161 |
162 |
163 | #endregion Install Other Modules
164 |
165 | #region Declare Functions and Variables
166 | $anyFilesChanged = $false
167 | filter ProcessScriptOutput {
168 | $out = $_
169 | $outItem = Get-Item -Path $out -ErrorAction SilentlyContinue
170 | $fullName, $shouldCommit =
171 | if ($out -is [IO.FileInfo]) {
172 | $out.FullName, (git status $out.Fullname -s)
173 | } elseif ($outItem) {
174 | $outItem.FullName, (git status $outItem.Fullname -s)
175 | }
176 | if ($shouldCommit) {
177 | git add $fullName
178 | if ($out.Message) {
179 | git commit -m "$($out.Message)"
180 | } elseif ($out.CommitMessage) {
181 | git commit -m "$($out.CommitMessage)"
182 | } elseif ($out.SourceFile) {
183 | "Source File: $($out.SourceFile)" | Out-Host
184 | $lastCommitMessage = $out.SourceFile |
185 | git log -n 1 |
186 | Select-Object -ExpandProperty CommitMessage
187 | if ($lastCommitMessage) {
188 | git commit -m $lastCommitMessage
189 | }
190 | } elseif ($gitHubEvent.head_commit.message) {
191 | git commit -m "$($gitHubEvent.head_commit.message)"
192 | }
193 | $anyFilesChanged = $true
194 | }
195 | $out
196 | }
197 |
198 | #endregion Declare Functions and Variables
199 |
200 |
201 | #region Actual Action
202 |
203 | $ShowDemoScriptStart = [DateTime]::Now
204 | if ($ShowDemoScript) {
205 | Invoke-Expression -Command $ShowDemoScript |
206 | . processScriptOutput |
207 | Out-Host
208 | }
209 | $ShowDemoScriptTook = [Datetime]::Now - $ShowDemoScriptStart
210 | $ShowDemoPS1Start = [DateTime]::Now
211 | $ShowDemoPS1List = @()
212 | if (-not $SkipShowDemoPS1) {
213 | $ShowDemoFiles = @(
214 | Get-ChildItem -Recurse -Path $env:GITHUB_WORKSPACE |
215 | Where-Object Name -Match '\.ShowDemo\.ps1$')
216 |
217 | if ($ShowDemoFiles) {
218 | $ShowDemoFiles|
219 | ForEach-Object {
220 | $ShowDemoPS1List += $_.FullName.Replace($env:GITHUB_WORKSPACE, '').TrimStart('/')
221 | $ShowDemoPS1Count++
222 | "::notice title=Running::$($_.Fullname)" | Out-Host
223 | . $_.FullName |
224 | . processScriptOutput |
225 | Out-Host
226 | }
227 | }
228 | }
229 |
230 | "Fetching Changes" | Out-Host
231 | git fetch --unshallow | Out-Host
232 |
233 | #region Export-Demo
234 | "Looking for demos in $env:GITHUB_WORKSPACE" | Out-Host
235 | Get-ChildItem -Path $env:GITHUB_WORKSPACE -Recurse -Filter *.ps1 |
236 | Where-Object Name -Match '(?<=\.|^)(?>demo|walkthru)\.ps1$' |
237 | ForEach-Object {
238 | $demoFile = $_
239 | $demoFileOut =
240 | $demoFile |
241 | Export-Demo -OutputPath {
242 | $_.FullName -replace '\.ps1$', '.md'
243 | }
244 |
245 | $lastCommitMessage =
246 | git log $demoFile.FullName |
247 | Select-Object -ExpandProperty CommitMessage -First 1
248 |
249 | "LastCommitMessage for $($demoFile.Name): $lastcommitMessage" | Out-Host
250 | $demoFileOut |
251 | Add-Member NoteProperty CommitMessage $lastCommitMessage -Force -PassThru |
252 | . processScriptOutput
253 | }
254 |
255 |
256 | #endregion Export-Demo
257 |
258 | $ShowDemoPS1EndStart = [DateTime]::Now
259 | $ShowDemoPS1Took = [Datetime]::Now - $ShowDemoPS1Start
260 | if ($CommitMessage -or $anyFilesChanged) {
261 | if ($CommitMessage) {
262 | dir $env:GITHUB_WORKSPACE -Recurse |
263 | ForEach-Object {
264 | $gitStatusOutput = git status $_.Fullname -s
265 | if ($gitStatusOutput) {
266 | git add $_.Fullname
267 | }
268 | }
269 |
270 | git commit -m $ExecutionContext.SessionState.InvokeCommand.ExpandString($CommitMessage)
271 | }
272 |
273 | $checkDetached = git symbolic-ref -q HEAD
274 | if (-not $LASTEXITCODE) {
275 | "::notice::Pulling Updates" | Out-Host
276 | git pull
277 | "::notice::Pushing Changes" | Out-Host
278 | git push
279 | "Git Push Output: $($gitPushed | Out-String)"
280 | } else {
281 | "::notice::Not pushing changes (on detached head)" | Out-Host
282 | $LASTEXITCODE = 0
283 | exit 0
284 | }
285 | }
286 |
287 | #endregion Actual Action
288 |
289 |
--------------------------------------------------------------------------------
/Build/GitHub/Jobs/BuildShowDemo.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | "runs-on" = "ubuntu-latest"
3 | if = '${{ success() }}'
4 | steps = @(
5 | @{
6 | name = 'Check out repository'
7 | uses = 'actions/checkout@v3'
8 | },
9 | @{
10 | name = 'GitLogger'
11 | uses = 'GitLogging/GitLoggerAction@main'
12 | id = 'GitLogger'
13 | },
14 | @{
15 | name = 'Use PSSVG Action'
16 | uses = 'StartAutomating/PSSVG@main'
17 | id = 'PSSVG'
18 | },
19 | 'RunPipeScript',
20 | 'RunEZOut',
21 | @{
22 | name = 'Use GitPub Action'
23 | uses = 'StartAutomating/GitPub@main'
24 | id = 'GitPub'
25 | with = @{
26 | PublishParameters = @'
27 | {
28 | "Get-GitPubIssue": {
29 | "Repository": '${{github.repository}}'
30 | },
31 | "Get-GitPubRelease": {
32 | "Repository": '${{github.repository}}'
33 | },
34 | "Publish-GitPubJekyll": {
35 | "OutputPath": "docs/_posts"
36 | }
37 | }
38 | '@
39 | }
40 | },
41 | @{
42 | name = 'Run ShowDemo (on branch)'
43 | if = '${{github.ref_name != ''main''}}'
44 | uses = './'
45 | id = 'ShowDemo'
46 | },
47 | 'RunHelpOut',
48 | @{
49 | name = 'PSA'
50 | uses = 'StartAutomating/PSA@main'
51 | id = 'PSA'
52 | }
53 | @{
54 | 'name'='Log in to the Container registry (on branch)'
55 | 'uses'='docker/login-action@master'
56 | 'with'=@{
57 | 'registry'='${{ env.REGISTRY }}'
58 | 'username'='${{ github.actor }}'
59 | 'password'='${{ secrets.GITHUB_TOKEN }}'
60 | }
61 | },
62 | @{
63 | 'name'='Extract metadata (tags, labels) for Docker'
64 | if = '${{github.ref_name != ''main'' && github.ref_name != ''master'' && github.ref_name != ''latest''}}'
65 | 'id'='meta'
66 | 'uses'='docker/metadata-action@master'
67 | 'with'=@{
68 | 'images'='${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}'
69 | }
70 | },
71 | @{
72 | 'name'='Extract metadata (tags, labels) for Docker'
73 | if = '${{github.ref_name == ''main'' || github.ref_name == ''master'' || github.ref_name == ''latest''}}'
74 | 'id'='metaMain'
75 | 'uses'='docker/metadata-action@master'
76 | 'with'=@{
77 | 'images'='${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}'
78 | 'flavor'='latest=true'
79 | }
80 | },
81 | @{
82 | name = 'Build and push Docker image (from main)'
83 | if = '${{github.ref_name == ''main'' || github.ref_name == ''master'' || github.ref_name == ''latest''}}'
84 | uses = 'docker/build-push-action@master'
85 | 'with'=@{
86 | 'context'='.'
87 | 'push'='true'
88 | 'tags'='${{ steps.metaMain.outputs.tags }}'
89 | 'labels'='${{ steps.metaMain.outputs.labels }}'
90 | }
91 | },
92 | @{
93 | name = 'Build and push Docker image (from branch)'
94 | if = '${{github.ref_name != ''main'' && github.ref_name != ''master'' && github.ref_name != ''latest''}}'
95 | uses = 'docker/build-push-action@master'
96 | with = @{
97 | 'context'='.'
98 | 'push'='true'
99 | 'tags'='${{ steps.meta.outputs.tags }}'
100 | 'labels'='${{ steps.meta.outputs.labels }}'
101 | }
102 | }
103 | )
104 | }
--------------------------------------------------------------------------------
/Build/GitHub/Jobs/SendPSA.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | "runs-on" = "ubuntu-latest"
3 | if = '${{ success() }}'
4 | steps = @(
5 | @{
6 | name = 'Check out repository'
7 | uses = 'actions/checkout@v3'
8 | },
9 | @{
10 | name = 'PSA'
11 | uses = 'StartAutomating/PSA@main'
12 | id = 'PSA'
13 | }
14 | )
15 | }
--------------------------------------------------------------------------------
/Build/ShowDemo.GitHubAction.PSDevOps.ps1:
--------------------------------------------------------------------------------
1 | #requires -Module PSDevOps
2 | #requires -Module ShowDemo
3 | Import-BuildStep -SourcePath (
4 | Join-Path $PSScriptRoot 'GitHub'
5 | ) -BuildSystem GitHubWorkflow
6 |
7 | Push-Location ($PSScriptRoot | Split-Path)
8 | New-GitHubAction -Name "DemoPowerShell" -Description @'
9 | Make Demos of your PowerShell projects.
10 | '@ -Action DemoPowerShell -Icon terminal -OutputPath .\action.yml
11 | Pop-Location
--------------------------------------------------------------------------------
/Build/ShowDemo.GitHubWorkflow.PSDevOps.ps1:
--------------------------------------------------------------------------------
1 |
2 | #requires -Module PSDevOps
3 | #requires -Module ShowDemo
4 | #requires -Module GitPub
5 |
6 | Import-BuildStep -SourcePath (
7 | Join-Path $PSScriptRoot 'GitHub'
8 | ) -BuildSystem GitHubWorkflow
9 |
10 | Push-Location ($PSScriptRoot | Split-Path)
11 |
12 | New-GitHubWorkflow -Job PowerShellStaticAnalysis, TestPowerShellOnLinux, TagReleaseAndPublish, BuildShowDemo -OutputPath @'
13 | .\.github\workflows\BuildShowDemo.yml
14 | '@ -Name "Build, Test, and Release ShowDemo" -On Push, PullRequest -Env ([Ordered]@{
15 | "AT_PROTOCOL_HANDLE" = "mrpowershell.bsky.social"
16 | "AT_PROTOCOL_APP_PASSWORD" = '${{ secrets.AT_PROTOCOL_APP_PASSWORD }}'
17 | "REGISTRY" = "ghcr.io"
18 | "IMAGE_NAME" = '${{ github.repository }}'
19 | })
20 |
21 | Import-BuildStep -ModuleName GitPub
22 |
23 | New-GitHubWorkflow -On Demand -Job RunGitPub -Name GitPub -OutputPath @'
24 | .\.github\workflows\GitPub.yml
25 | '@
26 |
27 | New-GitHubWorkflow -On Demand -Name 'show-demo-psa' -Job SendPSA -OutputPath .\.github\workflows\SendPSA.yml -Env @{
28 | "AT_PROTOCOL_HANDLE" = "mrpowershell.bsky.social"
29 | "AT_PROTOCOL_APP_PASSWORD" = '${{ secrets.AT_PROTOCOL_APP_PASSWORD }}'
30 | }
31 |
32 | Pop-Location
33 |
34 |
--------------------------------------------------------------------------------
/Build/ShowDemo.HelpOut.ps1:
--------------------------------------------------------------------------------
1 | #requires -Module HelpOut
2 | Push-Location ($PSScriptRoot | Split-Path)
3 | Import-Module .\ShowDemo.psd1
4 | Save-MarkdownHelp -Module ShowDemo -PassThru
5 | Pop-Location
--------------------------------------------------------------------------------
/Build/ShowDemo.PSA.ps1:
--------------------------------------------------------------------------------
1 | # Any *.PSA.ps1 file will be run when PSA runs.
2 |
3 | # A good thing to do at the start of this file is to connect.
4 |
5 | Connect-BlueSky
6 |
7 | # If $env:AT_PROTOCOL_HANDLE or $env:AT_PROTOCOL_EMAIL is set, it will be treated as the username
8 | # If $env:AT_PROTOCOL_APP_PASSWORD is set, it will be treated as the App Password.
9 | # _Never_ use your actual BlueSky password
10 |
11 | # Once we're connected, we can do anything our app password allows.
12 |
13 | # However, you _might_ want to output some information first, so that you can see you're connected.
14 |
15 | Get-BskyActorProfile -Actor $env:AT_PROTOCOL_HANDLE -Cache | Out-Host
16 |
17 | # To ensure you're not going to send a skeet on every checkin, it's a good idea to ask what GitHub is up to
18 |
19 | # There will be a variable, $GitHubEvent, that contains information about the event.
20 |
21 | # A fairly common scenario is to perform an annoucement whenever a PR is merged.
22 |
23 | $isMergeToMain =
24 | ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and
25 | $gitHubEvent.ref -eq 'refs/heads/main'
26 |
27 | $importedModule = Import-Module .\ShowDemo.psd1 -Global -PassThru
28 | $importedModule | Out-Host
29 | $moduleAndVersion = "$($importedModule.Name) $($importedModule.Version)"
30 |
31 | $isManuallyTriggered = $gitHubEvent.psobject.properties["inputs"]
32 |
33 | if ($isMergeToMain -or $isManuallyTriggered) {
34 |
35 | $fullMessage = @(
36 | "Show off your scripts",
37 | "Demo your PowerShell",
38 | "Never typo during a talk again" | Get-Random
39 |
40 | "$($ImportedModule.Name)"
41 | ) -join [Environment]::NewLine
42 |
43 | $sendSplat = [Ordered]@{
44 | Text = $fullMessage
45 | }
46 | if ($importedModule.PrivateData.PSData.ProjectURI) {
47 | $sendSplat.WebCard = @{Url=$importedModule.PrivateData.PSData.ProjectURI}
48 | $sendSplat.LinkPattern = @{$importedModule.Name=$importedModule.PrivateData.PSData.ProjectURI}
49 | }
50 |
51 | Send-AtProto @sendSplat
52 | return
53 | }
--------------------------------------------------------------------------------
/Build/ShowDemo.PSSVG.ps1:
--------------------------------------------------------------------------------
1 | #requires -Module PSSVG
2 | Push-Location ($psScriptRoot | Split-Path)
3 | $powerShellChevron = Invoke-RestMethod https://pssvg.start-automating.com/Examples/PowerShellChevron.svg
4 | $assetsPath = Join-Path $pwd Assets
5 | $scaleMin = 1
6 | $scaleMax = 1.02
7 |
8 |
9 | $FontSplat = [Ordered]@{
10 | FontFamily = "sans-serif"
11 | }
12 |
13 | $φ = (1.0 + [Math]::Sqrt(5))/2
14 |
15 | $AnimateSplat = [Ordered]@{
16 | Dur = 60/128*8
17 | AttributeName = 'font-size'
18 | Values = "${scaleMin}em;${scaleMax}em;${scaleMin}em"
19 | RepeatCount = 'indefinite'
20 | }
21 |
22 | $AnimateSplat2 = [Ordered]@{} + $AnimateSplat
23 | $AnimateSplat2.Values = "${scaleMax}em;${scaleMin}em;${scaleMax}em"
24 |
25 | if (-not (Test-path $assetsPath)) {
26 | $null = New-Item -ItemType Directory -Path $assetsPath -Force
27 | }
28 |
29 |
30 | foreach ($variant in '','animated') {
31 | svg @(
32 | SVG.GoogleFont -FontName $FontName
33 | svg.symbol -ViewBox $powerShellChevron.svg.viewBox -Content $powerShellChevron.svg.symbol.InnerXml -Id psChevron
34 | $RectSplat = [Ordered]@{
35 | Rx=30
36 | Ry=(30 / $φ)
37 | Stroke="#4488ff"
38 | StrokeWidth="1%"
39 | Fill='transparent'
40 | Width = 250
41 | Height = 250 / $φ
42 | X = 25
43 | Y = 25 / $φ
44 | }
45 | SVG.rect @RectSplat
46 |
47 | svg.use -href '#psChevron' -X '12.5%' -Y '-2%' -Width '12.5%' -Stroke '#4488ff' -Fill '#4488ff'
48 | svg.text @(
49 | svg.tspan "Show" -Children @(
50 | if ($variant -match 'animated') {
51 | SVG.animate @AnimateSplat
52 | }
53 | ) -FontSize "${scaleMin}em"
54 | svg.tspan "Demo" -Children @(
55 | if ($variant -match 'animated') {
56 | SVG.animate @AnimateSplat2 -Begin ($dur / 2)
57 | }
58 | ) -FontSize "${scaleMin}em"
59 | ) -FontSize 32 -Fill '#4488ff' -X 50% -DominantBaseline 'middle' -TextAnchor 'middle' -Y 50% @FontSplat
60 | ) -ViewBox 300, ([Math]::Floor(300 / $φ)) -OutputPath (Join-Path $assetsPath "ShowDemo$(if ($variant){"-$($variant)"}).svg")
61 | }
62 |
63 | Pop-Location
64 |
--------------------------------------------------------------------------------
/Build/ShowDemo.ezout.ps1:
--------------------------------------------------------------------------------
1 | #requires -Module EZOut
2 | # Install-Module EZOut or https://github.com/StartAutomating/EZOut
3 | $myFile = $MyInvocation.MyCommand.ScriptBlock.File
4 | $myModuleName = 'ShowDemo'
5 | $myRoot = $myFile | Split-Path | Split-Path
6 | Push-Location $myRoot
7 | $formatting = @(
8 | # Add your own Write-FormatView here,
9 | # or put them in a Formatting or Views directory
10 | foreach ($potentialDirectory in 'Formatting','Views','Types') {
11 | Join-Path $myRoot $potentialDirectory |
12 | Get-ChildItem -ea ignore |
13 | Import-FormatView -FilePath {$_.Fullname}
14 | }
15 | )
16 |
17 | $destinationRoot = $myRoot
18 |
19 | if ($formatting) {
20 | $myFormatFile = Join-Path $destinationRoot "$myModuleName.format.ps1xml"
21 | $formatting | Out-FormatData -Module $MyModuleName | Set-Content $myFormatFile -Encoding UTF8
22 | Get-Item $myFormatFile
23 | }
24 |
25 | $types = @(
26 | # Add your own Write-TypeView statements here
27 | # or declare them in the 'Types' directory
28 | Join-Path $myRoot Types |
29 | Get-Item -ea ignore |
30 | Import-TypeView
31 |
32 | )
33 |
34 | if ($types) {
35 | $myTypesFile = Join-Path $destinationRoot "$myModuleName.types.ps1xml"
36 | $types | Out-TypeData | Set-Content $myTypesFile -Encoding UTF8
37 | Get-Item $myTypesFile
38 | }
39 | Pop-Location
40 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | > Like It? [Star It](https://github.com/StartAutomating/ShowDemo)
2 | > Love It? [Support It](https://github.com/sponsors/StartAutomating)
3 |
4 | ---
5 |
6 | ## ShowDemo 0.1.7:
7 |
8 | * ShowDemo in Docker (#103)
9 | * Added Dockerfile (#104)
10 | * Publishing all builds to GitHub Container Registry (#105)
11 | * Added Trinity of Discoverability Demo (#51)
12 | * Exporting $ShowDemo (#106)
13 | * Mounting as ShowDemo: (#107)
14 |
15 | ---
16 |
17 | ## ShowDemo 0.1.6:
18 |
19 | * Show-Demo Syncing Console Encoding ( Fixes #101 )
20 | * Show-Demo -PauseBetweenLine(s) ( Fixes #100 )
21 | * Adjusting Default Type Speed ( Fixes #97 )
22 | * Showing unknown steps in White, not Output ( Fixes #99 )
23 |
24 | ---
25 |
26 | ## ShowDemo 0.1.5:
27 |
28 | * Demos are now more eventful (#66)
29 | * Nearly every part of ShowDemo transmits PowerShell engine events
30 | * These can be used for highly customized display of demos
31 | * Refactoring all *-Demo commands to use a single -From parameter (#86)
32 | * Added Logo (#90)
33 | * Integrated PSA (#91)
34 |
35 | ---
36 |
37 | ## ShowDemo 0.1.4:
38 |
39 | * ShowDemo - Adding Recommendations (Fixes #63)
40 | * Demo Format - Honoring .StartMessage/.EndMessage (Fixes #62)
41 | * Show-Demo - Adding -StartMessage/-EndMessage (Fixes #61)
42 |
43 | ---
44 |
45 | ## ShowDemo 0.1.3:
46 |
47 | * Adding support for prompts in demos
48 | * Demo.Step - Adding .ShowPrompt()/HidePrompt() (#54/#55)
49 | * Demo Formatting - Supporting ShowPrompt (#56)
50 | * Show-Demo - Adding -ShowPrompt (#53)
51 | * Import-Demo - Linking Chapters (#57)
52 | * Partitioning repository (#48, #49, #50)
53 |
54 | ---
55 |
56 | ## ShowDemo 0.1.2:
57 |
58 | * Get-Demo - Skipping $pwd if in $filePaths (Fixes #43)
59 | * Show-Demo - Adding -Record (Fixes #42)
60 | * Import-Demo - Including .DemoScript (Fixes #44)
61 | * Adding Demo.ToMarkdown (Fixes #45)
62 |
63 | ---
64 |
65 | ## ShowDemo 0.1.1
66 |
67 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep (#39)
68 | * Export-Demo - Defaults to English when invariant culture (Fixes #37)
69 | * Improvements in how steps are determined (#35 #36)
70 | * Please Sponsor ShowDemo (#38)
71 |
72 | ---
73 |
74 | ## ShowDemo 0.1
75 |
76 | Initial Release of Show-Demo.
77 |
78 | * List Demos with Get-Demo
79 | * Show Demos with Show-Demo
80 | * Export Demos with Export-Demo.
81 | * ShowDemo GitHub Action
82 |
83 |
--------------------------------------------------------------------------------
/Commands/Export-Demo.ps1:
--------------------------------------------------------------------------------
1 | function Export-Demo
2 | {
3 | <#
4 | .SYNOPSIS
5 | Exports Demos
6 | .DESCRIPTION
7 | Exports a Demo.
8 |
9 | Demos can be saved to a Markdown (.md) file or a Clixml (.clixml/.clix)file
10 | .EXAMPLE
11 | Export-Demo -DemoPath .\demo.ps1 -OutputPath .\demo.md
12 | #>
13 | param(
14 | # The source of the demo. This can be a string, file, command, module, or path.
15 | [Parameter(Mandatory,
16 | ValueFromPipelineByPropertyName,
17 | ParameterSetName='DemoFile')]
18 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
19 | [PSObject]
20 | $From,
21 |
22 | # The output path. This is the location the demo will be saved to.
23 | [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
24 | [ValidatePattern('\.(?>clixml|clix|md)$')]
25 | [Alias('OutputFile')]
26 | [string]
27 | $OutputPath
28 | )
29 |
30 | begin {
31 | $extensionPattern = '\.(?>ps1|clixml|clix|md|html)$'
32 |
33 | # In order to make Markdown demos render correctly, we need to override Write-Host
34 | # (in case the demo Writes to the host)
35 | function Write-Host {
36 | <#
37 | .ForwardHelpTargetName Write-Host
38 | .ForwardHelpCategory Cmdlet
39 | #>
40 |
41 | [CmdletBinding()]
42 | [OutputType([Nullable])]
43 | param(
44 | [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
45 | [System.Object]
46 | ${Object},
47 |
48 | [Switch]
49 | ${NoNewline},
50 |
51 | [System.Object]
52 | ${Separator},
53 |
54 | [System.ConsoleColor]
55 | ${ForegroundColor},
56 |
57 | [System.ConsoleColor]
58 | ${BackgroundColor}
59 | )
60 | process {
61 | #region Override Write-Host for inside of the demo
62 | if ($demo) {
63 | # Write as HTML
64 | $objectHtml = $Object
65 |
66 | $styleChunk = if ($ForegroundColor -or $backgroundColor) {
67 | if ($ForegroundColor -and $backgroundcolor) {
68 | " style='color:${ForeGroundColor};background=${BackgroundColor}'"
69 | } else {
70 | if ($ForegroundColor) {
71 | " style='color:${ForeGroundColor}'"
72 | } else {
73 | " style='background=${BackgroundColor}'"
74 | }
75 | }
76 | } else {
77 | ""
78 | }
79 | $tag = "span"
80 | "<${tag}${styleChunk}>${ObjectHtml}${tag}>$(if (-not $NoNewLine) {' '})"
81 | } else {
82 | # If we're not in a web site...
83 | Microsoft.PowerShell.Utility\Write-Host @psboundParameters
84 | }
85 | #endregion Override Write-Host for web context
86 | }
87 | }
88 |
89 | }
90 | process {
91 | $demoContents = Get-Demo -From $From
92 | if (-not $demoContents) { return }
93 |
94 | if ($OutputPath -notmatch $extensionPattern) {
95 | Write-Error "Invalid Extension"
96 | return
97 | }
98 | $extension = $matches.0
99 | $unresolvedOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
100 | if ($extension -in '.clixml', '.clix') {
101 | Export-Clixml -LiteralPath $unresolvedOutput -InputObject $demoContents
102 |
103 | Get-Item -LiteralPath $unresolvedOutput |
104 | Add-Member NoteProperty SourceFile $demoContents.DemoFile -Force -PassThru
105 | }
106 | elseif ($extension -eq '.md') {
107 | if ([Globalization.CultureInfo]::CurrentCulture.LCID -eq 127) {
108 | [Globalization.CultureInfo]::CurrentCulture = 'en-us'
109 | }
110 | $demoContents | Add-Member NoteProperty Markdown $true -Force
111 | $demoContents | Add-Member NoteProperty Interactive $false -Force
112 | $demoContents |
113 | Format-Custom |
114 | Out-String -Width 1mb |
115 | Set-Content -LiteralPath $unresolvedOutput -Encoding UTF8
116 |
117 | Get-Item -LiteralPath $unresolvedOutput |
118 | Add-Member NoteProperty SourceFile $demoContents.DemoFile -Force -PassThru
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Commands/Get-Demo.ps.ps1:
--------------------------------------------------------------------------------
1 | function Get-Demo
2 | {
3 | <#
4 | .SYNOPSIS
5 | Gets Demos
6 | .DESCRIPTION
7 | Gets PowerShell Demos.
8 |
9 | Demos located in ShowDemo and all modules that tag ShowDemo will be automatically discovered.
10 | .LINK
11 | Import-Demo
12 | .EXAMPLE
13 | Get-Demo
14 | #>
15 | [CmdletBinding(DefaultParameterSetName='LoadedDemos')]
16 | param(
17 | # The source of the demo. This can be a string, file, command, module, or path.
18 | [vbn(Mandatory,ParameterSetName='DemoFile')]
19 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
20 | [PSObject]
21 | $From
22 | )
23 |
24 | begin {
25 | # If we have not initialized a cache and we're inside of a module
26 | if (-not $script:CachedDemos.Count -and $MyInvocation.MyCommand.ScriptBlock.Module) {
27 | $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
28 | Import-Demo -From $MyModule | Out-Null
29 | }
30 | }
31 |
32 | process {
33 | if ($from) {
34 | Import-Demo -From $from
35 | } elseif ($script:CachedDemos.Count) {
36 | $script:CachedDemos.Values
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Commands/Get-Demo.ps1:
--------------------------------------------------------------------------------
1 | function Get-Demo {
2 |
3 | <#
4 | .SYNOPSIS
5 | Gets Demos
6 | .DESCRIPTION
7 | Gets PowerShell Demos.
8 |
9 | Demos located in ShowDemo and all modules that tag ShowDemo will be automatically discovered.
10 | .LINK
11 | Import-Demo
12 | .EXAMPLE
13 | Get-Demo
14 | #>
15 | [CmdletBinding(DefaultParameterSetName='LoadedDemos')]
16 | param(
17 | # The source of the demo. This can be a string, file, command, module, or path.
18 | [Parameter(Mandatory,ParameterSetName='DemoFile',ValueFromPipelineByPropertyName)]
19 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
20 | [PSObject]
21 | $From
22 | )
23 |
24 | begin {
25 | # If we have not initialized a cache and we're inside of a module
26 | if (-not $script:CachedDemos.Count -and $MyInvocation.MyCommand.ScriptBlock.Module) {
27 | $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
28 | Import-Demo -From $MyModule | Out-Null
29 | }
30 | }
31 |
32 | process {
33 | if ($from) {
34 | Import-Demo -From $from
35 | } elseif ($script:CachedDemos.Count) {
36 | $script:CachedDemos.Values
37 | }
38 | }
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/Commands/Import-Demo.ps.ps1:
--------------------------------------------------------------------------------
1 | function Import-Demo
2 | {
3 | <#
4 | .SYNOPSIS
5 | Imports Demos
6 | .DESCRIPTION
7 | Imports a Demo script.
8 | .LINK
9 | Export-Demo
10 | .LINK
11 | Get-Demo
12 | .LINK
13 | Start-Demo
14 | .EXAMPLE
15 | Import-Demo -DemoName "Demo"
16 | #>
17 | param(
18 | # The source of the demo. This can be a string, file, command, module, or path.
19 | [Parameter(Mandatory,ValueFromPipelineByPropertyName,ParameterSetName='DemoFile')]
20 | [ValidateTypes(TypeName={
21 | [IO.FileInfo]
22 | [IO.DirectoryInfo]
23 | [Management.Automation.PathInfo]
24 | [Management.Automation.CommandInfo]
25 | [Management.Automation.PSModuleInfo]
26 | [string]
27 | })]
28 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
29 | [PSObject]
30 | $From
31 | )
32 |
33 | begin {
34 | $ChapterExpression = '^\s{0,}(?(?:\d+\.){1,})\s{0,}'
35 | $ValidDemoFile = [regex]::new('\.demo\.(?>ps1|clixml)$','IgnoreCase')
36 | if (-not $script:CachedDemos) {
37 | $script:CachedDemos = [Ordered]@{}
38 | }
39 | # If we have not initialized a cache and we're inside of a module
40 | if (-not $script:CachedDemos.Count -and $MyInvocation.MyCommand.ScriptBlock.Module) {
41 | # we'll need to import our own demos.
42 | # to ensure this only happens once,
43 | $MyCmd = $MyInvocation.MyCommand
44 | $myDepth = 0
45 | foreach ($frame in Get-PSCallStack) { # we callstack peek
46 | if ($frame.InvocationInfo.MyCommand.Name -eq "$MyCmd") {
47 | $myDepth++ # and track our depth
48 | }
49 | }
50 | if ($myDepth -eq 1) { # and only import if we are one level deep.
51 | $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
52 | Import-Demo -From $MyModule | Out-Null
53 | }
54 | }
55 |
56 | # Next we declare a few internal functions.
57 | # Essentially, our import will boil down to a few possible scenarios:
58 |
59 | # The easy one is Importing a demo .clixml
60 | function Import-DemoClixml {
61 | if ($FromFileInfo -match '\.(clix|clixml)$') {
62 | return Import-Clixml -LiteralPath $FromFileInfo.FullName
63 | }
64 | }
65 | # The big one is Importing a demo from a script
66 | function Import-DemoScript {
67 | if (-not $FromScriptBlock) {
68 | return
69 | }
70 |
71 | $demoName =
72 | if ($FromCommand) {
73 | $FromCommand.Name -replace $ValidDemoFile
74 | } elseif ($FromFileInfo) {
75 | $FromFileInfo.Name -replace $ValidDemoFile
76 | }
77 |
78 | $astString = "$from"
79 | $psTokens = [Management.Automation.PSParser]::Tokenize($astString, [ref]$null)
80 | if (-not $psTokens) { return }
81 |
82 | $chapters = @()
83 | $currentChapter = $null
84 | $chapterTokens = @()
85 |
86 | # We want every step to be able to run independently.
87 | # This would be untrue if the code is unbalanced when a chapter would start
88 | # Thus, while we're primarily looking for comments, we also need to track groups
89 | $groupDepth = 0
90 | $previousToken = $null
91 | # Walk thru every token in the file.
92 | foreach ($token in $psTokens) {
93 | Add-Member NoteProperty PreviousToken $previousToken -Force -InputObject $token
94 | Add-Member NoteProperty Text $astString -Force -InputObject $token
95 | $previousToken = $token
96 | if ($token.Type -in 'Variable', 'String') {
97 | $realContent = $astString.Substring($token.Start, $token.Length)
98 | Add-Member NoteProperty Content $realContent -Force -InputObject $token
99 | }
100 | # If the token is a group start
101 | if ($token.Type -eq 'GroupStart')
102 | {
103 | $groupDepth++ # increment depth.
104 | }
105 | # If the token was a group end
106 | elseif ($token.Type -eq 'GroupEnd')
107 | {
108 | $groupDepth-- # decrement depth.
109 | }
110 | # If there was no depth
111 | # and the token was a comment starting in the first column.
112 | elseif (
113 | (-not $groupDepth) -and
114 | $token.Type -eq 'Comment' -and $token.StartColumn -le 1
115 | )
116 | {
117 | $tokenContent = $token.Content -replace '^#' -replace '#$'
118 | # Then it could be the start of a chapter.
119 |
120 | # If it is not,
121 | if ($tokenContent -notmatch $ChapterExpression) {
122 | $chapterTokens += $token # add it to the current chapter.
123 | }
124 |
125 | # If the comment does start a chapter
126 | else {
127 | # get the chapter number from `$matches`.
128 | $chapterNumber = $matches.cn
129 | # Then get the chapter name by replacing the regex.
130 | $chapterName = $tokenContent -replace $ChapterExpression
131 |
132 | # Create a new chapter, starting at the current token.
133 | $newChapter = [Ordered]@{
134 | Number = $chapterNumber
135 | Name = $chapterName
136 | Text = $astString
137 | Start = $token.Start
138 | DemoFile = $FromFileInfo.FullName
139 | }
140 |
141 | # If there was already a current chapter
142 | if ($currentChapter) {
143 | # finalize it by marking it's end
144 | $currentChapter.Length =
145 | $chapterTokens[-1].Start + $chapterTokens[-1].End - $currentChapter.Start
146 | # and attaching the tokens we have so far.
147 | $currentChapter.Tokens = $chapterTokens
148 | $chapterTokens = @()
149 | $chapters += $currentChapter
150 | }
151 |
152 | # Then, make the new chapter the current chapter
153 | $currentChapter = $newChapter
154 | }
155 | }
156 | else
157 | {
158 | $chapterTokens += $token
159 | }
160 | }
161 |
162 | # If we have a named chapter
163 | if ($currentChapter) {
164 | # add any remaining tokens to it.
165 | $currentChapter.Tokens = $chapterTokens
166 | $chapterTokens = @()
167 | # and add the chapter.
168 | $chapters += $currentChapter
169 | } elseif ($chapterTokens) {
170 | # Otherwise, if we have chapter tokens by no named chapter
171 | # create an empty chapter and add the steps to it.
172 | $chapters += [Ordered]@{
173 | Number = ''
174 | Name = ''
175 | Tokens = $chapterTokens
176 | Text = $astString
177 | }
178 | }
179 |
180 | $demoScript = [scriptblock]::create($astString)
181 |
182 | # Create the demo object.
183 | $demoFile = [PSCustomObject][Ordered]@{
184 | PSTypeName = 'Demo'
185 | Name = $demoName
186 | DemoFile = $fileInfo.FullName
187 | DemoScript = $DemoScript
188 | DemoText = "$DemoScript"
189 | }
190 |
191 |
192 | if ($demoName) {
193 | $script:CachedDemos[$demoName] = $demoFile
194 | }
195 |
196 | # And add each chapter to it
197 | $demoFile |
198 | Add-Member NoteProperty Chapters @(
199 | foreach ($chapter in $chapters) {
200 | # linking back to the demo as we go (#57).
201 | [PSCustomObject]([Ordered]@{PSTypeName='Demo.Chapter';Demo=$demoFile} + $chapter)
202 | }
203 | ) -Force -PassThru # We -PassThru the modified object to return it.
204 | }
205 | }
206 |
207 | process {
208 | # Since -From can be many things, but a demo has to be a ScriptBlock,
209 | # the purpose of this function is to essentially resolve many things to one or more ScriptBlocks.
210 | $fromModule, $FromDirectory, $FromCommand, $FromFileInfo, $fromScriptBlock = $null, $null,$null, $null, $null
211 |
212 | # If -From was a string
213 | if ($From -is [string]) {
214 | # It could be a module, so check those first.
215 | :ResolveFromString do {
216 | if ($from -match '\n') {
217 | # If the -From had newlines, try to make a [ScriptBlock]
218 | $fromScriptBlock = try { [ScriptBlock]::create($from) } catch { }
219 | if ($fromScriptBlock) { break }
220 | }
221 |
222 | foreach ($loadedModule in @(Get-Module)) {
223 | # If we find the module, don't try to resolve -From as a path
224 | break ResolveFromString
225 | if ($loadedModule.Name -eq $from) {
226 | # (just set -From again and let the function continue)
227 | $from = $fromModule = $loadedModule
228 | }
229 | }
230 | if ($script:CachedDemos["$From"]) {
231 | $script:CachedDemos["$From"]
232 | } else {
233 | # If we think from was a path
234 | foreach ($resolvedPath in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from)) {
235 | Import-Demo -From $resolvedPath
236 | }
237 | }
238 | return
239 | } while ($false)
240 | }
241 |
242 | if ($From -is [Management.Automation.PSModuleInfo]) {
243 | # then, make -From a directory
244 | if ($from.Path) {
245 | $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue
246 | }
247 | }
248 |
249 | # If -From is a path
250 | if ($from -is [Management.Automation.PathInfo]) {
251 | $from = Get-Item -LiteralPath "$from" # turn it into a file or a directory
252 | }
253 |
254 | # If -From is a directory
255 | if ($from -is [IO.DirectoryInfo]) {
256 | $FromDirectory = $from
257 | # recursively call ourselves with all matching scripts
258 | Get-ChildItem -LiteralPath $from.FullName -Recurse -File |
259 | Where-Object Name -match $ValidDemoFile |
260 | Import-Demo
261 | return
262 | }
263 |
264 | # If -From is a file
265 | if ($from -is [IO.FileInfo]) {
266 | # set $FromFileInfo
267 | $FromFileInfo = $from
268 | # and make -From a command.
269 | if ($from.Extension -eq '.ps1') {
270 | $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript')
271 | }
272 | }
273 |
274 | # If -From is a command
275 | if ($from -is [Management.Automation.CommandInfo]) {
276 | $FromCommand = $From
277 | # and it is an external script
278 | if ($from -is [Management.Automation.ExternalScriptInfo]) {
279 | # then use that script's contents as the source
280 | $FromScriptBlock = $from.ScriptBlock
281 | if (-not $FromFileInfo) {
282 | $FromFileInfo = [IO.FileInfo]$from.Source
283 | }
284 | $From = "$FromScriptBlock" # (stringified)
285 | }
286 | else {
287 |
288 | # Otherwise, see if it has help and examples
289 | $cmdHelp = Get-Help $from -ErrorAction Ignore
290 | if ($cmdHelp -and $cmdHelp.examples.example) {
291 | # and make the demo the combined sequence of examples
292 | $allExampleLines = @(
293 | foreach ($example in $cmdHelp.Examples.example) {
294 | $example.Code
295 | foreach ($remark in $example.Remarks.text) {
296 | if (-not $remark) { continue }
297 | $remark
298 | }
299 | }
300 | ) -join [Environment]::NewLine
301 | try {
302 | # at least, assuming we can convert it into a [ScriptBlock]
303 | $From = $fromScriptBlock = [scriptblock]::create($allExampleLines)
304 | } catch {
305 | $ex = $_
306 | Write-Error "Example in $from cannot be converted into a ScriptBlock"
307 | return
308 | }
309 | }
310 | }
311 | }
312 |
313 |
314 | if ($fromScriptBlock) {
315 | . Import-DemoScript
316 | }
317 | elseif ($FromFileInfo) {
318 | . Import-DemoClixml
319 | }
320 | }
321 | }
322 |
323 |
--------------------------------------------------------------------------------
/Commands/Import-Demo.ps1:
--------------------------------------------------------------------------------
1 | function Import-Demo {
2 |
3 | <#
4 | .SYNOPSIS
5 | Imports Demos
6 | .DESCRIPTION
7 | Imports a Demo script.
8 | .LINK
9 | Export-Demo
10 | .LINK
11 | Get-Demo
12 | .LINK
13 | Start-Demo
14 | .EXAMPLE
15 | Import-Demo -DemoName "Demo"
16 | #>
17 | param(
18 | # The source of the demo. This can be a string, file, command, module, or path.
19 | [Parameter(Mandatory,ValueFromPipelineByPropertyName,ParameterSetName='DemoFile')]
20 | [ValidateScript({
21 | $validTypeList = [System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.PathInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo],[System.String]
22 |
23 | $thisType = $_.GetType()
24 | $IsTypeOk =
25 | $(@( foreach ($validType in $validTypeList) {
26 | if ($_ -as $validType) {
27 | $true;break
28 | }
29 | }))
30 |
31 | if (-not $isTypeOk) {
32 | throw "Unexpected type '$(@($thisType)[0])'. Must be 'System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.PathInfo','System.Management.Automation.CommandInfo','psmoduleinfo','string'."
33 | }
34 | return $true
35 | })]
36 |
37 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
38 | [PSObject]
39 | $From
40 | )
41 |
42 | begin {
43 | $ChapterExpression = '^\s{0,}(?(?:\d+\.){1,})\s{0,}'
44 | $ValidDemoFile = [regex]::new('\.demo\.(?>ps1|clixml)$','IgnoreCase')
45 | if (-not $script:CachedDemos) {
46 | $script:CachedDemos = [Ordered]@{}
47 | }
48 | # If we have not initialized a cache and we're inside of a module
49 | if (-not $script:CachedDemos.Count -and $MyInvocation.MyCommand.ScriptBlock.Module) {
50 | # we'll need to import our own demos.
51 | # to ensure this only happens once,
52 | $MyCmd = $MyInvocation.MyCommand
53 | $myDepth = 0
54 | foreach ($frame in Get-PSCallStack) { # we callstack peek
55 | if ($frame.InvocationInfo.MyCommand.Name -eq "$MyCmd") {
56 | $myDepth++ # and track our depth
57 | }
58 | }
59 | if ($myDepth -eq 1) { # and only import if we are one level deep.
60 | $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
61 | Import-Demo -From $MyModule | Out-Null
62 | }
63 | }
64 |
65 | # Next we declare a few internal functions.
66 | # Essentially, our import will boil down to a few possible scenarios:
67 |
68 | # The easy one is Importing a demo .clixml
69 | function Import-DemoClixml {
70 |
71 | if ($FromFileInfo -match '\.(clix|clixml)$') {
72 | return Import-Clixml -LiteralPath $FromFileInfo.FullName
73 | }
74 |
75 | }
76 | # The big one is Importing a demo from a script
77 | function Import-DemoScript {
78 |
79 | if (-not $FromScriptBlock) {
80 | return
81 | }
82 |
83 | $demoName =
84 | if ($FromCommand) {
85 | $FromCommand.Name -replace $ValidDemoFile
86 | } elseif ($FromFileInfo) {
87 | $FromFileInfo.Name -replace $ValidDemoFile
88 | }
89 |
90 | $astString = "$from"
91 | $psTokens = [Management.Automation.PSParser]::Tokenize($astString, [ref]$null)
92 | if (-not $psTokens) { return }
93 |
94 | $chapters = @()
95 | $currentChapter = $null
96 | $chapterTokens = @()
97 |
98 | # We want every step to be able to run independently.
99 | # This would be untrue if the code is unbalanced when a chapter would start
100 | # Thus, while we're primarily looking for comments, we also need to track groups
101 | $groupDepth = 0
102 | $previousToken = $null
103 | # Walk thru every token in the file.
104 | foreach ($token in $psTokens) {
105 | Add-Member NoteProperty PreviousToken $previousToken -Force -InputObject $token
106 | Add-Member NoteProperty Text $astString -Force -InputObject $token
107 | $previousToken = $token
108 | if ($token.Type -in 'Variable', 'String') {
109 | $realContent = $astString.Substring($token.Start, $token.Length)
110 | Add-Member NoteProperty Content $realContent -Force -InputObject $token
111 | }
112 | # If the token is a group start
113 | if ($token.Type -eq 'GroupStart')
114 | {
115 | $groupDepth++ # increment depth.
116 | }
117 | # If the token was a group end
118 | elseif ($token.Type -eq 'GroupEnd')
119 | {
120 | $groupDepth-- # decrement depth.
121 | }
122 | # If there was no depth
123 | # and the token was a comment starting in the first column.
124 | elseif (
125 | (-not $groupDepth) -and
126 | $token.Type -eq 'Comment' -and $token.StartColumn -le 1
127 | )
128 | {
129 | $tokenContent = $token.Content -replace '^#' -replace '#$'
130 | # Then it could be the start of a chapter.
131 |
132 | # If it is not,
133 | if ($tokenContent -notmatch $ChapterExpression) {
134 | $chapterTokens += $token # add it to the current chapter.
135 | }
136 |
137 | # If the comment does start a chapter
138 | else {
139 | # get the chapter number from `$matches`.
140 | $chapterNumber = $matches.cn
141 | # Then get the chapter name by replacing the regex.
142 | $chapterName = $tokenContent -replace $ChapterExpression
143 |
144 | # Create a new chapter, starting at the current token.
145 | $newChapter = [Ordered]@{
146 | Number = $chapterNumber
147 | Name = $chapterName
148 | Text = $astString
149 | Start = $token.Start
150 | DemoFile = $FromFileInfo.FullName
151 | }
152 |
153 | # If there was already a current chapter
154 | if ($currentChapter) {
155 | # finalize it by marking it's end
156 | $currentChapter.Length =
157 | $chapterTokens[-1].Start + $chapterTokens[-1].End - $currentChapter.Start
158 | # and attaching the tokens we have so far.
159 | $currentChapter.Tokens = $chapterTokens
160 | $chapterTokens = @()
161 | $chapters += $currentChapter
162 | }
163 |
164 | # Then, make the new chapter the current chapter
165 | $currentChapter = $newChapter
166 | }
167 | }
168 | else
169 | {
170 | $chapterTokens += $token
171 | }
172 | }
173 |
174 | # If we have a named chapter
175 | if ($currentChapter) {
176 | # add any remaining tokens to it.
177 | $currentChapter.Tokens = $chapterTokens
178 | $chapterTokens = @()
179 | # and add the chapter.
180 | $chapters += $currentChapter
181 | } elseif ($chapterTokens) {
182 | # Otherwise, if we have chapter tokens by no named chapter
183 | # create an empty chapter and add the steps to it.
184 | $chapters += [Ordered]@{
185 | Number = ''
186 | Name = ''
187 | Tokens = $chapterTokens
188 | Text = $astString
189 | }
190 | }
191 |
192 | $demoScript = [scriptblock]::create($astString)
193 |
194 | # Create the demo object.
195 | $demoFile = [PSCustomObject][Ordered]@{
196 | PSTypeName = 'Demo'
197 | Name = $demoName
198 | DemoFile = $fileInfo.FullName
199 | DemoScript = $DemoScript
200 | DemoText = "$DemoScript"
201 | }
202 |
203 |
204 | if ($demoName) {
205 | $script:CachedDemos[$demoName] = $demoFile
206 | }
207 |
208 | # And add each chapter to it
209 | $demoFile |
210 | Add-Member NoteProperty Chapters @(
211 | foreach ($chapter in $chapters) {
212 | # linking back to the demo as we go (#57).
213 | [PSCustomObject]([Ordered]@{PSTypeName='Demo.Chapter';Demo=$demoFile} + $chapter)
214 | }
215 | ) -Force -PassThru # We -PassThru the modified object to return it.
216 |
217 | }
218 | }
219 |
220 | process {
221 | # Since -From can be many things, but a demo has to be a ScriptBlock,
222 | # the purpose of this function is to essentially resolve many things to one or more ScriptBlocks.
223 | $fromModule, $FromDirectory, $FromCommand, $FromFileInfo, $fromScriptBlock = $null, $null,$null, $null, $null
224 |
225 | # If -From was a string
226 | if ($From -is [string]) {
227 | # It could be a module, so check those first.
228 | :ResolveFromString do {
229 | if ($from -match '\n') {
230 | # If the -From had newlines, try to make a [ScriptBlock]
231 | $fromScriptBlock = try { [ScriptBlock]::create($from) } catch { }
232 | if ($fromScriptBlock) { break }
233 | }
234 |
235 | foreach ($loadedModule in @(Get-Module)) {
236 | # If we find the module, don't try to resolve -From as a path
237 | if ($loadedModule.Name -eq $from) {
238 | # (just set -From again and let the function continue)
239 | $from = $fromModule = $loadedModule;break ResolveFromString
240 | }
241 |
242 | }
243 | if ($script:CachedDemos["$From"]) {
244 | $script:CachedDemos["$From"]
245 | } else {
246 | # If we think from was a path
247 | foreach ($resolvedPath in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from)) {
248 | Import-Demo -From $resolvedPath
249 | }
250 | }
251 | return
252 | } while ($false)
253 | }
254 |
255 | if ($From -is [Management.Automation.PSModuleInfo]) {
256 | # then, make -From a directory
257 | if ($from.Path) {
258 | $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue
259 | }
260 | }
261 |
262 | # If -From is a path
263 | if ($from -is [Management.Automation.PathInfo]) {
264 | $from = Get-Item -LiteralPath "$from" # turn it into a file or a directory
265 | }
266 |
267 | # If -From is a directory
268 | if ($from -is [IO.DirectoryInfo]) {
269 | $FromDirectory = $from
270 | # recursively call ourselves with all matching scripts
271 | Get-ChildItem -LiteralPath $from.FullName -Recurse -File |
272 | Where-Object Name -match $ValidDemoFile |
273 | Import-Demo
274 | return
275 | }
276 |
277 | # If -From is a file
278 | if ($from -is [IO.FileInfo]) {
279 | # set $FromFileInfo
280 | $FromFileInfo = $from
281 | # and make -From a command.
282 | if ($from.Extension -eq '.ps1') {
283 | $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript')
284 | }
285 | }
286 |
287 | # If -From is a command
288 | if ($from -is [Management.Automation.CommandInfo]) {
289 | $FromCommand = $From
290 | # and it is an external script
291 | if ($from -is [Management.Automation.ExternalScriptInfo]) {
292 | # then use that script's contents as the source
293 | $FromScriptBlock = $from.ScriptBlock
294 | if (-not $FromFileInfo) {
295 | $FromFileInfo = [IO.FileInfo]$from.Source
296 | }
297 | $From = "$FromScriptBlock" # (stringified)
298 | }
299 | else {
300 |
301 | # Otherwise, see if it has help and examples
302 | $cmdHelp = Get-Help $from -ErrorAction Ignore
303 | if ($cmdHelp -and $cmdHelp.examples.example) {
304 | # and make the demo the combined sequence of examples
305 | $allExampleLines = @(
306 | foreach ($example in $cmdHelp.Examples.example) {
307 | $example.Code
308 | foreach ($remark in $example.Remarks.text) {
309 | if (-not $remark) { continue }
310 | $remark
311 | }
312 | }
313 | ) -join [Environment]::NewLine
314 | try {
315 | # at least, assuming we can convert it into a [ScriptBlock]
316 | $From = $fromScriptBlock = [scriptblock]::create($allExampleLines)
317 | } catch {
318 | $ex = $_
319 | Write-Error "Example in $from cannot be converted into a ScriptBlock"
320 | return
321 | }
322 | }
323 | }
324 | }
325 |
326 |
327 | if ($fromScriptBlock) {
328 | . Import-DemoScript
329 | }
330 | elseif ($FromFileInfo) {
331 | . Import-DemoClixml
332 | }
333 | }
334 |
335 | }
336 |
337 |
338 |
--------------------------------------------------------------------------------
/Commands/Resume-Demo.ps1:
--------------------------------------------------------------------------------
1 | function Resume-Demo
2 | {
3 | <#
4 | .SYNOPSIS
5 | Resumes a Demo
6 | .DESCRIPTION
7 | Resumes a Demo that was paused or debugged with `!`.
8 | .EXAMPLE
9 | Resume-Demo
10 | .LINK
11 | Show-Demo
12 | .LINK
13 | Get-Demo
14 | #>
15 | param(
16 | # The demo that will be resumed.
17 | [Parameter(ValueFromPipeline)]
18 | [PSTypeName('Demo')]
19 | $DemoToResume
20 | )
21 |
22 | process {
23 | if (-not $DemoToResume -and $demo) {
24 | $DemoToResume = $demo
25 | }
26 | if (-not $DemoToResume) {
27 | Write-Error "No demo to resume"
28 | return
29 | }
30 | $DemoToResume | Format-Custom
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Commands/Show-Demo.ps1:
--------------------------------------------------------------------------------
1 | function Show-Demo
2 | {
3 | <#
4 | .SYNOPSIS
5 | Shows a Demo
6 | .DESCRIPTION
7 | Shows a PowerShell Demo Script.
8 | .EXAMPLE
9 | Show-Demo
10 | .LINK
11 | Get-Demo
12 | #>
13 | [Alias('Start-Demo')]
14 | [CmdletBinding(DefaultParameterSetName='LoadedDemos')]
15 | param(
16 | # The source of the demo. This can be a string, file, command, module, or path.
17 | [Parameter(ValueFromPipelineByPropertyName)]
18 | [Alias('DemoPath','DemoName','DemoText','DemoScript','FullName', 'DemoFile', 'File', 'Source')]
19 | [PSObject]
20 | $From,
21 |
22 | # The name of the chapter
23 | [string]
24 | $Chapter,
25 |
26 | # The current step (within -Chapter)
27 | [ValidateRange(1,10000)]
28 | [int]
29 | $Step,
30 |
31 | # The typing style. Can be letters, words, or none.
32 | [ValidateSet('Letters','Words','None')]
33 | [string]
34 | $TypeStyle = 'Letters',
35 |
36 | # If this is an integer less than 10000, it will be considered 'words per minute'
37 | # Otherwise, this will be the timespan to wait between words / letters being displayed.
38 | [timespan]
39 | $TypeSpeed = [Timespan]"00:00:00.0028",
40 |
41 | # The amount of time to wait between each step.
42 | # If provided, implies -AutoPlay.
43 | [Alias('PauseBetweenSteps')]
44 | [timespan]
45 | $PauseBetweenStep,
46 |
47 | # The amount of time to wait between each line.
48 | # This can help demos that display a lot of information at once.
49 | [Alias('PauseBetweenLines')]
50 | [Timespan]
51 | $PauseBetweenLine = [timespan]"00:00:00.014",
52 |
53 | # If set, will automatically play demos.
54 | # Use -PauseBetweenStep to specify how long to wait between each step.
55 | [switch]
56 | $AutoPlay,
57 |
58 | # If set, will make the demo noniteractive.
59 | [switch]
60 | $NonInteractive,
61 |
62 | # If set, will show the prompt between each step.
63 | # This can also be enabled or disabled within a demo, with .ShowPrompt or .HidePrompt
64 | [switch]
65 | $ShowPrompt,
66 |
67 | # If set, will attempt to record the demo.
68 | # This presumes that [obs-powershell](https://github.com/StartAutomating/obs-powershell) is installed.
69 | [switch]
70 | $Record,
71 |
72 | # If provided, will set the message displayed at demo start.
73 | [string]
74 | $StartMessage,
75 |
76 | # If provided, will set the message displayed at demo start.
77 | [string]
78 | $EndMessage
79 | )
80 |
81 | process {
82 | $demoFile =
83 | if ($From) {
84 | Get-Demo -From $From
85 | } else {
86 | Get-Demo
87 | }
88 |
89 |
90 | if (-not $demoFile) {
91 | Write-Error "No demo to show"
92 | }
93 |
94 | $demoFile | Add-Member StartMessage $StartMessage -Force
95 | $demoFile | Add-Member EndMessage $EndMessage -Force
96 |
97 | if ($chapter) {
98 | $demoFile | Add-Member CurrentChapter $Chapter -Force
99 | }
100 | if ($step) {
101 | $demoFile | Add-Member CurrentStep $step -Force
102 | }
103 |
104 | if ($Record) {
105 | $demoFile | Add-Member RecordDemo $true -Force
106 | }
107 |
108 | if ($ShowPrompt) {
109 | $demoFile | Add-Member ShowPrompt $true -Force
110 | }
111 |
112 | $demoFile | Add-Member TypeStyle $TypeStyle -Force
113 | if ($TypeStyle -eq 'Letters' -and -not $TypeSpeed) {
114 | $TypeSpeed = [timespan]::FromMilliseconds(27)
115 | }
116 | elseif ($TypeStyle -eq 'Words' -and -not $TypeSpeed) {
117 | $TypeSpeed = [timespan]::FromMilliseconds(37)
118 | }
119 | $demoFile | Add-Member TypeSpeed $TypeSpeed -Force
120 |
121 |
122 | if ($NonInteractive -or
123 | ($Host.Name -eq 'Default Host') -or
124 | $env:BUILD_ID -or
125 | $env:GITHUB_WORKSPACE
126 | ) {
127 | $demoFile | Add-Member Interactive $false -Force
128 | } else {
129 | $demoFile | Add-Member Interactive $true -Force
130 | }
131 |
132 | if ($AutoPlay -or $PauseBetweenStep.TotalMilliseconds) {
133 | if (-not $PauseBetweenStep.TotalMilliseconds) {
134 | $PauseBetweenStep = [timespan]::FromMilliseconds(500)
135 | }
136 | $demoFile | Add-Member Autoplay $true -Force
137 | $demoFile | Add-Member PauseBetweenStep $PauseBetweenStep -Force
138 | }
139 |
140 | if ($PauseBetweenLine.TotalMilliseconds) {
141 | $demoFile | Add-Member PauseBetweenLine $PauseBetweenLine -Force
142 | }
143 |
144 | if ($NonInteractive) {
145 | $demoFile | Format-Custom | Out-String -Width 1mb
146 | } else {
147 | $demoFile | Format-Custom
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/Demos/Demo.demo.ps1:
--------------------------------------------------------------------------------
1 | # 1. Hello World in PowerShell
2 |
3 | # .Silent cls
4 |
5 | # 'Hello world' is a really simple script to write in PowerShell.
6 | # You just put it in quotes.
7 |
8 | "hello world"
9 |
10 | # This is because in PowerShell, unassigned output is returned.
11 |
12 | # 2. PowerShell and Objects
13 |
14 | # Everything in PowerShell is an object.
15 | # So I can tell you how many characters there are in hello world just by getting the .Length
16 | "hello world".Length
17 |
18 | # 3. Basic Math in PowerShell
19 |
20 | <#
21 | Math in PowerShell is also really straightforward.
22 | #>
23 | 1 + 1
24 |
25 | 9 / 5
26 |
27 | 1 + 1 + 2 + 1
28 |
29 | # 4. Basic string formatting with PowerShell
30 |
31 | # You can use .NET string formatting with PowerShell by using the -f operator
32 | '{0:c}' -f 1.99
33 |
34 | # The format string in on the left, and the value you're formatting is on the right.
35 |
36 | # '{0:c}' means 'format as currency'
37 |
38 | # The value we are formatting is 1.99.
39 |
40 | # We can also use the .NET type [string] to do the formatting:
41 | [string]::Format("{0:c}", 1.99)
42 |
43 | # You can do some fun things with PowerShell, like multiply strings to repeat them.
44 | '$' * 10
45 |
46 | # 5. The Object Pipeline (is money)
47 |
48 | # A cool and unique part of PowerShell is the object pipeline
49 |
50 | # You can send every object to a command by 'Piping' the object.
51 |
52 | # You can pipe as many commands together as you would like.
53 |
54 | # So you basically program in PowerShell by connecting the dots.
55 |
56 | # To display information in a color, we use the built in command Write-Host.
57 |
58 | # So let's see how much money we can make by connecting the dots.
59 |
60 | # The joke for a long time has been PowerShell + a pulse is $50/hr.
61 |
62 | '$' * 50 | Write-Host
63 |
64 | '{0:c}' -f 50
65 |
66 | # 40 hours a week
67 | '$' * 50 * 40 | Write-Host
68 |
69 | '{0:c}' -f (50 * 40)
70 |
71 | # 52 weeks a year
72 | '$' * 50 * 40 * 52 | Write-Host
73 | '{0:c}' -f (50 * 40 * 52)
74 |
75 | # Learn PowerShell.
76 | # Write Scripts.
77 | # Make Money.
--------------------------------------------------------------------------------
/Demos/Trinity-Of-Discoverability.demo.ps1:
--------------------------------------------------------------------------------
1 | #1. Get-Command
2 |
3 | # Get-Command is one of three commands that make up the Trinity of Discoverability.
4 |
5 | # These three commands will help you find your way around what PowerShell can do.
6 |
7 | # Get-Command helps you find out what commands exist.
8 |
9 | Get-Command -Module ShowDemo
10 |
11 | # Because everything is an object in PowerShell, we can pipe this into other commands.
12 |
13 | # So, if we wanted to just get a random loaded command from Show-Demo, we'd use:
14 |
15 | Get-Command -Module ShowDemo | Get-Random
16 |
17 | # In PowerShell, it's common for commands to share similar names.
18 |
19 | # So we can search for commands by wildcard:
20 |
21 | Get-Command *Demo*
22 |
23 | #2. Get-Help
24 |
25 | # Get-Help helps us figure out how commands work.
26 |
27 | Get-Help Show-Demo
28 |
29 | # By default, you see an overview of available help.
30 |
31 | # We can get help about each parameter by using -Parameter *
32 |
33 | Get-Help Show-Demo -Parameter *
34 |
35 | #3. Get-Member
36 |
37 | # Get-Member helps us figure out what properties and methods are available on an object.
38 |
39 | # For example, we can see what properties are available on the output of Get-Command:
40 |
41 | Get-Command -Module ShowDemo | Get-Member
42 |
43 | # Every object has members. Let's get the members the current process.
44 |
45 | Get-Process -Id $PID | Get-Member
46 |
47 | # We can also get -Static members. This is especially useful for classes.
48 |
49 | [Math] | Get-Member -Static
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/powershell
2 | COPY . ./usr/local/share/powershell/Modules/ShowDemo
3 | RUN pwsh -c "New-Item -Path /root/.config/powershell/Microsoft.PowerShell_profile.ps1 -Value 'Import-Module ShowDemo' -Force"
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 James Brundage
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | # Showcase your Scripts
13 |
14 | Want to showcase something you built in PowerShell?
15 |
16 | You can make a .demo.ps1 file to showcase your script line by line, like this:
17 |
18 | 
19 |
20 | You can also make a .demo.ps1 file as markdown, like [this](demo.md).
21 |
22 | Give it a try!
23 |
24 | ~~~PowerShell
25 | Install-Module ShowDemo -Scope CurrentUser -Force
26 | Import-Module ShowDemo -Force -PassThru
27 | Show-Demo
28 | ~~~
29 |
30 | ## Writing Demos
31 |
32 | Demo files just simple scripts, named either demo.ps1 or *.demo.ps1.
33 |
34 | Each comment or statement that starts in the first column is considered a step.
35 |
36 | For an example, check out [demo.ps1](https://github.com/StartAutomating/ShowDemo/blob/main/demo.ps1)
37 |
38 | ## Using the GitHub Action
39 |
40 | To use ShowDemo in a GitHub Action, simply add this line to your workflow:
41 |
42 | ~~~yaml
43 | - uses: StartAutomating/ShowDemo@main
44 | ~~~
45 |
46 | This will take any demo files and export them as markdown.
47 |
48 | ## ShowDemo Commands
49 |
50 | ShowDemo is a module of few commands. They are:
51 |
52 | |Name|Synopsis|
53 | |-|-|
54 | |Get-Demo | Gets Demos |
55 | |Export-Demo| Exports Demos|
56 | |Import-Demo| Imports Demos|
57 | |Resume-Demo| Resumes Demos|
58 | |Show-Demo | Shows Demos |
59 |
60 | You can Show your demo by running: `Show-Demo -DemoPath .\My.demo.ps1`
61 |
62 | Show-Demo is aliased to Start-Demo, it's inspiration
63 |
64 | ## Inspiration, History, and Goals
65 |
66 | In the early days of PowerShell, Jeffery Snover created a useful little script called Start-Demo.
67 |
68 | Start-Demo was incredibly useful.
69 |
70 | It helped showcase just how cool PowerShell could be, and gave every scripter a simple tool to showcase their scripts.
71 |
72 | Start-Demo was written all the way back in PowerShell v1; before the parser API, before markdown, and well before colorized output in Windows Terminal.
73 |
74 | ShowDemo is designed to update and replace the old Start-Demo and provide a foundation to give it even more modern capabilities.
75 |
--------------------------------------------------------------------------------
/ShowDemo.ps.psm1:
--------------------------------------------------------------------------------
1 | [Include('*-*.ps1')]$PSScriptRoot
2 |
3 | $thisModule = $MyInvocation.MyCommand.ScriptBlock.Module
4 | $thisModule.pstypenames.insert(0, $thisModule.Name)
5 | $ExecutionContext.SessionState.PSVariable.Set($thisModule.Name, $thisModule)
6 |
7 | $newDriveSplat = [ordered]@{
8 | Name = $thisModule.Name
9 | PSProvider = 'FileSystem'
10 | Root = $PSScriptRoot
11 | ErrorAction = 'Ignore'
12 | }
13 |
14 | New-PSDrive @newDriveSplat
15 |
16 | Export-ModuleMember -Variable $thisModule.Name -Function * -Alias *
--------------------------------------------------------------------------------
/ShowDemo.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | Author = 'James Brundage'
3 | CompanyName = 'Start-Automating'
4 | Copyright = '2022-2024 Start-Automating'
5 | Description = 'A simple tool to showcase your scripts.'
6 | Guid = 'c4516317-f99e-44cf-b138-d8c4d1eadf66'
7 | ModuleVersion = '0.1.7'
8 | RootModule = 'ShowDemo.psm1'
9 | FormatsToProcess = 'ShowDemo.format.ps1xml'
10 | TypesToProcess = 'ShowDemo.types.ps1xml'
11 | PrivateData = @{
12 | PSData = @{
13 | Tags = 'PowerShell', 'Demo', 'ShowDemo'
14 | ProjectURI = 'https://github.com/StartAutomating/ShowDemo'
15 | LicenseURI = 'https://github.com/StartAutomating/ShowDemo/blob/main/LICENSE'
16 | ReleaseNotes = @'
17 | ## ShowDemo 0.1.7:
18 |
19 | * ShowDemo in Docker (#103)
20 | * Added Dockerfile (#104)
21 | * Publishing all builds to GitHub Container Registry (#105)
22 | * Added Trinity of Discoverability Demo (#51)
23 | * Exporting $ShowDemo (#106)
24 | * Mounting as ShowDemo: (#107)
25 |
26 | ---
27 |
28 | Full history in [CHANGELOG](https://github.com/StartAutomating/ShowDemo/blob/main/CHANGELOG.md)
29 |
30 | > Like It? [Star It](https://github.com/StartAutomating/ShowDemo)
31 | > Love It? [Support It](https://github.com/sponsors/StartAutomating)
32 | '@
33 | Recommendation = 'obs-powershell', 'Posh'
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ShowDemo.psm1:
--------------------------------------------------------------------------------
1 | :ToIncludeFiles foreach ($file in (Get-ChildItem -Path "$PSScriptRoot" -Filter "*-*.ps1" -Recurse)) {
2 | if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1
3 | foreach ($exclusion in '\.[^\.]+\.ps1$') {
4 | if (-not $exclusion) { continue }
5 | if ($file.Name -match $exclusion) {
6 | continue ToIncludeFiles # Skip excluded files
7 | }
8 | }
9 | . $file.FullName
10 | }
11 |
12 | $thisModule = $MyInvocation.MyCommand.ScriptBlock.Module
13 | $thisModule.pstypenames.insert(0, $thisModule.Name)
14 | $ExecutionContext.SessionState.PSVariable.Set($thisModule.Name, $thisModule)
15 |
16 | $newDriveSplat = [ordered]@{
17 | Name = $thisModule.Name
18 | PSProvider = 'FileSystem'
19 | Root = $PSScriptRoot
20 | ErrorAction = 'Ignore'
21 | }
22 |
23 | New-PSDrive @newDriveSplat
24 |
25 | Export-ModuleMember -Variable $thisModule.Name -Function * -Alias *
26 |
--------------------------------------------------------------------------------
/ShowDemo.tests.ps1:
--------------------------------------------------------------------------------
1 | describe ShowDemo {
2 | it 'Shows Demos' {
3 | $showedADemo = Show-Demo -NonInteractive
4 | $showedADemo | Should -match '\e\[' # and it had an escape sequence for color
5 | }
6 |
7 | it 'Can export a demo as markdown' {
8 | $exportedDemo = Get-Demo -DemoName Demo | Export-Demo -OutputPath .\demo.md
9 | $exportedDemo.Extension | Should -be '.md'
10 | $exportedContent = Get-Content $exportedDemo.FullName -Raw
11 | $exportedContent | Should -BeLike '*###*1.*'
12 | $exportedContent | Should -BeLike '*Learn*PowerShell*'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ShowDemo.types.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Demo
6 |
7 |
8 | Dump
9 |
26 |
27 |
28 | NextChapter
29 |
48 |
49 |
50 | NextStep
51 |
67 |
68 |
69 | ProcessInput
70 |
140 |
141 |
142 | Reset
143 |
159 |
160 |
161 | SetChapter
162 |
203 |
204 |
205 | SetStatus
206 |
219 |
220 |
221 | ShowStep
222 |
295 |
296 |
297 | Start
298 |
310 |
311 |
312 | StartChapter
313 |
316 |
317 |
318 | Stop
319 |
334 |
335 |
336 | ToMarkdown
337 |
365 |
366 |
367 | TotalSteps
368 |
369 | $stepCount = 0
370 | foreach ($chapter in $this.Chapters) {
371 | $stepCount += $chapter.Steps.Length
372 | }
373 | $stepCount
374 |
375 |
376 |
377 |
378 |
379 | Demo.Chapter
380 |
381 |
382 | Steps
383 |
384 | $text = $this.Text
385 | $step = @()
386 | $ThisChapterSteps = @()
387 |
388 | # We want every step to be able to run independently.
389 | # This would be untrue if the code is unbalanced when a chapter would start
390 | # Thus, while we're primarily looking for comments, we also need to track groups
391 | $groupDepth = 0
392 | for ($tokenNumber =0 ; $tokenNumber -lt $this.Tokens.Length; $tokenNumber++) {
393 | $token = $this.tokens[$tokenNumber]
394 | # If the token is a group start
395 | if ($token.Type -eq 'GroupStart')
396 | {
397 | $groupDepth++ # increment depth.
398 | }
399 | # If the token was a group end
400 | elseif ($token.Type -eq 'GroupEnd')
401 | {
402 | $groupDepth-- # decrement depth.
403 | }
404 |
405 | # and
406 |
407 | elseif (
408 | (-not $groupDepth) -and # If there was no depth and
409 | $token.StartColumn -le 1 -and # the token was a comment starting in the first column
410 | $token[-1].Type -ne 'Keyword' -and # and it wasn't preceeded by a keyword
411 | $token[0].Type -ne 'Keyword' -and # and it wasn't a keyword
412 | $token[0].Type -ne 'Newline' # and it wasn't a newline
413 | ) {
414 | # Then it's the start of a new step
415 | if ($step) {
416 | $stepEnd = $step[-1].Start + $step[-1].Length
417 | $stepStart = $step[0].Start
418 | # Get the content of the last step
419 | $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$'
420 | if ($stepScript) {
421 | # and make it into a PSObject
422 | $stepScript = [PSObject]::new($stepScript)
423 | # with the PSTypeName 'Demo.Step'
424 | $stepScript.pstypenames.add('Demo.Step')
425 | # and add the .Chapter property, pointing to $this
426 | $stepScript.psobject.properties.add([psnoteproperty]::new(
427 | 'Chapter',$this
428 | ))
429 |
430 | $ThisChapterSteps += $stepScript
431 | }
432 | # then reset the collection of tokens in the current step.
433 | $step = @()
434 | }
435 | }
436 |
437 | # Add any token we see into the current step.
438 | $step += $token
439 | }
440 |
441 | # If there were any steps remaining
442 | if ($step) {
443 | $stepEnd = $step[-1].Start + $step[-1].Length
444 | $stepStart = $step[0].Start
445 | $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$'
446 | if ($stepScript) {
447 | # make them into 'Demo.Step' objects
448 | $stepScript = [PSObject]::new($stepScript)
449 | $stepScript.pstypenames.add('Demo.Step')
450 | $stepScript.psobject.properties.add([psnoteproperty]::new(
451 | 'Chapter',$this
452 | )) # and add the chapter.
453 | $ThisChapterSteps += $stepScript
454 | }
455 | }
456 |
457 | # Force steps to be returned as a list.
458 | ,$ThisChapterSteps
459 |
460 |
461 |
462 |
463 |
464 | Demo.Step
465 |
466 |
467 | HidePrompt
468 |
487 |
488 |
489 | Invoke
490 |
509 |
510 |
511 | ShowPrompt
512 |
531 |
532 |
533 | Silent
534 |
549 |
550 |
551 | HiddenStep
552 |
553 | $specialStepNameRegex = '^\s{0,}\<{0,1}\#\s{0,}\.(?<st>[\S]+)'
554 | if ($this -notmatch $specialStepNameRegex) { return $null }
555 | [PSCustomObject]@{
556 | StepType = $matches.st
557 | Arguments = $this -replace $specialStepNameRegex
558 | }
559 |
560 |
561 |
562 | IsComment
563 |
564 | $stepTokens = [Management.Automation.PSParser]::Tokenize($this, [ref]$null)
565 | foreach ($token in $stepTokens) {
566 | if ($token.Type -notin 'Comment', 'Newline') {
567 | return $false
568 | }
569 | }
570 | return $true
571 |
572 |
573 |
574 |
575 |
576 |
--------------------------------------------------------------------------------
/Types/Demo.Chapter/get_Steps.ps1:
--------------------------------------------------------------------------------
1 | $text = $this.Text
2 | $step = @()
3 | $ThisChapterSteps = @()
4 |
5 | # We want every step to be able to run independently.
6 | # This would be untrue if the code is unbalanced when a chapter would start
7 | # Thus, while we're primarily looking for comments, we also need to track groups
8 | $groupDepth = 0
9 | for ($tokenNumber =0 ; $tokenNumber -lt $this.Tokens.Length; $tokenNumber++) {
10 | $token = $this.tokens[$tokenNumber]
11 | # If the token is a group start
12 | if ($token.Type -eq 'GroupStart')
13 | {
14 | $groupDepth++ # increment depth.
15 | }
16 | # If the token was a group end
17 | elseif ($token.Type -eq 'GroupEnd')
18 | {
19 | $groupDepth-- # decrement depth.
20 | }
21 |
22 | # and
23 |
24 | elseif (
25 | (-not $groupDepth) -and # If there was no depth and
26 | $token.StartColumn -le 1 -and # the token was a comment starting in the first column
27 | $token[-1].Type -ne 'Keyword' -and # and it wasn't preceeded by a keyword
28 | $token[0].Type -ne 'Keyword' -and # and it wasn't a keyword
29 | $token[0].Type -ne 'Newline' # and it wasn't a newline
30 | ) {
31 | # Then it's the start of a new step
32 | if ($step) {
33 | $stepEnd = $step[-1].Start + $step[-1].Length
34 | $stepStart = $step[0].Start
35 | # Get the content of the last step
36 | $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$'
37 | if ($stepScript) {
38 | # and make it into a PSObject
39 | $stepScript = [PSObject]::new($stepScript)
40 | # with the PSTypeName 'Demo.Step'
41 | $stepScript.pstypenames.add('Demo.Step')
42 | # and add the .Chapter property, pointing to $this
43 | $stepScript.psobject.properties.add([psnoteproperty]::new(
44 | 'Chapter',$this
45 | ))
46 |
47 | $ThisChapterSteps += $stepScript
48 | }
49 | # then reset the collection of tokens in the current step.
50 | $step = @()
51 | }
52 | }
53 |
54 | # Add any token we see into the current step.
55 | $step += $token
56 | }
57 |
58 | # If there were any steps remaining
59 | if ($step) {
60 | $stepEnd = $step[-1].Start + $step[-1].Length
61 | $stepStart = $step[0].Start
62 | $stepScript = $text.Substring($stepStart, $stepEnd - $stepStart) -replace '^\s{0,}$'
63 | if ($stepScript) {
64 | # make them into 'Demo.Step' objects
65 | $stepScript = [PSObject]::new($stepScript)
66 | $stepScript.pstypenames.add('Demo.Step')
67 | $stepScript.psobject.properties.add([psnoteproperty]::new(
68 | 'Chapter',$this
69 | )) # and add the chapter.
70 | $ThisChapterSteps += $stepScript
71 | }
72 | }
73 |
74 | # Force steps to be returned as a list.
75 | ,$ThisChapterSteps
--------------------------------------------------------------------------------
/Types/Demo.Step/HidePrompt.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Hides the prompt
4 | .DESCRIPTION
5 | Hides the prompt within a demo.
6 | .EXAMPLE
7 | #.HidePrompt
8 | #>
9 | param(
10 | # Any additional parameters for the step.
11 | # This is ignored when hiding prompts.
12 | $step
13 | )
14 |
15 | $this.Chapter.Demo | Add-Member NoteProperty ShowPrompt $false
16 |
17 | $null = New-Event -SourceIdentifier Demo.HidePrompt -Sender $this.Chapter.Demo -EventArguments @($step)
--------------------------------------------------------------------------------
/Types/Demo.Step/Invoke.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Invokes a demo step
4 | .DESCRIPTION
5 | Invokes a step in a demo file.
6 | #>
7 | $hiddenStep = $this.HiddenStep
8 |
9 | $invokeResults =
10 | if (-not $hiddenStep) {
11 | Invoke-Expression $this
12 | } elseif ($this.$($hiddenStep.StepType).Invoke) {
13 | $this.$($hiddenStep.StepType).Invoke($hiddenStep.Arguments)
14 | }
15 | $invokeResults
16 | $null = New-Event -SourceIdentifier Demo.Step.Invoke -Sender $this -EventArguments $args -MessageData $invokeResults
17 |
--------------------------------------------------------------------------------
/Types/Demo.Step/ShowPrompt.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Shows the prompt
4 | .DESCRIPTION
5 | Show the prompt within a demo.
6 | .EXAMPLE
7 | #.ShowPrompt
8 | #>
9 | param(
10 | # Any additional parameters for the step.
11 | # This is ignored when showing prompts.
12 | $step
13 | )
14 |
15 | $this.Chapter.Demo | Add-Member NoteProperty ShowPrompt $true
16 |
17 | $null = New-Event -SourceIdentifier Demo.ShowPrompt -Sender $this.Chapter.Demo -EventArguments @($step)
--------------------------------------------------------------------------------
/Types/Demo.Step/Silent.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Run a silent step
4 | .DESCRIPTION
5 | Run a silent step of a demo.
6 |
7 | Silent steps do not display their results.
8 | #>
9 | param($silentStep)
10 |
11 | Invoke-Expression $silentStep | Out-Null
12 |
13 | $null = New-Event -SourceIdentifier Demo.Step.Silent -Sender $this -EventArguments @($silentStep)
--------------------------------------------------------------------------------
/Types/Demo.Step/get_HiddenStep.ps1:
--------------------------------------------------------------------------------
1 | $specialStepNameRegex = '^\s{0,}\<{0,1}\#\s{0,}\.(?[\S]+)'
2 | if ($this -notmatch $specialStepNameRegex) { return $null }
3 | [PSCustomObject]@{
4 | StepType = $matches.st
5 | Arguments = $this -replace $specialStepNameRegex
6 | }
--------------------------------------------------------------------------------
/Types/Demo.Step/get_IsComment.ps1:
--------------------------------------------------------------------------------
1 | $stepTokens = [Management.Automation.PSParser]::Tokenize($this, [ref]$null)
2 | foreach ($token in $stepTokens) {
3 | if ($token.Type -notin 'Comment', 'Newline') {
4 | return $false
5 | }
6 | }
7 | return $true
--------------------------------------------------------------------------------
/Types/Demo/Demo.format.ps1:
--------------------------------------------------------------------------------
1 | Write-FormatView -TypeName Demo -Property Name, TotalSteps, Chapters -Wrap -VirtualProperty @{
2 | Chapters = {
3 | @(foreach ($chap in $_.Chapters) {
4 | $chap.Number + ' ' + $chap.Name
5 | }) -join [Environment]::NewLine
6 | }
7 | }
8 |
9 | Write-FormatView -TypeName DemoViewer -Name DemoViewer -AsControl -Action {
10 | Write-FormatViewExpression -If {
11 | # If the demo has not started yet
12 | -not $_.DemoStarted
13 | } -ScriptBlock {
14 | $demo = $_
15 |
16 | if ($demo.RecordDemo) {
17 | $startRecordingCommand = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Start-Recording','Function,Alias')
18 | if ($startRecordingCommand) {
19 | $null = Start-Recording
20 | } else {
21 | Write-Warning "Start-Recording was not found. Have you installed/imported obs-powershell?"
22 | }
23 | }
24 |
25 | # Start the demo.
26 | $demo.Start()
27 |
28 | # Then, create a message indicating we've started.
29 | if ($demo.StartMessage) {
30 | $demoStartMessage =
31 | Format-RichText -ForegroundColor Warning -InputObject (
32 | $demo.StartMessage +
33 | ([Environment]::NewLine * 2)
34 | ) -Italic
35 |
36 | # If the demo is being run interactively,
37 | if ($demo.Interactive) {
38 | # write that message to the host.
39 | $demoStartMessage | Out-Host
40 | ""
41 | }
42 | # Otherwise, as long as we are not outputting markdown
43 | elseif (-not $demo.Markdown) {
44 | # output the demo started message.
45 | ($demoStartMessage -join '') + [Environment]::NewLine
46 | } else {
47 | ''
48 | }
49 | }
50 |
51 | if ($demo.Interactive) {
52 | [Console]::OutputEncoding = $OutputEncoding
53 | }
54 | }
55 |
56 | Write-FormatViewExpression -If {
57 | # If the demo has started, is interactive, and not markdown
58 | $_.DemoStarted -and $_.Interactive -and -not $_.Markdown
59 | } {
60 | # attempt to to change the window title.
61 | $Duration = [DateTime]::Now - $_.DemoStarted
62 | $Host.UI.RawUI.WindowTitle = "{0}[{1}m, {2}s] {2}" -f $_.Name, [int]$Duration.TotalMinutes, [int]$Duration.Seconds
63 | ""
64 | }
65 |
66 |
67 | Write-FormatViewExpression -If {
68 | # If we do not have a current chapter
69 | -not $_.CurrentChapter
70 | } -ScriptBlock {
71 | # pick the first chapter and modify the object.
72 | $firstChapter = $_.Chapters[0]
73 | $_ | Add-Member NoteProperty CurrentChapter $firstChapter -Force
74 | $_ | Add-Member NoteProperty CurrentStep 0 -Force
75 | ""
76 | }
77 |
78 | Write-FormatViewExpression -If {
79 | # If we do not have a current step
80 | -not $_.CurrentStep
81 | } -ScriptBlock {
82 | # Start the chapter
83 | $_.StartChapter()
84 | $demo = $_
85 | # and get the first step
86 | $stepToRun = $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]
87 |
88 | # while the step is hidden
89 | while ($stepToRun.HiddenStep) {
90 | $stepToRun.Invoke() # run it
91 | $demo.NextStep() # and move onto the next step.
92 | $stepToRun = $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]
93 | }
94 |
95 | # declare a chapter heading
96 | $chapterHeading =
97 | $demo.CurrentChapter.Number +
98 | ' ' +
99 | $demo.CurrentChapter.Name + ([Environment]::NewLine * 2)
100 |
101 | $ChapterHeadingSplat = [Ordered]@{ForegroundColor='Verbose';InputObject=$chapterHeading;Underline=$true}
102 | # and get a rich text version of that heading
103 | $null = New-Event -SourceIdentifier Demo.WriteChapterName -Sender $demo -MessageData ([Ordered]@{} + $ChapterHeadingSplat)
104 | $currentChapterText =
105 | Format-RichText @ChapterHeadingSplat
106 |
107 | # If we are running interactively
108 | if ($_.Interactive) {
109 | # output that message now
110 | $currentChapterText | Out-Host
111 | ''
112 | } elseif ($demo.Markdown) {
113 | # otherwise, if we are generating markdown,
114 | # determine the heading size.
115 | $headingSize = [int][math]::max($demo.HeadingSize, 3)
116 | # use Format-Markdown
117 | (Format-Markdown -HeadingSize $headingSize -InputObject $chapterHeading)
118 | } else {
119 | # if we are not running interactively, output the rich text from our formatter.
120 | ($currentChapterText -join '') + [Environment]::NewLine
121 | }
122 |
123 | }
124 |
125 | Write-FormatViewExpression -If {
126 | # If there is a current step
127 | $_.CurrentStep
128 | } -ScriptBlock {
129 |
130 | $demo = $_
131 | # set step to run
132 | $stepToRun = $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]
133 |
134 | # while the step is hidden
135 | while ($stepToRun.HiddenStep) {
136 | $stepToRun.Invoke() # run it
137 | $demo.NextStep() # and move onto the next step.
138 | $stepToRun = $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]
139 | }
140 | }
141 |
142 | Write-FormatViewExpression -If {
143 | $_.StepToRun -and
144 | $_.ShowPrompt -and
145 | (-not $_.DemoFinished)
146 | } -ScriptBlock {
147 | $promptOutput = prompt
148 | $null = New-Event -SourceIdentifier Demo.WritePrompt -Sender $demo -MessageData $promptOutput
149 | if ($_.Interactive) {
150 | $promptOutput | Out-Host
151 | } # and we're running interactively
152 | else {
153 | $promptOutput | Out-String
154 | }
155 | }
156 |
157 | Write-FormatViewExpression -If {
158 | # If we still have a current step
159 | $_.CurrentStep
160 | } -ScriptBlock {
161 | $demo = $_
162 | $stepToRun = $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]
163 | # Update the object with it.
164 | $demo | Add-Member NoteProperty StepToRun $stepToRun -Force
165 | # If we are rendering markdown
166 | if ($demo.Markdown) {
167 | # and the step is a comment
168 | if ($stepToRun.IsComment) {
169 | # replace the comment start and end
170 | ("$stepToRun" -replace '^\<{0,1}\#{1}' -replace '\#\>\s{0,}$').Trim()
171 | }
172 | # If the step was not a comment
173 | else
174 | {
175 | # Make it a PowerShell code block.
176 | [Environment]::NewLine +
177 | (Format-Markdown -InputObject $stepToRun.Trim() -CodeLanguage PowerShell) +
178 | [Environment]::NewLine
179 |
180 | }
181 |
182 | # If we are outputting markdown, we're done with this formatting step.
183 | return
184 | }
185 |
186 | # If we are not outputting markdown, then let's figure out our real sleep
187 | $realSleep =
188 | # If it's greater than a millisecond
189 | if ($demo.TypeSpeed.TotalMilliseconds -ge 1) {
190 | $demo.TypeSpeed # trust their input
191 | } elseif ($demo.TypeSpeed.Ticks)
192 | {
193 | # Otherwise, try to convert from
194 | $letterPerMillisecond =
195 | ($demo.TypeSpeed.Ticks * 6) # words per minute
196 | / (60 * 1000) # to milliseconds.
197 | [TimeSpan]::FromMilliseconds($letterPerMillisecond)
198 | } else {
199 | # otherwise, have no delay
200 | [timespan]0
201 | }
202 |
203 | # Now run over each segment of colorized output for the step
204 | $strOut = @(foreach ($output in $demo.ShowStep($stepToRun)) {
205 | $outputCopy = @{} + $output
206 | if ($output.InputObject) {
207 | # Start off by setting the rich text formatting used for this sequence of tokens
208 | $null = New-Event -SourceIdentifier Demo.WriteStep -Sender $demo -MessageData ([Ordered]@{} + $output)
209 | $outputCopy.NoClear = $true
210 | $outputCopy.InputObject = ''
211 | # If we're running interactively, write that to the console now
212 | if ($demo.Interactive) {
213 | [Console]::Write((Format-RichText @outputCopy) -join '')
214 | } else {
215 | # otherwise, add it to $strOut.
216 | Format-RichText @outputCopy
217 | }
218 | # Next, determine our chunks of output
219 | $chunks =
220 | # If we're going letter-by-letter
221 | if ($demo.TypeStyle -eq 'Letters') {
222 | # it's a character array.
223 | "$($output.InputObject)".ToCharArray()
224 | }
225 | # If we're going word by word,
226 | elseif ($demo.TypeStyle -eq 'Words') {
227 | # it's split next to each space.
228 | "$($output.InputObject)" -split '(?=\s)'
229 | }
230 | # otherwise, just output the block
231 | else{
232 | "$($output.InputObject)"
233 | }
234 |
235 | # Walk over each chunk of output
236 | foreach ($chunk in $chunks) {
237 | if (-not $chunk) { continue }
238 | # If running interactively,
239 | if ($demo.Interactive) {
240 | # write it to the console
241 | [Console]::Write("$chunk")
242 | } else {
243 | # otherwise, add it to $strOut
244 | $chunk
245 | }
246 |
247 | # If we are running interactively, sleep.
248 | if ($realSleep.Ticks -and $demo.Interactive) {
249 | # (this gives us our typing effect)
250 | $null = Start-Sleep -Milliseconds $realSleep.TotalMilliseconds
251 | }
252 | }
253 |
254 | # Now we need to do one more write to close the formatting
255 | $null = $outputCopy.Remove('NoClear')
256 | $outputCopy.InputObject = ' '
257 | $output.InputObject = ''
258 | # If we're running interactively
259 | if ($demo.Interactive) {
260 | # that goes to the console now.
261 | [Console]::Write((Format-RichText @output) -join '')
262 | } else {
263 | (Format-RichText @output) -join ''
264 | }
265 | }
266 | })
267 |
268 | # If we are running interactively
269 | if ($demo.Interactive) {
270 | '' # emit nothing from the formatter (since we've already written to the console)
271 | } else {
272 | # otherwise, emit the string.
273 | $strOut -join ''
274 | }
275 | }
276 |
277 | Write-FormatViewExpression -If {
278 | # If the demo is not done and it is interactive
279 | -not $_.DemoFinished -and $_.Interactive -and -not $_.AutoPlay
280 | } -ScriptBlock {
281 | $demo = $_
282 | # Read input
283 | $hostInput = Read-Host
284 | # and the process it
285 | foreach ($output in $demo.ProcessInput($hostInput)) {
286 | # any output we want to display as a warning
287 | if ($output -is [string]) {
288 | Format-RichText -ForegroundColor Warning -InputObject $output | Out-Host
289 | }
290 | # unless it was a series of splats for Format-RichText
291 | elseif ($output -is [Collections.IDictionary]) {
292 | Format-RichText @output | Out-Host # ( which we will run and output )
293 | }
294 | # or a scriptblock.
295 | elseif ($output -is [scriptblock]) {
296 | . $output | Out-Host # (which we will run).
297 | }
298 | }
299 | }
300 |
301 | Write-FormatViewExpression -If {
302 | $_.StepToRun # If we have a StepToRun
303 | } -ScriptBlock {
304 | $demo = $_
305 | # Run it.
306 | $DemoStepOutput = $null
307 |
308 | if ($PSStyle) {
309 | $PSStyle.OutputRendering = 'ANSI'
310 | }
311 |
312 | if ($demo.Interactive) {
313 | [Console]::WriteLine()
314 | if ($demo.PauseBetweenLine) {
315 | # If we're running interactively, pipe it out.
316 | $DemoStepOutput = @(Invoke-Expression -Command $demo.StepToRun *>&1 |
317 | Out-String) -split '(?>\r\n|\n)'
318 | foreach ($demoOutputLine in $DemoStepOutput) {
319 | Start-Sleep -Milliseconds $demo.PauseBetweenLine.TotalMilliseconds
320 | Write-Host $demoOutputLine
321 | }
322 | } else {
323 | # If we're running interactively, pipe it out.
324 | Invoke-Expression -Command $demo.StepToRun *>&1 |
325 | Out-String -OutVariable DemoStepOutput |
326 | Out-Host
327 | }
328 | }
329 | else{
330 | # Otherwise, pipe it to Out-String
331 | $stepOutput =
332 | Invoke-Expression -Command $demo.StepToRun *>&1 |
333 | Out-String -Width 1kb -OutVariable DemoStepOutput
334 |
335 | # If we're outputting markdown
336 | if ($demo.Markdown) {
337 | # add a newline above and below
338 | [Environment]::NewLine + $(
339 | @(
340 | # If it looks like a tag
341 | if ($stepOutput -match '^\<') {
342 | $stepOutput # include without indentation
343 | } else {
344 | # Otherwise, indent 4 chars so it is seen as preformatted text.
345 | foreach ($line in @($stepOutput -split '(?>\r\n|\n)')) {
346 | (' ' * 4) + $line
347 | }
348 | }
349 | ) -join [Environment]::NewLine
350 | ) +
351 | [Environment]::NewLine
352 | } else {
353 | [Environment]::NewLine + $stepOutput
354 | }
355 | }
356 |
357 | if ($DemoStepOutput) {
358 | $null = New-Event -SourceIdentifier Demo.WriteOutput -Sender $demo -EventArguments $demo.StepToRun -MessageData $DemoStepOutput
359 | }
360 | }
361 |
362 | Write-FormatViewExpression -If {
363 | # If we had a step to run
364 | $_.StepToRun -and
365 | (-not $_.DemoFinished) -and # and the demo's not done
366 | (-not $_.StepToRun.IsComment) -and # and the step is not a comment
367 | (-not $_.Autoplay) -and # and we're not autoplaying
368 | $_.Interactive # and we're running interactively
369 | } -ScriptBlock {
370 | $demo = $_
371 |
372 | # Prompt and process again
373 | $hostInput = Read-Host
374 | foreach ($output in $demo.ProcessInput($hostInput)) {
375 | if ($output -is [string]) {
376 | Format-RichText -ForegroundColor Warning -InputObject $output | Out-Host
377 | }
378 | elseif ($output -is [Collections.IDictionary]) {
379 | Format-RichText @output | Out-Host
380 | }
381 | elseif ($output -is [scriptblock]) {
382 | . $output | Out-Host
383 | }
384 | }
385 | }
386 |
387 | Write-FormatViewExpression -If {
388 | $_.Autoplay
389 | } -ScriptBlock {
390 | Start-Sleep -Milliseconds $_.PauseBetweenStep.TotalMilliseconds
391 | }
392 |
393 |
394 | Write-FormatViewExpression -If {
395 | # If we had a current step
396 | $_.CurrentStep
397 | } -ScriptBlock {
398 | # Advanced to the next step
399 | $_.NextStep()
400 | }
401 |
402 | Write-FormatViewExpression -If {
403 | -not $_.DemoFinished # If the demo was not finished
404 | } -ControlName DemoViewer -ScriptBlock {
405 | # recursively call this formatter
406 | $_
407 | }
408 |
409 | Write-FormatViewExpression -If {
410 | # If the demo is finished
411 | $_.DemoFinished
412 | } -ScriptBlock {
413 | $demo = $_
414 | # figure out how long it took
415 | $duration = $_.DemoFinished - $_.DemoStarted
416 | # change the status
417 | $demo.SetStatus('Finished')
418 | if ($demo.RecordDemo) {
419 | $stopRecording = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Stop-Recording', 'Alias,Function')
420 | if ($stopRecording) {
421 | $recordingOutputFile = Stop-Recording
422 | $newRecordingName = "$($demo.Name).$($demo.DemoStarted.ToString('s') -replace ':', '-')$($recordingOutputFile.Extension)"
423 | if ($recordingOutputFile) {
424 | try {
425 | Copy-Item $recordingOutputFile.FullName -Destination ($recordingOutputFile.FullName | Split-Path | Join-Path -ChildPath $newRecordingName )
426 | } catch {
427 | Write-Warning "Could not copy $($recordingOutputFile) to $($demo.Name): $($_ | Out-String)"
428 | }
429 | }
430 | }
431 | }
432 | # and prepare a message.
433 | if ($demo.EndMessage) {
434 | $finishedMessage =
435 | Format-RichText -InputObject (
436 | $demo.EndMessage -f [int]$duration.TotalMinutes, [int]$duration.Seconds
437 | ) -ForegroundColor Warning -Italic
438 |
439 | # If the demo was interactive
440 | if ($demo.Interactive) {
441 | # writ the message
442 | $finishedMessage | Out-Host
443 | # and if we had a nested prompt, exit it
444 | if ($NestedPromptLevel) {
445 | $host.ExitNestedPrompt()
446 | }
447 | } elseif (-not $demo.Markdown) {
448 | $finishedMessage -join ''
449 | }
450 | }
451 |
452 |
453 | # Last but not least, reset the demo.
454 | $demo.Reset()
455 | }
456 | }
457 |
458 | Write-FormatView -TypeName Demo -Action {
459 | Write-FormatViewExpression -ScriptBlock {
460 | # set a script variable to contain the current demo
461 | $ExecutionContext.SessionState.PSVariable.Set('script:currentDemo',$_)
462 | }
463 | Write-FormatViewExpression -ScriptBlock {
464 | $_ # display the demo using the DemoViewer control
465 | } -ControlName DemoViewer
466 |
467 | Write-FormatViewExpression -ScriptBlock {
468 | # unset a script variable
469 | $ExecutionContext.SessionState.PSVariable.Set('script:currentDemo',$null)
470 | }
471 | }
472 |
473 |
--------------------------------------------------------------------------------
/Types/Demo/Dump.ps1:
--------------------------------------------------------------------------------
1 | $demoContent =
2 | @(foreach ($chapter in $this.Chapters) {
3 | "# $($chapter.Number) $($chapter.Name)"
4 | $stepIndex = 0
5 | foreach ($step in $chapter.Steps) {
6 | $stepIndex++
7 | if ($this.CurrentChapter -eq $chapter -and $this.CurrentStep -eq $stepIndex) {
8 | " <# *** You Are Here! *** #>"
9 | }
10 | $step
11 |
12 | }
13 | }) -join [Environment]::NewLine
14 |
15 | $demoContent
--------------------------------------------------------------------------------
/Types/Demo/NextChapter.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Go to the Next Chapter in a Demo
4 | .DESCRIPTION
5 | Advances a demo to the next chapter.
6 | #>
7 | $demo = $this
8 | $chapterIndex = $demo.Chapters.IndexOf($demo.CurrentChapter)
9 | $chapterIndex++
10 | if (-not $demo.Chapters[$chapterIndex]) {
11 | $demo | Add-Member NoteProperty DemoFinished ([datetime]::Now) -Force
12 | $null = New-Event -SourceIdentifier Demo.Complete -Sender $this
13 | } else {
14 | $null = New-Event -SourceIdentifier Demo.NextChapter -Sender $this -EventArguments $demo.Chapters[$chapterIndex].Name -MessageData $demo.Chapters[$chapterIndex]
15 | $demo | Add-Member NoteProperty CurrentChapter $demo.Chapters[$chapterIndex] -Force
16 | $demo | Add-Member NoteProperty CurrentStep 0 -Force
17 | }
--------------------------------------------------------------------------------
/Types/Demo/NextStep.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Go to the next demo step
4 | .DESCRIPTION
5 | Advances a demo to the next step.
6 | #>
7 | $demo = $this
8 | $demo.CurrentStep++
9 | # No more steps in this chapter
10 | if (-not $demo.CurrentChapter.Steps[$demo.CurrentStep - 1]) {
11 | $demo.NextChapter()
12 | }
13 |
14 | $null = New-Event -SourceIdentifier Demo.NextStep -Sender $this -EventArguments $args
--------------------------------------------------------------------------------
/Types/Demo/ProcessInput.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Processes demo input
4 | .DESCRIPTION
5 | Processes user input to a demo file.
6 | #>
7 | param($hostInput)
8 | $demo = $this
9 |
10 | $null = New-Event -SourceIdentifier Demo.ProcessInput -Sender $this -EventArguments @($hostinput)
11 |
12 | switch ($hostInput) {
13 | '?' {
14 | @{
15 | ForegroundColor = 'Cyan'
16 | InputObject = @"
17 | Running demo: $($demo.Name)
18 | (q) Quit
19 | (# ...) Goto Chapter/Step
20 | (f ...) Find cmds using X
21 | (t) Timecheck
22 | (s) Skip
23 | (!) Debug Demo
24 | (d) Dump demo
25 | "@
26 | }
27 | }
28 | 'q' {
29 | $demo.Stop()
30 | }
31 | 'd' {
32 | $demoDump = $demo.Dump()
33 | $demoDump | Out-Host
34 | if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
35 | $demoDump | Set-Clipboard
36 | }
37 | }
38 | 's' {
39 | $demo | Add-Member NoteProperty StepToRun $null -Force
40 | }
41 | 't' {
42 | $duration = [Datetime]::now - $demo.DemoStarted
43 | @{
44 | ForegroundColor = 'Warning'
45 | Italic = $true
46 | InputObject =
47 | "{0} {1} [{2}m, {3}s]" -f $demo.Name, $demo.Status, [int]$Duration.TotalMinutes, [int]$Duration.Seconds
48 | }
49 | }
50 | '!' {
51 | Write-Warning "Debugging Demo: Use Resume-Demo to resume."
52 | $host.EnterNestedPrompt()
53 |
54 | }
55 | default {
56 |
57 | if ($hostInput -match '^\s{0,}\#\s{0,}\d') {
58 | $demo | Add-Member NoteProperty StepToRun $null -Force
59 | $demo.SetChapter($hostInput -replace '^\s{0,}\#\s{0,}')
60 | }
61 | elseif ($hostInput -match '^\s{0,}(?>f|/)\s{0,}\S') {
62 | $toFind = $hostInput -replace '^\s{0,}(?>f|/)\s{0,}'
63 | Select-String -Path $demo.DemoFile -Pattern $toFind | Out-Host
64 | {}
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Types/Demo/Reset.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Resets a demo
4 | .DESCRIPTION
5 | Resets a demo file, so that it can be replayed.
6 | #>
7 | $this | Add-Member NoteProperty Status "NotStarted" -Force
8 | $this | Add-Member NoteProperty StepToRun $null -Force
9 | $demo | Add-Member NoteProperty DemoFinished $null -Force
10 | $demo | Add-Member NoteProperty DemoStarted $null -Force
11 | $demo | Add-Member NoteProperty CurrentChapter $null -Force
12 | $demo | Add-Member NoteProperty CurrentStep $null -Force
13 |
14 | $null = New-Event -SourceIdentifier Demo.Reset -Sender $this -EventArguments $args
--------------------------------------------------------------------------------
/Types/Demo/SetChapter.ps1:
--------------------------------------------------------------------------------
1 | param([string]$Chapter)
2 |
3 | if (-not $this.Chapters) { throw "No Chapters" }
4 | $chapterParts = @($chapter -split '\.')
5 | $potentialChapterNumbers = @(
6 | if ($chapterParts.Length -gt 1) {
7 | ($chapterParts[0..($chapterParts.Length - 2)] -join '\.') + '*'
8 | }
9 | $chapter + '*'
10 | )
11 |
12 |
13 |
14 | $newChapter, $newStep =
15 | :nextChapter foreach ($chap in $this.Chapters) {
16 | foreach ($potential in $potentialChapterNumbers) {
17 | if ($chap.Number -like $potential) {
18 | if ($potential -ne ($Chapter + '*')) {
19 | # Setting chapter and step number
20 | $chap, $chapterParts[-1] -as [int]
21 | break nextChapter
22 | } else {
23 | $chap, 1
24 |
25 | }
26 | }
27 | }
28 |
29 | }
30 |
31 | if (-not $newChapter) {
32 | throw "Could not find chapter '$chapter'"
33 | }
34 |
35 | $this | Add-Member NoteProperty CurrentChapter $newChapter -Force
36 | $this | Add-Member NoteProperty CurrentStep ($newStep - 1) -Force
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Types/Demo/SetStatus.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Sets demo status
4 | .DESCRIPTION
5 | Sets the status of a demo.
6 | #>
7 | param([string]$Status)
8 |
9 | $this | Add-Member Status $status -Force
10 |
11 | $null = New-Event -SourceIdentifier Demo.SetStatus -Sender $this -EventArguments @($Status)
--------------------------------------------------------------------------------
/Types/Demo/ShowStep.ps1:
--------------------------------------------------------------------------------
1 | param($step)
2 |
3 | $null = New-Event -SourceIdentifier Demo.ShowStep -Sender $this -EventArguments @($step)
4 |
5 | $stepTokens = [Management.Automation.PSParser]::Tokenize($step, [ref]$null)
6 | $PreviousToken = $null
7 | foreach ($_ in $stepTokens) {
8 | $content = $_.Content
9 | if ($_.Type -in 'Variable', 'String') {
10 | $content = $step.Substring($_.Start, $_.Length)
11 | }
12 | if ($PreviousToken) {
13 | $token = $_
14 | $prevEnd = $PreviousToken.Start + $PreviousToken.Length
15 | $substring = $step.Substring($prevEnd, $token.Start - $prevEnd)
16 | if ($substring) {
17 | @{InputObject=$substring}
18 | }
19 | }
20 |
21 | if ($_.Type -eq 'Comment') {
22 | @{
23 | ForegroundColor='Success'
24 | InputObject = $content
25 | }
26 | }
27 | elseif ($_.Type -in 'Keyword', 'String', 'CommandArgument') {
28 | @{
29 | ForegroundColor='Verbose'
30 | InputObject = $Content
31 | }
32 | }
33 | elseif ($_.Type -in 'Variable', 'Command') {
34 | @{
35 | ForegroundColor='Warning'
36 | InputObject = $Content
37 | }
38 | }
39 | elseif ($_.Type -in 'CommandParameter') {
40 | @{
41 | ForegroundColor='Magenta'
42 | InputObject = $Content
43 | }
44 | }
45 | elseif (
46 | $_.Type -in 'Operator','GroupStart', 'GroupEnd'
47 | ) {
48 | @{
49 | ForegroundColor='Magenta'
50 | InputObject = $Content
51 | }
52 | }
53 | elseif (
54 | $_.Type -notin 'Comment',
55 | 'Keyword', 'String', 'CommandArgument',
56 | 'Variable', 'Command',
57 | 'CommandParameter',
58 | 'Operator','GroupStart', 'GroupEnd'
59 | ) {
60 | @{
61 | ForegroundColor='White'
62 | InputObject=$Content
63 | }
64 | } else {
65 | @{
66 | ForegroundColor='White'
67 | InputObject=$Content
68 | }
69 | }
70 | $PreviousToken = $_
71 | }
--------------------------------------------------------------------------------
/Types/Demo/Start.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Starts a Demo
4 | .DESCRIPTION
5 | Starts a Demo file.
6 | #>
7 | $this | Add-Member NoteProperty Status Running -Force
8 | $this | Add-Member NoteProperty DemoStarted ([DateTime]::Now) -Force
9 |
10 | $null = New-Event -SourceIdentifier Demo.Start -Sender $this -EventArguments $args
--------------------------------------------------------------------------------
/Types/Demo/StartChapter.ps1:
--------------------------------------------------------------------------------
1 | $this | Add-Member NoteProperty CurrentStep 1 -Force
--------------------------------------------------------------------------------
/Types/Demo/Stop.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Stops a demo
4 | .DESCRIPTION
5 | Stops a demo that is currently running
6 | #>
7 | $this | Add-Member NoteProperty Status Stopped -Force
8 | $this | Add-Member NoteProperty StepToRun $null -Force
9 | $this | Add-Member NoteProperty DemoFinished ([datetime]::Now) -Force
10 | $demo | Add-Member NoteProperty CurrentChapter $null -Force
11 | $demo | Add-Member NoteProperty CurrentStep $null -Force
12 |
13 | $null = New-Event -SourceIdentifier Demo.Stop -Sender $this -EventArguments $args
--------------------------------------------------------------------------------
/Types/Demo/ToMarkdown.ps1:
--------------------------------------------------------------------------------
1 | # We don't want to modify this object
2 | $demoCopy = # so create a copy
3 | if ($this.DemoFile) { # by importing the file
4 | Import-Demo -DemoPath $this.DemoFile
5 | } elseif ($this.DemoScript) { # or the script block.
6 | Import-Demo -DemoPath $this.DemoScript
7 | }
8 |
9 | # We need Write-Host to be overridden in the same way as Export-Demo does.
10 | # So find Export-Demo's Abstract Syntax Tree
11 | $exportDemoAst = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Export-Demo','Function').ScriptBlock.Ast.Body
12 | # and find our inner function
13 | $writeHost = $exportDemoAst.Find({param($ast)
14 | $ast.Name -eq 'Write-Host' -and $ast.IsFilter -eq $false
15 | }, $false)
16 | # And override it here
17 | ${function:Write-Host} = $writeHost.Body.GetScriptBlock()
18 |
19 | # Now, modify our demo copy to make it non-interactive
20 | $demoCopy | Add-Member NoteProperty Interactive $false -Force
21 | # and markdown
22 | $demoCopy | Add-Member NoteProperty Markdown $true -Force
23 | # then use the formatter to get the markdown as a string.
24 | $demoCopy |
25 | Format-Custom |
26 | Out-String -Width 1mb
--------------------------------------------------------------------------------
/Types/Demo/get_TotalSteps.ps1:
--------------------------------------------------------------------------------
1 | $stepCount = 0
2 | foreach ($chapter in $this.Chapters) {
3 | $stepCount += $chapter.Steps.Length
4 | }
5 | $stepCount
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 |
2 | name: DemoPowerShell
3 | description: Make Demos of your PowerShell projects.
4 | inputs:
5 | ShowDemoScript:
6 | required: false
7 | description: |
8 | A PowerShell Script that uses ShowDemo.
9 | Any files outputted from the script will be added to the repository.
10 | If those files have a .Message attached to them, they will be committed with that message.
11 | SkipShowDemoPS1:
12 | required: false
13 | description: 'If set, will not process any files named *.ShowDemo.ps1'
14 | ModuleName:
15 | required: false
16 | description: |
17 | The name of the module for which types and formats are being generated.
18 | If not provided, this will be assumed to be the name of the root directory.
19 | CommitMessage:
20 | required: false
21 | description: If provided, will commit any remaining changes made to the workspace with this commit message.
22 | UserEmail:
23 | required: false
24 | description: The user email associated with a git commit.
25 | UserName:
26 | required: false
27 | description: The user name associated with a git commit.
28 | branding:
29 | icon: terminal
30 | color: blue
31 | runs:
32 | using: composite
33 | steps:
34 | - name: DemoPowerShell
35 | id: DemoPowerShell
36 | shell: pwsh
37 | env:
38 | UserEmail: ${{inputs.UserEmail}}
39 | SkipShowDemoPS1: ${{inputs.SkipShowDemoPS1}}
40 | ModuleName: ${{inputs.ModuleName}}
41 | UserName: ${{inputs.UserName}}
42 | ShowDemoScript: ${{inputs.ShowDemoScript}}
43 | CommitMessage: ${{inputs.CommitMessage}}
44 | run: |
45 | $Parameters = @{}
46 | $Parameters.ShowDemoScript = ${env:ShowDemoScript}
47 | $Parameters.SkipShowDemoPS1 = ${env:SkipShowDemoPS1}
48 | $Parameters.SkipShowDemoPS1 = $parameters.SkipShowDemoPS1 -match 'true';
49 | $Parameters.ModuleName = ${env:ModuleName}
50 | $Parameters.CommitMessage = ${env:CommitMessage}
51 | $Parameters.UserEmail = ${env:UserEmail}
52 | $Parameters.UserName = ${env:UserName}
53 | foreach ($k in @($parameters.Keys)) {
54 | if ([String]::IsNullOrEmpty($parameters[$k])) {
55 | $parameters.Remove($k)
56 | }
57 | }
58 | Write-Host "::debug:: DemoPowerShell $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')"
59 | & {<#
60 | .Synopsis
61 | GitHub Action for ShowDemo
62 | .Description
63 | GitHub Action for ShowDemo. This will:
64 |
65 | * Import ShowDemo
66 | * Get all demos in the current directory
67 | * Export each demo to a markdown file.
68 | * Run any .ShowDemo.ps1 scripts
69 | * Run the content of the .ShowDemoScript parameter
70 |
71 | Any files changed can be outputted by the script, and those changes can be checked back into the repo.
72 | Make sure to use the "persistCredentials" option with checkout.
73 | #>
74 |
75 | param(
76 | # A PowerShell Script that uses ShowDemo.
77 | # Any files outputted from the script will be added to the repository.
78 | # If those files have a .Message attached to them, they will be committed with that message.
79 | [string]
80 | $ShowDemoScript,
81 |
82 | # If set, will not process any files named *.ShowDemo.ps1
83 | [switch]
84 | $SkipShowDemoPS1,
85 |
86 | # The name of the module for which types and formats are being generated.
87 | # If not provided, this will be assumed to be the name of the root directory.
88 | [string]
89 | $ModuleName,
90 |
91 | # If provided, will commit any remaining changes made to the workspace with this commit message.
92 | [string]
93 | $CommitMessage,
94 |
95 | # The user email associated with a git commit.
96 | [string]
97 | $UserEmail,
98 |
99 | # The user name associated with a git commit.
100 | [string]
101 | $UserName
102 | )
103 |
104 | #region Initial Logging
105 |
106 | # Output the parameters passed to this script (for debugging)
107 | "::group::Parameters" | Out-Host
108 | [PSCustomObject]$PSBoundParameters | Format-List | Out-Host
109 | "::endgroup::" | Out-Host
110 |
111 | # Get the GitHub Event
112 | $gitHubEvent =
113 | if ($env:GITHUB_EVENT_PATH) {
114 | [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json
115 | } else { $null }
116 |
117 | # Log the GitHub Event
118 | @"
119 | ::group::GitHubEvent
120 | $($gitHubEvent | ConvertTo-Json -Depth 100)
121 | ::endgroup::
122 | "@ | Out-Host
123 |
124 | # Check that there is a workspace (and throw if there is not)
125 | if (-not $env:GITHUB_WORKSPACE) { throw "No GitHub workspace" }
126 |
127 | #endregion Initial Logging
128 |
129 | # Check to ensure we are on a branch
130 | $branchName = git rev-parse --abrev-ref HEAD
131 | # If we were not, return.
132 | if (-not $branchName) {
133 | "::warning::Not on a branch" | Out-Host
134 | return
135 | }
136 |
137 | #region Configure UserName and Email
138 | if (-not $UserName) {
139 | $UserName =
140 | if ($env:GITHUB_TOKEN) {
141 | Invoke-RestMethod -uri "https://api.github.com/user" -Headers @{
142 | Authorization = "token $env:GITHUB_TOKEN"
143 | } |
144 | Select-Object -First 1 -ExpandProperty name
145 | } else {
146 | $env:GITHUB_ACTOR
147 | }
148 | }
149 |
150 | if (-not $UserEmail) {
151 | $GitHubUserEmail =
152 | if ($env:GITHUB_TOKEN) {
153 | Invoke-RestMethod -uri "https://api.github.com/user/emails" -Headers @{
154 | Authorization = "token $env:GITHUB_TOKEN"
155 | } |
156 | Select-Object -First 1 -ExpandProperty email
157 | } else {''}
158 | $UserEmail =
159 | if ($GitHubUserEmail) {
160 | $GitHubUserEmail
161 | } else {
162 | "$UserName@github.com"
163 | }
164 | }
165 | git config --global user.email $UserEmail
166 | git config --global user.name $UserName
167 | #endregion Configure UserName and Email
168 |
169 |
170 | git pull | Out-Host
171 |
172 |
173 | #region Load Action Module
174 | $ActionModuleName = "ShowDemo"
175 | $ActionModuleFileName = "$ActionModuleName.psd1"
176 |
177 | # Try to find a local copy of the action's module.
178 | # This allows the action to use the current branch's code instead of the action's implementation.
179 | $PSD1Found = Get-ChildItem -Recurse -Filter "*.psd1" |
180 | Where-Object Name -eq $ActionModuleFileName |
181 | Select-Object -First 1
182 |
183 | $ActionModulePath, $ActionModule =
184 | # If there was a .PSD1 found
185 | if ($PSD1Found) {
186 | $PSD1Found.FullName # import from there.
187 | Import-Module $PSD1Found.FullName -Force -PassThru
188 | }
189 | # Otherwise, if we have a GITHUB_ACTION_PATH
190 | elseif ($env:GITHUB_ACTION_PATH)
191 | {
192 | $actionModulePath = Join-Path $env:GITHUB_ACTION_PATH $ActionModuleFileName
193 | if (Test-path $actionModulePath) {
194 | $actionModulePath
195 | Import-Module $actionModulePath -Force -PassThru
196 | } else {
197 | throw "$actionModuleName not found"
198 | }
199 | }
200 | elseif (-not (Get-Module $ActionModuleName)) {
201 | throw "$actionModulePath could not be loaded."
202 | }
203 |
204 | "::notice title=ModuleLoaded::$actionModuleName Loaded from Path - $($actionModulePath)" | Out-Host
205 | #endregion Load Action Module
206 |
207 |
208 | #region Install/Import Other Modules
209 | @"
210 | ::group::Installing Modules
211 | $(
212 | "Installing ugit" | Out-Host
213 | Install-Module -Name ugit -Scope CurrentUser -Force
214 | "Importing ugit" | Out-Host
215 | Import-Module ugit -Force -PassThru -Global | Out-Host
216 | )
217 | ::endgroup::
218 | "@ | Out-Host
219 |
220 |
221 | #endregion Install Other Modules
222 |
223 | #region Declare Functions and Variables
224 | $anyFilesChanged = $false
225 | filter ProcessScriptOutput {
226 | $out = $_
227 | $outItem = Get-Item -Path $out -ErrorAction SilentlyContinue
228 | $fullName, $shouldCommit =
229 | if ($out -is [IO.FileInfo]) {
230 | $out.FullName, (git status $out.Fullname -s)
231 | } elseif ($outItem) {
232 | $outItem.FullName, (git status $outItem.Fullname -s)
233 | }
234 | if ($shouldCommit) {
235 | git add $fullName
236 | if ($out.Message) {
237 | git commit -m "$($out.Message)"
238 | } elseif ($out.CommitMessage) {
239 | git commit -m "$($out.CommitMessage)"
240 | } elseif ($out.SourceFile) {
241 | "Source File: $($out.SourceFile)" | Out-Host
242 | $lastCommitMessage = $out.SourceFile |
243 | git log -n 1 |
244 | Select-Object -ExpandProperty CommitMessage
245 | if ($lastCommitMessage) {
246 | git commit -m $lastCommitMessage
247 | }
248 | } elseif ($gitHubEvent.head_commit.message) {
249 | git commit -m "$($gitHubEvent.head_commit.message)"
250 | }
251 | $anyFilesChanged = $true
252 | }
253 | $out
254 | }
255 |
256 | #endregion Declare Functions and Variables
257 |
258 |
259 | #region Actual Action
260 |
261 | $ShowDemoScriptStart = [DateTime]::Now
262 | if ($ShowDemoScript) {
263 | Invoke-Expression -Command $ShowDemoScript |
264 | . processScriptOutput |
265 | Out-Host
266 | }
267 | $ShowDemoScriptTook = [Datetime]::Now - $ShowDemoScriptStart
268 | $ShowDemoPS1Start = [DateTime]::Now
269 | $ShowDemoPS1List = @()
270 | if (-not $SkipShowDemoPS1) {
271 | $ShowDemoFiles = @(
272 | Get-ChildItem -Recurse -Path $env:GITHUB_WORKSPACE |
273 | Where-Object Name -Match '\.ShowDemo\.ps1$')
274 |
275 | if ($ShowDemoFiles) {
276 | $ShowDemoFiles|
277 | ForEach-Object {
278 | $ShowDemoPS1List += $_.FullName.Replace($env:GITHUB_WORKSPACE, '').TrimStart('/')
279 | $ShowDemoPS1Count++
280 | "::notice title=Running::$($_.Fullname)" | Out-Host
281 | . $_.FullName |
282 | . processScriptOutput |
283 | Out-Host
284 | }
285 | }
286 | }
287 |
288 | "Fetching Changes" | Out-Host
289 | git fetch --unshallow | Out-Host
290 |
291 | #region Export-Demo
292 | "Looking for demos in $env:GITHUB_WORKSPACE" | Out-Host
293 | Get-ChildItem -Path $env:GITHUB_WORKSPACE -Recurse -Filter *.ps1 |
294 | Where-Object Name -Match '(?<=\.|^)(?>demo|walkthru)\.ps1$' |
295 | ForEach-Object {
296 | $demoFile = $_
297 | $demoFileOut =
298 | $demoFile |
299 | Export-Demo -OutputPath {
300 | $_.FullName -replace '\.ps1$', '.md'
301 | }
302 |
303 | $lastCommitMessage =
304 | git log $demoFile.FullName |
305 | Select-Object -ExpandProperty CommitMessage -First 1
306 |
307 | "LastCommitMessage for $($demoFile.Name): $lastcommitMessage" | Out-Host
308 | $demoFileOut |
309 | Add-Member NoteProperty CommitMessage $lastCommitMessage -Force -PassThru |
310 | . processScriptOutput
311 | }
312 |
313 |
314 | #endregion Export-Demo
315 |
316 | $ShowDemoPS1EndStart = [DateTime]::Now
317 | $ShowDemoPS1Took = [Datetime]::Now - $ShowDemoPS1Start
318 | if ($CommitMessage -or $anyFilesChanged) {
319 | if ($CommitMessage) {
320 | dir $env:GITHUB_WORKSPACE -Recurse |
321 | ForEach-Object {
322 | $gitStatusOutput = git status $_.Fullname -s
323 | if ($gitStatusOutput) {
324 | git add $_.Fullname
325 | }
326 | }
327 |
328 | git commit -m $ExecutionContext.SessionState.InvokeCommand.ExpandString($CommitMessage)
329 | }
330 |
331 | $checkDetached = git symbolic-ref -q HEAD
332 | if (-not $LASTEXITCODE) {
333 | "::notice::Pulling Updates" | Out-Host
334 | git pull
335 | "::notice::Pushing Changes" | Out-Host
336 | git push
337 | "Git Push Output: $($gitPushed | Out-String)"
338 | } else {
339 | "::notice::Not pushing changes (on detached head)" | Out-Host
340 | $LASTEXITCODE = 0
341 | exit 0
342 | }
343 | }
344 |
345 | #endregion Actual Action
346 |
347 | } @Parameters
348 |
349 |
--------------------------------------------------------------------------------
/docs/2022-12-04.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2022/12/04/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2022 12 04" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2022-12.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2022/12/
3 | ---
4 | {% assign currentYearMonth = "2022 12" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2022.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2022/
3 | ---
4 | {% assign currentYear = "2022" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "[%B](%m) %Y" %}
8 | {% if postYear != currentYear %}
9 | {% continue %}
10 | {% endif %}
11 | {% if hasDisplayedYear != postYear %}
12 | ## [{{postYear}}](.)
13 | {% endif %}
14 | {% assign hasDisplayedYear = postYear %}
15 | {% if hasDisplayedYearMonth != postYearMonth %}
16 | ### {{postYearMonth}}
17 | {% endif %}
18 | {% assign hasDisplayedYearMonth = postYearMonth %}
19 | * [ {{ post.title }} ]( {{ post.url }} )
20 | {% endfor %}
21 |
--------------------------------------------------------------------------------
/docs/2023-05-23.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/05/23/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2023 05 23" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2023-05.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/05/
3 | ---
4 | {% assign currentYearMonth = "2023 05" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2023-06-15.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/06/15/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2023 06 15" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2023-06.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/06/
3 | ---
4 | {% assign currentYearMonth = "2023 06" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2023-08-02.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/08/02/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2023 08 02" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2023-08-18.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/08/18/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2023 08 18" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2023-08.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/08/
3 | ---
4 | {% assign currentYearMonth = "2023 08" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2023-10-10.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/10/10/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2023 10 10" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2023-10.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/10/
3 | ---
4 | {% assign currentYearMonth = "2023 10" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2023.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2023/
3 | ---
4 | {% assign currentYear = "2023" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "[%B](%m) %Y" %}
8 | {% if postYear != currentYear %}
9 | {% continue %}
10 | {% endif %}
11 | {% if hasDisplayedYear != postYear %}
12 | ## [{{postYear}}](.)
13 | {% endif %}
14 | {% assign hasDisplayedYear = postYear %}
15 | {% if hasDisplayedYearMonth != postYearMonth %}
16 | ### {{postYearMonth}}
17 | {% endif %}
18 | {% assign hasDisplayedYearMonth = postYearMonth %}
19 | * [ {{ post.title }} ]( {{ post.url }} )
20 | {% endfor %}
21 |
--------------------------------------------------------------------------------
/docs/2024-03-09.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2024/03/09/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2024 03 09" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2024-03.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2024/03/
3 | ---
4 | {% assign currentYearMonth = "2024 03" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2024-04-14.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2024/04/14/
3 | ---
4 | {% for post in site.posts %}
5 | {% assign currentdate = post.date | date: "%Y %m %d" %}
6 | {% assign friendlydate = post.date | date: "[%B](..) [%d](.) [%Y](../..)" %}
7 | {% if currentdate != "2024 04 14" %}
8 | {% continue %}
9 | {% endif %}
10 | {% if currentdate != date %}
11 | ## {{friendlydate}}
12 | {% assign date = currentdate %}
13 | {% endif %}
14 | * [ {{ post.title }} ]( {{ post.url }} )
15 | {% endfor %}
16 |
--------------------------------------------------------------------------------
/docs/2024-04.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2024/04/
3 | ---
4 | {% assign currentYearMonth = "2024 04" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "%B [%Y](..)" %}
8 | {% assign postYM = post.date | date: "%Y %m" %}
9 | {% if postYM != currentYearMonth %}
10 | {% continue %}
11 | {% endif %}
12 | {% if hasDisplayedYearMonth != postYearMonth %}
13 | ## {{postYearMonth}}
14 | {% endif %}
15 | {% assign hasDisplayedYearMonth = postYearMonth %}
16 | * [ {{ post.title }} ]( {{ post.url }} )
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/docs/2024.md:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /2024/
3 | ---
4 | {% assign currentYear = "2024" %}
5 | {% for post in site.posts %}
6 | {% assign postYear = post.date | date: "%Y" %}
7 | {% assign postYearMonth = post.date | date: "[%B](%m) %Y" %}
8 | {% if postYear != currentYear %}
9 | {% continue %}
10 | {% endif %}
11 | {% if hasDisplayedYear != postYear %}
12 | ## [{{postYear}}](.)
13 | {% endif %}
14 | {% assign hasDisplayedYear = postYear %}
15 | {% if hasDisplayedYearMonth != postYearMonth %}
16 | ### {{postYearMonth}}
17 | {% endif %}
18 | {% assign hasDisplayedYearMonth = postYearMonth %}
19 | * [ {{ post.title }} ]( {{ post.url }} )
20 | {% endfor %}
21 |
--------------------------------------------------------------------------------
/docs/Assets/ShowDemo-animated.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Show
13 |
14 | Demo
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/Assets/ShowDemo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Show
12 | Demo
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/Assets/ShowDemo@1080p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/docs/Assets/ShowDemo@1080p.png
--------------------------------------------------------------------------------
/docs/Assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/docs/Assets/demo.gif
--------------------------------------------------------------------------------
/docs/Assets/demo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StartAutomating/ShowDemo/5390fddbccb1835cabd77e6fb5c70ebc40829654/docs/Assets/demo.mp4
--------------------------------------------------------------------------------
/docs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | > Like It? [Star It](https://github.com/StartAutomating/ShowDemo)
2 | > Love It? [Support It](https://github.com/sponsors/StartAutomating)
3 |
4 | ---
5 |
6 | ## ShowDemo 0.1.7:
7 |
8 | * ShowDemo in Docker (#103)
9 | * Added Dockerfile (#104)
10 | * Publishing all builds to GitHub Container Registry (#105)
11 | * Added Trinity of Discoverability Demo (#51)
12 | * Exporting $ShowDemo (#106)
13 | * Mounting as ShowDemo: (#107)
14 |
15 | ---
16 |
17 | ## ShowDemo 0.1.6:
18 |
19 | * Show-Demo Syncing Console Encoding ( Fixes #101 )
20 | * Show-Demo -PauseBetweenLine(s) ( Fixes #100 )
21 | * Adjusting Default Type Speed ( Fixes #97 )
22 | * Showing unknown steps in White, not Output ( Fixes #99 )
23 |
24 | ---
25 |
26 | ## ShowDemo 0.1.5:
27 |
28 | * Demos are now more eventful (#66)
29 | * Nearly every part of ShowDemo transmits PowerShell engine events
30 | * These can be used for highly customized display of demos
31 | * Refactoring all *-Demo commands to use a single -From parameter (#86)
32 | * Added Logo (#90)
33 | * Integrated PSA (#91)
34 |
35 | ---
36 |
37 | ## ShowDemo 0.1.4:
38 |
39 | * ShowDemo - Adding Recommendations (Fixes #63)
40 | * Demo Format - Honoring .StartMessage/.EndMessage (Fixes #62)
41 | * Show-Demo - Adding -StartMessage/-EndMessage (Fixes #61)
42 |
43 | ---
44 |
45 | ## ShowDemo 0.1.3:
46 |
47 | * Adding support for prompts in demos
48 | * Demo.Step - Adding .ShowPrompt()/HidePrompt() (#54/#55)
49 | * Demo Formatting - Supporting ShowPrompt (#56)
50 | * Show-Demo - Adding -ShowPrompt (#53)
51 | * Import-Demo - Linking Chapters (#57)
52 | * Partitioning repository (#48, #49, #50)
53 |
54 | ---
55 |
56 | ## ShowDemo 0.1.2:
57 |
58 | * Get-Demo - Skipping $pwd if in $filePaths (Fixes #43)
59 | * Show-Demo - Adding -Record (Fixes #42)
60 | * Import-Demo - Including .DemoScript (Fixes #44)
61 | * Adding Demo.ToMarkdown (Fixes #45)
62 |
63 | ---
64 |
65 | ## ShowDemo 0.1.1
66 |
67 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep (#39)
68 | * Export-Demo - Defaults to English when invariant culture (Fixes #37)
69 | * Improvements in how steps are determined (#35 #36)
70 | * Please Sponsor ShowDemo (#38)
71 |
72 | ---
73 |
74 | ## ShowDemo 0.1
75 |
76 | Initial Release of Show-Demo.
77 |
78 | * List Demos with Get-Demo
79 | * Show Demos with Show-Demo
80 | * Export Demos with Export-Demo.
81 | * ShowDemo GitHub Action
82 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | showdemo.start-automating.com
--------------------------------------------------------------------------------
/docs/Demo/NextChapter.md:
--------------------------------------------------------------------------------
1 | Demo.NextChapter()
2 | ------------------
3 |
4 | ### Synopsis
5 | Go to the Next Chapter in a Demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Advances a demo to the next chapter.
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Demo/NextStep.md:
--------------------------------------------------------------------------------
1 | Demo.NextStep()
2 | ---------------
3 |
4 | ### Synopsis
5 | Go to the next demo step
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Advances a demo to the next step.
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Demo/ProcessInput.md:
--------------------------------------------------------------------------------
1 | Demo.ProcessInput()
2 | -------------------
3 |
4 | ### Synopsis
5 | Processes demo input
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Processes user input to a demo file.
12 |
13 | ---
14 |
15 | ### Parameters
16 | #### **hostInput**
17 |
18 | |Type |Required|Position|PipelineInput|
19 | |----------|--------|--------|-------------|
20 | |`[Object]`|false |1 |false |
21 |
22 | ---
23 |
--------------------------------------------------------------------------------
/docs/Demo/README.md:
--------------------------------------------------------------------------------
1 | ## Demo
2 |
3 |
4 | ### Script Methods
5 |
6 |
7 | * [NextChapter](NextChapter.md)
8 | * [NextStep](NextStep.md)
9 | * [ProcessInput](ProcessInput.md)
10 | * [Reset](Reset.md)
11 | * [SetStatus](SetStatus.md)
12 | * [Start](Start.md)
13 | * [Stop](Stop.md)
14 |
--------------------------------------------------------------------------------
/docs/Demo/Reset.md:
--------------------------------------------------------------------------------
1 | Demo.Reset()
2 | ------------
3 |
4 | ### Synopsis
5 | Resets a demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Resets a demo file, so that it can be replayed.
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Demo/SetStatus.md:
--------------------------------------------------------------------------------
1 | Demo.SetStatus()
2 | ----------------
3 |
4 | ### Synopsis
5 | Sets demo status
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Sets the status of a demo.
12 |
13 | ---
14 |
15 | ### Parameters
16 | #### **Status**
17 |
18 | |Type |Required|Position|PipelineInput|
19 | |----------|--------|--------|-------------|
20 | |`[String]`|false |1 |false |
21 |
22 | ---
23 |
--------------------------------------------------------------------------------
/docs/Demo/Start.md:
--------------------------------------------------------------------------------
1 | Demo.Start()
2 | ------------
3 |
4 | ### Synopsis
5 | Starts a Demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Starts a Demo file.
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Demo/Step/HidePrompt.md:
--------------------------------------------------------------------------------
1 | Demo.Step.HidePrompt()
2 | ----------------------
3 |
4 | ### Synopsis
5 | Hides the prompt
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Hides the prompt within a demo.
12 |
13 | ---
14 |
15 | ### Examples
16 | .HidePrompt
17 |
18 | ---
19 |
20 | ### Parameters
21 | #### **step**
22 | Any additional parameters for the step.
23 | This is ignored when hiding prompts.
24 |
25 | |Type |Required|Position|PipelineInput|
26 | |----------|--------|--------|-------------|
27 | |`[Object]`|false |1 |false |
28 |
29 | ---
30 |
--------------------------------------------------------------------------------
/docs/Demo/Step/Invoke.md:
--------------------------------------------------------------------------------
1 | Demo.Step.Invoke()
2 | ------------------
3 |
4 | ### Synopsis
5 | Invokes a demo step
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Invokes a step in a demo file.
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Demo/Step/README.md:
--------------------------------------------------------------------------------
1 | ## Demo.Step
2 |
3 |
4 | ### Script Methods
5 |
6 |
7 | * [HidePrompt](HidePrompt.md)
8 | * [Invoke](Invoke.md)
9 | * [ShowPrompt](ShowPrompt.md)
10 | * [Silent](Silent.md)
11 |
--------------------------------------------------------------------------------
/docs/Demo/Step/ShowPrompt.md:
--------------------------------------------------------------------------------
1 | Demo.Step.ShowPrompt()
2 | ----------------------
3 |
4 | ### Synopsis
5 | Shows the prompt
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Show the prompt within a demo.
12 |
13 | ---
14 |
15 | ### Examples
16 | .ShowPrompt
17 |
18 | ---
19 |
20 | ### Parameters
21 | #### **step**
22 | Any additional parameters for the step.
23 | This is ignored when showing prompts.
24 |
25 | |Type |Required|Position|PipelineInput|
26 | |----------|--------|--------|-------------|
27 | |`[Object]`|false |1 |false |
28 |
29 | ---
30 |
--------------------------------------------------------------------------------
/docs/Demo/Step/Silent.md:
--------------------------------------------------------------------------------
1 | Demo.Step.Silent()
2 | ------------------
3 |
4 | ### Synopsis
5 | Run a silent step
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Run a silent step of a demo.
12 |
13 | Silent steps do not display their results.
14 |
15 | ---
16 |
17 | ### Parameters
18 | #### **silentStep**
19 |
20 | |Type |Required|Position|PipelineInput|
21 | |----------|--------|--------|-------------|
22 | |`[Object]`|false |1 |false |
23 |
24 | ---
25 |
--------------------------------------------------------------------------------
/docs/Demo/Stop.md:
--------------------------------------------------------------------------------
1 | Demo.Stop()
2 | -----------
3 |
4 | ### Synopsis
5 | Stops a demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Stops a demo that is currently running
12 |
13 | ---
14 |
--------------------------------------------------------------------------------
/docs/Export-Demo.md:
--------------------------------------------------------------------------------
1 | Export-Demo
2 | -----------
3 |
4 | ### Synopsis
5 | Exports Demos
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Exports a Demo.
12 |
13 | Demos can be saved to a Markdown (.md) file or a Clixml (.clixml/.clix)file
14 |
15 | ---
16 |
17 | ### Examples
18 | > EXAMPLE 1
19 |
20 | ```PowerShell
21 | Export-Demo -DemoPath .\demo.ps1 -OutputPath .\demo.md
22 | ```
23 |
24 | ---
25 |
26 | ### Parameters
27 | #### **From**
28 | The source of the demo. This can be a string, file, command, module, or path.
29 |
30 | |Type |Required|Position|PipelineInput |Aliases |
31 | |------------|--------|--------|---------------------|-----------------------------------------------------------------------------------------------|
32 | |`[PSObject]`|true |named |true (ByPropertyName)|DemoPath DemoName DemoText DemoScript FullName DemoFile File Source|
33 |
34 | #### **OutputPath**
35 | The output path. This is the location the demo will be saved to.
36 |
37 | |Type |Required|Position|PipelineInput |Aliases |
38 | |----------|--------|--------|---------------------|----------|
39 | |`[String]`|true |named |true (ByPropertyName)|OutputFile|
40 |
41 | ---
42 |
43 | ### Syntax
44 | ```PowerShell
45 | Export-Demo -From -OutputPath []
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/Get-Demo.md:
--------------------------------------------------------------------------------
1 | Get-Demo
2 | --------
3 |
4 | ### Synopsis
5 | Gets Demos
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Gets PowerShell Demos.
12 |
13 | Demos located in ShowDemo and all modules that tag ShowDemo will be automatically discovered.
14 |
15 | ---
16 |
17 | ### Related Links
18 | * [Import-Demo](Import-Demo.md)
19 |
20 | ---
21 |
22 | ### Examples
23 | > EXAMPLE 1
24 |
25 | ```PowerShell
26 | Get-Demo
27 | ```
28 |
29 | ---
30 |
31 | ### Parameters
32 | #### **From**
33 | The source of the demo. This can be a string, file, command, module, or path.
34 |
35 | |Type |Required|Position|PipelineInput |Aliases |
36 | |------------|--------|--------|---------------------|-----------------------------------------------------------------------------------------------|
37 | |`[PSObject]`|true |named |true (ByPropertyName)|DemoPath DemoName DemoText DemoScript FullName DemoFile File Source|
38 |
39 | ---
40 |
41 | ### Syntax
42 | ```PowerShell
43 | Get-Demo []
44 | ```
45 | ```PowerShell
46 | Get-Demo -From []
47 | ```
48 |
--------------------------------------------------------------------------------
/docs/Import-Demo.md:
--------------------------------------------------------------------------------
1 | Import-Demo
2 | -----------
3 |
4 | ### Synopsis
5 | Imports Demos
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Imports a Demo script.
12 |
13 | ---
14 |
15 | ### Related Links
16 | * [Export-Demo](Export-Demo.md)
17 |
18 | * [Get-Demo](Get-Demo.md)
19 |
20 | * [Start-Demo](Start-Demo.md)
21 |
22 | ---
23 |
24 | ### Examples
25 | > EXAMPLE 1
26 |
27 | ```PowerShell
28 | Import-Demo -DemoName "Demo"
29 | ```
30 |
31 | ---
32 |
33 | ### Parameters
34 | #### **From**
35 | The source of the demo. This can be a string, file, command, module, or path.
36 |
37 | |Type |Required|Position|PipelineInput |Aliases |
38 | |------------|--------|--------|---------------------|-----------------------------------------------------------------------------------------------|
39 | |`[PSObject]`|true |named |true (ByPropertyName)|DemoPath DemoName DemoText DemoScript FullName DemoFile File Source|
40 |
41 | ---
42 |
43 | ### Syntax
44 | ```PowerShell
45 | Import-Demo -From []
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | # Showcase your Scripts
13 |
14 | Want to showcase something you built in PowerShell?
15 |
16 | You can make a .demo.ps1 file to showcase your script line by line, like this:
17 |
18 | 
19 |
20 | You can also make a .demo.ps1 file as markdown, like [this](demo.md).
21 |
22 | Give it a try!
23 |
24 | ~~~PowerShell
25 | Install-Module ShowDemo -Scope CurrentUser -Force
26 | Import-Module ShowDemo -Force -PassThru
27 | Show-Demo
28 | ~~~
29 |
30 | ## Writing Demos
31 |
32 | Demo files just simple scripts, named either demo.ps1 or *.demo.ps1.
33 |
34 | Each comment or statement that starts in the first column is considered a step.
35 |
36 | For an example, check out [demo.ps1](https://github.com/StartAutomating/ShowDemo/blob/main/demo.ps1)
37 |
38 | ## Using the GitHub Action
39 |
40 | To use ShowDemo in a GitHub Action, simply add this line to your workflow:
41 |
42 | ~~~yaml
43 | - uses: StartAutomating/ShowDemo@main
44 | ~~~
45 |
46 | This will take any demo files and export them as markdown.
47 |
48 | ## ShowDemo Commands
49 |
50 | ShowDemo is a module of few commands. They are:
51 |
52 | |Name|Synopsis|
53 | |-|-|
54 | |Get-Demo | Gets Demos |
55 | |Export-Demo| Exports Demos|
56 | |Import-Demo| Imports Demos|
57 | |Resume-Demo| Resumes Demos|
58 | |Show-Demo | Shows Demos |
59 |
60 | You can Show your demo by running: `Show-Demo -DemoPath .\My.demo.ps1`
61 |
62 | Show-Demo is aliased to Start-Demo, it's inspiration
63 |
64 | ## Inspiration, History, and Goals
65 |
66 | In the early days of PowerShell, Jeffery Snover created a useful little script called Start-Demo.
67 |
68 | Start-Demo was incredibly useful.
69 |
70 | It helped showcase just how cool PowerShell could be, and gave every scripter a simple tool to showcase their scripts.
71 |
72 | Start-Demo was written all the way back in PowerShell v1; before the parser API, before markdown, and well before colorized output in Windows Terminal.
73 |
74 | ShowDemo is designed to update and replace the old Start-Demo and provide a foundation to give it even more modern capabilities.
75 |
--------------------------------------------------------------------------------
/docs/Resume-Demo.md:
--------------------------------------------------------------------------------
1 | Resume-Demo
2 | -----------
3 |
4 | ### Synopsis
5 | Resumes a Demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Resumes a Demo that was paused or debugged with `!`.
12 |
13 | ---
14 |
15 | ### Related Links
16 | * [Show-Demo](Show-Demo.md)
17 |
18 | * [Get-Demo](Get-Demo.md)
19 |
20 | ---
21 |
22 | ### Examples
23 | > EXAMPLE 1
24 |
25 | ```PowerShell
26 | Resume-Demo
27 | ```
28 |
29 | ---
30 |
31 | ### Parameters
32 | #### **DemoToResume**
33 | The demo that will be resumed.
34 |
35 | |Type |Required|Position|PipelineInput |
36 | |----------|--------|--------|--------------|
37 | |`[Object]`|false |1 |true (ByValue)|
38 |
39 | ---
40 |
41 | ### Syntax
42 | ```PowerShell
43 | Resume-Demo [[-DemoToResume] ] []
44 | ```
45 |
--------------------------------------------------------------------------------
/docs/Show-Demo.md:
--------------------------------------------------------------------------------
1 | Show-Demo
2 | ---------
3 |
4 | ### Synopsis
5 | Shows a Demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Shows a PowerShell Demo Script.
12 |
13 | ---
14 |
15 | ### Related Links
16 | * [Get-Demo](Get-Demo.md)
17 |
18 | ---
19 |
20 | ### Examples
21 | > EXAMPLE 1
22 |
23 | ```PowerShell
24 | Show-Demo
25 | ```
26 |
27 | ---
28 |
29 | ### Parameters
30 | #### **From**
31 | The source of the demo. This can be a string, file, command, module, or path.
32 |
33 | |Type |Required|Position|PipelineInput |Aliases |
34 | |------------|--------|--------|---------------------|-----------------------------------------------------------------------------------------------|
35 | |`[PSObject]`|false |1 |true (ByPropertyName)|DemoPath DemoName DemoText DemoScript FullName DemoFile File Source|
36 |
37 | #### **Chapter**
38 | The name of the chapter
39 |
40 | |Type |Required|Position|PipelineInput|
41 | |----------|--------|--------|-------------|
42 | |`[String]`|false |2 |false |
43 |
44 | #### **Step**
45 | The current step (within -Chapter)
46 |
47 | |Type |Required|Position|PipelineInput|
48 | |---------|--------|--------|-------------|
49 | |`[Int32]`|false |3 |false |
50 |
51 | #### **TypeStyle**
52 | The typing style. Can be letters, words, or none.
53 | Valid Values:
54 |
55 | * Letters
56 | * Words
57 | * None
58 |
59 | |Type |Required|Position|PipelineInput|
60 | |----------|--------|--------|-------------|
61 | |`[String]`|false |4 |false |
62 |
63 | #### **TypeSpeed**
64 | If this is an integer less than 10000, it will be considered 'words per minute'
65 | Otherwise, this will be the timespan to wait between words / letters being displayed.
66 |
67 | |Type |Required|Position|PipelineInput|
68 | |------------|--------|--------|-------------|
69 | |`[TimeSpan]`|false |5 |false |
70 |
71 | #### **PauseBetweenStep**
72 | The amount of time to wait between each step.
73 | If provided, implies -AutoPlay.
74 |
75 | |Type |Required|Position|PipelineInput|Aliases |
76 | |------------|--------|--------|-------------|-----------------|
77 | |`[TimeSpan]`|false |6 |false |PauseBetweenSteps|
78 |
79 | #### **PauseBetweenLine**
80 | The amount of time to wait between each line.
81 | This can help demos that display a lot of information at once.
82 |
83 | |Type |Required|Position|PipelineInput|Aliases |
84 | |------------|--------|--------|-------------|-----------------|
85 | |`[TimeSpan]`|false |7 |false |PauseBetweenLines|
86 |
87 | #### **AutoPlay**
88 | If set, will automatically play demos.
89 | Use -PauseBetweenStep to specify how long to wait between each step.
90 |
91 | |Type |Required|Position|PipelineInput|
92 | |----------|--------|--------|-------------|
93 | |`[Switch]`|false |named |false |
94 |
95 | #### **NonInteractive**
96 | If set, will make the demo noniteractive.
97 |
98 | |Type |Required|Position|PipelineInput|
99 | |----------|--------|--------|-------------|
100 | |`[Switch]`|false |named |false |
101 |
102 | #### **ShowPrompt**
103 | If set, will show the prompt between each step.
104 | This can also be enabled or disabled within a demo, with .ShowPrompt or .HidePrompt
105 |
106 | |Type |Required|Position|PipelineInput|
107 | |----------|--------|--------|-------------|
108 | |`[Switch]`|false |named |false |
109 |
110 | #### **Record**
111 | If set, will attempt to record the demo.
112 | This presumes that [obs-powershell](https://github.com/StartAutomating/obs-powershell) is installed.
113 |
114 | |Type |Required|Position|PipelineInput|
115 | |----------|--------|--------|-------------|
116 | |`[Switch]`|false |named |false |
117 |
118 | #### **StartMessage**
119 | If provided, will set the message displayed at demo start.
120 |
121 | |Type |Required|Position|PipelineInput|
122 | |----------|--------|--------|-------------|
123 | |`[String]`|false |8 |false |
124 |
125 | #### **EndMessage**
126 | If provided, will set the message displayed at demo start.
127 |
128 | |Type |Required|Position|PipelineInput|
129 | |----------|--------|--------|-------------|
130 | |`[String]`|false |9 |false |
131 |
132 | ---
133 |
134 | ### Syntax
135 | ```PowerShell
136 | Show-Demo [[-From] ] [[-Chapter] ] [[-Step] ] [[-TypeStyle] ] [[-TypeSpeed] ] [[-PauseBetweenStep] ] [[-PauseBetweenLine] ] [-AutoPlay] [-NonInteractive] [-ShowPrompt] [-Record] [[-StartMessage] ] [[-EndMessage] ] []
137 | ```
138 |
--------------------------------------------------------------------------------
/docs/Start-Demo.md:
--------------------------------------------------------------------------------
1 | Show-Demo
2 | ---------
3 |
4 | ### Synopsis
5 | Shows a Demo
6 |
7 | ---
8 |
9 | ### Description
10 |
11 | Shows a PowerShell Demo Script.
12 |
13 | ---
14 |
15 | ### Related Links
16 | * [Get-Demo](Get-Demo.md)
17 |
18 | ---
19 |
20 | ### Examples
21 | > EXAMPLE 1
22 |
23 | ```PowerShell
24 | Show-Demo
25 | ```
26 |
27 | ---
28 |
29 | ### Parameters
30 | #### **From**
31 | The source of the demo. This can be a string, file, command, module, or path.
32 |
33 | |Type |Required|Position|PipelineInput |Aliases |
34 | |------------|--------|--------|---------------------|-----------------------------------------------------------------------------------------------|
35 | |`[PSObject]`|false |1 |true (ByPropertyName)|DemoPath DemoName DemoText DemoScript FullName DemoFile File Source|
36 |
37 | #### **Chapter**
38 | The name of the chapter
39 |
40 | |Type |Required|Position|PipelineInput|
41 | |----------|--------|--------|-------------|
42 | |`[String]`|false |2 |false |
43 |
44 | #### **Step**
45 | The current step (within -Chapter)
46 |
47 | |Type |Required|Position|PipelineInput|
48 | |---------|--------|--------|-------------|
49 | |`[Int32]`|false |3 |false |
50 |
51 | #### **TypeStyle**
52 | The typing style. Can be letters, words, or none.
53 | Valid Values:
54 |
55 | * Letters
56 | * Words
57 | * None
58 |
59 | |Type |Required|Position|PipelineInput|
60 | |----------|--------|--------|-------------|
61 | |`[String]`|false |4 |false |
62 |
63 | #### **TypeSpeed**
64 | If this is an integer less than 10000, it will be considered 'words per minute'
65 | Otherwise, this will be the timespan to wait between words / letters being displayed.
66 |
67 | |Type |Required|Position|PipelineInput|
68 | |------------|--------|--------|-------------|
69 | |`[TimeSpan]`|false |5 |false |
70 |
71 | #### **PauseBetweenStep**
72 | The amount of time to wait between each step.
73 | If provided, implies -AutoPlay.
74 |
75 | |Type |Required|Position|PipelineInput|Aliases |
76 | |------------|--------|--------|-------------|-----------------|
77 | |`[TimeSpan]`|false |6 |false |PauseBetweenSteps|
78 |
79 | #### **PauseBetweenLine**
80 | The amount of time to wait between each line.
81 | This can help demos that display a lot of information at once.
82 |
83 | |Type |Required|Position|PipelineInput|Aliases |
84 | |------------|--------|--------|-------------|-----------------|
85 | |`[TimeSpan]`|false |7 |false |PauseBetweenLines|
86 |
87 | #### **AutoPlay**
88 | If set, will automatically play demos.
89 | Use -PauseBetweenStep to specify how long to wait between each step.
90 |
91 | |Type |Required|Position|PipelineInput|
92 | |----------|--------|--------|-------------|
93 | |`[Switch]`|false |named |false |
94 |
95 | #### **NonInteractive**
96 | If set, will make the demo noniteractive.
97 |
98 | |Type |Required|Position|PipelineInput|
99 | |----------|--------|--------|-------------|
100 | |`[Switch]`|false |named |false |
101 |
102 | #### **ShowPrompt**
103 | If set, will show the prompt between each step.
104 | This can also be enabled or disabled within a demo, with .ShowPrompt or .HidePrompt
105 |
106 | |Type |Required|Position|PipelineInput|
107 | |----------|--------|--------|-------------|
108 | |`[Switch]`|false |named |false |
109 |
110 | #### **Record**
111 | If set, will attempt to record the demo.
112 | This presumes that [obs-powershell](https://github.com/StartAutomating/obs-powershell) is installed.
113 |
114 | |Type |Required|Position|PipelineInput|
115 | |----------|--------|--------|-------------|
116 | |`[Switch]`|false |named |false |
117 |
118 | #### **StartMessage**
119 | If provided, will set the message displayed at demo start.
120 |
121 | |Type |Required|Position|PipelineInput|
122 | |----------|--------|--------|-------------|
123 | |`[String]`|false |8 |false |
124 |
125 | #### **EndMessage**
126 | If provided, will set the message displayed at demo start.
127 |
128 | |Type |Required|Position|PipelineInput|
129 | |----------|--------|--------|-------------|
130 | |`[String]`|false |9 |false |
131 |
132 | ---
133 |
134 | ### Syntax
135 | ```PowerShell
136 | Show-Demo [[-From] ] [[-Chapter] ] [[-Step] ] [[-TypeStyle] ] [[-TypeSpeed] ] [[-PauseBetweenStep] ] [[-PauseBetweenLine] ] [-AutoPlay] [-NonInteractive] [-ShowPrompt] [-Record] [[-StartMessage] ] [[-EndMessage] ] []
137 | ```
138 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | permalink: pretty
--------------------------------------------------------------------------------
/docs/_posts/2022-12-04-ShowDemo-0.1.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1
8 |
9 | Initial Release of Show-Demo.
10 |
11 | * List Demos with Get-Demo
12 | * Show Demos with Show-Demo
13 | * Export Demos with Export-Demo.
14 | * ShowDemo GitHub Action
15 |
16 |
--------------------------------------------------------------------------------
/docs/_posts/2022-12-04-Showing-PowerShell-Demos.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: Showing PowerShell Demos
4 | author: StartAutomating
5 | sourceURL: https://github.com/StartAutomating/ShowDemo/issues/1
6 | ---
7 | If you're trying to show off your scripts, typos are not your friend.
8 |
9 | It would be really nice if you could just show your scripts step-by-step.
10 |
11 | Long ago, Jeffery Snover wrote a little demo player, Start-Demo, that did exactly that. This is a cool script, but it got lost to the sands of time, and hasn't been updated too much since V1.
12 |
13 | ShowDemo is a small PowerShell module that makes sleek PowerShell demos.
14 |
15 | Any file named: `*.demo.ps1` or `*.walkthru.ps1` will be considered a demo.
16 |
17 | A demo file will follow this format:
18 |
19 | ~~~PowerShell
20 |
21 | # 1. Chapters are comments that start with a number
22 |
23 | # Comments or statements that start a line and are balanced will be considered a step
24 |
25 | "like this is one step"
26 |
27 | "but so is this whole pipeline,
28 | that stretches for multiple lines" -split
29 | ','
30 |
31 | # Each step will output in full color
32 |
33 | # and wait for you to press enter
34 |
35 | "then a step will run"
36 |
37 | # You press enter one more time after a command has run
38 |
39 | # And so it goes.
40 |
41 | # step by step
42 |
43 | # until a demo is done
44 | ~~~
45 |
--------------------------------------------------------------------------------
/docs/_posts/2023-05-23-ShowDemo-0.1.1.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.1
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.1
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.1
8 |
9 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep ([#39](https://github.com/StartAutomating/ShowDemo/issues/39))
10 | * Export-Demo - Defaults to English when invariant culture (Fixes [#37](https://github.com/StartAutomating/ShowDemo/issues/37))
11 | * Improvements in how steps are determined ([#35](https://github.com/StartAutomating/ShowDemo/issues/35) [#36](https://github.com/StartAutomating/ShowDemo/issues/36))
12 | * Please Sponsor ShowDemo ([#38](https://github.com/StartAutomating/ShowDemo/issues/38))
13 |
14 | ---
15 |
16 | ## ShowDemo 0.1
17 |
18 | Initial Release of Show-Demo.
19 |
20 | * List Demos with Get-Demo
21 | * Show Demos with Show-Demo
22 | * Export Demos with Export-Demo.
23 | * ShowDemo GitHub Action
24 |
25 |
--------------------------------------------------------------------------------
/docs/_posts/2023-06-15-ShowDemo-0.1.2.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.2
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.2
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.2:
8 |
9 | * Get-Demo - Skipping $pwd if in $filePaths (Fixes [#43](https://github.com/StartAutomating/ShowDemo/issues/43))
10 | * Show-Demo - Adding -Record (Fixes [#42](https://github.com/StartAutomating/ShowDemo/issues/42))
11 | * Import-Demo - Including .DemoScript (Fixes [#44](https://github.com/StartAutomating/ShowDemo/issues/44))
12 | * Adding Demo.ToMarkdown (Fixes [#45](https://github.com/StartAutomating/ShowDemo/issues/45))
13 |
14 | ---
15 |
16 | ## ShowDemo 0.1.1:
17 |
18 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep ([#39](https://github.com/StartAutomating/ShowDemo/issues/39))
19 | * Export-Demo - Defaults to English when invariant culture (Fixes [#37](https://github.com/StartAutomating/ShowDemo/issues/37))
20 | * Improvements in how steps are determined ([#35](https://github.com/StartAutomating/ShowDemo/issues/35) [#36](https://github.com/StartAutomating/ShowDemo/issues/36))
21 | * Please Sponsor ShowDemo ([#38](https://github.com/StartAutomating/ShowDemo/issues/38))
22 |
23 | ---
24 |
25 | ## ShowDemo 0.1:
26 |
27 | Initial Release of Show-Demo.
28 |
29 | * List Demos with Get-Demo
30 | * Show Demos with Show-Demo
31 | * Export Demos with Export-Demo.
32 | * ShowDemo GitHub Action
33 |
34 |
--------------------------------------------------------------------------------
/docs/_posts/2023-08-02-ShowDemo-0.1.3.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.3
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.3
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.3:
8 |
9 | * Adding support for prompts in demos
10 | * Demo.Step - Adding .ShowPrompt()/HidePrompt() ([#54](https://github.com/StartAutomating/ShowDemo/issues/54)/[#55](https://github.com/StartAutomating/ShowDemo/issues/55))
11 | * Demo Formatting - Supporting ShowPrompt ([#56](https://github.com/StartAutomating/ShowDemo/issues/56))
12 | * Show-Demo - Adding -ShowPrompt ([#53](https://github.com/StartAutomating/ShowDemo/issues/53))
13 | * Import-Demo - Linking Chapters ([#57](https://github.com/StartAutomating/ShowDemo/issues/57))
14 | * Partitioning repository ([#48](https://github.com/StartAutomating/ShowDemo/issues/48), [#49](https://github.com/StartAutomating/ShowDemo/issues/49), [#50](https://github.com/StartAutomating/ShowDemo/issues/50))
15 |
16 | ---
17 |
18 | ## ShowDemo 0.1.2:
19 |
20 | * Get-Demo - Skipping $pwd if in $filePaths (Fixes [#43](https://github.com/StartAutomating/ShowDemo/issues/43))
21 | * Show-Demo - Adding -Record (Fixes [#42](https://github.com/StartAutomating/ShowDemo/issues/42))
22 | * Import-Demo - Including .DemoScript (Fixes [#44](https://github.com/StartAutomating/ShowDemo/issues/44))
23 | * Adding Demo.ToMarkdown (Fixes [#45](https://github.com/StartAutomating/ShowDemo/issues/45))
24 |
25 | ---
26 |
27 | ## ShowDemo 0.1.1:
28 |
29 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep ([#39](https://github.com/StartAutomating/ShowDemo/issues/39))
30 | * Export-Demo - Defaults to English when invariant culture (Fixes [#37](https://github.com/StartAutomating/ShowDemo/issues/37))
31 | * Improvements in how steps are determined ([#35](https://github.com/StartAutomating/ShowDemo/issues/35) [#36](https://github.com/StartAutomating/ShowDemo/issues/36))
32 | * Please Sponsor ShowDemo ([#38](https://github.com/StartAutomating/ShowDemo/issues/38))
33 |
34 | ---
35 |
36 | ## ShowDemo 0.1:
37 |
38 | Initial Release of Show-Demo.
39 |
40 | * List Demos with Get-Demo
41 | * Show Demos with Show-Demo
42 | * Export Demos with Export-Demo.
43 | * ShowDemo GitHub Action
44 |
45 |
--------------------------------------------------------------------------------
/docs/_posts/2023-08-18-ShowDemo-0.1.4.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.4
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.4
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.4:
8 |
9 | * ShowDemo - Adding Recommendations (Fixes [#63](https://github.com/StartAutomating/ShowDemo/issues/63))
10 | * Demo Format - Honoring .StartMessage/.EndMessage (Fixes [#62](https://github.com/StartAutomating/ShowDemo/issues/62))
11 | * Show-Demo - Adding -StartMessage/-EndMessage (Fixes [#61](https://github.com/StartAutomating/ShowDemo/issues/61))
12 |
13 | ---
14 |
15 | ## ShowDemo 0.1.3:
16 |
17 | * Adding support for prompts in demos
18 | * Demo.Step - Adding .ShowPrompt()/HidePrompt() ([#54](https://github.com/StartAutomating/ShowDemo/issues/54)/[#55](https://github.com/StartAutomating/ShowDemo/issues/55))
19 | * Demo Formatting - Supporting ShowPrompt ([#56](https://github.com/StartAutomating/ShowDemo/issues/56))
20 | * Show-Demo - Adding -ShowPrompt ([#53](https://github.com/StartAutomating/ShowDemo/issues/53))
21 | * Import-Demo - Linking Chapters ([#57](https://github.com/StartAutomating/ShowDemo/issues/57))
22 | * Partitioning repository ([#48](https://github.com/StartAutomating/ShowDemo/issues/48), [#49](https://github.com/StartAutomating/ShowDemo/issues/49), [#50](https://github.com/StartAutomating/ShowDemo/issues/50))
23 |
24 | ---
25 |
26 | ## ShowDemo 0.1.2:
27 |
28 | * Get-Demo - Skipping $pwd if in $filePaths (Fixes [#43](https://github.com/StartAutomating/ShowDemo/issues/43))
29 | * Show-Demo - Adding -Record (Fixes [#42](https://github.com/StartAutomating/ShowDemo/issues/42))
30 | * Import-Demo - Including .DemoScript (Fixes [#44](https://github.com/StartAutomating/ShowDemo/issues/44))
31 | * Adding Demo.ToMarkdown (Fixes [#45](https://github.com/StartAutomating/ShowDemo/issues/45))
32 |
33 | ---
34 |
35 | ## ShowDemo 0.1.1:
36 |
37 | * Show-Demo now supports -AutoPlay/-PauseBetweenStep ([#39](https://github.com/StartAutomating/ShowDemo/issues/39))
38 | * Export-Demo - Defaults to English when invariant culture (Fixes [#37](https://github.com/StartAutomating/ShowDemo/issues/37))
39 | * Improvements in how steps are determined ([#35](https://github.com/StartAutomating/ShowDemo/issues/35) [#36](https://github.com/StartAutomating/ShowDemo/issues/36))
40 | * Please Sponsor ShowDemo ([#38](https://github.com/StartAutomating/ShowDemo/issues/38))
41 |
42 | ---
43 |
44 | ## ShowDemo 0.1:
45 |
46 | Initial Release of Show-Demo.
47 |
48 | * List Demos with Get-Demo
49 | * Show Demos with Show-Demo
50 | * Export Demos with Export-Demo.
51 | * ShowDemo GitHub Action
52 |
53 |
--------------------------------------------------------------------------------
/docs/_posts/2023-10-10-ShowDemo-0.1.5.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.5
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.5
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.5:
8 |
9 | * Demos are now more eventful ([#66](https://github.com/StartAutomating/ShowDemo/issues/66))
10 | * Nearly every part of ShowDemo transmits PowerShell engine events
11 | * These can be used for highly customized display of demos
12 | * Refactoring all *-Demo commands to use a single -From parameter ([#86](https://github.com/StartAutomating/ShowDemo/issues/86))
13 | * Added Logo ([#90](https://github.com/StartAutomating/ShowDemo/issues/90))
14 | * Integrated PSA ([#91](https://github.com/StartAutomating/ShowDemo/issues/91))
15 |
16 | ---
17 |
18 | Previous release notes in [CHANGELOG](https://github.com/StartAutomating/ShowDemo/blob/main/CHANGELOG.md)
19 |
20 | Like It? Star It! Love It? Support It!
21 |
--------------------------------------------------------------------------------
/docs/_posts/2024-03-09-ShowDemo-0.1.6.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.6
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.6
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.6:
8 |
9 | * Show-Demo Syncing Console Encoding ( Fixes [#101](https://github.com/StartAutomating/ShowDemo/issues/101) )
10 | * Show-Demo -PauseBetweenLine(s) ( Fixes [#100](https://github.com/StartAutomating/ShowDemo/issues/100) )
11 | * Adjusting Default Type Speed ( Fixes [#97](https://github.com/StartAutomating/ShowDemo/issues/97) )
12 | * Showing unknown steps in White, not Output ( Fixes [#99](https://github.com/StartAutomating/ShowDemo/issues/99) )
13 |
14 | ---
15 |
16 | Previous release notes in [CHANGELOG](https://github.com/StartAutomating/ShowDemo/blob/main/CHANGELOG.md)
17 |
18 | Like It? [Star It](https://github.com/StartAutomating/ShowDemo)! Love It? [Support It](https://github.com/sponsors/StartAutomating)!
19 |
--------------------------------------------------------------------------------
/docs/_posts/2024-04-14-ShowDemo-0.1.7.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | title: ShowDemo 0.1.7
4 | sourceURL: https://github.com/StartAutomating/ShowDemo/releases/tag/v0.1.7
5 | tag: release
6 | ---
7 | ## ShowDemo 0.1.7:
8 |
9 | * ShowDemo in Docker ([#103](https://github.com/StartAutomating/ShowDemo/issues/103))
10 | * Added Dockerfile ([#104](https://github.com/StartAutomating/ShowDemo/issues/104))
11 | * Publishing all builds to GitHub Container Registry ([#105](https://github.com/StartAutomating/ShowDemo/issues/105))
12 | * Added Trinity of Discoverability Demo ([#51](https://github.com/StartAutomating/ShowDemo/issues/51))
13 | * Exporting $ShowDemo ([#106](https://github.com/StartAutomating/ShowDemo/issues/106))
14 | * Mounting as ShowDemo: ([#107](https://github.com/StartAutomating/ShowDemo/issues/107))
15 |
16 | ---
17 |
18 | Full history in [CHANGELOG](https://github.com/StartAutomating/ShowDemo/blob/main/CHANGELOG.md)
19 |
20 | > Like It? [Star It](https://github.com/StartAutomating/ShowDemo)
21 | > Love It? [Support It](https://github.com/sponsors/StartAutomating)
22 |
--------------------------------------------------------------------------------
/docs/rss.xml:
--------------------------------------------------------------------------------
1 | ---
2 | layout: null
3 | ---
4 |
5 |
6 |
7 | {{ site.title | xml_escape }}
8 | {{ site.description | xml_escape }}
9 | {{ site.url }}{{ site.baseurl }}/
10 |
11 | {{ site.time | date_to_rfc822 }}
12 | {{ site.time | date_to_rfc822 }}
13 | Jekyll v{{ jekyll.version }}
14 | {% for post in site.posts limit:1000 %}
15 | {% if post.sitemap != false %}
16 | -
17 |
{{ post.title | xml_escape }}
18 | {{ post.content | xml_escape }}
19 | {{ post.date | date_to_rfc822 }}
20 | {{ post.url | prepend: site.baseurl | prepend: site.url }}
21 | {{ post.url | prepend: site.baseurl | prepend: site.url }}
22 | {% for tag in post.tags %}
23 | {{ tag | xml_escape }}
24 | {% endfor %}
25 | {% for cat in post.categories %}
26 | {{ cat | xml_escape }}
27 | {% endfor %}
28 |
29 | {% endif %}
30 | {% endfor %}
31 |
32 |
33 |
--------------------------------------------------------------------------------