| 1 | # update_spec.py |
| 2 | |
| 3 | > **Scope boundary**: this tool updates only deterministic global color and font |
| 4 | > substitutions, writes the authoritative `spec_lock.md` only after the SVG |
| 5 | > updates succeed, and relies on version control for rollback instead of |
| 6 | > creating parallel backups. |
| 7 | |
| 8 | Propagate a `spec_lock.md` value change to both the lock file and every `svg_output/*.svg`. The single edit surface for bulk style tweaks after generation. |
| 9 | |
| 10 | ## Usage |
| 11 | |
| 12 | ```bash |
| 13 | python3 skills/ppt-master/scripts/update_spec.py <project_path> <section>.<key>=<value> |
| 14 | ``` |
| 15 | |
| 16 | Bare `<key>=<value>` (no dot) is treated as `colors.<key>=<value>` for backward compat. |
| 17 | |
| 18 | One invocation = one change. The tool: |
| 19 | |
| 20 | 1. Reads the old value from `<project_path>/spec_lock.md` |
| 21 | 2. Plans and propagates the change into every `.svg` under `svg_output/` |
| 22 | 3. Writes the new value into `spec_lock.md`; a global font replacement updates |
| 23 | every existing `typography.*_family` row together |
| 24 | 4. Prints the list of files touched |
| 25 | |
| 26 | ## Examples |
| 27 | |
| 28 | ```bash |
| 29 | # swap the primary color deck-wide (bare key → colors.primary) |
| 30 | python3 skills/ppt-master/scripts/update_spec.py projects/acme_ppt169_20260301 primary=#0066AA |
| 31 | |
| 32 | # explicit section.key form |
| 33 | python3 skills/ppt-master/scripts/update_spec.py projects/acme_ppt169_20260301 colors.accent=#FF6B35 |
| 34 | |
| 35 | # change the deck-wide font family |
| 36 | python3 skills/ppt-master/scripts/update_spec.py projects/acme_ppt169_20260301 \ |
| 37 | 'typography.font_family=Arial, "Microsoft YaHei", sans-serif' |
| 38 | ``` |
| 39 | |
| 40 | ## v2 scope |
| 41 | |
| 42 | - **Supported**: |
| 43 | - `colors.*` — HEX value replacement across `svg_output/*.svg` (case-insensitive). |
| 44 | - `typography.font_family` — replaces the inner value of every |
| 45 | `font-family="..."` / `font-family='...'` attribute and sets all existing |
| 46 | `typography.*_family` lock rows to that universal family. |
| 47 | - **Not supported**: typography sizes, icons, images, canvas, forbidden — these involve attribute-scoped or semantic replacements whose risk/benefit does not warrant bulk propagation. Edit `spec_lock.md` and the affected SVGs by hand, or re-author the pages. |
| 48 | |
| 49 | ## When to use |
| 50 | |
| 51 | - "Change the primary color across the whole deck" → one `update_spec.py` call |
| 52 | - "Switch the deck-wide font family" → one `update_spec.py` call |
| 53 | - "Switch an individual page's accent" → just edit that page's SVG directly |
| 54 | - "Re-design the palette / type system" → update `spec_lock.md` manually, then the Executor can regenerate affected pages |
| 55 | |
| 56 | ## Safety |
| 57 | |
| 58 | - HEX values (e.g. `#005587`) are unique enough in SVG content that literal replacement is safe |
| 59 | - `font-family` substitution is scoped to the attribute; the outer quote character is preserved, and switched automatically if the new value contains the same quote |
| 60 | - a global font substitution rewrites all existing family-role lock rows in one |
| 61 | file write, so the universal SVG result cannot leave stale title/body roles |
| 62 | - The tool refuses non-HEX inputs, unknown keys, and unsupported sections |
| 63 | - No backups are created — the project folder should be under git so you can diff / revert |
| 64 | |
| 65 | ### Note on first `font-family` update |
| 66 | |
| 67 | The script writes the `spec_lock.md` value verbatim into every SVG's `font-family` attribute. If the Executor generated SVGs with quote-flattened font names (e.g. `font-family="Microsoft YaHei, Arial, sans-serif"`) while `spec_lock.md` holds the quoted form (`"Microsoft YaHei", Arial, sans-serif`), the **first** substitution will normalize every SVG to match the `spec_lock.md` literal (e.g. `font-family='"Microsoft YaHei", Arial, sans-serif'`). The two forms are semantically equivalent (CSS and DrawingML parse them identically), but the normalization produces byte-level diffs across every SVG that contains text. Subsequent updates only touch files where the value actually changes. |
| 68 |