Skip to content

fix: preserve CJK layout names in defineSlideMaster() titles - #1

Open
wwsinagogogo wants to merge 3 commits into
anotb:mainfrom
wwsinagogogo:fix/cjk-layout-names
Open

fix: preserve CJK layout names in defineSlideMaster() titles#1
wwsinagogogo wants to merge 3 commits into
anotb:mainfrom
wwsinagogogo:fix/cjk-layout-names

Conversation

@wwsinagogogo

Copy link
Copy Markdown

Problem

When a .pptx template's slide layouts use Chinese / Japanese / Korean names (e.g. 标题页, 目次, 표지), pptx-masters crashes with:

Error: defineSlideMaster() object argument requires a `title` value.
(https://gitbrent.github.io/PptxGenJS/docs/masters.html)

Root cause

toUpperSnakeCase() stripped every non-ASCII character via replace(/[^a-zA-Z0-9\s]/g, ''). CJK characters are letters, not punctuation, but the regex didn't know that — so 标题页 collapsed to "" and was forwarded straight to defineSlideMaster({ title: '' }).

A second issue: the function never produced unique titles, so two layouts whose names normalized to the same value silently overwrote each other.

Fix

  • toUpperSnakeCase() uses Unicode property escapes (\p{L}\p{N}\p{M}) so all-language letters / digits / combining marks are preserved. Fullwidth space \u3000 is treated as a separator via the /u flag.
  • extract() runs a final dedup pass: empty titles fall back to LAYOUT_<n>, collisions get _2, _3, … suffixes — every layout reaching downstream code has a unique non-empty title.
  • resolveTitle() helper is exported from src/generator/code.js. All four downstream generators (generateMastersCode, generateAgentInstructions, generatePreview, generateBrandSkill) route through it, so library callers passing hand-built masterData (e.g. tests) get a self-contained master.title → toUpperSnakeCase(name) → LAYOUT_FALLBACK chain. LAYOUT_FALLBACK is the last-resort sentinel guaranteeing PptxGenJS never sees an empty title.

Verification

  • 509/509 unit tests pass, including 9 new CJK / Unicode / fullwidth-space / non-string-input cases
  • End-to-end CLI: a PPTX whose layout is named 标题页 now extracts to title: '标题页' in masters.js and runs through PptxGenJS without error
  • Edge case: a PPTX with the <p:cSld name="..."> attribute stripped off a layout falls back to LAYOUT_2 instead of crashing

Out of scope

src/cli.js has an unrelated createRequire fallback so bun build --compile produces a working standalone binary. That change is local and stays out of this PR.

Commits

  1. fix: preserve CJK characters in toUpperSnakeCase and add resolveTitle helper — core fix + unit tests
  2. fix: dedup master titles in extract with empty-title fallback — pipeline dedup pass
  3. refactor: route downstream generators through resolveTitle helper — wire preview.js / skill.js through the helper

🤖 Generated with Sisyphus

Co-authored-by: Sisyphus clio-agent@sisyphuslabs.ai

Titan and others added 3 commits June 30, 2026 08:42
… helper

PptxGenJS rejects empty titles with "requires a `title` value", but the old toUpperSnakeCase stripped every non-ASCII character via /[^a-zA-Z0-9\s]/g, so a pure-Chinese layout name like "标题页" collapsed to "" and crashed defineSlideMaster(). Switch the character class to Unicode property escapes (\p{L}\p{N}\p{M}) and add a resolveTitle() helper so downstream generators (preview, skill, masters.js, agent docs) get a self-contained fallback to master.name when extract() is bypassed (e.g. unit-test fixtures). Final LAYOUT_FALLBACK sentinel guarantees PptxGenJS never sees an empty title.

Adds 9 unit tests covering pure-CJK, mixed ASCII+CJK, Japanese/Korean, fullwidth space (\u3000), symbol-only input, and non-string input (undefined/null/number).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Two distinct layouts can normalize to the same title (e.g. whitespace-padded CJK variants), and PptxGenJS rejects the empty title that toUpperSnakeCase previously produced for symbol-only or whitespace-only names. Add a final pass over masterData that assigns LAYOUT_<n> to empty titles and appends _2, _3, ... suffixes to collisions, so every layout reaching the downstream generators has a unique non-empty title.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
preview.js and skill.js each computed titles via toUpperSnakeCase(master.name) directly, which would re-introduce the empty-title crash whenever they ran without extract()'s dedup pass (e.g. library consumers calling generatePreview or generateBrandSkill with hand-built masterData). Switch both to the resolveTitle() helper exported by the previous commit so they share the same master.title → toUpperSnakeCase(name) → LAYOUT_FALLBACK chain.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant