Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 5 additions & 25 deletions .github/workflows/build-index.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
name: Build script catalog

# Keeps index.json - the catalog PaletteShell's in-palette "Browse community scripts" page
# fetches - in sync with whatever is actually under scripts/. Contributors never touch
# index.json themselves; it's generated by tools/Build-Index.ps1 and committed back by this
# workflow after a merge to main. Pull requests only get a dry-run build, so a script that
# fails to parse is caught before merge instead of silently breaking the catalog.
# Manually regenerates index.json - the catalog PaletteShell's in-palette "Browse community
# scripts" page fetches - from whatever is actually under scripts/. Contributors never touch
# index.json themselves; run this workflow when the committed catalog should be refreshed.

on:
push:
branches: [main]
paths:
- "scripts/**"
- "tools/Build-Index.ps1"
- ".github/workflows/build-index.yml"
pull_request:
paths:
- "scripts/**"
- "tools/Build-Index.ps1"
workflow_dispatch: {}

permissions:
contents: write

# index.json itself is excluded from the path filters above, so the bot's own commit doesn't
# retrigger this workflow - no [skip ci] dance needed. This concurrency group just protects
# against overlapping runs from rapid consecutive pushes.
concurrency:
group: build-index-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -41,11 +26,8 @@ jobs:
with:
fetch-depth: 0

# Manual runs are restricted to codeowners (.github/CODEOWNERS) - push-to-main and PR
# validation builds are untouched by this and need no such check (push already requires
# write access; PR builds are a read-only parse check open to any contributor).
# Manual runs are restricted to codeowners (.github/CODEOWNERS).
- name: Restrict manual runs to codeowners
if: github.event_name == 'workflow_dispatch'
env:
ACTOR: ${{ github.actor }}
run: |
Expand All @@ -68,10 +50,8 @@ jobs:
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

# Only push on a direct push to main with real write permissions - pull_request runs
# (including ones from forks, which get a read-only token) are build-only validation.
- name: Commit index.json
if: github.event_name != 'pull_request' && steps.diff.outputs.changed == 'true'
if: steps.diff.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
Expand Down
15 changes: 1 addition & 14 deletions .github/workflows/validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,4 @@ jobs:
- name: Validate contribution checklist
shell: pwsh
run: ./tools/Test-Contribution.ps1

- name: Build catalog
shell: pwsh
run: ./tools/Build-Index.ps1 -OutputPath ([System.IO.Path]::GetTempFileName())

- name: Check generated catalog was not edited
if: github.event_name == 'pull_request'
shell: pwsh
run: |
$changedIndex = git diff --name-only '${{ github.event.pull_request.base.sha }}' '${{ github.event.pull_request.head.sha }}' -- index.json
if ($changedIndex) {
Write-Error 'index.json is generated after merge. Remove it from this PR and let the build-index workflow update it on main.'
exit 1
}

