Skip to content

Commit 97c40e8

Browse files
auyidi1auyidi
andauthored
feat(skills): add 6 PowerPoint skill enhancements (#1481)
# Pull Request ## Description Adds 6 enhancements to the PowerPoint skill addressing the most common pain points in multi-theme deck production: manual spacing validation, hardcoded theme colors in content-extra.py, per-project theme generation boilerplate, lack of pre-flight checks, no audio pipeline, and missing SVG export. ### New Scripts (4) | Script | Enhancement | Lines | |---|---|---| | `validate_geometry.py` | Structural validation for edge margins, adjacent gaps, boundary overflow (including left/top), and title clearance | ~530 | | `generate_themes.py` | YAML-driven theme variant generation with single-pass hex and RGBColor color remapping | ~270 | | `embed_audio.py` | WAV audio embedding into PPTX slides with per-slide file matching | ~220 | | `export_svg.py` | PPTX-to-SVG export via LibreOffice PDF conversion and PyMuPDF rendering | ~250 | ### Modified Files (4) | File | Change | |---|---| | `build_deck.py` | `--dry-run` pre-flight validation, `--verbose` flag, `configure_logging()`, per-slide theme color lookup via `themes[].slides` | | `Invoke-PptxPipeline.ps1` | Wired geometric validation as Step 2b in the existing Validate action | | `SKILL.md` | CLI docs for dry-run, theme generation, audio embedding, SVG export; updated script architecture table | | `pptx.instructions.md` | Validation pipeline intro references 3-step pipeline; theme colors in content-extra.py section | ## Related Issue(s) Closes #1482 ## Type of Change **Code & Documentation:** * [x] New feature (non-breaking change adding functionality) * [x] Documentation update **AI Artifacts:** * [x] Reviewed contribution with `prompt-builder` agent and addressed all feedback (N/A — scripts and wrappers are the primary deliverables; instructions and SKILL.md updates follow established patterns verified by automated validation) * [x] Copilot instructions (`.github/instructions/*.instructions.md`) * [x] Copilot skill (`.github/skills/*/SKILL.md`) **Other:** * [x] Script/automation (`.ps1`, `.sh`, `.py`) ## Testing - 4 new test files: `test_validate_geometry.py` (37 tests), `test_generate_themes.py` (20 tests), `test_embed_audio.py` (18 tests), `test_export_svg.py` (11 tests) - Full suite: **757 tests pass, 89.26% coverage** (above 85% threshold) - ruff check and ruff format clean on all files - All 4 new Python scripts pass AST parsing validation - Geometric validation integrates into existing Validate pipeline (exit code 1 for warnings, 2 for errors) - `--dry-run` validates YAML parsing, speaker notes, content-extra.py AST, and image refs without building - Single-pass color remapping in both `remap_hex_in_text` and `remap_rgb_in_python` prevents chain remapping - Per-slide theme color lookup uses `themes[].slides` list with `themes[0]` fallback; shallow-copies style dict ## Sample Prompts ### User Request > "Build my presentation from the content directory, but first validate the geometry and generate theme variants for the blue and green palettes." ### Execution Flow 1. Agent reads `SKILL.md` to discover available scripts and their CLI flags. 2. Runs `python scripts/build_deck.py --content-dir content/ --style content/global/style.yaml --dry-run` to pre-flight validate content YAML, speaker notes, and image references. 3. Runs `python scripts/generate_themes.py --content-dir content/ --themes themes.yaml --output-dir ../` to produce themed content directory copies with remapped colors. 4. Runs `python scripts/build_deck.py --content-dir content/ --style content/global/style.yaml --output presentation.pptx` to build the deck. 5. Runs `python scripts/validate_geometry.py --input presentation.pptx --report report.md` to check margins, gaps, and overflow. 6. Optionally runs `python scripts/embed_audio.py --input presentation.pptx --audio-dir voice-over/` to embed narration audio. 7. Optionally runs `python scripts/export_svg.py --input presentation.pptx --output-dir svg/` to export slides as SVG. ### Output Artifacts * `presentation.pptx` — Built deck with themed colors applied. * `content-blue/`, `content-green/` — Generated theme variant content directories. * `geometry-validation-report.md` — Markdown report of margin, gap, and overflow findings. * `geometry-validation-results.json` — Machine-readable validation results. * `voice-over/*.wav` → embedded into PPTX slides (when audio pipeline is used). * `svg/slide-001.svg`, `svg/slide-002.svg`, ... — Exported SVG images. ### Success Indicators * `--dry-run` exits with code 0 and reports no errors. * `validate_geometry.py` exits with code 0 (clean) or 1 (warnings only, no errors). * Theme variant directories contain remapped hex colors in YAML and `RGBColor()` calls in Python. * Embedded audio plays automatically in PowerPoint's video export workflow. * SVG files render correctly when opened in a browser. ## Checklist ### Required Checks * [x] Documentation is updated (if applicable) * [x] Files follow existing naming conventions * [x] Changes are backwards compatible (if applicable) * [x] Tests added for new functionality (if applicable) ### AI Artifact Contributions * [x] Used `/prompt-analyze` to review contribution (N/A — instructions and SKILL.md updates are additive documentation of new CLI scripts; patterns match existing validated sections) * [x] Addressed all feedback from `prompt-builder` review (N/A — see above) * [x] Verified contribution follows common standards and type-specific requirements ### Required Automated Checks * [x] Markdown linting: `npm run lint:md` * [x] Spell checking: `npm run spell-check` * [x] Frontmatter validation: `npm run lint:frontmatter` * [x] Skill structure validation: `npm run validate:skills` * [x] Link validation: `npm run lint:md-links` * [x] PowerShell analysis: `npm run lint:ps` * [x] Plugin freshness: `npm run plugin:generate` * [x] Docusaurus tests: `npm run docs:test` ## Security Considerations * [x] This PR does not contain any sensitive or NDA information * [x] Any new dependencies have been reviewed for security issues * [x] Security-related scripts follow the principle of least privilege ## Additional Notes Enhancement priority ranking derived from pain points during multi-theme deck production: | # | Enhancement | Priority | |---|---|---| | 4 | Structural/geometric validation | High | | 5 | Color palette in content-extra.py style dict | High | | 2 | Theme variant generation action | Medium | | 6 | `--dry-run` pre-flight check | Medium | | 3 | Audio embedding action | Medium | | 1 | SVG export pipeline | Low | --------- Co-authored-by: auyidi <auyidi@microsoft.com>
1 parent 84e47f0 commit 97c40e8

23 files changed

Lines changed: 3607 additions & 17 deletions

.github/instructions/experimental/pptx.instructions.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ For partial rebuild workflows (update a few slides in an existing deck):
7979

8080
## Validation Criteria
8181

82-
These criteria define the quality standards agents verify after building or updating slides. Visual checks use `validate_slides.py` and PPTX-only checks use `validate_deck.py`.
82+
These criteria define the quality standards agents verify after building or updating slides. The Validate pipeline runs three checks in sequence: PPTX property validation (`validate_deck.py`), geometric validation (`validate_geometry.py`), and optionally vision-based validation (`validate_slides.py`).
83+
84+
Geometric validation runs automatically during the Validate action and checks the element positioning rules below programmatically. It catches margin violations, boundary overflow, and insufficient gaps without requiring vision model access.
8385

8486
### Element Positioning
8587

@@ -116,6 +118,10 @@ These criteria define the quality standards agents verify after building or upda
116118

117119
Use `#RRGGBB` hex values or `@theme_name` references for all colors. See the Color Syntax section in `content-yaml-template.md` for the full specification including theme brightness adjustments and dict syntax.
118120

121+
### Theme Colors in content-extra.py
122+
123+
When `style.yaml` defines a `themes` section, the build script populates `style[\"colors\"]` with the color map for the theme assigned to each slide via `themes[].slides`. Slides not explicitly assigned fall back to the first theme in the list. Use `style.get(\"colors\", {}).get(\"accent_blue\", \"#0078D4\")` in `content-extra.py` to reference theme-aware colors. This enables theme portability. The same script produces correct colors across all theme variants without regex replacement.
124+
119125
## Contextual Styling
120126

121127
Slide decks often contain multiple visual themes (title slides, content slides, section dividers, dark vs. light themes). Rather than enforcing a single global style, derive colors, fonts, and layout patterns from context:

.github/skills/experimental/powerpoint/SKILL.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,70 @@ python scripts/render_pdf_images.py \
404404

405405
**Dependencies**: Requires LibreOffice for PPTX-to-PDF conversion and either `pdftoppm` (from `poppler`) or `pymupdf` (pip) for PDF-to-JPG rendering.
406406

407+
### Dry-Run Validation
408+
409+
```bash
410+
python scripts/build_deck.py \
411+
--content-dir content/ \
412+
--style content/global/style.yaml \
413+
--dry-run
414+
```
415+
416+
Validates content files without producing a PPTX. Parses all `content.yaml` files, checks for speaker notes, runs AST validation on `content-extra.py` scripts, and counts image assets. Exit codes:
417+
418+
* code 0: no errors found
419+
* code 1: one or more slide-level content errors (YAML parse failures, invalid scripts)
420+
* code 2: configuration error (e.g., no slide content found in the content directory)
421+
422+
### Generate Theme Variants
423+
424+
```bash
425+
python scripts/generate_themes.py \
426+
--content-dir content/ \
427+
--themes themes.yaml \
428+
--output-dir ../
429+
```
430+
431+
Generates themed content directories from a base content directory using a color mapping YAML file. The themes YAML defines color replacement tables:
432+
433+
```yaml
434+
themes:
435+
fluent:
436+
label: "Microsoft Fluent"
437+
colors:
438+
"#1B1B1F": "#FFFFFF"
439+
"#F8F8FC": "#242424"
440+
```
441+
442+
Each theme gets its own output directory with remapped `content.yaml`, `style.yaml`, and `content-extra.py` files. Images are copied as-is. Run `build_deck.py` on each themed directory to produce the PPTX.
443+
444+
### Embed Audio
445+
446+
```bash
447+
python scripts/embed_audio.py \
448+
--input slide-deck/presentation.pptx \
449+
--audio-dir voice-over/ \
450+
--output slide-deck/presentation-narrated.pptx
451+
```
452+
453+
Embeds WAV audio files into PPTX slides. Audio files are matched to slides by naming convention (`slide-001.wav`, `slide-002.wav`, etc.). The audio icon is placed off-screen (below the slide boundary) to keep it hidden during presentation. Pass `--slides` to embed audio on specific slides only.
454+
455+
**Dependencies**: Requires `pillow` (`pip install pillow`) for poster frame generation.
456+
457+
> [!NOTE]
458+
> WAV files are embedded uncompressed. For large narrated decks, consider pre-compressing audio before embedding to manage PPTX file size.
459+
460+
### Export Slides to SVG
461+
462+
```bash
463+
python scripts/export_svg.py \
464+
--input slide-deck/presentation.pptx \
465+
--output-dir slide-deck/svg/ \
466+
--slides 3,5,10
467+
```
468+
469+
Exports slides to SVG format via LibreOffice (PPTX → PDF) and PyMuPDF (PDF → SVG). Output files are named `slide-NNN.svg`. Pass `--slides` to export specific slides. **Dependencies**: Requires LibreOffice and `pymupdf`.
470+
407471
## Script Architecture
408472

409473
The build and extraction scripts use shared modules in the `scripts/` directory:
@@ -419,8 +483,12 @@ The build and extraction scripts use shared modules in the `scripts/` directory:
419483
| `pptx_tables.py` | Table element creation and extraction with cell merging, banding, and per-cell styling |
420484
| `pptx_charts.py` | Chart element creation and extraction for 12 chart types (column, bar, line, pie, scatter, bubble, etc.) |
421485
| `validate_deck.py` | PPTX-only validation for speaker notes and slide count |
486+
| `validate_geometry.py` | Structural validation for element edge margins, adjacent gaps, boundary overflow, and title clearance |
422487
| `validate_slides.py` | Vision-based slide issue detection and quality validation via Copilot SDK with built-in checks and plain-text per-slide output |
423488
| `render_pdf_images.py` | PDF-to-JPG rendering via PyMuPDF with optional slide-number-based naming |
489+
| `generate_themes.py` | Theme variant generation from a base content directory using a color mapping YAML file |
490+
| `embed_audio.py` | WAV audio embedding into PPTX slides with per-slide file matching and off-screen audio icon placement |
491+
| `export_svg.py` | PPTX-to-SVG export via LibreOffice PDF conversion and PyMuPDF SVG rendering |
424492

425493
## python-pptx Constraints
426494

.github/skills/experimental/powerpoint/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ requires-python = ">=3.11"
55
dependencies = [
66
"python-pptx",
77
"pyyaml",
8+
"ruamel.yaml", # required by generate_themes.py for round-trip YAML fidelity
89
"cairosvg",
910
"Pillow",
1011
"pymupdf",
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env pwsh
2+
# Copyright (c) Microsoft Corporation.
3+
# SPDX-License-Identifier: MIT
4+
#Requires -Version 7.0
5+
6+
<#
7+
.SYNOPSIS
8+
Embed WAV audio files into a PowerPoint deck.
9+
10+
.DESCRIPTION
11+
Wrapper script that manages the Python virtual environment and invokes
12+
embed_audio.py to embed per-slide WAV files into a PPTX presentation.
13+
14+
.PARAMETER InputPath
15+
Input PPTX file path.
16+
17+
.PARAMETER AudioDir
18+
Directory containing slide-NNN.wav files.
19+
20+
.PARAMETER OutputPath
21+
Output PPTX file path.
22+
23+
.PARAMETER Slides
24+
Comma-separated slide numbers to embed audio on (optional).
25+
26+
.PARAMETER SkipVenvSetup
27+
Skip virtual environment setup.
28+
29+
.EXAMPLE
30+
./Invoke-EmbedAudio.ps1 -InputPath deck.pptx -AudioDir voice-over/ -OutputPath out.pptx
31+
32+
.NOTES
33+
Part of the powerpoint skill. Manages uv virtual environment setup
34+
and delegates to embed_audio.py for WAV embedding into PPTX slides.
35+
#>
36+
37+
[CmdletBinding()]
38+
param(
39+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$InputPath,
40+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$AudioDir,
41+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$OutputPath,
42+
[Parameter(Mandatory = $false)][string]$Slides,
43+
[Parameter(Mandatory = $false)][switch]$SkipVenvSetup
44+
)
45+
46+
$ErrorActionPreference = 'Stop'
47+
48+
#region Environment Setup
49+
50+
$ScriptDir = $PSScriptRoot
51+
$SkillRoot = Split-Path -Parent $ScriptDir
52+
$VenvDir = Join-Path $SkillRoot '.venv'
53+
54+
#endregion Environment Setup
55+
56+
#region Main
57+
58+
if ($MyInvocation.InvocationName -ne '.') {
59+
60+
try {
61+
if (-not $SkipVenvSetup) {
62+
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
63+
throw 'uv is required but was not found on PATH.'
64+
}
65+
uv sync --directory $SkillRoot
66+
}
67+
68+
$python = if (Test-Path (Join-Path $VenvDir 'Scripts/python.exe')) {
69+
Join-Path $VenvDir 'Scripts/python.exe'
70+
} elseif (Test-Path (Join-Path $VenvDir 'bin/python')) {
71+
Join-Path $VenvDir 'bin/python'
72+
} else {
73+
throw "Python interpreter not found in venv. Run: uv sync --directory `"$SkillRoot`""
74+
}
75+
76+
$script = Join-Path $ScriptDir 'embed_audio.py'
77+
$ScriptArgs = @($script, '--input', $InputPath, '--audio-dir', $AudioDir, '--output', $OutputPath)
78+
if ($Slides) { $ScriptArgs += '--slides'; $ScriptArgs += $Slides }
79+
if ($VerbosePreference -eq 'Continue') { $ScriptArgs += '-v' }
80+
81+
& $python @ScriptArgs
82+
exit $LASTEXITCODE
83+
}
84+
catch {
85+
Write-Error -ErrorAction Continue "Invoke-EmbedAudio failed: $($_.Exception.Message)"
86+
exit 1
87+
}
88+
89+
}
90+
91+
#endregion Main
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env pwsh
2+
# Copyright (c) Microsoft Corporation.
3+
# SPDX-License-Identifier: MIT
4+
#Requires -Version 7.0
5+
6+
<#
7+
.SYNOPSIS
8+
Export PowerPoint slides to SVG images.
9+
10+
.DESCRIPTION
11+
Wrapper script that manages the Python virtual environment and invokes
12+
export_svg.py to convert PPTX slides to SVG via LibreOffice and PyMuPDF.
13+
14+
.PARAMETER InputPath
15+
Input PPTX file path.
16+
17+
.PARAMETER OutputDir
18+
Output directory for SVG files.
19+
20+
.PARAMETER Slides
21+
Comma-separated slide numbers to export (optional).
22+
23+
.PARAMETER SkipVenvSetup
24+
Skip virtual environment setup.
25+
26+
.EXAMPLE
27+
./Invoke-ExportSvg.ps1 -InputPath deck.pptx -OutputDir svg/
28+
29+
.NOTES
30+
Part of the powerpoint skill. Manages uv virtual environment setup
31+
and delegates to export_svg.py for PPTX-to-SVG conversion.
32+
#>
33+
34+
[CmdletBinding()]
35+
param(
36+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$InputPath,
37+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$OutputDir,
38+
[Parameter(Mandatory = $false)][string]$Slides,
39+
[Parameter(Mandatory = $false)][switch]$SkipVenvSetup
40+
)
41+
42+
$ErrorActionPreference = 'Stop'
43+
44+
#region Environment Setup
45+
46+
$ScriptDir = $PSScriptRoot
47+
$SkillRoot = Split-Path -Parent $ScriptDir
48+
$VenvDir = Join-Path $SkillRoot '.venv'
49+
50+
#endregion Environment Setup
51+
52+
#region Main
53+
54+
if ($MyInvocation.InvocationName -ne '.') {
55+
56+
try {
57+
if (-not $SkipVenvSetup) {
58+
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
59+
throw 'uv is required but was not found on PATH.'
60+
}
61+
uv sync --directory $SkillRoot
62+
}
63+
64+
$python = if (Test-Path (Join-Path $VenvDir 'Scripts/python.exe')) {
65+
Join-Path $VenvDir 'Scripts/python.exe'
66+
} elseif (Test-Path (Join-Path $VenvDir 'bin/python')) {
67+
Join-Path $VenvDir 'bin/python'
68+
} else {
69+
throw "Python interpreter not found in venv. Run: uv sync --directory `"$SkillRoot`""
70+
}
71+
72+
$script = Join-Path $ScriptDir 'export_svg.py'
73+
$ScriptArgs = @($script, '--input', $InputPath, '--output-dir', $OutputDir)
74+
if ($Slides) { $ScriptArgs += '--slides'; $ScriptArgs += $Slides }
75+
if ($VerbosePreference -eq 'Continue') { $ScriptArgs += '-v' }
76+
77+
& $python @ScriptArgs
78+
exit $LASTEXITCODE
79+
}
80+
catch {
81+
Write-Error -ErrorAction Continue "Invoke-ExportSvg failed: $($_.Exception.Message)"
82+
exit 1
83+
}
84+
85+
}
86+
87+
#endregion Main
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env pwsh
2+
# Copyright (c) Microsoft Corporation.
3+
# SPDX-License-Identifier: MIT
4+
#Requires -Version 7.0
5+
6+
<#
7+
.SYNOPSIS
8+
Generate themed content directory variants from a base deck.
9+
10+
.DESCRIPTION
11+
Wrapper script that manages the Python virtual environment and invokes
12+
generate_themes.py to produce themed content copies with remapped colors.
13+
14+
.PARAMETER ContentDir
15+
Path to the base theme's content directory.
16+
17+
.PARAMETER ThemesPath
18+
Path to a YAML file defining theme color mappings.
19+
20+
.PARAMETER OutputDir
21+
Parent directory where themed content directories are created.
22+
23+
.PARAMETER SkipVenvSetup
24+
Skip virtual environment setup.
25+
26+
.EXAMPLE
27+
./Invoke-GenerateThemes.ps1 -ContentDir content/ -ThemesPath themes.yaml -OutputDir ../
28+
29+
.NOTES
30+
Part of the powerpoint skill. Manages uv virtual environment setup
31+
and delegates to generate_themes.py for themed content generation.
32+
#>
33+
34+
[CmdletBinding()]
35+
param(
36+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ContentDir,
37+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ThemesPath,
38+
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$OutputDir,
39+
[Parameter(Mandatory = $false)][switch]$SkipVenvSetup
40+
)
41+
42+
$ErrorActionPreference = 'Stop'
43+
44+
#region Environment Setup
45+
46+
$ScriptDir = $PSScriptRoot
47+
$SkillRoot = Split-Path -Parent $ScriptDir
48+
$VenvDir = Join-Path $SkillRoot '.venv'
49+
50+
#endregion Environment Setup
51+
52+
#region Main
53+
54+
if ($MyInvocation.InvocationName -ne '.') {
55+
56+
try {
57+
if (-not $SkipVenvSetup) {
58+
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
59+
throw 'uv is required but was not found on PATH.'
60+
}
61+
uv sync --directory $SkillRoot
62+
}
63+
64+
$python = if (Test-Path (Join-Path $VenvDir 'Scripts/python.exe')) {
65+
Join-Path $VenvDir 'Scripts/python.exe'
66+
} elseif (Test-Path (Join-Path $VenvDir 'bin/python')) {
67+
Join-Path $VenvDir 'bin/python'
68+
} else {
69+
throw "Python interpreter not found in venv. Run: uv sync --directory `"$SkillRoot`""
70+
}
71+
72+
$script = Join-Path $ScriptDir 'generate_themes.py'
73+
$ScriptArgs = @($script, '--content-dir', $ContentDir, '--themes', $ThemesPath, '--output-dir', $OutputDir)
74+
if ($VerbosePreference -eq 'Continue') { $ScriptArgs += '-v' }
75+
76+
& $python @ScriptArgs
77+
exit $LASTEXITCODE
78+
}
79+
catch {
80+
Write-Error -ErrorAction Continue "Invoke-GenerateThemes failed: $($_.Exception.Message)"
81+
exit 1
82+
}
83+
84+
}
85+
86+
#endregion Main

0 commit comments

Comments
 (0)