diff --git a/pages/Build-Site.ps1 b/pages/Build-Site.ps1 index f44fd37069..612f3d98b9 100644 --- a/pages/Build-Site.ps1 +++ b/pages/Build-Site.ps1 @@ -1,196 +1,261 @@ -# copy documentation to output folder -#New-Item -Path "./dev/pages/cmdlets/released" -ItemType Directory -#New-Item -Path "./dev/pages/cmdlets/nightly" -ItemType Directory +param( + [string]$SourceRoot = (Split-Path -Parent $PSScriptRoot), + [string]$PublishPath, + [switch]$SkipPublish +) -$nightlycmdlets = Get-ChildItem "./dev/documentation/*.md" | ForEach-Object { $_ | Select-Object -ExpandProperty BaseName } +$SourceRoot = (Resolve-Path $SourceRoot).Path +$PagesPath = Join-Path $SourceRoot "pages" +$DocumentationPath = Join-Path $SourceRoot "documentation" +$CmdletsPath = Join-Path $PagesPath "cmdlets" +$DocFxConfigPath = Join-Path $PagesPath "docfx.json" +$SitePath = Join-Path $PagesPath "_site" +$CmdletIndexPath = Join-Path $CmdletsPath "index.md" +$AliasTemplatePath = Join-Path $CmdletsPath "alias.template" -class FrontMatters { - [hashtable] GetHeader($path) { - - $c = get-content $path - $header = @{} - if ($c[0].equals("---")) { - for ($q = 1; $q -lt $c.Length; $q++) { - if ($c[$q] -eq "---") { - # front-matter ended - $q = $c.Length; - } - else { - $colonIndex = $c[$q].IndexOf(":"); - $key = $c[$q].Substring(0, $colonIndex).Trim() - $value = $c[$q].Substring($colonIndex + 1).Trim() - $header[$key] = $value; - } - } - } - return $header - } - - [string] WriteHeader($path, $header) { - - $c = get-content $path - - - if ($c[0].equals("---")) { - $newFile = [System.Collections.ArrayList]@() - - $frontMatterEnded = $false - for ($q = 1; $q -lt $c.Length; $q++) { - if ($c[$q] -eq "---") { - $frontMatterEnded = $true - $q++; - } - if ($frontMatterEnded -ne $false) { - $newFile.Add($c[$q]) - } - } - $contents = "" - foreach ($line in $newFile) { - $contents += "$line`n" - } - - $newHeader = "---`n"; - $header.Keys.ForEach({ $newHeader += "$($_): $($header.Item($_))`n" }); - - $newHeader += "---`n" - Set-Content -Path $path -Value "$newHeader $contents" -Force - } - return $null - } +if (!(Test-Path $DocumentationPath)) { + throw "Unable to find documentation folder at $DocumentationPath" +} + +if (!(Test-Path $DocFxConfigPath)) { + throw "Unable to find DocFX configuration at $DocFxConfigPath" +} + +if (!(Test-Path $CmdletIndexPath)) { + throw "Unable to find cmdlet index template at $CmdletIndexPath" +} + +if (!(Test-Path $AliasTemplatePath)) { + throw "Unable to find alias template at $AliasTemplatePath" } -$fm = New-Object -TypeName FrontMatters - -$aliasCmdletsCount = 0 -$aliasCmdlets = @() -Try { - Write-Host "Generating documentation files for alias cmdlets" -ForegroundColor Yellow - # Load the Module in a new PowerShell session - $scriptBlockNightlyRelease = { - Write-Host "Installing latest nightly release of PnP PowerShell" - Install-Module PnP.PowerShell -AllowPrerelease -Force - - Write-Host "Retrieving PnP PowerShell alias cmdlets" - $cmdlets = Get-Command -Module PnP.PowerShell | Where-Object CommandType -eq "Alias" | Select-Object -Property @{N="Alias";E={$_.Name}}, @{N="ReferencedCommand";E={$_.ReferencedCommand.Name}} - $cmdlets - Write-Host "$($cmdlets.Length) alias cmdlets retrieved" +function Clear-GeneratedCmdletPages { + if (Test-Path $CmdletsPath) { + Get-ChildItem -Path $CmdletsPath -File | Where-Object { $_.Name -notin @("index.md", "alias.template") } | Remove-Item -Force } - $aliasCmdlets = Start-ThreadJob -ScriptBlock $scriptBlockNightlyRelease | Receive-Job -Wait +} + +New-Item -Path $CmdletsPath -ItemType Directory -Force | Out-Null +$cmdletIndexTemplateBytes = [System.IO.File]::ReadAllBytes($CmdletIndexPath) - $aliasCmdletsCount = $aliasCmdlets.Length +class FrontMatters { + [hashtable] GetHeader($path) { - $scriptBlockStableRelease = { - Write-Host "Retrieving PnP PowerShell cmdlets from latest stable release" - $cmdlets = (Find-Module -Name PnP.PowerShell).AdditionalMetadata.Cmdlets.Split(" ") - $cmdlets - Write-Host "$($cmdlets.Length) cmdlets retrieved" + $c = get-content $path + $header = @{} + if ($c[0].equals("---")) { + for ($q = 1; $q -lt $c.Length; $q++) { + if ($c[$q] -eq "---") { + # front-matter ended + $q = $c.Length; + } + else { + $colonIndex = $c[$q].IndexOf(":"); + $key = $c[$q].Substring(0, $colonIndex).Trim() + $value = $c[$q].Substring($colonIndex + 1).Trim() + $header[$key] = $value; + } + } + } + return $header } - $stableReleaseCmdlets = Start-ThreadJob -ScriptBlock $scriptBlockStableRelease | Receive-Job -Wait - Write-Host "- Retrieving alias template page" - $aliasTemplatePageContent = Get-Content -Path "./dev/pages/cmdlets/alias.template" -Raw + [string] WriteHeader($path, $header) { - ForEach($aliasCmdlet in $aliasCmdlets) - { - $destinationFileName = "./dev/documentation/$($aliasCmdlet.Alias).md" + $c = get-content $path + + + if ($c[0].equals("---")) { + $newFile = [System.Collections.ArrayList]@() - Write-Host "- Creating page for $($aliasCmdlet.Alias) being an alias for $($aliasCmdlet.ReferencedCommand) as $destinationFileName" -ForegroundColor Yellow - $aliasTemplatePageContent.Replace("%%cmdletname%%", $aliasCmdlet.Alias).Replace("%%referencedcmdletname%%", $aliasCmdlet.ReferencedCommand) | Out-File $destinationFileName -Force + $frontMatterEnded = $false + for ($q = 1; $q -lt $c.Length; $q++) { + if ($c[$q] -eq "---") { + $frontMatterEnded = $true + $q++; + } + if ($frontMatterEnded -ne $false) { + $newFile.Add($c[$q]) + } + } + $contents = "" + foreach ($line in $newFile) { + $contents += "$line`n" + } + + $newHeader = "---`n"; + $header.Keys.ForEach({ $newHeader += "$($_): $($header.Item($_))`n" }); + + $newHeader += "---`n" + Set-Content -Path $path -Value "$newHeader $contents" -Force + } + return $null } } -Catch { - Write-Host "Error: Cannot generate alias documentation files" - Write-Host $_ -} -Write-Host "Copying documentation files to page cmdlets" +try { + Clear-GeneratedCmdletPages -Copy-Item -Path "./dev/documentation/*.md" -Destination "./dev/pages/cmdlets" -Force + $nightlycmdlets = Get-ChildItem (Join-Path $DocumentationPath "*.md") | ForEach-Object { $_ | Select-Object -ExpandProperty BaseName } + $fm = New-Object -TypeName FrontMatters -foreach ($nightlycmdlet in $nightlycmdlets) { - if (!($stableReleaseCmdlets -like $nightlycmdlet)) { - Copy-Item "./dev/documentation/$nightlycmdlet.md" -Destination "./dev/pages/cmdlets" -Force | Out-Null - # update the document to state it's only available in the nightly build - $header = $fm.GetHeader("./dev/pages/cmdlets/$nightlycmdlet.md") - $header["tags"] = "Available in the current Nightly Release only." - #Write-Host "Writing $nightlycmdlet.md" - $fm.WriteHeader("./dev/pages/cmdlets/$nightlycmdlet.md",$header) - } -} + $aliasCmdletsCount = 0 + $aliasCmdlets = @() + $stableReleaseCmdlets = @() + Try { + Write-Host "Generating documentation files for alias cmdlets" -ForegroundColor Yellow + # Load the Module in a new PowerShell session + $scriptBlockNightlyRelease = { + Write-Host "Installing latest nightly release of PnP PowerShell" + Install-Module PnP.PowerShell -AllowPrerelease -Force -# Generate cmdlet toc -Write-Host "Retrieving all cmdlet pages" + Write-Host "Retrieving PnP PowerShell alias cmdlets" + $cmdlets = Get-Command -Module PnP.PowerShell | Where-Object CommandType -eq "Alias" | Select-Object -Property @{N="Alias";E={$_.Name}}, @{N="ReferencedCommand";E={$_.ReferencedCommand.Name}} + $cmdlets + Write-Host "$($cmdlets.Length) alias cmdlets retrieved" + } + $aliasCmdlets = Start-ThreadJob -ScriptBlock $scriptBlockNightlyRelease | Receive-Job -Wait -$cmdletPages = Get-ChildItem -Path "./dev/pages/cmdlets/*.md" -Exclude "index.md","alias.template" -$toc = "" -foreach ($cmdletPage in $cmdletPages) { - $toc = $toc + "- name: $($cmdletPage.BaseName)`n href: $($cmdletPage.Name)`n" -} + $aliasCmdletsCount = $aliasCmdlets.Length -$toc | Out-File "./dev/pages/cmdlets/toc.yml" -Force - -# Generate cmdlet index page - -Write-Host "Creating cmdlets index page" - -$cmdletIndexPageContent = Get-Content -Path "./dev/pages/cmdlets/index.md" -Raw -$cmdletIndexPageContent = $cmdletIndexPageContent.Replace("%%cmdletcount%%", $cmdletPages.Length - $aliasCmdletsCount) - -$cmdletIndexPageList = "" -$previousCmdletVerb = "" -foreach ($cmdletPage in $cmdletPages) -{ - Write-Host "- $($cmdletPage.Name)" - - # Define the verb of the cmdlet - if($cmdletPage.BaseName.Contains("-")) - { - $cmdletVerb = $cmdletPage.BaseName.Remove($cmdletPage.BaseName.IndexOf("-")) - - if($cmdletVerb -ne $previousCmdletVerb) - { - # Add a new heading for the new verb - $cmdletIndexPageList += "## $($cmdletVerb)`n" - } - } - else - { - $cmdletVerb = "" - } - - # Add a new entry for the verb - $cmdletIndexPageList += "- [$($cmdletPage.BaseName)]($($cmdletPage.Name))" - - # Check if the cmdlet only exists in the nightly build - if (!($stableReleaseCmdlets -like $cmdletPage.BaseName)) - { - # Add a 1 to the cmdlet name if it's only available in the nightly build - $cmdletIndexPageList = $cmdletIndexPageList + " 1" - - Write-Host " - Nightly only" - } - - # Check if the cmdlet is an alias - if ($aliasCmdlets.Alias -contains $cmdletPage.BaseName) - { - # Add a 2 to the cmdlet name if it's an alias - $cmdletIndexPageList = $cmdletIndexPageList + " 2" - - Write-Host " - Alias" - } - - $cmdletIndexPageList = $cmdletIndexPageList + "`n" - - if($cmdletVerb -ne "") - { - # Track the last verb so we know if we need to add a new heading for the next cmdlet - $previousCmdletVerb = $cmdletVerb - } -} + $scriptBlockStableRelease = { + Write-Host "Retrieving PnP PowerShell cmdlets from latest stable release" + $cmdlets = (Find-Module -Name PnP.PowerShell).AdditionalMetadata.Cmdlets.Split(" ") + $cmdlets + Write-Host "$($cmdlets.Length) cmdlets retrieved" + } + $stableReleaseCmdlets = Start-ThreadJob -ScriptBlock $scriptBlockStableRelease | Receive-Job -Wait + } + Catch { + Write-Host "Error: Cannot generate alias documentation files" + Write-Host $_ + } + + Write-Host "Copying documentation files to page cmdlets" + + Copy-Item -Path (Join-Path $DocumentationPath "*.md") -Destination $CmdletsPath -Force + + if ($aliasCmdletsCount -gt 0) { + Write-Host "- Retrieving alias template page" + $aliasTemplatePageContent = Get-Content -Path $AliasTemplatePath -Raw + + ForEach($aliasCmdlet in $aliasCmdlets) + { + $destinationFileName = Join-Path $CmdletsPath "$($aliasCmdlet.Alias).md" + + Write-Host "- Creating page for $($aliasCmdlet.Alias) being an alias for $($aliasCmdlet.ReferencedCommand) as $destinationFileName" -ForegroundColor Yellow + $aliasTemplatePageContent.Replace("%%cmdletname%%", $aliasCmdlet.Alias).Replace("%%referencedcmdletname%%", $aliasCmdlet.ReferencedCommand) | Out-File $destinationFileName -Force + } + } + + foreach ($nightlycmdlet in $nightlycmdlets) { + if (!($stableReleaseCmdlets -like $nightlycmdlet)) { + Copy-Item (Join-Path $DocumentationPath "$nightlycmdlet.md") -Destination $CmdletsPath -Force | Out-Null + # update the document to state it's only available in the nightly build + $cmdletPagePath = Join-Path $CmdletsPath "$nightlycmdlet.md" + $header = $fm.GetHeader($cmdletPagePath) + $header["tags"] = "Available in the current Nightly Release only." + #Write-Host "Writing $nightlycmdlet.md" + $fm.WriteHeader($cmdletPagePath,$header) + } + } + + # Generate cmdlet toc + Write-Host "Retrieving all cmdlet pages" + + $cmdletPages = Get-ChildItem -Path (Join-Path $CmdletsPath "*.md") -Exclude "index.md","alias.template" + $toc = "" + foreach ($cmdletPage in $cmdletPages) { + $toc = $toc + "- name: $($cmdletPage.BaseName)`n href: $($cmdletPage.Name)`n" + } + + $toc | Out-File (Join-Path $CmdletsPath "toc.yml") -Force + + # Generate cmdlet index page + + Write-Host "Creating cmdlets index page" + + $cmdletIndexPageContent = Get-Content -Path $CmdletIndexPath -Raw + $cmdletIndexPageContent = $cmdletIndexPageContent.Replace("%%cmdletcount%%", $cmdletPages.Length - $aliasCmdletsCount) + + $cmdletIndexPageList = "" + $previousCmdletVerb = "" + foreach ($cmdletPage in $cmdletPages) + { + Write-Host "- $($cmdletPage.Name)" + + # Define the verb of the cmdlet + if($cmdletPage.BaseName.Contains("-")) + { + $cmdletVerb = $cmdletPage.BaseName.Remove($cmdletPage.BaseName.IndexOf("-")) -$cmdletIndexPageContent = $cmdletIndexPageContent.Replace("%%cmdletlisting%%", $cmdletIndexPageList) -$cmdletIndexPageContent | Out-File "./dev/pages/cmdlets/index.md" -Force + if($cmdletVerb -ne $previousCmdletVerb) + { + # Add a new heading for the new verb + $cmdletIndexPageList += "## $($cmdletVerb)`n" + } + } + else + { + $cmdletVerb = "" + } -docfx build ./dev/pages/docfx.json + # Add a new entry for the verb + $cmdletIndexPageList += "- [$($cmdletPage.BaseName)]($($cmdletPage.Name))" -Copy-Item -Path "./dev/pages/_site/*" -Destination "./gh-pages" -Force -Recurse + # Check if the cmdlet only exists in the nightly build + if (!($stableReleaseCmdlets -like $cmdletPage.BaseName)) + { + # Add a 1 to the cmdlet name if it's only available in the nightly build + $cmdletIndexPageList = $cmdletIndexPageList + " 1" + + Write-Host " - Nightly only" + } + + # Check if the cmdlet is an alias + if ($aliasCmdlets.Alias -contains $cmdletPage.BaseName) + { + # Add a 2 to the cmdlet name if it's an alias + $cmdletIndexPageList = $cmdletIndexPageList + " 2" + + Write-Host " - Alias" + } + + $cmdletIndexPageList = $cmdletIndexPageList + "`n" + + if($cmdletVerb -ne "") + { + # Track the last verb so we know if we need to add a new heading for the next cmdlet + $previousCmdletVerb = $cmdletVerb + } + } + + $cmdletIndexPageContent = $cmdletIndexPageContent.Replace("%%cmdletlisting%%", $cmdletIndexPageList) + $cmdletIndexPageContent | Out-File $CmdletIndexPath -Force + + & docfx build $DocFxConfigPath + if ($LASTEXITCODE -ne 0) { + throw "DocFX build failed with exit code $LASTEXITCODE" + } + + if (!$SkipPublish) { + if ([string]::IsNullOrWhiteSpace($PublishPath)) { + $publishCandidate = Join-Path (Split-Path -Parent $SourceRoot) "gh-pages" + if (Test-Path $publishCandidate) { + $PublishPath = $publishCandidate + } + } + + if (![string]::IsNullOrWhiteSpace($PublishPath)) { + Write-Host "Copying generated site to $PublishPath" + Copy-Item -Path (Join-Path $SitePath "*") -Destination $PublishPath -Force -Recurse + } + else { + Write-Host "No publish path found. Skipping copy to gh-pages." + } + } +} +finally { + [System.IO.File]::WriteAllBytes($CmdletIndexPath, $cmdletIndexTemplateBytes) + Clear-GeneratedCmdletPages +} \ No newline at end of file diff --git a/pages/articles/buildingdocumentation.md b/pages/articles/buildingdocumentation.md index 41fa94f279..4170e3072e 100644 --- a/pages/articles/buildingdocumentation.md +++ b/pages/articles/buildingdocumentation.md @@ -4,12 +4,11 @@ uid: buildingdocumentation # Building the documentation site locally -The documentation site is built with [DocFX](https://dotnet.github.io/docfx/). If you're changing articles, cmdlet documentation, the site template, or the documentation build scripts, it's worth building the site locally before you submit your pull request. +The documentation site is built with [DocFX](https://dotnet.github.io/docfx/). If you're changing articles, cmdlet documentation, the site template, or the documentation build scripts, build the site locally before you submit your pull request. -There are two useful ways to test the site locally: +Do not run `docfx build ./pages/docfx.json` directly when you want to check the site as users will see it. That only builds the files that already exist under `pages` and skips the generated cmdlet pages. The result is a partial site where the home page can load, but the cmdlets section and parts of the navigation will be missing. -- A quick DocFX build from your current checkout. Use this when you changed articles, images, templates, styles, or `pages/docfx.json`. -- A full build that follows the same folder layout as the GitHub Actions workflow. Use this when you want to test `Build-Site.ps1`, cmdlet documentation copying, generated alias pages, or the final output that gets copied to the `gh-pages` branch. +Use `pages/Build-Site.ps1` instead. It copies the cmdlet markdown files from `documentation` into `pages/cmdlets`, generates the cmdlets table of contents and index page, runs DocFX, and then cleans the generated source files again. The generated site remains available under `pages/_site` for local preview. The GitHub Actions workflows use DocFX 2.78.5 and the .NET SDK 10. The commands below use the same DocFX version. @@ -29,9 +28,9 @@ pwsh --version dotnet --version ``` -The full build also needs access to the PowerShell Gallery. During that build, `Build-Site.ps1` installs the latest prerelease version of PnP PowerShell in order to discover aliases and generate documentation pages for them. +The build also needs access to the PowerShell Gallery. During the build, `Build-Site.ps1` installs the latest prerelease version of PnP PowerShell in order to discover aliases and generate documentation pages for them. -## Quick build on Windows +## Build on Windows Open PowerShell 7 and navigate to your local clone of the repository. @@ -52,10 +51,16 @@ If you already have DocFX installed in that folder and want to refresh it to the dotnet tool update docfx --tool-path .\.tools --version 2.78.5 ``` +Add the local tools folder to the current PowerShell session path so the build script can call `docfx`: + +```powershell +$env:PATH = "$(Resolve-Path .\.tools);$env:PATH" +``` + Build the documentation site: ```powershell -.\.tools\docfx.exe build .\pages\docfx.json +.\pages\Build-Site.ps1 -SkipPublish ``` Serve the generated site locally: @@ -72,7 +77,7 @@ When you're done, remove the generated output: Remove-Item .\pages\_site, .\pages\obj -Recurse -Force -ErrorAction SilentlyContinue ``` -## Quick build on macOS or Linux +## Build on macOS or Linux Open a terminal and navigate to your local clone of the repository. @@ -93,10 +98,16 @@ If you already have DocFX installed in that folder and want to refresh it to the dotnet tool update docfx --tool-path ./.tools --version 2.78.5 ``` +Add the local tools folder to the current shell path so the build script can call `docfx`: + +```bash +export PATH="$(pwd)/.tools:$PATH" +``` + Build the documentation site: ```bash -./.tools/docfx build ./pages/docfx.json +pwsh ./pages/Build-Site.ps1 -SkipPublish ``` Serve the generated site locally: @@ -113,11 +124,11 @@ When you're done, remove the generated output: rm -rf ./pages/_site ./pages/obj ``` -## Full build on Windows +## Testing the workflow layout -The documentation site workflow checks out three branches next to each other: `master`, `dev`, and `gh-pages`. The `Build-Site.ps1` script expects that layout and uses paths such as `./dev/pages` and `./gh-pages`. +The normal local build above is enough for most documentation changes. If you want to test the same folder layout used by the GitHub Actions workflow, create worktrees for `master`, `dev`, and `gh-pages` next to each other. -From your regular repository checkout, create a temporary worktree layout: +### Windows ```powershell $repo = "C:\repos\powershell" @@ -131,7 +142,7 @@ git -C $repo worktree add "$root\master" origin/master git -C $repo worktree add "$root\gh-pages" origin/gh-pages ``` -Install DocFX into the temporary build folder and add it to the current session path so `Build-Site.ps1` can call `docfx`: +Install DocFX in the temporary build folder and run the workflow build: ```powershell Push-Location $root @@ -139,11 +150,7 @@ Push-Location $root New-Item -ItemType Directory -Force .\.tools | Out-Null dotnet tool install docfx --tool-path .\.tools --version 2.78.5 $env:PATH = "$(Resolve-Path .\.tools);$env:PATH" -``` - -Run the same build script used by the workflow: -```powershell .\dev\pages\Build-Site.ps1 ``` @@ -166,9 +173,7 @@ git -C $repo worktree remove "$root\gh-pages" --force Remove-Item $root -Recurse -Force -ErrorAction SilentlyContinue ``` -## Full build on macOS or Linux - -The same workflow layout can be created with Git worktrees on macOS or Linux. +### macOS or Linux ```bash repo=~/repos/powershell @@ -182,7 +187,7 @@ git -C "$repo" worktree add "$root/master" origin/master git -C "$repo" worktree add "$root/gh-pages" origin/gh-pages ``` -Install DocFX into the temporary build folder and add it to the current session path: +Install DocFX in the temporary build folder and run the workflow build: ```bash cd "$root" @@ -190,17 +195,12 @@ cd "$root" mkdir -p .tools dotnet tool install docfx --tool-path ./.tools --version 2.78.5 export PATH="$(pwd)/.tools:$PATH" -``` - -Run the same build script used by the workflow: -```bash pwsh ./dev/pages/Build-Site.ps1 ``` Serve the generated site: - ```bash ./.tools/docfx serve ./dev/pages/_site --port 8080 ``` @@ -221,14 +221,17 @@ rm -rf "$root" At minimum, verify the following: 1. The home page loads and is not blank. -1. The navigation on the left opens the article or cmdlet page you changed. +1. The top navigation contains Articles and Cmdlets. +1. The Articles link opens the articles section and shows the article table of contents. +1. The Cmdlets link opens the cmdlets index and shows cmdlet pages in the table of contents. 1. Search opens without JavaScript errors. -1. The browser developer tools do not show 404 responses for `docfx.vendor.min.css` or `docfx.vendor.min.js`. +1. The theme selector in the top navigation can switch between Light, Dark and Auto. +1. The browser developer tools do not show 404 responses for `public/docfx.min.css`, `public/docfx.min.js`, `public/main.css` or `public/main.js`. -DocFX 2.77 and newer emit the vendor assets as minified files. If the browser shows 404 responses for `docfx.vendor.css` or `docfx.vendor.js`, the template is still using the old asset names and the site can appear as a blank page. +DocFX 2.77 and newer emit the vendor assets as minified files. With the modern template, the site should load its built-in assets from the `public` folder and the PnP branding overrides from `public/main.css` and `public/main.js`. If these files return 404 responses, the template stack is not being applied correctly and the site can appear broken or unstyled. ## Build warnings DocFX can finish successfully while still reporting warnings. Treat warnings in files you touched as something to fix before submitting your pull request. Existing warnings elsewhere in the site should not block you from validating your local change, but do not introduce new ones. -Common warnings are broken file links, broken bookmarks, or links to generated cmdlet pages that are not present in a quick build. If you need to verify generated cmdlet pages, use the full build. \ No newline at end of file +Common warnings are broken file links or broken bookmarks. If you see a warning for a file you changed, fix it before you submit the pull request. \ No newline at end of file diff --git a/pages/docfx.json b/pages/docfx.json index 7be8fad186..5cdba57022 100644 --- a/pages/docfx.json +++ b/pages/docfx.json @@ -30,7 +30,8 @@ "fileMetadataFiles": [], "template": [ "default", - "templates/material" + "modern", + "templates/pnp-modern" ], "postProcessors": [], "markdownEngineName": "markdig", @@ -40,6 +41,7 @@ "disableGitFeatures": false, "globalMetadata": { "_appTitle": "PnP PowerShell", + "_appName": "PnP PowerShell", "_gitContribute": { "repo": "https://github.com/pnp/powershell", "branch": "dev" diff --git a/pages/templates/material/styles/docfx.js b/pages/templates/material/styles/docfx.js index e1ec11fb55..14612ce8c4 100644 --- a/pages/templates/material/styles/docfx.js +++ b/pages/templates/material/styles/docfx.js @@ -232,7 +232,7 @@ $(function () { // Highlight the searching keywords function highlightKeywords() { var q = url('?q'); - if (q !== null) { + if (q) { var keywords = q.split("%20"); keywords.forEach(function (keyword) { if (keyword !== "") { diff --git a/pages/templates/pnp-modern/public/main.css b/pages/templates/pnp-modern/public/main.css new file mode 100644 index 0000000000..a86766ea3c --- /dev/null +++ b/pages/templates/pnp-modern/public/main.css @@ -0,0 +1,174 @@ +:root, +[data-bs-theme="light"] { + --pnp-header-bg: #2E7FBE; + --pnp-header-bg-rgb: 46, 127, 190; + --pnp-header-fg: #F5F5F6; + --pnp-highlight: #3FACFF; + --pnp-highlight-rgb: 63, 172, 255; + --pnp-highlight-dark: #1A4668; + --pnp-footer-bg: #263238; + --pnp-footer-fg: #F5F5F6; + --bs-primary: var(--pnp-header-bg); + --bs-primary-rgb: var(--pnp-header-bg-rgb); + --bs-primary-text-emphasis: var(--pnp-highlight-dark); + --bs-primary-bg-subtle: #DDF1FF; + --bs-primary-border-subtle: #9BD6FF; + --bs-link-color: var(--pnp-header-bg); + --bs-link-color-rgb: var(--pnp-header-bg-rgb); + --bs-link-hover-color: var(--pnp-highlight-dark); + --bs-link-hover-color-rgb: 26, 70, 104; + --bs-focus-ring-color: rgba(var(--pnp-highlight-rgb), .28); +} + +[data-bs-theme="dark"] { + --pnp-header-bg: #1A4668; + --pnp-header-bg-rgb: 26, 70, 104; + --pnp-header-fg: #F5F5F6; + --pnp-highlight: #3FACFF; + --pnp-highlight-rgb: 63, 172, 255; + --pnp-highlight-dark: #9BD6FF; + --pnp-footer-bg: #18242A; + --pnp-footer-fg: #F5F5F6; + --bs-primary: var(--pnp-highlight); + --bs-primary-rgb: var(--pnp-highlight-rgb); + --bs-primary-text-emphasis: #9BD6FF; + --bs-primary-bg-subtle: rgba(var(--pnp-highlight-rgb), .16); + --bs-primary-border-subtle: rgba(var(--pnp-highlight-rgb), .42); + --bs-link-color: #7FC8FF; + --bs-link-color-rgb: 127, 200, 255; + --bs-link-hover-color: #B9E0FF; + --bs-link-hover-color-rgb: 185, 224, 255; + --bs-focus-ring-color: rgba(var(--pnp-highlight-rgb), .34); +} + +::selection { + background: rgba(var(--pnp-highlight-rgb), .28); +} + +a { + text-underline-offset: .16em; +} + +a:hover, +a:focus { + text-decoration-thickness: .08em; +} + +body > header { + background: var(--pnp-header-bg) !important; + border-bottom-color: rgba(255, 255, 255, .16) !important; + box-shadow: 0 1px 2px rgba(0, 0, 0, .12); +} + +.navbar .navbar-brand, +.navbar .navbar-brand:hover, +.navbar .navbar-brand:focus, +.navbar .nav-link, +.navbar .btn, +.navbar .btn:hover, +.navbar .btn:focus { + color: var(--pnp-header-fg); +} + +.navbar .navbar-brand { + align-items: center; + display: inline-flex; + gap: .75rem; + font-weight: 600; +} + +.navbar .nav-link { + border-bottom: 3px solid transparent; + font-weight: 500; +} + +.navbar .nav-link:hover, +.navbar .nav-link:focus, +.navbar .nav-link.active { + border-bottom-color: var(--pnp-header-fg); + color: var(--pnp-header-fg); +} + +.navbar .btn:hover, +.navbar .btn:focus, +.navbar .dropdown-toggle.show { + background: rgba(255, 255, 255, .14); +} + +.navbar #search-query { + background: rgba(255, 255, 255, .16); + border-color: rgba(255, 255, 255, .22); + color: var(--pnp-header-fg); +} + +.navbar #search-query::placeholder, +.navbar #search i { + color: rgba(245, 245, 246, .82); +} + +.navbar #search-query:focus { + background: var(--bs-body-bg); + border-color: var(--pnp-highlight); + color: var(--bs-body-color); +} + +.toc a, +.affix a { + text-decoration: none; +} + +.toc a:hover, +.toc a:focus, +.affix a:hover, +.affix a:focus { + color: var(--bs-link-hover-color); + text-decoration: underline; +} + +.toc li.active > a, +.affix li.active > a { + color: var(--bs-link-color); + font-weight: 600; +} + +article h1, +article h2, +article h3, +article h4 { + font-weight: 600; +} + +article h2, +article h3, +article h4 { + scroll-margin-top: 76px; +} + +article h4 { + border-bottom: 1px solid var(--bs-border-color); + padding-bottom: .35rem; +} + +.alert-info, +.alert-primary { + --bs-alert-color: var(--bs-primary-text-emphasis); + --bs-alert-bg: var(--bs-primary-bg-subtle); + --bs-alert-border-color: var(--bs-primary-border-subtle); + --bs-alert-link-color: var(--bs-primary-text-emphasis); +} + +body > footer { + background: var(--pnp-footer-bg); + border-top-color: transparent !important; + color: var(--pnp-footer-fg) !important; +} + +body > footer a, +body > footer span { + color: var(--pnp-footer-fg); +} + +body > footer a:hover, +body > footer a:focus { + color: var(--pnp-highlight); +} \ No newline at end of file diff --git a/pages/templates/pnp-modern/public/main.js b/pages/templates/pnp-modern/public/main.js new file mode 100644 index 0000000000..2e23bc5b75 --- /dev/null +++ b/pages/templates/pnp-modern/public/main.js @@ -0,0 +1,3 @@ +export default { + defaultTheme: "auto" +} \ No newline at end of file