4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Set-ClipboardText $guid
| `[ScriptVersion('1.0.0')]` | Script version (SemVer recommended) — lets tools detect when a newer copy is available |
| `[ScriptIcon('🆔')]` | Emoji or glyph icon |
| `[ScriptTimeout(5000)]` | Timeout in milliseconds |
| `[ScriptOutput('None'\|'Toast'\|'Clipboard'\|'Markdown'\|'File'\|'List')]` | How stdout is handled |
| `[ScriptOutput('None'\|'Toast'\|'Clipboard'\|'Markdown'\|'Result'\|'File'\|'File:csv'\|'List')]` | How stdout is handled. `File:<extension>` opens captured stdout as that file type. |
| `[ScriptCwd('path')]` | Working directory |
| `[ScriptEnv('KEY','value')]` | Set an environment variable |
| `[RequiresElevation()]` | Run as administrator (or use `#Requires -RunAsAdministrator`) |
Expand All @@ -68,4 +68,4 @@ Set-ClipboardText $guid
- [ ] Everything is under `scripts/<your-handle>/`.
- [ ] Each script has a `.SYNOPSIS` and `.DESCRIPTION`.
- [ ] Destructive or elevated scripts declare `[ConfirmBeforeRun(...)]` / `[RequiresElevation()]`.
- [ ] Your folder's `README.md` lists your scripts.
- [ ] Your folder's `README.md` lists your scripts.
32 changes: 32 additions & 0 deletions scripts/paletteshell/Base64-Decode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Base64 Decode Clipboard
.DESCRIPTION
Decode Base64 clipboard text and copy back
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔓')]
[ScriptTimeout(10000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

try {
$bytes = [System.Convert]::FromBase64String($text)
$decoded = [System.Text.Encoding]::UTF8.GetString($bytes)
Set-ClipboardText $decoded
Write-Host "Decoded Base64 to $($decoded.Length) characters and copied to clipboard"
}
catch {
Write-Host "Invalid Base64 string"
exit 1
}
26 changes: 26 additions & 0 deletions scripts/paletteshell/Base64-Encode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Base64 Encode Clipboard
.DESCRIPTION
Encode clipboard text to Base64 and copy back
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔐')]
[ScriptTimeout(10000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$base64 = [System.Convert]::ToBase64String($bytes)
Set-ClipboardText $base64
Write-Host "Encoded $($text.Length) characters to Base64 and copied to clipboard"
44 changes: 44 additions & 0 deletions scripts/paletteshell/Clear-TempFiles.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Clean Temp Files
.DESCRIPTION
Delete files in your user TEMP folder older than the given number of days, then report
how many were removed and how much space was freed. Because it's destructive it asks for
confirmation first — and since it takes a parameter, the prompt appears after you submit
the form (demonstrates ConfirmBeforeRun on a parameterized script).
.PARAMETER OlderThanDays
Only delete files last modified more than this many days ago.
#>
[ScriptGroup('System')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🧹')]
[ScriptTimeout(60000)]
[ScriptOutput('Toast')]
[ConfirmBeforeRun('This permanently deletes old files from your TEMP folder.')]
param(
[ValidateRange(0, 3650)]
[int]$OlderThanDays = 7
)

$cutoff = (Get-Date).AddDays(-$OlderThanDays)
$files = Get-ChildItem -Path $env:TEMP -File -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $cutoff }

$count = 0
$bytes = 0L
foreach ($file in $files) {
try {
$size = $file.Length
Remove-Item -LiteralPath $file.FullName -Force -ErrorAction Stop
$count++
$bytes += $size
}
catch {
# File is in use or protected; skip it.
}
}

$mb = [math]::Round($bytes / 1MB, 1)
Write-Host "Deleted $count files · freed $mb MB"
28 changes: 28 additions & 0 deletions scripts/paletteshell/Clipboard-RemoveDuplicateLines.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Remove Duplicate Lines
.DESCRIPTION
Remove duplicate lines from clipboard text
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔍')]
[ScriptTimeout(10000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$lines = $text -split "(\r\n|\n)" | Where-Object { $_ -match '\S' }
$originalCount = $lines.Count
$unique = $lines | Select-Object -Unique
$result = $unique -join [Environment]::NewLine
Set-ClipboardText $result
Write-Host "Original: $originalCount lines → Unique: $($unique.Count) lines"
27 changes: 27 additions & 0 deletions scripts/paletteshell/Clipboard-SortLines.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Sort Lines in Clipboard
.DESCRIPTION
Sort clipboard lines alphabetically
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔤')]
[ScriptTimeout(10000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$lines = $text -split "(\r\n|\n)" | Where-Object { $_ -match '\S' }
$sorted = $lines | Sort-Object
$result = $sorted -join [Environment]::NewLine
Set-ClipboardText $result
Write-Host "Sorted $($lines.Count) lines alphabetically"
21 changes: 21 additions & 0 deletions scripts/paletteshell/Clipboard-ToCSV.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Clipboard → CSV (quoted)
.DESCRIPTION
Convert multiline clipboard text to comma-delimited, quote-wrapped CSV
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('📋')]
[ScriptTimeout(15000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
$lines = $text -split "\r\n|\n|\r" | Where-Object { $_ -ne "" }
$csv = ($lines | ForEach-Object { '"' + ($_ -replace '"','""') + '"' }) -join ','
Set-ClipboardText $csv
Write-Host "Converted $($lines.Count) lines to CSV and copied to clipboard"
25 changes: 25 additions & 0 deletions scripts/paletteshell/Clipboard-ToLowerCase.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Convert to lowercase
.DESCRIPTION
Convert clipboard text to lowercase
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔡')]
[ScriptTimeout(5000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$lower = $text.ToLower()
Set-ClipboardText $lower
Write-Host "Converted to lowercase and copied to clipboard"
25 changes: 25 additions & 0 deletions scripts/paletteshell/Clipboard-ToUpperCase.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Convert to UPPERCASE
.DESCRIPTION
Convert clipboard text to uppercase
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('🔠')]
[ScriptTimeout(5000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$upper = $text.ToUpper()
Set-ClipboardText $upper
Write-Host "Converted to uppercase and copied to clipboard"
27 changes: 27 additions & 0 deletions scripts/paletteshell/Clipboard-TrimLines.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Trim Lines in Clipboard
.DESCRIPTION
Remove leading and trailing whitespace from each line
#>
[ScriptGroup('Clipboard')]
[ScriptVersion('1.0.0')]
[ScriptIcon('✂️')]
[ScriptTimeout(10000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$text = Get-ClipboardText
if ([string]::IsNullOrEmpty($text)) {
Write-Host "Clipboard is empty"
exit 1
}

$lines = $text -split "(\r\n|\n)"
$trimmed = $lines | ForEach-Object { $_.Trim() }
$result = $trimmed -join [Environment]::NewLine
Set-ClipboardText $result
Write-Host "Trimmed whitespace from $($lines.Count) lines"
19 changes: 19 additions & 0 deletions scripts/paletteshell/Clipboard-UnixTimestamp.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using module .\PaletteScriptAttributes.psm1

<#
.SYNOPSIS
Get Unix Timestamp
.DESCRIPTION
Generate current Unix timestamp and copy to clipboard
#>
[ScriptGroup('Utilities')]
[ScriptVersion('1.0.0')]
[ScriptIcon('⏰')]
[ScriptTimeout(5000)]
[ScriptOutput('None')]
[CmdletBinding()]
param()

$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
Set-ClipboardText $timestamp.ToString()
Write-Host "Unix Timestamp: $timestamp copied to clipboard"
Loading
Loading