@cola1900/dsh-ppt 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/UPSTREAM.md +17 -0
- package/cordis.patch.yml +8 -0
- package/lib/bin.js +4070 -0
- package/lib/catalog.js +3 -0
- package/lib/client.js +916 -0
- package/lib/index.js +3688 -0
- package/lib/invariant.js +13 -0
- package/lib/pptd-2VqVzr_T.js +2173 -0
- package/lib/pptd.js +2 -0
- package/lib/preview-assets.js +59 -0
- package/lib/preview-manifest.js +2 -0
- package/lib/text-wrap.js +20 -0
- package/lib/types/bin.d.ts +12 -0
- package/lib/types/client/OfficePptHero.d.ts +42 -0
- package/lib/types/client/curated-previews.d.ts +3 -0
- package/lib/types/client/curated-previews.generated.d.ts +2 -0
- package/lib/types/client/index.d.ts +6 -0
- package/lib/types/client/locales.d.ts +15 -0
- package/lib/types/client/rpc.d.ts +23 -0
- package/lib/types/client/standard.d.ts +31 -0
- package/lib/types/client.d.ts +1 -0
- package/lib/types/data-series.d.ts +31 -0
- package/lib/types/index.d.ts +25 -0
- package/lib/types/invariant.d.ts +8 -0
- package/lib/types/layouts.d.ts +54 -0
- package/lib/types/planning.d.ts +35 -0
- package/lib/types/ppt-ppt-skill.d.ts +6 -0
- package/lib/types/ppt-reference-semantics.generated.d.ts +2 -0
- package/lib/types/ppt-rpc.d.ts +5 -0
- package/lib/types/ppt-service.d.ts +34 -0
- package/lib/types/ppt-store.d.ts +47 -0
- package/lib/types/ppt-template-catalog.generated.d.ts +2 -0
- package/lib/types/ppt-template-definitions.d.ts +1 -0
- package/lib/types/ppt-tools.d.ts +10 -0
- package/lib/types/pptd-colors.d.ts +15 -0
- package/lib/types/pptd-convert.d.ts +21 -0
- package/lib/types/pptd-preview.d.ts +6 -0
- package/lib/types/pptd-publish.d.ts +8 -0
- package/lib/types/pptd-repair.d.ts +18 -0
- package/lib/types/pptd-tools.d.ts +5 -0
- package/lib/types/pptd.d.ts +89 -0
- package/lib/types/protocol.d.ts +547 -0
- package/lib/types/template-reference.d.ts +28 -0
- package/lib/types/template-semantics.d.ts +12 -0
- package/lib/types/templates.d.ts +2 -0
- package/lib/types/text-layout.d.ts +37 -0
- package/lib/validation.js +84 -0
- package/licenses/composer-NOTICES.md +29 -0
- package/licenses/dsh-desktop-LICENSE +21 -0
- package/licenses/html-anything/LICENSE +202 -0
- package/licenses/html-anything/deck-blueprint.md +22 -0
- package/licenses/html-anything/deck-course-module.md +23 -0
- package/licenses/zara/LICENSE +21 -0
- package/licenses/zara/templates/blue-professional/design.md +701 -0
- package/licenses/zara/templates/blue-professional/template.html +1423 -0
- package/licenses/zara/templates/blue-professional/template.json +43 -0
- package/licenses/zara/templates/broadside/design.md +493 -0
- package/licenses/zara/templates/broadside/template.html +2144 -0
- package/licenses/zara/templates/broadside/template.json +48 -0
- package/licenses/zara/templates/cartesian/design.md +513 -0
- package/licenses/zara/templates/cartesian/template.html +1136 -0
- package/licenses/zara/templates/cartesian/template.json +46 -0
- package/licenses/zara/templates/editorial-forest/design.md +497 -0
- package/licenses/zara/templates/editorial-forest/template.html +843 -0
- package/licenses/zara/templates/editorial-forest/template.json +44 -0
- package/licenses/zara/templates/monochrome/design.md +482 -0
- package/licenses/zara/templates/monochrome/template.html +2596 -0
- package/licenses/zara/templates/monochrome/template.json +49 -0
- package/licenses/zara/templates/neo-grid-bold/design.md +534 -0
- package/licenses/zara/templates/neo-grid-bold/template.html +865 -0
- package/licenses/zara/templates/neo-grid-bold/template.json +46 -0
- package/licenses/zara/templates/playful/design.md +480 -0
- package/licenses/zara/templates/playful/template.html +1355 -0
- package/licenses/zara/templates/playful/template.json +42 -0
- package/licenses/zara/templates/sakura-chroma/design.md +632 -0
- package/licenses/zara/templates/sakura-chroma/template.html +1405 -0
- package/licenses/zara/templates/sakura-chroma/template.json +53 -0
- package/licenses/zara/templates/signal/design.md +517 -0
- package/licenses/zara/templates/signal/template.html +2558 -0
- package/licenses/zara/templates/signal/template.json +49 -0
- package/licenses/zara/templates/soft-editorial/design.md +615 -0
- package/licenses/zara/templates/soft-editorial/template.html +1183 -0
- package/licenses/zara/templates/soft-editorial/template.json +46 -0
- package/package.json +107 -0
- package/skills/dsh-ppt/SKILL.md +36 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/design.md +61 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/metadata.json +2886 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/01.page +142 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/03.page +817 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/04.page +408 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/05.page +407 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/06.page +760 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/07.page +551 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/08.page +339 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/09.page +297 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/10.page +365 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source/pages/12.page +130 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/01.page +142 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/02.page +382 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/03.page +817 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/04.page +408 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/05.page +407 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/06.page +760 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/07.page +551 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/08.page +339 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/09.page +297 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/10.page +365 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/academic/dsh-course-workshop/source-zh/pages/12.page +130 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/design.md +61 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/metadata.json +2989 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/01.page +86 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/03.page +479 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/04.page +551 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/05.page +817 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/06.page +760 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/07.page +576 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/08.page +339 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/09.page +295 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/10.page +407 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/01.page +86 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/02.page +382 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/03.page +479 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/04.page +551 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/05.page +817 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/06.page +760 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/07.page +576 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/08.page +339 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/09.page +295 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/10.page +407 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/academic/dsh-monochrome/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/design.md +53 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/metadata.json +3152 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/01.page +100 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/02.page +310 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/03.page +381 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/04.page +853 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/05.page +436 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/06.page +732 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/07.page +574 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/08.page +767 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/09.page +282 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/10.page +350 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/11.page +462 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/01.page +100 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/02.page +310 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/03.page +381 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/04.page +853 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/05.page +436 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/06.page +732 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/07.page +574 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/08.page +769 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/09.page +282 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/10.page +350 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/11.page +462 -0
- package/skills/dsh-ppt/references/business/dsh-blue-professional/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/design.md +61 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/metadata.json +3883 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/01.page +355 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/02.page +452 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/03.page +561 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/04.page +830 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/05.page +887 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/06.page +646 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/07.page +881 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/08.page +788 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/09.page +365 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/10.page +617 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/11.page +645 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/01.page +354 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/02.page +452 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/03.page +561 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/04.page +830 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/05.page +887 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/06.page +646 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/07.page +881 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/08.page +788 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/09.page +365 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/10.page +617 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/11.page +645 -0
- package/skills/dsh-ppt/references/business/dsh-neo-grid-bold/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/composition.md +19 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/design.md +124 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/metadata.json +1930 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/deck.pptd +91 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/01.page +259 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/02.page +325 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/03.page +511 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/04.page +398 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/05.page +439 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/06.page +493 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/07.page +194 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/08.page +422 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/09.page +458 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/10.page +746 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/11.page +604 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source/pages/12.page +343 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/deck.pptd +91 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/01.page +259 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/02.page +325 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/03.page +511 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/04.page +398 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/05.page +439 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/06.page +493 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/07.page +194 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/08.page +417 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/09.page +458 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/10.page +746 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/11.page +604 -0
- package/skills/dsh-ppt/references/consulting/curated-swiss-signal-grid/source-zh/pages/12.page +343 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/design.md +61 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/metadata.json +3196 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/01.page +98 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/03.page +817 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/04.page +479 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/05.page +576 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/06.page +760 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/07.page +339 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/08.page +718 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/09.page +295 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/10.page +547 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/01.page +98 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/02.page +382 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/03.page +817 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/04.page +479 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/05.page +576 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/06.page +760 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/07.page +339 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/08.page +718 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/09.page +295 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/10.page +547 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/consulting/dsh-cartesian/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/design.md +61 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/metadata.json +3172 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/01.page +100 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/03.page +817 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/04.page +760 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/05.page +576 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/06.page +718 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/07.page +295 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/08.page +551 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/09.page +339 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/10.page +547 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/01.page +100 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/02.page +382 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/03.page +817 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/04.page +760 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/05.page +576 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/06.page +718 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/07.page +295 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/08.page +551 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/09.page +339 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/10.page +547 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/consulting/dsh-signal/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/design.md +61 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/metadata.json +3091 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/01.page +124 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/03.page +479 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/04.page +365 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/05.page +817 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/06.page +760 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/07.page +576 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/08.page +295 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/09.page +339 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/10.page +547 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source/pages/12.page +135 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/01.page +124 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/02.page +384 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/03.page +479 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/04.page +365 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/05.page +817 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/06.page +760 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/07.page +576 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/08.page +295 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/09.page +341 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/10.page +547 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/editorial/dsh-editorial-notebook/source-zh/pages/12.page +135 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/design.md +61 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/metadata.json +3119 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/01.page +117 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/03.page +479 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/04.page +817 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/05.page +760 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/06.page +295 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/07.page +365 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/08.page +576 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/09.page +408 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/10.page +547 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/01.page +117 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/02.page +384 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/03.page +479 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/04.page +817 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/05.page +760 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/06.page +295 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/07.page +365 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/08.page +576 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/09.page +408 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/10.page +547 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/editorial/dsh-soft-editorial/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/pptd.md +55 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/design.md +53 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/metadata.json +2329 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/01.page +72 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/02.page +156 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/03.page +350 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/04.page +210 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/05.page +436 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/06.page +420 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/07.page +480 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/08.page +294 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/09.page +210 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/10.page +392 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/11.page +560 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source/pages/12.page +58 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/01.page +72 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/02.page +156 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/03.page +350 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/04.page +210 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/05.page +436 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/06.page +420 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/07.page +480 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/08.page +294 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/09.page +210 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/10.page +392 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/11.page +560 -0
- package/skills/dsh-ppt/references/promotion/dsh-broadside/source-zh/pages/12.page +58 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/design.md +61 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/metadata.json +3070 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/01.page +101 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/02.page +410 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/03.page +436 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/04.page +845 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/05.page +579 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/06.page +367 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/07.page +519 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/08.page +323 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/09.page +604 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/10.page +575 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/11.page +603 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/01.page +100 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/02.page +410 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/03.page +436 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/04.page +845 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/05.page +579 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/06.page +367 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/07.page +519 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/08.page +323 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/09.page +604 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/10.page +575 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/11.page +603 -0
- package/skills/dsh-ppt/references/promotion/dsh-playful/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/design.md +61 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/metadata.json +3203 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/01.page +173 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/02.page +424 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/03.page +450 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/04.page +593 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/05.page +521 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/06.page +533 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/07.page +618 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/08.page +381 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/09.page +337 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/10.page +407 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/11.page +617 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source/pages/12.page +145 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/01.page +173 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/02.page +424 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/03.page +450 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/04.page +593 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/05.page +521 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/06.page +533 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/07.page +618 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/08.page +381 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/09.page +337 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/10.page +407 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/11.page +617 -0
- package/skills/dsh-ppt/references/promotion/dsh-sakura-chroma/source-zh/pages/12.page +145 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/design.md +122 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/metadata.json +1659 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/deck.pptd +105 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/01.page +499 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/02.page +499 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/03.page +577 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/04.page +408 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/05.page +660 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/06.page +641 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/07.page +415 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/08.page +606 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/09.page +567 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/10.page +595 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/11.page +555 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source/pages/12.page +895 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/deck.pptd +105 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/01.page +499 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/02.page +499 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/03.page +577 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/04.page +408 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/05.page +660 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/06.page +641 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/07.page +415 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/08.page +606 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/09.page +567 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/10.page +595 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/11.page +555 -0
- package/skills/dsh-ppt/references/work/curated-modular-logistics-system/source-zh/pages/12.page +895 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/design.md +111 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/metadata.json +1712 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/deck.pptd +93 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/01.page +395 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/02.page +574 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/03.page +307 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/04.page +359 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/05.page +443 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/06.page +437 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/07.page +181 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/08.page +557 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/09.page +337 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/10.page +732 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/11.page +589 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source/pages/12.page +385 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/deck.pptd +93 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/01.page +395 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/02.page +574 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/03.page +307 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/04.page +359 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/05.page +443 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/06.page +437 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/07.page +181 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/08.page +557 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/09.page +337 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/10.page +732 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/11.page +589 -0
- package/skills/dsh-ppt/references/work/curated-nordic-operating-report/source-zh/pages/12.page +385 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/design.md +61 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/metadata.json +3338 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/01.page +87 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/02.page +382 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/03.page +760 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/04.page +718 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/05.page +479 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/06.page +576 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/07.page +551 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/08.page +295 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/09.page +811 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/10.page +547 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/11.page +575 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source/pages/12.page +86 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/01.page +87 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/02.page +382 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/03.page +760 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/04.page +718 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/05.page +479 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/06.page +576 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/07.page +551 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/08.page +295 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/09.page +811 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/10.page +547 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/11.page +575 -0
- package/skills/dsh-ppt/references/work/dsh-editorial-forest/source-zh/pages/12.page +86 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/design.md +61 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/metadata.json +3147 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/01.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/02.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/03.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/04.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/05.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/06.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/07.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/08.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/09.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/10.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/11.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/pages/12.jpg +0 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/deck.pptd +21 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/01.page +390 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/02.page +1082 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/03.page +1511 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/04.page +1227 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/05.page +1517 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/06.page +1460 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/07.page +1276 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/08.page +1039 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/09.page +995 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/10.page +1275 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/11.page +1275 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source/pages/12.page +125 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/deck.pptd +21 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/01.page +390 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/02.page +1084 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/03.page +1511 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/04.page +1227 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/05.page +1517 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/06.page +1460 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/07.page +1276 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/08.page +1041 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/09.page +995 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/10.page +1275 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/11.page +1275 -0
- package/skills/dsh-ppt/references/work/dsh-engineering-blueprint/source-zh/pages/12.page +125 -0
|
@@ -0,0 +1,2173 @@
|
|
|
1
|
+
const MISPLACED_TEXT_STYLE_FIELDS = new Set(["fontFamily", "fontSize", "bold", "italic", "color", "lineHeight", "letterSpacing", "wrap", "align", "verticalAlign", "textDirection", "style"]);
|
|
2
|
+
import { wrapTextLines } from "./text-wrap.js";
|
|
3
|
+
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import PptxGenJSImport from "pptxgenjs";
|
|
8
|
+
//#region lib/types/pptd-colors.js
|
|
9
|
+
/** Shared PPTD color normalization used by checking, preview, and PPTX export. */
|
|
10
|
+
/** Deterministic series palette used when a chart series omits an explicit color. */
|
|
11
|
+
const PPTD_CHART_SERIES_PALETTE = [
|
|
12
|
+
"#2563EB",
|
|
13
|
+
"#F59E0B",
|
|
14
|
+
"#10B981",
|
|
15
|
+
"#EF4444",
|
|
16
|
+
"#8B5CF6",
|
|
17
|
+
"#06B6D4"
|
|
18
|
+
];
|
|
19
|
+
function record$1(value) {
|
|
20
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
21
|
+
}
|
|
22
|
+
function themeColors(project) {
|
|
23
|
+
return record$1(project.theme.colors) ?? {};
|
|
24
|
+
}
|
|
25
|
+
/** Resolve a literal or theme-referenced PPTD color without applying a silent fallback. */
|
|
26
|
+
function resolvePptdColor(project, value) {
|
|
27
|
+
let current = value;
|
|
28
|
+
const colors = themeColors(project);
|
|
29
|
+
for (let depth = 0; depth < 8 && typeof current === "string" && current.startsWith("$"); depth += 1) current = colors[current.slice(1)];
|
|
30
|
+
return typeof current === "string" && /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/iu.test(current) ? current : void 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalize chart-series colors from either color scalars or solid paint objects.
|
|
34
|
+
* Pie series may supply an array; unsupported paints and invalid colors return undefined.
|
|
35
|
+
*/
|
|
36
|
+
function resolvePptdChartSeriesColors(project, value) {
|
|
37
|
+
if (value === void 0) return void 0;
|
|
38
|
+
const values = Array.isArray(value) ? value : [value];
|
|
39
|
+
if (values.length === 0) return void 0;
|
|
40
|
+
const resolved = values.map((item) => {
|
|
41
|
+
const paint = record$1(item);
|
|
42
|
+
return resolvePptdColor(project, paint === void 0 ? item : paint.type === "solid" ? paint.color : void 0);
|
|
43
|
+
});
|
|
44
|
+
return resolved.every((item) => item !== void 0) ? resolved : void 0;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region lib/types/text-layout.js
|
|
48
|
+
/** Deterministic text-box capacity estimates shared by template references and PPT renderers. */
|
|
49
|
+
const WIDTH_RESERVE = .95;
|
|
50
|
+
const DEFAULT_LINE_HEIGHT = 1.15;
|
|
51
|
+
function positive(value, fallback) {
|
|
52
|
+
return value !== void 0 && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
53
|
+
}
|
|
54
|
+
function glyphWidth(character) {
|
|
55
|
+
if (/\p{Mark}/u.test(character)) return 0;
|
|
56
|
+
if (/\s/u.test(character)) return .33;
|
|
57
|
+
if (/^[ilI1|.,'`:;!]$/u.test(character)) return .3;
|
|
58
|
+
if (/^[mwMW@%&]$/u.test(character)) return .82;
|
|
59
|
+
if (/^[A-Z]$/u.test(character)) return .68;
|
|
60
|
+
if (/^[\u0000-\u00ff]$/u.test(character)) return .56;
|
|
61
|
+
if (/\p{Extended_Pictographic}/u.test(character)) return 1;
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
function lineWidth(text, fontSize, letterSpacing, bold) {
|
|
65
|
+
const characters = Array.from(text);
|
|
66
|
+
const glyphs = characters.reduce((sum, character) => sum + glyphWidth(character), 0);
|
|
67
|
+
const spacing = Math.max(0, characters.length - 1) * letterSpacing;
|
|
68
|
+
return glyphs * fontSize * (bold ? 1.04 : 1) + spacing;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Estimate native text wrapping while retaining the authored font size.
|
|
72
|
+
* @param input - Text, box geometry, and effective text style in PowerPoint points.
|
|
73
|
+
* @returns Estimated line use and overflow state.
|
|
74
|
+
*/
|
|
75
|
+
function measureTextLayout(input) {
|
|
76
|
+
const fontSize = positive(input.fontSize, 18);
|
|
77
|
+
const lineHeight = positive(input.lineHeight, DEFAULT_LINE_HEIGHT);
|
|
78
|
+
const letterSpacing = typeof input.letterSpacing === "number" && Number.isFinite(input.letterSpacing) ? input.letterSpacing : 0;
|
|
79
|
+
const availableLineWidth = Math.max(0, input.width) * WIDTH_RESERVE;
|
|
80
|
+
const paragraphWidths = input.text.length === 0 ? [] : input.text.split("\n").map((line) => lineWidth(line, fontSize, letterSpacing, input.bold === true));
|
|
81
|
+
const widestLine = Math.max(0, ...paragraphWidths);
|
|
82
|
+
const wrap = input.wrap !== false;
|
|
83
|
+
const lineCount = input.text.length === 0 ? 0 : wrapTextLines(input.text, availableLineWidth, text => lineWidth(text, fontSize, letterSpacing, input.bold === true), wrap).length;
|
|
84
|
+
const requiredHeight = lineCount * fontSize * lineHeight;
|
|
85
|
+
const maxLineCount = Math.max(0, Math.floor(Math.max(0, input.height) / (fontSize * lineHeight)));
|
|
86
|
+
const horizontalOverflow = !wrap && widestLine > availableLineWidth;
|
|
87
|
+
return {
|
|
88
|
+
lineCount,
|
|
89
|
+
maxLineCount,
|
|
90
|
+
requiredHeight,
|
|
91
|
+
availableLineWidth,
|
|
92
|
+
widestLine,
|
|
93
|
+
horizontalOverflow,
|
|
94
|
+
overflow: horizontalOverflow || lineCount > maxLineCount
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Derive a conservative full-width character budget for an empty text region.
|
|
99
|
+
* @param width - Text-region width in PowerPoint points.
|
|
100
|
+
* @param height - Text-region height in PowerPoint points.
|
|
101
|
+
* @param fontSize - Intended font size in points.
|
|
102
|
+
* @param lineHeight - Line-height multiplier.
|
|
103
|
+
* @returns Recommended full-width character count.
|
|
104
|
+
*/
|
|
105
|
+
function recommendedTextCapacity(width, height, fontSize, lineHeight = DEFAULT_LINE_HEIGHT) {
|
|
106
|
+
const size = positive(fontSize, 18);
|
|
107
|
+
const spacing = positive(lineHeight, DEFAULT_LINE_HEIGHT);
|
|
108
|
+
const charactersPerLine = Math.max(1, Math.floor(Math.max(0, width) * WIDTH_RESERVE / size));
|
|
109
|
+
const lines = Math.max(1, Math.floor(Math.max(0, height) / (size * spacing)));
|
|
110
|
+
return Math.max(1, Math.min(1e3, Math.floor(charactersPerLine * lines * .9)));
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region lib/types/pptd.js
|
|
114
|
+
/** Local PPTD v2 parser, checker, loader, and native editable PPTX renderer. */
|
|
115
|
+
const PptxGenJS = typeof PptxGenJSImport === "function" ? PptxGenJSImport : PptxGenJSImport.default;
|
|
116
|
+
const POINTS_PER_INCH = 72;
|
|
117
|
+
const MAX_MANIFEST_BYTES = 512 * 1024;
|
|
118
|
+
const MAX_PAGE_BYTES = 2 * 1024 * 1024;
|
|
119
|
+
const MAX_ASSET_BYTES = 32 * 1024 * 1024;
|
|
120
|
+
const MAX_TOTAL_ASSET_BYTES = 256 * 1024 * 1024;
|
|
121
|
+
const MAX_PAGES = 200;
|
|
122
|
+
const MAX_ELEMENTS_PER_PAGE = 2e3;
|
|
123
|
+
function record(value) {
|
|
124
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
125
|
+
}
|
|
126
|
+
function string(value) {
|
|
127
|
+
return typeof value === "string" ? value : void 0;
|
|
128
|
+
}
|
|
129
|
+
function number(value) {
|
|
130
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
131
|
+
}
|
|
132
|
+
function boolean(value) {
|
|
133
|
+
return typeof value === "boolean" ? value : void 0;
|
|
134
|
+
}
|
|
135
|
+
const NATIVE_CHART_TYPES = new Set([
|
|
136
|
+
"bar",
|
|
137
|
+
"line",
|
|
138
|
+
"area",
|
|
139
|
+
"scatter",
|
|
140
|
+
"bubble",
|
|
141
|
+
"pie",
|
|
142
|
+
"radar"
|
|
143
|
+
]);
|
|
144
|
+
const UNSUPPORTED_CHART_TYPES = new Set([
|
|
145
|
+
"candlestick",
|
|
146
|
+
"waterfall",
|
|
147
|
+
"heatmap",
|
|
148
|
+
"treemap",
|
|
149
|
+
"sunburst",
|
|
150
|
+
"sankey"
|
|
151
|
+
]);
|
|
152
|
+
const NATIVE_SHAPE_NAMES = new Set(Object.values(new PptxGenJS().ShapeType));
|
|
153
|
+
const MANIFEST_FIELDS = new Set([
|
|
154
|
+
"version",
|
|
155
|
+
"title",
|
|
156
|
+
"size",
|
|
157
|
+
"template",
|
|
158
|
+
"theme",
|
|
159
|
+
"pages"
|
|
160
|
+
]);
|
|
161
|
+
const TEMPLATE_FIELDS = new Set([
|
|
162
|
+
"id",
|
|
163
|
+
"name",
|
|
164
|
+
"sourceFile",
|
|
165
|
+
"sourceSha256"
|
|
166
|
+
]);
|
|
167
|
+
const PAGE_FIELDS = new Set([
|
|
168
|
+
"pageType",
|
|
169
|
+
"templateReference",
|
|
170
|
+
"background",
|
|
171
|
+
"notes",
|
|
172
|
+
"elements"
|
|
173
|
+
]);
|
|
174
|
+
const TEMPLATE_REFERENCE_FIELDS = new Set([
|
|
175
|
+
"sourceSlideNumber",
|
|
176
|
+
"rationale",
|
|
177
|
+
"contentRelationship"
|
|
178
|
+
]);
|
|
179
|
+
const TEMPLATE_RELATIONSHIPS = new Set([
|
|
180
|
+
"statement",
|
|
181
|
+
"independent",
|
|
182
|
+
"comparison",
|
|
183
|
+
"process",
|
|
184
|
+
"timeline",
|
|
185
|
+
"overlap",
|
|
186
|
+
"data",
|
|
187
|
+
"generic"
|
|
188
|
+
]);
|
|
189
|
+
const ELEMENT_BASE_FIELDS = [
|
|
190
|
+
"elementId",
|
|
191
|
+
"elementType",
|
|
192
|
+
"bounds"
|
|
193
|
+
];
|
|
194
|
+
const ELEMENT_FIELDS = {
|
|
195
|
+
text: new Set([
|
|
196
|
+
...ELEMENT_BASE_FIELDS,
|
|
197
|
+
"rotation",
|
|
198
|
+
"opacity",
|
|
199
|
+
"flip",
|
|
200
|
+
"content"
|
|
201
|
+
]),
|
|
202
|
+
shape: new Set([
|
|
203
|
+
...ELEMENT_BASE_FIELDS,
|
|
204
|
+
"rotation",
|
|
205
|
+
"opacity",
|
|
206
|
+
"flip",
|
|
207
|
+
"shapeName",
|
|
208
|
+
"adjustments",
|
|
209
|
+
"viewBox",
|
|
210
|
+
"path",
|
|
211
|
+
"fill",
|
|
212
|
+
"border",
|
|
213
|
+
"shadow"
|
|
214
|
+
]),
|
|
215
|
+
line: new Set([
|
|
216
|
+
...ELEMENT_BASE_FIELDS,
|
|
217
|
+
"rotation",
|
|
218
|
+
"opacity",
|
|
219
|
+
"flip",
|
|
220
|
+
"viewBox",
|
|
221
|
+
"points",
|
|
222
|
+
"curve",
|
|
223
|
+
"arrow",
|
|
224
|
+
"border",
|
|
225
|
+
"shadow"
|
|
226
|
+
]),
|
|
227
|
+
image: new Set([
|
|
228
|
+
...ELEMENT_BASE_FIELDS,
|
|
229
|
+
"rotation",
|
|
230
|
+
"opacity",
|
|
231
|
+
"flip",
|
|
232
|
+
"src",
|
|
233
|
+
"cropShape",
|
|
234
|
+
"fit",
|
|
235
|
+
"crop",
|
|
236
|
+
"border",
|
|
237
|
+
"shadow"
|
|
238
|
+
]),
|
|
239
|
+
icon: new Set([
|
|
240
|
+
...ELEMENT_BASE_FIELDS,
|
|
241
|
+
"rotation",
|
|
242
|
+
"opacity",
|
|
243
|
+
"flip",
|
|
244
|
+
"iconName",
|
|
245
|
+
"color",
|
|
246
|
+
"fill",
|
|
247
|
+
"border",
|
|
248
|
+
"shadow"
|
|
249
|
+
]),
|
|
250
|
+
table: new Set([
|
|
251
|
+
...ELEMENT_BASE_FIELDS,
|
|
252
|
+
"columnWidths",
|
|
253
|
+
"rowHeights",
|
|
254
|
+
"rows",
|
|
255
|
+
"style",
|
|
256
|
+
"fill",
|
|
257
|
+
"shadow"
|
|
258
|
+
]),
|
|
259
|
+
chart: new Set([
|
|
260
|
+
...ELEMENT_BASE_FIELDS,
|
|
261
|
+
"data",
|
|
262
|
+
"series",
|
|
263
|
+
"seriesDefaults",
|
|
264
|
+
"xAxis",
|
|
265
|
+
"yAxis",
|
|
266
|
+
"barWidth",
|
|
267
|
+
"barGap",
|
|
268
|
+
"categoryGap",
|
|
269
|
+
"spokeAxis",
|
|
270
|
+
"title",
|
|
271
|
+
"legend",
|
|
272
|
+
"dataLabels",
|
|
273
|
+
"fontFamily",
|
|
274
|
+
"fill",
|
|
275
|
+
"border",
|
|
276
|
+
"shadow"
|
|
277
|
+
])
|
|
278
|
+
};
|
|
279
|
+
function unknownFields(value, allowed) {
|
|
280
|
+
return Object.keys(value).filter((key) => !allowed.has(key));
|
|
281
|
+
}
|
|
282
|
+
function compatibilitySummary(project) {
|
|
283
|
+
const summary = {
|
|
284
|
+
native: 0,
|
|
285
|
+
normalized: 0,
|
|
286
|
+
vectorFallback: 0,
|
|
287
|
+
rasterFallback: 0,
|
|
288
|
+
unsupported: 0
|
|
289
|
+
};
|
|
290
|
+
const recordLevel = (level) => {
|
|
291
|
+
if (level === "native") summary.native += 1;
|
|
292
|
+
else if (level === "normalized") summary.normalized += 1;
|
|
293
|
+
else if (level === "vector-fallback") summary.vectorFallback += 1;
|
|
294
|
+
else if (level === "raster-fallback") summary.rasterFallback += 1;
|
|
295
|
+
else summary.unsupported += 1;
|
|
296
|
+
};
|
|
297
|
+
for (const page of project.pages) for (const element of page.elements) {
|
|
298
|
+
const type = string(element.elementType);
|
|
299
|
+
if (type === "icon") {
|
|
300
|
+
recordLevel("normalized");
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (type === "shape" && element.shapeName === "custom") {
|
|
304
|
+
recordLevel("vector-fallback");
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (type === "line" && (string(element.curve) === "smooth" || (string(element.points)?.trim().split(/\s+/u).length ?? 0) > 2)) {
|
|
308
|
+
recordLevel("vector-fallback");
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (type === "image" && record(element.cropShape)?.shapeName === "custom") {
|
|
312
|
+
recordLevel("raster-fallback");
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (type === "chart") {
|
|
316
|
+
const chartTypes = (Array.isArray(element.series) ? element.series.map(record).filter((item) => item !== void 0) : []).map((item) => string(item.type)).filter((item) => item !== void 0);
|
|
317
|
+
if (chartTypes.some((item) => UNSUPPORTED_CHART_TYPES.has(item))) {
|
|
318
|
+
recordLevel("unsupported");
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (chartTypes.some((item) => !NATIVE_CHART_TYPES.has(item))) {
|
|
322
|
+
recordLevel("unsupported");
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const fill = record(element.fill);
|
|
327
|
+
const content = record(element.content);
|
|
328
|
+
recordLevel(fill?.type === "gradient" || fill?.type === "image" || content?.gradient !== void 0 || content?.shadow !== void 0 || element.shadow !== void 0 || element.adjustments !== void 0 || element.crop !== void 0 || element.cropShape !== void 0 ? "normalized" : "native");
|
|
329
|
+
}
|
|
330
|
+
return summary;
|
|
331
|
+
}
|
|
332
|
+
function tuple(value, size) {
|
|
333
|
+
if (!Array.isArray(value) || value.length !== size) return void 0;
|
|
334
|
+
const values = value.map(number);
|
|
335
|
+
return values.every((item) => item !== void 0) ? values : void 0;
|
|
336
|
+
}
|
|
337
|
+
function safeProjectPath(value) {
|
|
338
|
+
if (value === "" || path.isAbsolute(value) || value.includes("\\")) return void 0;
|
|
339
|
+
const normalized = path.posix.normalize(value);
|
|
340
|
+
return normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/") ? void 0 : normalized;
|
|
341
|
+
}
|
|
342
|
+
function parseYaml(content, file, issues) {
|
|
343
|
+
try {
|
|
344
|
+
const value = record(yaml.load(content, {
|
|
345
|
+
schema: yaml.JSON_SCHEMA,
|
|
346
|
+
json: true
|
|
347
|
+
}));
|
|
348
|
+
if (value !== void 0) return value;
|
|
349
|
+
issues.push({
|
|
350
|
+
code: "yaml-root",
|
|
351
|
+
severity: "error",
|
|
352
|
+
file,
|
|
353
|
+
message: `${file} 的 YAML 根节点必须是对象。`
|
|
354
|
+
});
|
|
355
|
+
} catch (error) {
|
|
356
|
+
issues.push({
|
|
357
|
+
code: "yaml-syntax",
|
|
358
|
+
severity: "error",
|
|
359
|
+
file,
|
|
360
|
+
message: `${file} 无法解析:${error instanceof Error ? error.message : String(error)}`
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/** Parse a bounded in-memory PPTD v2 project into a renderer-independent AST. */
|
|
365
|
+
function parsePptdProject(source) {
|
|
366
|
+
const issues = [...source.issues ?? []];
|
|
367
|
+
const manifest = parseYaml(source.manifest, source.entryName, issues) ?? {};
|
|
368
|
+
for (const field of unknownFields(manifest, MANIFEST_FIELDS)) issues.push({
|
|
369
|
+
code: "unknown-field",
|
|
370
|
+
severity: "error",
|
|
371
|
+
file: source.entryName,
|
|
372
|
+
message: `PPTD 包含未知字段 ${field}。`
|
|
373
|
+
});
|
|
374
|
+
if (manifest.version !== "v2") issues.push({
|
|
375
|
+
code: "version",
|
|
376
|
+
severity: "error",
|
|
377
|
+
file: source.entryName,
|
|
378
|
+
message: "PPTD version 必须为 v2。"
|
|
379
|
+
});
|
|
380
|
+
const size = tuple(manifest.size, 2);
|
|
381
|
+
const pageWidth = size?.[0];
|
|
382
|
+
const pageHeight = size?.[1];
|
|
383
|
+
if (pageWidth === void 0 || pageHeight === void 0 || pageWidth <= 0 || pageHeight <= 0) issues.push({
|
|
384
|
+
code: "page-size",
|
|
385
|
+
severity: "error",
|
|
386
|
+
file: source.entryName,
|
|
387
|
+
message: "PPTD size 必须是两个正数。"
|
|
388
|
+
});
|
|
389
|
+
const template = manifest.template === void 0 ? void 0 : record(manifest.template);
|
|
390
|
+
if (manifest.template !== void 0 && template === void 0) issues.push({
|
|
391
|
+
code: "template",
|
|
392
|
+
severity: "error",
|
|
393
|
+
file: source.entryName,
|
|
394
|
+
message: "PPTD template 必须是对象。"
|
|
395
|
+
});
|
|
396
|
+
if (template !== void 0) {
|
|
397
|
+
for (const field of unknownFields(template, TEMPLATE_FIELDS)) issues.push({
|
|
398
|
+
code: "unknown-field",
|
|
399
|
+
severity: "error",
|
|
400
|
+
file: source.entryName,
|
|
401
|
+
message: `PPTD template 包含未知字段 ${field}。`
|
|
402
|
+
});
|
|
403
|
+
if (typeof template.id !== "string" || typeof template.name !== "string") issues.push({
|
|
404
|
+
code: "template",
|
|
405
|
+
severity: "error",
|
|
406
|
+
file: source.entryName,
|
|
407
|
+
message: "PPTD template.id 和 template.name 必须是字符串。"
|
|
408
|
+
});
|
|
409
|
+
if (template.sourceFile !== void 0 && typeof template.sourceFile !== "string") issues.push({
|
|
410
|
+
code: "template",
|
|
411
|
+
severity: "error",
|
|
412
|
+
file: source.entryName,
|
|
413
|
+
message: "PPTD template.sourceFile 必须是字符串。"
|
|
414
|
+
});
|
|
415
|
+
if (template.sourceSha256 !== void 0 && (typeof template.sourceSha256 !== "string" || !/^[0-9a-f]{64}$/iu.test(template.sourceSha256))) issues.push({
|
|
416
|
+
code: "template",
|
|
417
|
+
severity: "error",
|
|
418
|
+
file: source.entryName,
|
|
419
|
+
message: "PPTD template.sourceSha256 必须是 SHA-256。"
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
const pageRefs = Array.isArray(manifest.pages) ? manifest.pages : [];
|
|
423
|
+
if (!Array.isArray(manifest.pages) || pageRefs.length === 0 || pageRefs.length > MAX_PAGES) issues.push({
|
|
424
|
+
code: "pages",
|
|
425
|
+
severity: "error",
|
|
426
|
+
file: source.entryName,
|
|
427
|
+
message: `PPTD pages 必须包含 1 到 ${MAX_PAGES} 个页面路径。`
|
|
428
|
+
});
|
|
429
|
+
const pages = [];
|
|
430
|
+
const seen = /* @__PURE__ */ new Set();
|
|
431
|
+
for (const [index, rawRef] of pageRefs.slice(0, MAX_PAGES).entries()) {
|
|
432
|
+
const ref = typeof rawRef === "string" ? safeProjectPath(rawRef) : void 0;
|
|
433
|
+
if (ref === void 0 || !ref.endsWith(".page")) {
|
|
434
|
+
issues.push({
|
|
435
|
+
code: "page-path",
|
|
436
|
+
severity: "error",
|
|
437
|
+
page: index + 1,
|
|
438
|
+
message: `第 ${index + 1} 个页面路径无效。`
|
|
439
|
+
});
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (seen.has(ref)) {
|
|
443
|
+
issues.push({
|
|
444
|
+
code: "duplicate-page",
|
|
445
|
+
severity: "error",
|
|
446
|
+
file: ref,
|
|
447
|
+
page: index + 1,
|
|
448
|
+
message: `页面 ${ref} 被重复引用。`
|
|
449
|
+
});
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
seen.add(ref);
|
|
453
|
+
const content = source.pages.get(ref);
|
|
454
|
+
if (content === void 0) {
|
|
455
|
+
issues.push({
|
|
456
|
+
code: "missing-page",
|
|
457
|
+
severity: "error",
|
|
458
|
+
file: ref,
|
|
459
|
+
page: index + 1,
|
|
460
|
+
message: `找不到页面文件 ${ref}。`
|
|
461
|
+
});
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const parsed = parseYaml(content, ref, issues);
|
|
465
|
+
if (parsed === void 0) continue;
|
|
466
|
+
for (const field of unknownFields(parsed, PAGE_FIELDS)) issues.push({
|
|
467
|
+
code: "unknown-field",
|
|
468
|
+
severity: "error",
|
|
469
|
+
file: ref,
|
|
470
|
+
page: index + 1,
|
|
471
|
+
message: `页面包含未知字段 ${field}。`
|
|
472
|
+
});
|
|
473
|
+
const elements = Array.isArray(parsed.elements) ? parsed.elements.map(record).filter((item) => item !== void 0) : [];
|
|
474
|
+
if (!Array.isArray(parsed.elements) || elements.length > MAX_ELEMENTS_PER_PAGE) issues.push({
|
|
475
|
+
code: "elements",
|
|
476
|
+
severity: "error",
|
|
477
|
+
file: ref,
|
|
478
|
+
page: index + 1,
|
|
479
|
+
message: `页面 elements 必须是数组且不超过 ${MAX_ELEMENTS_PER_PAGE} 个元素。`
|
|
480
|
+
});
|
|
481
|
+
const background = record(parsed.background);
|
|
482
|
+
const templateReference = parsed.templateReference === void 0 ? void 0 : record(parsed.templateReference);
|
|
483
|
+
if (parsed.templateReference !== void 0 && templateReference === void 0) issues.push({
|
|
484
|
+
code: "template-reference",
|
|
485
|
+
severity: "error",
|
|
486
|
+
file: ref,
|
|
487
|
+
page: index + 1,
|
|
488
|
+
message: "页面 templateReference 必须是对象。"
|
|
489
|
+
});
|
|
490
|
+
if (templateReference !== void 0) {
|
|
491
|
+
for (const field of unknownFields(templateReference, TEMPLATE_REFERENCE_FIELDS)) issues.push({
|
|
492
|
+
code: "unknown-field",
|
|
493
|
+
severity: "error",
|
|
494
|
+
file: ref,
|
|
495
|
+
page: index + 1,
|
|
496
|
+
message: `页面 templateReference 包含未知字段 ${field}。`
|
|
497
|
+
});
|
|
498
|
+
if (!Number.isInteger(templateReference.sourceSlideNumber) || Number(templateReference.sourceSlideNumber) <= 0) issues.push({
|
|
499
|
+
code: "template-reference",
|
|
500
|
+
severity: "error",
|
|
501
|
+
file: ref,
|
|
502
|
+
page: index + 1,
|
|
503
|
+
message: "页面 templateReference.sourceSlideNumber 必须是正整数。"
|
|
504
|
+
});
|
|
505
|
+
if (typeof templateReference.rationale !== "string" || templateReference.rationale.trim() === "") issues.push({
|
|
506
|
+
code: "template-reference",
|
|
507
|
+
severity: "error",
|
|
508
|
+
file: ref,
|
|
509
|
+
page: index + 1,
|
|
510
|
+
message: "页面 templateReference.rationale 必须说明源页选择理由。"
|
|
511
|
+
});
|
|
512
|
+
if (templateReference.contentRelationship !== void 0 && (typeof templateReference.contentRelationship !== "string" || !TEMPLATE_RELATIONSHIPS.has(templateReference.contentRelationship))) issues.push({
|
|
513
|
+
code: "template-reference",
|
|
514
|
+
severity: "error",
|
|
515
|
+
file: ref,
|
|
516
|
+
page: index + 1,
|
|
517
|
+
message: "页面 templateReference.contentRelationship 必须是受支持的内容关系。"
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
for (const element of elements) {
|
|
521
|
+
const type = string(element.elementType);
|
|
522
|
+
const allowed = type === void 0 ? void 0 : ELEMENT_FIELDS[type];
|
|
523
|
+
if (allowed === void 0) continue;
|
|
524
|
+
for (const field of unknownFields(element, allowed)) issues.push({
|
|
525
|
+
code: type === "text" && MISPLACED_TEXT_STYLE_FIELDS.has(field) ? "misplaced-text-style" : "unknown-field",
|
|
526
|
+
severity: "error",
|
|
527
|
+
file: ref,
|
|
528
|
+
page: index + 1,
|
|
529
|
+
...typeof element.elementId === "string" ? { elementId: element.elementId } : {},
|
|
530
|
+
message: type === "text" && MISPLACED_TEXT_STYLE_FIELDS.has(field) ? `文本属性 ${field} 应放在 content 内,请修正 YAML 缩进。` : `元素包含未知字段 ${field}。`
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
pages.push({
|
|
534
|
+
file: ref,
|
|
535
|
+
...typeof parsed.pageType === "string" ? { pageType: parsed.pageType } : {},
|
|
536
|
+
...templateReference === void 0 || !Number.isInteger(templateReference.sourceSlideNumber) || typeof templateReference.rationale !== "string" ? {} : { templateReference: {
|
|
537
|
+
sourceSlideNumber: Number(templateReference.sourceSlideNumber),
|
|
538
|
+
rationale: templateReference.rationale,
|
|
539
|
+
...typeof templateReference.contentRelationship === "string" && TEMPLATE_RELATIONSHIPS.has(templateReference.contentRelationship) ? { contentRelationship: templateReference.contentRelationship } : {}
|
|
540
|
+
} },
|
|
541
|
+
...background === void 0 ? {} : { background },
|
|
542
|
+
notes: typeof parsed.notes === "string" ? parsed.notes : "",
|
|
543
|
+
elements: elements.slice(0, MAX_ELEMENTS_PER_PAGE)
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
source,
|
|
548
|
+
title: typeof manifest.title === "string" ? manifest.title : path.basename(source.entryName, ".pptd"),
|
|
549
|
+
width: pageWidth ?? 960,
|
|
550
|
+
height: pageHeight ?? 540,
|
|
551
|
+
...template === void 0 ? {} : { template },
|
|
552
|
+
theme: record(manifest.theme) ?? {},
|
|
553
|
+
pages,
|
|
554
|
+
parseIssues: issues
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function projectDigest(project) {
|
|
558
|
+
const hash = createHash("sha256").update(project.source.manifest);
|
|
559
|
+
for (const page of project.pages) hash.update(page.file).update(project.source.pages.get(page.file) ?? "");
|
|
560
|
+
for (const asset of [...project.source.assets.values()].sort((left, right) => left.path.localeCompare(right.path))) hash.update(asset.path).update(asset.sha256);
|
|
561
|
+
return hash.digest("hex");
|
|
562
|
+
}
|
|
563
|
+
function themeMap(project, key) {
|
|
564
|
+
return record(project.theme[key]) ?? {};
|
|
565
|
+
}
|
|
566
|
+
function themeReferenceIssue(project, value, mapName, file, page, elementId) {
|
|
567
|
+
if (typeof value !== "string" || !value.startsWith("$")) return void 0;
|
|
568
|
+
const key = value.slice(1);
|
|
569
|
+
if (key !== "" && key in themeMap(project, mapName)) return void 0;
|
|
570
|
+
return {
|
|
571
|
+
code: "invalid-theme",
|
|
572
|
+
severity: "error",
|
|
573
|
+
file,
|
|
574
|
+
...page === void 0 ? {} : { page },
|
|
575
|
+
...elementId === void 0 ? {} : { elementId },
|
|
576
|
+
message: `主题引用 ${value} 不存在于 theme.${mapName}。`
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function colorThemeIssues(project, value, file, page, elementId) {
|
|
580
|
+
const issues = [];
|
|
581
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
582
|
+
const visit = (current, field) => {
|
|
583
|
+
if (typeof current === "string") {
|
|
584
|
+
if (field === "color" || field === "backgroundColor" || field === "lineColor" || field === "areaColor" || field === "fill") {
|
|
585
|
+
const issue = themeReferenceIssue(project, current, "colors", file, page, elementId);
|
|
586
|
+
if (issue !== void 0) issues.push(issue);
|
|
587
|
+
}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (Array.isArray(current)) {
|
|
591
|
+
if (visited.has(current)) return;
|
|
592
|
+
visited.add(current);
|
|
593
|
+
for (const item of current) visit(item, field);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const object = record(current);
|
|
597
|
+
if (object === void 0) return;
|
|
598
|
+
if (visited.has(object)) return;
|
|
599
|
+
visited.add(object);
|
|
600
|
+
for (const [key, child] of Object.entries(object)) visit(child, key === "fill" ? "fill" : key);
|
|
601
|
+
};
|
|
602
|
+
visit(value);
|
|
603
|
+
return issues;
|
|
604
|
+
}
|
|
605
|
+
function themeColorChainIssues(project) {
|
|
606
|
+
const colors = themeMap(project, "colors");
|
|
607
|
+
const issues = [];
|
|
608
|
+
const reportedCycles = /* @__PURE__ */ new Set();
|
|
609
|
+
const reportedInvalidTerminals = /* @__PURE__ */ new Set();
|
|
610
|
+
for (const start of Object.keys(colors)) {
|
|
611
|
+
const path = [];
|
|
612
|
+
let key = start;
|
|
613
|
+
while (true) {
|
|
614
|
+
const cycleIndex = path.indexOf(key);
|
|
615
|
+
if (cycleIndex >= 0) {
|
|
616
|
+
const cycle = path.slice(cycleIndex);
|
|
617
|
+
const signature = [...cycle].sort().join("\0");
|
|
618
|
+
if (!reportedCycles.has(signature)) {
|
|
619
|
+
reportedCycles.add(signature);
|
|
620
|
+
issues.push({
|
|
621
|
+
code: "invalid-theme",
|
|
622
|
+
severity: "error",
|
|
623
|
+
file: project.source.entryName,
|
|
624
|
+
message: `theme.colors 存在循环引用:${[...cycle, key].map((item) => `$${item}`).join(" -> ")}。`
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
path.push(key);
|
|
630
|
+
const value = colors[key];
|
|
631
|
+
if (typeof value !== "string" || !value.startsWith("$")) {
|
|
632
|
+
if (key !== start && (typeof value !== "string" || !/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/iu.test(value))) {
|
|
633
|
+
if (!reportedInvalidTerminals.has(key)) {
|
|
634
|
+
reportedInvalidTerminals.add(key);
|
|
635
|
+
issues.push({
|
|
636
|
+
code: "invalid-theme",
|
|
637
|
+
severity: "error",
|
|
638
|
+
file: project.source.entryName,
|
|
639
|
+
message: `theme.colors.${start} 最终指向无效颜色 theme.colors.${key}。`
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
const next = value.slice(1);
|
|
646
|
+
if (!(next in colors)) break;
|
|
647
|
+
key = next;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return issues;
|
|
651
|
+
}
|
|
652
|
+
function resolveThemeReference(value, map) {
|
|
653
|
+
if (typeof value !== "string" || !value.startsWith("$")) return value;
|
|
654
|
+
return map[value.slice(1)];
|
|
655
|
+
}
|
|
656
|
+
function resolveColorValue(project, value, fallback = "#000000") {
|
|
657
|
+
return resolvePptdColor(project, value) ?? fallback;
|
|
658
|
+
}
|
|
659
|
+
function colorOptions(project, value, opacity = 1) {
|
|
660
|
+
const resolved = resolveColorValue(project, value);
|
|
661
|
+
const alpha = resolved.length === 9 ? Number.parseInt(resolved.slice(7, 9), 16) / 255 : 1;
|
|
662
|
+
const transparency = Math.round((1 - Math.max(0, Math.min(1, alpha * opacity))) * 100);
|
|
663
|
+
return {
|
|
664
|
+
color: resolved.slice(1, 7).toUpperCase(),
|
|
665
|
+
...transparency === 0 ? {} : { transparency }
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function readableForeground(background) {
|
|
669
|
+
const red = Number.parseInt(background.slice(0, 2), 16) / 255;
|
|
670
|
+
const green = Number.parseInt(background.slice(2, 4), 16) / 255;
|
|
671
|
+
const blue = Number.parseInt(background.slice(4, 6), 16) / 255;
|
|
672
|
+
return red * .2126 + green * .7152 + blue * .0722 < .5 ? "E2E8F0" : "1F2937";
|
|
673
|
+
}
|
|
674
|
+
function textStyle(project, content) {
|
|
675
|
+
return {
|
|
676
|
+
...record(resolveThemeReference(content.style, themeMap(project, "textStyles"))) ?? {},
|
|
677
|
+
...content
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function plainText(value) {
|
|
681
|
+
return value.replace(/<br\s*\/?\s*>/giu, "\n").replace(/<\/p\s*>/giu, "\n").replace(/<li(?:\s[^>]*)?>/giu, "• ").replace(/<\/li\s*>/giu, "\n").replace(/<[^>]+>/gu, "").replace(/</gu, "<").replace(/>/gu, ">").replace(/&/gu, "&").replace(/"/gu, "\"").replace(/\n{3,}/gu, "\n\n").trimEnd();
|
|
682
|
+
}
|
|
683
|
+
function textLayout(project, element) {
|
|
684
|
+
// Fix structural errors before estimating layout with fallback styles.
|
|
685
|
+
if (unknownFields(element, ELEMENT_FIELDS.text).length > 0) return void 0;
|
|
686
|
+
const bounds = tuple(element.bounds, 4);
|
|
687
|
+
const content = record(element.content);
|
|
688
|
+
if (bounds === void 0 || content === void 0 || typeof content.text !== "string" || plainText(content.text).trim() === "") return void 0;
|
|
689
|
+
const style = textStyle(project, content);
|
|
690
|
+
const fontSize = number(style.fontSize) ?? 18;
|
|
691
|
+
const inlineSizes = Array.from(content.text.matchAll(/font-size\s*:\s*(\d+(?:\.\d+)?)px/giu), (match) => Number(match[1]));
|
|
692
|
+
const vertical = style.textDirection === "vertical";
|
|
693
|
+
const lineHeight = number(style.lineHeight);
|
|
694
|
+
const letterSpacing = number(style.letterSpacing);
|
|
695
|
+
const wrap = boolean(style.wrap);
|
|
696
|
+
return measureTextLayout({
|
|
697
|
+
text: plainText(content.text),
|
|
698
|
+
width: bounds[vertical ? 3 : 2] ?? 0,
|
|
699
|
+
height: bounds[vertical ? 2 : 3] ?? 0,
|
|
700
|
+
fontSize: Math.max(fontSize, ...inlineSizes),
|
|
701
|
+
bold: boolean(style.bold) === true || /<(?:b|strong)(?:\s|>)/iu.test(content.text),
|
|
702
|
+
...lineHeight === void 0 ? {} : { lineHeight },
|
|
703
|
+
...letterSpacing === void 0 ? {} : { letterSpacing },
|
|
704
|
+
...wrap === void 0 ? {} : { wrap }
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
function localAssetPath(value) {
|
|
708
|
+
if (typeof value !== "string" || /^https?:\/\//iu.test(value)) return void 0;
|
|
709
|
+
return safeProjectPath(value);
|
|
710
|
+
}
|
|
711
|
+
function elementContext(page, element) {
|
|
712
|
+
return {
|
|
713
|
+
page,
|
|
714
|
+
...typeof element.elementId === "string" ? { elementId: element.elementId } : {}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function compatibilityIssue(page, context, level, feature) {
|
|
718
|
+
const severity = level === "unsupported" ? "error" : "warning";
|
|
719
|
+
return {
|
|
720
|
+
code: `compatibility-${level}`,
|
|
721
|
+
severity,
|
|
722
|
+
file: page.file,
|
|
723
|
+
...context,
|
|
724
|
+
message: `${feature} 的渲染兼容级别为 ${level}。`
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function validTableGrid(element) {
|
|
728
|
+
const rows = Array.isArray(element.rows) ? element.rows : [];
|
|
729
|
+
const columnWidths = Array.isArray(element.columnWidths) ? element.columnWidths.map(number) : [];
|
|
730
|
+
const rowHeights = Array.isArray(element.rowHeights) ? element.rowHeights.map(number) : [];
|
|
731
|
+
const columnCount = columnWidths.length;
|
|
732
|
+
const rowCount = rowHeights.length;
|
|
733
|
+
if (rowCount === 0 || columnCount === 0 || rows.length !== rowCount) return false;
|
|
734
|
+
if (columnWidths.some((value) => value === void 0 || value < 0 || value > 1) || rowHeights.some((value) => value === void 0 || value < 0 || value > 1)) return false;
|
|
735
|
+
const nearOne = (values) => {
|
|
736
|
+
const total = values.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
737
|
+
return Math.abs(total - 1) < .001;
|
|
738
|
+
};
|
|
739
|
+
if (!nearOne(columnWidths) || !nearOne(rowHeights)) return false;
|
|
740
|
+
const occupied = Array.from({ length: rowCount }, () => Array.from({ length: columnCount }, () => false));
|
|
741
|
+
for (const [rowIndex, rawRow] of rows.entries()) {
|
|
742
|
+
if (!Array.isArray(rawRow)) return false;
|
|
743
|
+
let columnIndex = 0;
|
|
744
|
+
for (const rawCell of rawRow) {
|
|
745
|
+
while (columnIndex < columnCount && occupied[rowIndex]?.[columnIndex] === true) columnIndex += 1;
|
|
746
|
+
const cell = record(rawCell) ?? {};
|
|
747
|
+
const rowSpan = number(cell.rowSpan) ?? 1;
|
|
748
|
+
const colSpan = number(cell.colSpan) ?? 1;
|
|
749
|
+
if (!Number.isInteger(rowSpan) || !Number.isInteger(colSpan) || rowSpan < 1 || colSpan < 1 || rowIndex + rowSpan > rowCount || columnIndex + colSpan > columnCount) return false;
|
|
750
|
+
for (let row = rowIndex; row < rowIndex + rowSpan; row += 1) {
|
|
751
|
+
const occupiedRow = occupied[row];
|
|
752
|
+
if (occupiedRow === void 0) return false;
|
|
753
|
+
for (let column = columnIndex; column < columnIndex + colSpan; column += 1) {
|
|
754
|
+
if (occupiedRow[column] === true) return false;
|
|
755
|
+
occupiedRow[column] = true;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
columnIndex += colSpan;
|
|
759
|
+
}
|
|
760
|
+
if (occupied[rowIndex]?.some((value) => !value) === true) return false;
|
|
761
|
+
}
|
|
762
|
+
return true;
|
|
763
|
+
}
|
|
764
|
+
function checkElement(project, page, pageNumber, element, ids) {
|
|
765
|
+
const issues = [];
|
|
766
|
+
const context = elementContext(pageNumber, element);
|
|
767
|
+
const id = string(element.elementId);
|
|
768
|
+
issues.push(...colorThemeIssues(project, element, page.file, pageNumber, id));
|
|
769
|
+
if (id === void 0 || id.trim() === "") issues.push({
|
|
770
|
+
code: "element-id",
|
|
771
|
+
severity: "error",
|
|
772
|
+
file: page.file,
|
|
773
|
+
...context,
|
|
774
|
+
message: "元素缺少 elementId。"
|
|
775
|
+
});
|
|
776
|
+
else if (ids.has(id)) issues.push({
|
|
777
|
+
code: "duplicate-id",
|
|
778
|
+
severity: "error",
|
|
779
|
+
file: page.file,
|
|
780
|
+
...context,
|
|
781
|
+
message: `元素 ID ${id} 重复。`
|
|
782
|
+
});
|
|
783
|
+
else ids.add(id);
|
|
784
|
+
const bounds = tuple(element.bounds, 4);
|
|
785
|
+
const [x = 0, y = 0, width = -1, height = -1] = bounds ?? [];
|
|
786
|
+
if (bounds === void 0 || width < 0 || height < 0) issues.push({
|
|
787
|
+
code: "bounds",
|
|
788
|
+
severity: "error",
|
|
789
|
+
file: page.file,
|
|
790
|
+
...context,
|
|
791
|
+
message: "元素 bounds 必须是 [x, y, width, height]。"
|
|
792
|
+
});
|
|
793
|
+
else if (x < 0 || y < 0 || x + width > project.width + .01 || y + height > project.height + .01) issues.push({
|
|
794
|
+
code: "out-of-bounds",
|
|
795
|
+
severity: "error",
|
|
796
|
+
file: page.file,
|
|
797
|
+
...context,
|
|
798
|
+
message: "元素超出 PPTD 页面边界。"
|
|
799
|
+
});
|
|
800
|
+
const type = string(element.elementType);
|
|
801
|
+
if (![
|
|
802
|
+
"text",
|
|
803
|
+
"shape",
|
|
804
|
+
"line",
|
|
805
|
+
"image",
|
|
806
|
+
"icon",
|
|
807
|
+
"table",
|
|
808
|
+
"chart"
|
|
809
|
+
].includes(type ?? "")) {
|
|
810
|
+
issues.push({
|
|
811
|
+
code: "element-type",
|
|
812
|
+
severity: "error",
|
|
813
|
+
file: page.file,
|
|
814
|
+
...context,
|
|
815
|
+
message: `不支持的 elementType:${type ?? "missing"}。`
|
|
816
|
+
});
|
|
817
|
+
return issues;
|
|
818
|
+
}
|
|
819
|
+
if (type === "text") {
|
|
820
|
+
const content = record(element.content);
|
|
821
|
+
const styleIssue = themeReferenceIssue(project, content?.style, "textStyles", page.file, pageNumber, id);
|
|
822
|
+
if (styleIssue !== void 0) issues.push(styleIssue);
|
|
823
|
+
if (content === void 0 || typeof content.text !== "string") issues.push({
|
|
824
|
+
code: "text-content",
|
|
825
|
+
severity: "error",
|
|
826
|
+
file: page.file,
|
|
827
|
+
...context,
|
|
828
|
+
message: "文本元素需要 content.text。"
|
|
829
|
+
});
|
|
830
|
+
else {
|
|
831
|
+
const layout = textLayout(project, element);
|
|
832
|
+
if (layout?.overflow === true) {
|
|
833
|
+
const message = layout.horizontalOverflow ? `文本设置为不换行,但预计宽度 ${Math.ceil(layout.widestLine)}pt 超过文本框可用宽度 ${Math.floor(layout.availableLineWidth)}pt;请缩短文案或增大文本框。` : `文本预计需要 ${layout.lineCount} 行,当前文本框可容纳 ${layout.maxLineCount} 行;请缩短文案、增大文本框或拆分页面。`;
|
|
834
|
+
issues.push({
|
|
835
|
+
code: "text-overflow",
|
|
836
|
+
severity: "error",
|
|
837
|
+
file: page.file,
|
|
838
|
+
...context,
|
|
839
|
+
message
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (content !== void 0 && (content.gradient !== void 0 || content.shadow !== void 0)) issues.push(compatibilityIssue(page, context, "normalized", "文本渐变或阴影"));
|
|
844
|
+
if (typeof content?.text === "string" && /<(?:u|s|sup|sub|a|ol)(?:\s|>)/iu.test(content.text)) issues.push(compatibilityIssue(page, context, "normalized", "高级富文本标签"));
|
|
845
|
+
}
|
|
846
|
+
if (type === "shape") {
|
|
847
|
+
const name = string(element.shapeName);
|
|
848
|
+
if (name === void 0) issues.push({
|
|
849
|
+
code: "shape-name",
|
|
850
|
+
severity: "error",
|
|
851
|
+
file: page.file,
|
|
852
|
+
...context,
|
|
853
|
+
message: "形状元素需要 shapeName。"
|
|
854
|
+
});
|
|
855
|
+
else if (name === "custom") if (tuple(element.viewBox, 2) === void 0 || typeof element.path !== "string") issues.push({
|
|
856
|
+
code: "custom-shape",
|
|
857
|
+
severity: "error",
|
|
858
|
+
file: page.file,
|
|
859
|
+
...context,
|
|
860
|
+
message: "自定义形状需要 viewBox 和 SVG path。"
|
|
861
|
+
});
|
|
862
|
+
else issues.push(compatibilityIssue(page, context, "vector-fallback", "自定义 SVG 形状"));
|
|
863
|
+
else if (!NATIVE_SHAPE_NAMES.has(name)) issues.push({
|
|
864
|
+
code: "shape-name",
|
|
865
|
+
severity: "error",
|
|
866
|
+
file: page.file,
|
|
867
|
+
...context,
|
|
868
|
+
message: `未知的内置形状:${name}。`
|
|
869
|
+
});
|
|
870
|
+
if (element.adjustments !== void 0) issues.push(compatibilityIssue(page, context, "normalized", "形状 adjustments"));
|
|
871
|
+
const fill = record(element.fill);
|
|
872
|
+
if (fill?.type === "gradient" || fill?.type === "image") issues.push(compatibilityIssue(page, context, "normalized", "形状渐变或图片填充"));
|
|
873
|
+
if (element.shadow !== void 0) issues.push(compatibilityIssue(page, context, "normalized", "形状阴影"));
|
|
874
|
+
}
|
|
875
|
+
if (type === "line") {
|
|
876
|
+
const points = typeof element.points === "string" ? element.points.trim().split(/\s+/u) : [];
|
|
877
|
+
const invalidPoint = points.some((point) => !/^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$/u.test(point));
|
|
878
|
+
if (tuple(element.viewBox, 2) === void 0 || points.length < 2 || invalidPoint) issues.push({
|
|
879
|
+
code: "line-path",
|
|
880
|
+
severity: "error",
|
|
881
|
+
file: page.file,
|
|
882
|
+
...context,
|
|
883
|
+
message: "线条元素需要有效的 viewBox 和至少两个 points。"
|
|
884
|
+
});
|
|
885
|
+
else if (points.length > 2 || element.curve === "smooth") issues.push(compatibilityIssue(page, context, "vector-fallback", "贝塞尔曲线"));
|
|
886
|
+
if (element.shadow !== void 0) issues.push(compatibilityIssue(page, context, "normalized", "线条阴影"));
|
|
887
|
+
}
|
|
888
|
+
if (type === "image") {
|
|
889
|
+
if (typeof element.src !== "string") issues.push({
|
|
890
|
+
code: "image-src",
|
|
891
|
+
severity: "error",
|
|
892
|
+
file: page.file,
|
|
893
|
+
...context,
|
|
894
|
+
message: "图片元素需要 src。"
|
|
895
|
+
});
|
|
896
|
+
else if (/^https?:\/\//iu.test(element.src)) issues.push({
|
|
897
|
+
code: "remote-image",
|
|
898
|
+
severity: "error",
|
|
899
|
+
file: page.file,
|
|
900
|
+
...context,
|
|
901
|
+
message: "本地 PPTD 渲染器不访问网络图片,请先将图片放入项目目录。"
|
|
902
|
+
});
|
|
903
|
+
else {
|
|
904
|
+
const assetPath = localAssetPath(element.src);
|
|
905
|
+
if (assetPath === void 0 || !project.source.assets.has(assetPath)) issues.push({
|
|
906
|
+
code: "missing-asset",
|
|
907
|
+
severity: "error",
|
|
908
|
+
file: page.file,
|
|
909
|
+
...context,
|
|
910
|
+
message: `找不到图片资源 ${element.src}。`
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const fit = record(element.fit);
|
|
914
|
+
if (fit !== void 0 && ![
|
|
915
|
+
"fill",
|
|
916
|
+
"contain",
|
|
917
|
+
"cover"
|
|
918
|
+
].includes(string(fit.mode) ?? "")) issues.push({
|
|
919
|
+
code: "image-fit",
|
|
920
|
+
severity: "error",
|
|
921
|
+
file: page.file,
|
|
922
|
+
...context,
|
|
923
|
+
message: "图片 fit.mode 必须是 fill、contain 或 cover。"
|
|
924
|
+
});
|
|
925
|
+
if (element.crop !== void 0 || element.cropShape !== void 0 || element.shadow !== void 0 || element.border !== void 0) {
|
|
926
|
+
const cropShape = record(element.cropShape);
|
|
927
|
+
issues.push(compatibilityIssue(page, context, cropShape?.shapeName === "custom" ? "raster-fallback" : "normalized", "图片裁剪、边框或阴影"));
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (type === "table") {
|
|
931
|
+
if (typeof element.style === "string") {
|
|
932
|
+
const styleIssue = themeReferenceIssue(project, element.style, "tableStyles", page.file, pageNumber, id);
|
|
933
|
+
if (styleIssue !== void 0) issues.push(styleIssue);
|
|
934
|
+
}
|
|
935
|
+
const rows = Array.isArray(element.rows) ? element.rows : [];
|
|
936
|
+
for (const rawRow of rows) for (const rawCell of Array.isArray(rawRow) ? rawRow : []) {
|
|
937
|
+
const styleIssue = themeReferenceIssue(project, record(rawCell)?.textStyle, "textStyles", page.file, pageNumber, id);
|
|
938
|
+
if (styleIssue !== void 0) issues.push(styleIssue);
|
|
939
|
+
}
|
|
940
|
+
if (!validTableGrid(element)) issues.push({
|
|
941
|
+
code: "table-data",
|
|
942
|
+
severity: "error",
|
|
943
|
+
file: page.file,
|
|
944
|
+
...context,
|
|
945
|
+
message: "表格网格、宽高比例或合并区域无效。"
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
if (type === "chart") {
|
|
949
|
+
const data = record(element.data);
|
|
950
|
+
const cols = Array.isArray(data?.cols) ? data.cols : [];
|
|
951
|
+
const rows = Array.isArray(data?.rows) ? data.rows : [];
|
|
952
|
+
const series = Array.isArray(element.series) ? element.series.map(record).filter((item) => item !== void 0) : [];
|
|
953
|
+
const validRows = rows.every((row) => Array.isArray(row) && row.length === cols.length);
|
|
954
|
+
const validSeries = series.length > 0 && series.every((item) => {
|
|
955
|
+
const encode = record(item.encode);
|
|
956
|
+
const refs = encode === void 0 ? [] : Object.values(encode).filter((value) => typeof value === "string");
|
|
957
|
+
return typeof item.type === "string" && refs.length >= 2 && refs.every((ref) => cols.includes(ref));
|
|
958
|
+
});
|
|
959
|
+
if (cols.length < 2 || rows.length === 0 || !validRows || !validSeries) issues.push({
|
|
960
|
+
code: "chart-data",
|
|
961
|
+
severity: "error",
|
|
962
|
+
file: page.file,
|
|
963
|
+
...context,
|
|
964
|
+
message: "图表 data/series/encode 结构不完整。"
|
|
965
|
+
});
|
|
966
|
+
const chartTypes = series.map((item) => string(item.type)).filter((item) => item !== void 0);
|
|
967
|
+
const unknownTypes = chartTypes.filter((item) => !NATIVE_CHART_TYPES.has(item) && !UNSUPPORTED_CHART_TYPES.has(item));
|
|
968
|
+
if (unknownTypes.length > 0) issues.push({
|
|
969
|
+
code: "chart-type",
|
|
970
|
+
severity: "error",
|
|
971
|
+
file: page.file,
|
|
972
|
+
...context,
|
|
973
|
+
message: `未知的图表类型:${[...new Set(unknownTypes)].join("、")}。`
|
|
974
|
+
});
|
|
975
|
+
const unsupportedTypes = chartTypes.filter((item) => UNSUPPORTED_CHART_TYPES.has(item));
|
|
976
|
+
if (unsupportedTypes.length > 0) issues.push(compatibilityIssue(page, context, "unsupported", `图表类型 ${[...new Set(unsupportedTypes)].join("、")}`));
|
|
977
|
+
const seriesDefaults = record(element.seriesDefaults) ?? {};
|
|
978
|
+
for (const [seriesIndex, item] of series.entries()) {
|
|
979
|
+
const chartType = string(item.type) ?? "bar";
|
|
980
|
+
const effective = {
|
|
981
|
+
...record(seriesDefaults[chartType]) ?? {},
|
|
982
|
+
...item
|
|
983
|
+
};
|
|
984
|
+
const paint = chartType === "line" || chartType === "area" || chartType === "radar" ? effective.lineColor ?? effective.areaColor : effective.fill;
|
|
985
|
+
if (paint === void 0) continue;
|
|
986
|
+
const multipleColors = Array.isArray(paint);
|
|
987
|
+
const validColors = resolvePptdChartSeriesColors(project, paint) !== void 0;
|
|
988
|
+
if (multipleColors && chartType !== "pie" || !validColors) issues.push({
|
|
989
|
+
code: "chart-series-color",
|
|
990
|
+
severity: "error",
|
|
991
|
+
file: page.file,
|
|
992
|
+
...context,
|
|
993
|
+
message: `图表序列 ${seriesIndex + 1} 的颜色需要使用 #RRGGBB、#RRGGBBAA、主题颜色引用或 solid 填充;饼图同时接受颜色数组。`
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (type === "icon") if (typeof element.iconName !== "string") issues.push({
|
|
998
|
+
code: "icon-name",
|
|
999
|
+
severity: "error",
|
|
1000
|
+
file: page.file,
|
|
1001
|
+
...context,
|
|
1002
|
+
message: "图标元素需要 iconName。"
|
|
1003
|
+
});
|
|
1004
|
+
else issues.push(compatibilityIssue(page, context, "normalized", "Font Awesome 图标"));
|
|
1005
|
+
return issues;
|
|
1006
|
+
}
|
|
1007
|
+
function textLayerIssues(project, page, pageNumber) {
|
|
1008
|
+
const issues = [];
|
|
1009
|
+
const overlap = (left, right) => {
|
|
1010
|
+
const rightEdge = Math.min((left[0] ?? 0) + (left[2] ?? 0), (right[0] ?? 0) + (right[2] ?? 0));
|
|
1011
|
+
const leftEdge = Math.max(left[0] ?? 0, right[0] ?? 0);
|
|
1012
|
+
const bottomEdge = Math.min((left[1] ?? 0) + (left[3] ?? 0), (right[1] ?? 0) + (right[3] ?? 0));
|
|
1013
|
+
const topEdge = Math.max(left[1] ?? 0, right[1] ?? 0);
|
|
1014
|
+
const width = Math.max(0, rightEdge - leftEdge);
|
|
1015
|
+
return {
|
|
1016
|
+
area: width * Math.max(0, bottomEdge - topEdge),
|
|
1017
|
+
width
|
|
1018
|
+
};
|
|
1019
|
+
};
|
|
1020
|
+
for (const [index, element] of page.elements.entries()) {
|
|
1021
|
+
if (element.elementType !== "text") continue;
|
|
1022
|
+
const textBounds = tuple(element.bounds, 4);
|
|
1023
|
+
if (textBounds === void 0 || (textBounds[2] ?? 0) <= 0 || (textBounds[3] ?? 0) <= 0) continue;
|
|
1024
|
+
const textArea = (textBounds[2] ?? 0) * (textBounds[3] ?? 0);
|
|
1025
|
+
const estimatedBottom = (textBounds[1] ?? 0) + (textLayout(project, element)?.requiredHeight ?? 0);
|
|
1026
|
+
for (const earlier of page.elements.slice(0, index)) {
|
|
1027
|
+
if (earlier.elementType !== "table" && earlier.elementType !== "chart") continue;
|
|
1028
|
+
const earlierBounds = tuple(earlier.bounds, 4);
|
|
1029
|
+
if (earlierBounds === void 0) continue;
|
|
1030
|
+
if (overlap(textBounds, earlierBounds).area / textArea >= .15) {
|
|
1031
|
+
issues.push({
|
|
1032
|
+
code: "text-occlusion",
|
|
1033
|
+
severity: "warning",
|
|
1034
|
+
file: page.file,
|
|
1035
|
+
page: pageNumber,
|
|
1036
|
+
...typeof element.elementId === "string" ? { elementId: element.elementId } : {},
|
|
1037
|
+
message: `文本与 ${string(earlier.elementId) ?? earlier.elementType} 的内容区域重叠。`
|
|
1038
|
+
});
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
for (const later of page.elements.slice(index + 1)) {
|
|
1043
|
+
if (later.elementType === "line" || number(later.opacity) === 0) continue;
|
|
1044
|
+
const laterBounds = tuple(later.bounds, 4);
|
|
1045
|
+
if (laterBounds === void 0) continue;
|
|
1046
|
+
const intersection = overlap(textBounds, laterBounds);
|
|
1047
|
+
if (intersection.area / textArea >= .15) {
|
|
1048
|
+
issues.push({
|
|
1049
|
+
code: "text-occlusion",
|
|
1050
|
+
severity: "warning",
|
|
1051
|
+
file: page.file,
|
|
1052
|
+
page: pageNumber,
|
|
1053
|
+
...typeof element.elementId === "string" ? { elementId: element.elementId } : {},
|
|
1054
|
+
message: `文本可能被后绘制元素 ${string(later.elementId) ?? later.elementType ?? "unknown"} 遮挡。`
|
|
1055
|
+
});
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
const laterTop = laterBounds[1] ?? 0;
|
|
1059
|
+
if (laterTop > (textBounds[1] ?? 0) && laterTop < (textBounds[1] ?? 0) + (textBounds[3] ?? 0) && estimatedBottom > laterTop && intersection.width / (textBounds[2] ?? 1) >= .3) {
|
|
1060
|
+
issues.push({
|
|
1061
|
+
code: "text-drift",
|
|
1062
|
+
severity: "warning",
|
|
1063
|
+
file: page.file,
|
|
1064
|
+
page: pageNumber,
|
|
1065
|
+
...typeof element.elementId === "string" ? { elementId: element.elementId } : {},
|
|
1066
|
+
message: `文本可能跨入下方元素 ${string(later.elementId) ?? later.elementType ?? "unknown"} 的边界。`
|
|
1067
|
+
});
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return issues;
|
|
1073
|
+
}
|
|
1074
|
+
function isCompositionBookend(page) {
|
|
1075
|
+
return /^(?:cover|title|opening|section|closing|final)$/iu.test(page.pageType?.trim() ?? "");
|
|
1076
|
+
}
|
|
1077
|
+
function visibleTextSizes(project, page) {
|
|
1078
|
+
return page.elements.flatMap((element) => {
|
|
1079
|
+
if (element.elementType !== "text") return [];
|
|
1080
|
+
const content = record(element.content);
|
|
1081
|
+
if (content === void 0 || typeof content.text !== "string" || plainText(content.text).trim() === "") return [];
|
|
1082
|
+
return [number(textStyle(project, content).fontSize) ?? 18];
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
function coverFocusIssues(project) {
|
|
1086
|
+
const canvasArea = project.width * project.height;
|
|
1087
|
+
return project.pages.flatMap((page, index) => {
|
|
1088
|
+
if (!/^(?:cover|title|opening)$/iu.test(page.pageType?.trim() ?? "")) return [];
|
|
1089
|
+
if (page.elements.reduce((largest, element) => {
|
|
1090
|
+
if (![
|
|
1091
|
+
"image",
|
|
1092
|
+
"chart",
|
|
1093
|
+
"table"
|
|
1094
|
+
].includes(string(element.elementType) ?? "")) return largest;
|
|
1095
|
+
const bounds = tuple(element.bounds, 4);
|
|
1096
|
+
if (bounds === void 0 || canvasArea <= 0) return largest;
|
|
1097
|
+
return Math.max(largest, (bounds[2] ?? 0) * (bounds[3] ?? 0) / canvasArea);
|
|
1098
|
+
}, 0) >= .25) return [];
|
|
1099
|
+
const sizes = visibleTextSizes(project, page).sort((left, right) => left - right);
|
|
1100
|
+
if (sizes.length < 3) return [];
|
|
1101
|
+
const middle = Math.floor(sizes.length / 2);
|
|
1102
|
+
const median = sizes.length % 2 === 1 ? sizes[middle] ?? 0 : ((sizes[middle - 1] ?? 0) + (sizes[middle] ?? 0)) / 2;
|
|
1103
|
+
const largest = sizes.at(-1) ?? 0;
|
|
1104
|
+
if (median > 0 && largest / median >= 3) return [];
|
|
1105
|
+
return [{
|
|
1106
|
+
code: "cover-focus",
|
|
1107
|
+
severity: "warning",
|
|
1108
|
+
file: page.file,
|
|
1109
|
+
page: index + 1,
|
|
1110
|
+
message: "封面需要更明确的首读焦点:让一张图片、图表或表格占据至少 25% 画布,或让最大标题达到可见文字中位字号的 3 倍。"
|
|
1111
|
+
}];
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
function compositionFamily(element) {
|
|
1115
|
+
const type = string(element.elementType);
|
|
1116
|
+
if (type === "text" || type === "icon") return "text";
|
|
1117
|
+
if (type === "image" || type === "chart" || type === "table") return "visual";
|
|
1118
|
+
if (type === "shape") return "shape";
|
|
1119
|
+
}
|
|
1120
|
+
function layoutSignature(project, page) {
|
|
1121
|
+
const canvasArea = project.width * project.height;
|
|
1122
|
+
const landmarks = /* @__PURE__ */ new Set();
|
|
1123
|
+
for (const element of page.elements) {
|
|
1124
|
+
const family = compositionFamily(element);
|
|
1125
|
+
const bounds = tuple(element.bounds, 4);
|
|
1126
|
+
if (family === void 0 || bounds === void 0 || canvasArea <= 0) continue;
|
|
1127
|
+
const [x = 0, y = 0, width = 0, height = 0] = bounds;
|
|
1128
|
+
if (width <= 0 || height <= 0) continue;
|
|
1129
|
+
const areaShare = width * height / canvasArea;
|
|
1130
|
+
if (family === "shape" && (areaShare < .01 || areaShare > .85)) continue;
|
|
1131
|
+
const column = Math.max(0, Math.min(3, Math.floor((x + width / 2) / project.width * 4)));
|
|
1132
|
+
const row = Math.max(0, Math.min(3, Math.floor((y + height / 2) / project.height * 4)));
|
|
1133
|
+
const scale = areaShare >= .25 ? "large" : areaShare >= .08 ? "medium" : "small";
|
|
1134
|
+
landmarks.add(`${family}:${column}:${row}:${scale}`);
|
|
1135
|
+
}
|
|
1136
|
+
return [...landmarks].sort().join("|");
|
|
1137
|
+
}
|
|
1138
|
+
function layoutRepetitionIssues(project) {
|
|
1139
|
+
const issues = [];
|
|
1140
|
+
let runStart = 0;
|
|
1141
|
+
let runSignature;
|
|
1142
|
+
const finishRun = (end) => {
|
|
1143
|
+
if (runSignature === void 0 || end - runStart < 3) return;
|
|
1144
|
+
const first = project.pages[runStart];
|
|
1145
|
+
if (first === void 0) return;
|
|
1146
|
+
issues.push({
|
|
1147
|
+
code: "layout-repetition",
|
|
1148
|
+
severity: "warning",
|
|
1149
|
+
file: first.file,
|
|
1150
|
+
page: runStart + 1,
|
|
1151
|
+
message: `第 ${runStart + 1}–${end} 页连续使用相同构图轮廓。调整主视觉位置、尺度或内容分组,并重新预览这组页面;固定指标序列可以保留重复。`
|
|
1152
|
+
});
|
|
1153
|
+
};
|
|
1154
|
+
for (const [index, page] of project.pages.entries()) {
|
|
1155
|
+
const signature = isCompositionBookend(page) ? "" : layoutSignature(project, page);
|
|
1156
|
+
if (signature === "") {
|
|
1157
|
+
finishRun(index);
|
|
1158
|
+
runStart = index + 1;
|
|
1159
|
+
runSignature = void 0;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
if (signature === runSignature) continue;
|
|
1163
|
+
finishRun(index);
|
|
1164
|
+
runStart = index;
|
|
1165
|
+
runSignature = signature;
|
|
1166
|
+
}
|
|
1167
|
+
finishRun(project.pages.length);
|
|
1168
|
+
return issues;
|
|
1169
|
+
}
|
|
1170
|
+
function compositionIssues(project) {
|
|
1171
|
+
return [...coverFocusIssues(project), ...layoutRepetitionIssues(project)];
|
|
1172
|
+
}
|
|
1173
|
+
/** Check structure, renderability, and measurable composition signals without external effects. */
|
|
1174
|
+
function checkPptdProject(project) {
|
|
1175
|
+
const issues = [...project.parseIssues];
|
|
1176
|
+
issues.push(...colorThemeIssues(project, project.theme, project.source.entryName));
|
|
1177
|
+
for (const [key, value] of Object.entries(themeMap(project, "colors"))) {
|
|
1178
|
+
const referenceIssue = themeReferenceIssue(project, value, "colors", project.source.entryName);
|
|
1179
|
+
if (referenceIssue !== void 0) issues.push(referenceIssue);
|
|
1180
|
+
if (typeof value !== "string" || !value.startsWith("$") && !/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/iu.test(value)) issues.push({
|
|
1181
|
+
code: "invalid-theme",
|
|
1182
|
+
severity: "error",
|
|
1183
|
+
file: project.source.entryName,
|
|
1184
|
+
message: `theme.colors.${key} 必须是颜色或颜色主题引用。`
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
issues.push(...themeColorChainIssues(project));
|
|
1188
|
+
for (const [index, page] of project.pages.entries()) {
|
|
1189
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1190
|
+
issues.push(...colorThemeIssues(project, page.background, page.file, index + 1));
|
|
1191
|
+
for (const element of page.elements) issues.push(...checkElement(project, page, index + 1, element, ids));
|
|
1192
|
+
issues.push(...textLayerIssues(project, page, index + 1));
|
|
1193
|
+
}
|
|
1194
|
+
issues.push(...compositionIssues(project));
|
|
1195
|
+
const errorCount = issues.filter((item) => item.severity === "error").length;
|
|
1196
|
+
const warningCount = issues.filter((item) => item.severity === "warning").length;
|
|
1197
|
+
return {
|
|
1198
|
+
status: errorCount > 0 ? "fail" : warningCount > 0 ? "warning" : "pass",
|
|
1199
|
+
digest: projectDigest(project),
|
|
1200
|
+
pageCount: project.pages.length,
|
|
1201
|
+
nativeObjectCount: project.pages.reduce((sum, page) => sum + page.elements.length, 0),
|
|
1202
|
+
warningCount,
|
|
1203
|
+
errorCount,
|
|
1204
|
+
issues,
|
|
1205
|
+
compatibility: compatibilitySummary(project)
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function inches(value) {
|
|
1209
|
+
return value / POINTS_PER_INCH;
|
|
1210
|
+
}
|
|
1211
|
+
function frame(element) {
|
|
1212
|
+
const [x = 0, y = 0, width = 0, height = 0] = tuple(element.bounds, 4) ?? [
|
|
1213
|
+
0,
|
|
1214
|
+
0,
|
|
1215
|
+
0,
|
|
1216
|
+
0
|
|
1217
|
+
];
|
|
1218
|
+
return {
|
|
1219
|
+
x: inches(x),
|
|
1220
|
+
y: inches(y),
|
|
1221
|
+
w: inches(width),
|
|
1222
|
+
h: inches(height)
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
function fontFace(value, fallback = "Arial", text = "") {
|
|
1226
|
+
if (typeof value === "string") return value;
|
|
1227
|
+
const font = record(value);
|
|
1228
|
+
const eastAsian = /[\u2e80-\u9fff\uf900-\ufaff]/u.test(text);
|
|
1229
|
+
const ea = process.platform === "darwin" ? font?.mac ?? font?.ea : process.platform === "win32" ? font?.win ?? font?.ea : font?.ea;
|
|
1230
|
+
return (eastAsian ? string(ea) ?? string(font?.latin) : string(font?.latin) ?? string(ea)) ?? fallback;
|
|
1231
|
+
}
|
|
1232
|
+
function inlineStyle(project, raw) {
|
|
1233
|
+
const options = {};
|
|
1234
|
+
for (const declaration of raw?.split(";") ?? []) {
|
|
1235
|
+
const [name, ...rest] = declaration.split(":");
|
|
1236
|
+
const value = rest.join(":").trim();
|
|
1237
|
+
if (name?.trim() === "color") options.color = colorOptions(project, value).color;
|
|
1238
|
+
if (name?.trim() === "font-size" && /^\d+(?:\.\d+)?px$/u.test(value)) options.fontSize = Number.parseFloat(value);
|
|
1239
|
+
if (name?.trim() === "font-family" && value !== "") {
|
|
1240
|
+
const face = value.replace(/^['"]|['"]$/gu, "").split(",")[0]?.trim();
|
|
1241
|
+
if (face !== void 0) options.fontFace = face;
|
|
1242
|
+
}
|
|
1243
|
+
if (name?.trim() === "background-color") options.highlight = colorOptions(project, value).color;
|
|
1244
|
+
if (name?.trim() === "font-weight" && (value === "bold" || Number(value) >= 600)) options.bold = true;
|
|
1245
|
+
if (name?.trim() === "font-style" && value === "italic") options.italic = true;
|
|
1246
|
+
}
|
|
1247
|
+
return options;
|
|
1248
|
+
}
|
|
1249
|
+
function richRuns(project, value) {
|
|
1250
|
+
const runs = [];
|
|
1251
|
+
const stack = [{
|
|
1252
|
+
tag: "root",
|
|
1253
|
+
options: {}
|
|
1254
|
+
}];
|
|
1255
|
+
const currentOptions = () => stack.at(-1)?.options ?? {};
|
|
1256
|
+
for (const token of value.split(/(<[^>]+>)/gu)) {
|
|
1257
|
+
if (token === "") continue;
|
|
1258
|
+
if (!token.startsWith("<")) {
|
|
1259
|
+
const decoded = plainText(token);
|
|
1260
|
+
if (decoded !== "") runs.push({
|
|
1261
|
+
text: decoded,
|
|
1262
|
+
options: { ...currentOptions() }
|
|
1263
|
+
});
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
const closing = /^<\/([a-z0-9]+)/iu.exec(token);
|
|
1267
|
+
if (closing !== null) {
|
|
1268
|
+
const tag = closing[1]?.toLowerCase();
|
|
1269
|
+
if (tag === void 0) continue;
|
|
1270
|
+
if (tag === "p" || tag === "li") runs.push({
|
|
1271
|
+
text: "\n",
|
|
1272
|
+
options: { ...currentOptions() }
|
|
1273
|
+
});
|
|
1274
|
+
while (stack.length > 1) if (stack.pop()?.tag === tag) break;
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
const opening = /^<([a-z0-9]+)/iu.exec(token);
|
|
1278
|
+
if (opening === null) continue;
|
|
1279
|
+
const tag = opening[1]?.toLowerCase();
|
|
1280
|
+
if (tag === void 0) continue;
|
|
1281
|
+
if (tag === "br") {
|
|
1282
|
+
runs.push({
|
|
1283
|
+
text: "\n",
|
|
1284
|
+
options: { ...currentOptions() }
|
|
1285
|
+
});
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
if (tag === "li") runs.push({
|
|
1289
|
+
text: "• ",
|
|
1290
|
+
options: { ...currentOptions() }
|
|
1291
|
+
});
|
|
1292
|
+
const style = /\sstyle=(?:"([^"]*)"|'([^']*)')/iu.exec(token);
|
|
1293
|
+
const options = {
|
|
1294
|
+
...currentOptions(),
|
|
1295
|
+
...inlineStyle(project, style?.[1] ?? style?.[2])
|
|
1296
|
+
};
|
|
1297
|
+
if (tag === "strong") options.bold = true;
|
|
1298
|
+
if (tag === "em") options.italic = true;
|
|
1299
|
+
if (tag === "u") options.underline = { style: "sng" };
|
|
1300
|
+
if (tag === "s") options.strike = "sngStrike";
|
|
1301
|
+
if (tag === "sup") options.superscript = true;
|
|
1302
|
+
if (tag === "sub") options.subscript = true;
|
|
1303
|
+
if (tag === "a") {
|
|
1304
|
+
const href = /\shref=(?:"([^"]*)"|'([^']*)')/iu.exec(token)?.slice(1).find((item) => item !== void 0);
|
|
1305
|
+
if (href !== void 0 && /^(?:https?:|mailto:)/iu.test(href)) options.hyperlink = { url: href };
|
|
1306
|
+
}
|
|
1307
|
+
stack.push({
|
|
1308
|
+
tag,
|
|
1309
|
+
options
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
while (runs.at(-1)?.text === "\n") runs.pop();
|
|
1313
|
+
return runs;
|
|
1314
|
+
}
|
|
1315
|
+
function flipOptions(element) {
|
|
1316
|
+
const flip = Array.isArray(element.flip) ? element.flip : [];
|
|
1317
|
+
return {
|
|
1318
|
+
...typeof flip[0] === "boolean" ? { flipH: flip[0] } : {},
|
|
1319
|
+
...typeof flip[1] === "boolean" ? { flipV: flip[1] } : {}
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
function shadowOptions(project, value) {
|
|
1323
|
+
const shadow = record(value);
|
|
1324
|
+
if (shadow === void 0 || number(shadow.blur) === void 0 || typeof shadow.color !== "string") return void 0;
|
|
1325
|
+
const resolved = resolveColorValue(project, shadow.color);
|
|
1326
|
+
const alpha = resolved.length === 9 ? Number.parseInt(resolved.slice(7, 9), 16) / 255 : 1;
|
|
1327
|
+
const [offsetX = 0, offsetY = 0] = tuple(shadow.offset, 2) ?? [0, 0];
|
|
1328
|
+
const angle = (Math.atan2(-offsetY, offsetX) * 180 / Math.PI + 360) % 360;
|
|
1329
|
+
return {
|
|
1330
|
+
type: "outer",
|
|
1331
|
+
color: resolved.slice(1, 7).toUpperCase(),
|
|
1332
|
+
opacity: alpha,
|
|
1333
|
+
blur: number(shadow.blur) ?? 0,
|
|
1334
|
+
offset: Math.hypot(offsetX, offsetY),
|
|
1335
|
+
angle
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
function dash(value) {
|
|
1339
|
+
return value === "dash" || value === "dot" ? value : "solid";
|
|
1340
|
+
}
|
|
1341
|
+
function border(project, value) {
|
|
1342
|
+
const config = record(value);
|
|
1343
|
+
if (config === void 0) return void 0;
|
|
1344
|
+
const style = dash(config.style);
|
|
1345
|
+
return {
|
|
1346
|
+
color: colorOptions(project, config.color).color,
|
|
1347
|
+
width: number(config.width) ?? 1,
|
|
1348
|
+
...style === "solid" ? {} : { dash: style }
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
function horizontalAlign(value, fallback) {
|
|
1352
|
+
return value === "center" || value === "right" || value === "justify" ? value : fallback;
|
|
1353
|
+
}
|
|
1354
|
+
function verticalAlign(value, fallback) {
|
|
1355
|
+
return value === "middle" || value === "bottom" ? value : fallback;
|
|
1356
|
+
}
|
|
1357
|
+
function renderText(project, slide, element) {
|
|
1358
|
+
const content = record(element.content) ?? {};
|
|
1359
|
+
const style = textStyle(project, content);
|
|
1360
|
+
const align = Array.isArray(style.align) ? style.align : [];
|
|
1361
|
+
const raw = string(content.text) ?? "";
|
|
1362
|
+
const runs = /<[^>]+>/u.test(raw) ? richRuns(project, raw) : raw;
|
|
1363
|
+
const textColor = colorOptions(project, style.color).color;
|
|
1364
|
+
const objectName = string(element.elementId);
|
|
1365
|
+
const charSpacing = number(style.letterSpacing);
|
|
1366
|
+
const horizontal = horizontalAlign(align[0], "left");
|
|
1367
|
+
const vertical = verticalAlign(align[1], "top");
|
|
1368
|
+
const textShadow = shadowOptions(project, style.shadow);
|
|
1369
|
+
slide.addText(runs, {
|
|
1370
|
+
...frame(element),
|
|
1371
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1372
|
+
fontFace: fontFace(style.fontFamily, "Arial", raw),
|
|
1373
|
+
fontSize: number(style.fontSize) ?? 18,
|
|
1374
|
+
color: textColor,
|
|
1375
|
+
bold: boolean(style.bold) ?? false,
|
|
1376
|
+
italic: boolean(style.italic) ?? false,
|
|
1377
|
+
align: horizontal,
|
|
1378
|
+
valign: vertical,
|
|
1379
|
+
margin: 0,
|
|
1380
|
+
breakLine: false,
|
|
1381
|
+
wrap: boolean(style.wrap) ?? true,
|
|
1382
|
+
textDirection: style.textDirection === "vertical" ? "vert" : "horz",
|
|
1383
|
+
rotate: number(element.rotation) ?? 0,
|
|
1384
|
+
...flipOptions(element),
|
|
1385
|
+
transparency: Math.round((1 - (number(element.opacity) ?? 1)) * 100),
|
|
1386
|
+
...textShadow === void 0 ? {} : { shadow: textShadow },
|
|
1387
|
+
...charSpacing === void 0 ? {} : { charSpacing }
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
function solidFill(project, value, opacity = 1) {
|
|
1391
|
+
const fill = record(value);
|
|
1392
|
+
if (fill === void 0) return void 0;
|
|
1393
|
+
if (fill.type === "solid") return colorOptions(project, fill.color, opacity);
|
|
1394
|
+
if (fill.type === "gradient" && Array.isArray(fill.stops)) {
|
|
1395
|
+
const first = record(fill.stops[0]);
|
|
1396
|
+
return first === void 0 ? void 0 : colorOptions(project, first.color, opacity);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
function shapeType(pptx, value) {
|
|
1400
|
+
const name = typeof value === "string" ? value : "rect";
|
|
1401
|
+
const shape = pptx.ShapeType[name];
|
|
1402
|
+
if (shape === void 0) throw new Error(`Unsupported PPTD preset shape: ${name}`);
|
|
1403
|
+
return shape;
|
|
1404
|
+
}
|
|
1405
|
+
function xmlEscape(value) {
|
|
1406
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
1407
|
+
}
|
|
1408
|
+
function svgPaint(project, fillValue, opacity) {
|
|
1409
|
+
const fill = record(fillValue);
|
|
1410
|
+
if (fill?.type === "gradient" && Array.isArray(fill.stops) && fill.stops.length >= 2) {
|
|
1411
|
+
const id = "pptd-gradient";
|
|
1412
|
+
const stops = fill.stops.map((rawStop) => {
|
|
1413
|
+
const stop = record(rawStop) ?? {};
|
|
1414
|
+
const color = colorOptions(project, stop.color, opacity);
|
|
1415
|
+
return `<stop offset="${Math.round(Math.max(0, Math.min(1, number(stop.position) ?? 0)) * 100)}%" stop-color="#${color.color}"${color.transparency === void 0 ? "" : ` stop-opacity="${1 - color.transparency / 100}"`}/>`;
|
|
1416
|
+
}).join("");
|
|
1417
|
+
if (fill.gradientType === "radial") return {
|
|
1418
|
+
paint: `url(#${id})`,
|
|
1419
|
+
definition: `<radialGradient id="${id}">${stops}</radialGradient>`
|
|
1420
|
+
};
|
|
1421
|
+
const angle = number(fill.angle) ?? 0;
|
|
1422
|
+
return {
|
|
1423
|
+
paint: `url(#${id})`,
|
|
1424
|
+
definition: `<linearGradient id="${id}" x1="0" y1="0.5" x2="1" y2="0.5" gradientTransform="rotate(${angle} 0.5 0.5)">${stops}</linearGradient>`
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
return {
|
|
1428
|
+
paint: `#${colorOptions(project, fill?.type === "solid" ? fill.color : "#000000", opacity).color}`,
|
|
1429
|
+
definition: ""
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
function svgData(svg) {
|
|
1433
|
+
return `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
|
|
1434
|
+
}
|
|
1435
|
+
function renderCustomShape(project, slide, element) {
|
|
1436
|
+
const viewBox = tuple(element.viewBox, 2) ?? [1, 1];
|
|
1437
|
+
const pathData = string(element.path) ?? "";
|
|
1438
|
+
const opacity = number(element.opacity) ?? 1;
|
|
1439
|
+
const fill = svgPaint(project, element.fill, opacity);
|
|
1440
|
+
const line = border(project, element.border);
|
|
1441
|
+
const shadow = shadowOptions(project, element.shadow);
|
|
1442
|
+
const shadowAngle = shadow?.angle ?? 0;
|
|
1443
|
+
const shadowOffset = shadow?.offset ?? 0;
|
|
1444
|
+
const shadowBlur = shadow?.blur ?? 0;
|
|
1445
|
+
const objectName = string(element.elementId);
|
|
1446
|
+
const [width = 1, height = 1] = viewBox;
|
|
1447
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"><defs>${[fill.definition, shadow === void 0 ? "" : [
|
|
1448
|
+
"<filter id=\"pptd-shadow\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">",
|
|
1449
|
+
`<feDropShadow dx="${Math.cos(shadowAngle * Math.PI / 180) * shadowOffset}" dy="${-Math.sin(shadowAngle * Math.PI / 180) * shadowOffset}" stdDeviation="${shadowBlur / 2}" flood-color="#${shadow?.color ?? "000000"}" flood-opacity="${shadow?.opacity ?? 1}"/>`,
|
|
1450
|
+
"</filter>"
|
|
1451
|
+
].join("")].join("")}</defs><path d="${xmlEscape(pathData)}" fill="${fill.paint}" fill-rule="evenodd"${line === void 0 ? " stroke=\"none\"" : ` stroke="#${line.color}" stroke-width="${line.width}"${line.dash === void 0 ? "" : ` stroke-dasharray="${line.dash === "dot" ? "1 2" : "4 3"}"`}`}${shadow === void 0 ? "" : " filter=\"url(#pptd-shadow)\""}/></svg>`;
|
|
1452
|
+
slide.addImage({
|
|
1453
|
+
...frame(element),
|
|
1454
|
+
data: svgData(svg),
|
|
1455
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1456
|
+
rotate: number(element.rotation) ?? 0,
|
|
1457
|
+
...flipOptions(element)
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
function renderShape(project, pptx, slide, element) {
|
|
1461
|
+
if (element.shapeName === "custom") {
|
|
1462
|
+
renderCustomShape(project, slide, element);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
const opacity = number(element.opacity) ?? 1;
|
|
1466
|
+
const fill = solidFill(project, element.fill, opacity);
|
|
1467
|
+
const line = border(project, element.border);
|
|
1468
|
+
const objectName = string(element.elementId);
|
|
1469
|
+
const shapeShadow = shadowOptions(project, element.shadow);
|
|
1470
|
+
slide.addShape(shapeType(pptx, element.shapeName), {
|
|
1471
|
+
...frame(element),
|
|
1472
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1473
|
+
rotate: number(element.rotation) ?? 0,
|
|
1474
|
+
...flipOptions(element),
|
|
1475
|
+
...fill === void 0 ? { fill: {
|
|
1476
|
+
color: "FFFFFF",
|
|
1477
|
+
transparency: 100
|
|
1478
|
+
} } : { fill },
|
|
1479
|
+
line: line === void 0 ? {
|
|
1480
|
+
color: "FFFFFF",
|
|
1481
|
+
transparency: 100
|
|
1482
|
+
} : line,
|
|
1483
|
+
...shapeShadow === void 0 ? {} : { shadow: shapeShadow }
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
function renderCurvedLine(project, slide, element, points) {
|
|
1487
|
+
const [width = 1, height = 1] = tuple(element.viewBox, 2) ?? [1, 1];
|
|
1488
|
+
const [first = [0, 0], ...rest] = points;
|
|
1489
|
+
const [startX = 0, startY = 0] = first;
|
|
1490
|
+
const pathData = rest.length === 3 ? `M ${startX} ${startY} C ${rest.map((point) => point.join(" ")).join(" ")}` : `M ${startX} ${startY} ${rest.map((point) => `L ${point[0] ?? 0} ${point[1] ?? 0}`).join(" ")}`;
|
|
1491
|
+
const line = border(project, element.border) ?? {
|
|
1492
|
+
color: "000000",
|
|
1493
|
+
width: 1
|
|
1494
|
+
};
|
|
1495
|
+
const arrow = Array.isArray(element.arrow) ? element.arrow : [];
|
|
1496
|
+
const marker = (id, kind) => kind === null || kind === void 0 ? "" : [
|
|
1497
|
+
`<marker id="${id}" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto" markerUnits="strokeWidth">`,
|
|
1498
|
+
kind === "oval" ? "<circle cx=\"4\" cy=\"4\" r=\"3\" fill=\"context-stroke\"/>" : kind === "diamond" ? "<path d=\"M0,4 L4,0 L8,4 L4,8 Z\" fill=\"context-stroke\"/>" : "<path d=\"M0,0 L8,4 L0,8 Z\" fill=\"context-stroke\"/>",
|
|
1499
|
+
"</marker>"
|
|
1500
|
+
].join("");
|
|
1501
|
+
const dash = line.dash === void 0 ? "" : ` stroke-dasharray="${line.dash === "dot" ? "1 2" : "4 3"}"`;
|
|
1502
|
+
const markerStart = arrow[0] === void 0 || arrow[0] === null ? "" : " marker-start=\"url(#start)\"";
|
|
1503
|
+
const markerEnd = arrow[1] === void 0 || arrow[1] === null ? "" : " marker-end=\"url(#end)\"";
|
|
1504
|
+
const svg = [
|
|
1505
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}">`,
|
|
1506
|
+
`<defs>${marker("start", arrow[0])}${marker("end", arrow[1])}</defs>`,
|
|
1507
|
+
`<path d="${pathData}" fill="none" stroke="#${line.color}" stroke-width="${line.width}"`,
|
|
1508
|
+
`${dash}${markerStart}${markerEnd}/></svg>`
|
|
1509
|
+
].join("");
|
|
1510
|
+
const objectName = string(element.elementId);
|
|
1511
|
+
slide.addImage({
|
|
1512
|
+
...frame(element),
|
|
1513
|
+
data: svgData(svg),
|
|
1514
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1515
|
+
rotate: number(element.rotation) ?? 0,
|
|
1516
|
+
...flipOptions(element)
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
function renderLine(project, pptx, slide, element) {
|
|
1520
|
+
const points = (string(element.points) ?? "").trim().split(/\s+/u).map((value) => value.split(",").map(Number));
|
|
1521
|
+
if (points.length > 2 || element.curve === "smooth") {
|
|
1522
|
+
renderCurvedLine(project, slide, element, points);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const viewBox = tuple(element.viewBox, 2) ?? [1, 1];
|
|
1526
|
+
const bounds = frame(element);
|
|
1527
|
+
const first = points[0] ?? [0, 0];
|
|
1528
|
+
const last = points.at(-1) ?? [viewBox[0] ?? 1, viewBox[1] ?? 1];
|
|
1529
|
+
const x1 = bounds.x + (first[0] ?? 0) / (viewBox[0] ?? 1) * bounds.w;
|
|
1530
|
+
const y1 = bounds.y + (first[1] ?? 0) / (viewBox[1] ?? 1) * bounds.h;
|
|
1531
|
+
const x2 = bounds.x + (last[0] ?? 0) / (viewBox[0] ?? 1) * bounds.w;
|
|
1532
|
+
const y2 = bounds.y + (last[1] ?? 0) / (viewBox[1] ?? 1) * bounds.h;
|
|
1533
|
+
const line = border(project, element.border) ?? {
|
|
1534
|
+
color: "000000",
|
|
1535
|
+
width: 1
|
|
1536
|
+
};
|
|
1537
|
+
const arrow = Array.isArray(element.arrow) ? element.arrow : [];
|
|
1538
|
+
const objectName = string(element.elementId);
|
|
1539
|
+
const lineShadow = shadowOptions(project, element.shadow);
|
|
1540
|
+
slide.addShape(pptx.ShapeType.line, {
|
|
1541
|
+
x: Math.min(x1, x2),
|
|
1542
|
+
y: Math.min(y1, y2),
|
|
1543
|
+
w: Math.abs(x2 - x1),
|
|
1544
|
+
h: Math.abs(y2 - y1),
|
|
1545
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1546
|
+
flipH: x2 < x1,
|
|
1547
|
+
flipV: y2 < y1,
|
|
1548
|
+
rotate: number(element.rotation) ?? 0,
|
|
1549
|
+
line: {
|
|
1550
|
+
...line,
|
|
1551
|
+
...arrow[0] === void 0 || arrow[0] === null ? {} : { beginArrowType: arrow[0] },
|
|
1552
|
+
...arrow[1] === void 0 || arrow[1] === null ? {} : { endArrowType: arrow[1] }
|
|
1553
|
+
},
|
|
1554
|
+
...lineShadow === void 0 ? {} : { shadow: lineShadow }
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
function renderImage(project, slide, element) {
|
|
1558
|
+
const assetPath = localAssetPath(element.src);
|
|
1559
|
+
const asset = assetPath === void 0 ? void 0 : project.source.assets.get(assetPath);
|
|
1560
|
+
if (asset === void 0) throw new Error(`PPTD image ${String(element.src)} is unavailable`);
|
|
1561
|
+
const objectName = string(element.elementId);
|
|
1562
|
+
const mode = string(record(element.fit)?.mode) ?? "cover";
|
|
1563
|
+
const bounds = frame(element);
|
|
1564
|
+
const cropShape = record(element.cropShape);
|
|
1565
|
+
const imageShadow = shadowOptions(project, element.shadow);
|
|
1566
|
+
slide.addImage({
|
|
1567
|
+
...bounds,
|
|
1568
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1569
|
+
data: `data:${asset.mediaType};base64,${Buffer.from(asset.bytes).toString("base64")}`,
|
|
1570
|
+
rotate: number(element.rotation) ?? 0,
|
|
1571
|
+
...flipOptions(element),
|
|
1572
|
+
transparency: Math.round((1 - (number(element.opacity) ?? 1)) * 100),
|
|
1573
|
+
...mode === "fill" ? {} : { sizing: {
|
|
1574
|
+
type: mode,
|
|
1575
|
+
w: bounds.w,
|
|
1576
|
+
h: bounds.h
|
|
1577
|
+
} },
|
|
1578
|
+
...cropShape?.shapeName === "ellipse" ? { rounding: true } : {},
|
|
1579
|
+
...imageShadow === void 0 ? {} : { shadow: imageShadow }
|
|
1580
|
+
});
|
|
1581
|
+
const imageBorder = border(project, element.border);
|
|
1582
|
+
if (imageBorder !== void 0) slide.addShape(cropShape?.shapeName === "ellipse" ? "ellipse" : "rect", {
|
|
1583
|
+
...bounds,
|
|
1584
|
+
objectName: `${objectName ?? "image"}-border`,
|
|
1585
|
+
fill: {
|
|
1586
|
+
color: "FFFFFF",
|
|
1587
|
+
transparency: 100
|
|
1588
|
+
},
|
|
1589
|
+
line: imageBorder,
|
|
1590
|
+
rotate: number(element.rotation) ?? 0,
|
|
1591
|
+
...flipOptions(element)
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
function mergeCellStyle(project, table, row, column, rowCount, columnCount, cell) {
|
|
1595
|
+
const tableTheme = record(resolveThemeReference(table.style, themeMap(project, "tableStyles"))) ?? {};
|
|
1596
|
+
const baseline = record(tableTheme.cellStyle) ?? {};
|
|
1597
|
+
const bodyStyles = Array.isArray(tableTheme.bodyStyles) ? tableTheme.bodyStyles.map(record).filter((item) => item !== void 0) : [];
|
|
1598
|
+
const body = row > 0 && row < rowCount - 1 && bodyStyles.length > 0 ? bodyStyles[(row - 1) % bodyStyles.length] ?? {} : {};
|
|
1599
|
+
const rowStyle = row === 0 ? record(tableTheme.firstRowStyle) ?? {} : row === rowCount - 1 ? record(tableTheme.lastRowStyle) ?? {} : body;
|
|
1600
|
+
const columnStyle = column === 0 ? record(tableTheme.firstColumnStyle) ?? {} : column === columnCount - 1 ? record(tableTheme.lastColumnStyle) ?? {} : {};
|
|
1601
|
+
return boolean(tableTheme.rowOverColumn) === false ? {
|
|
1602
|
+
...baseline,
|
|
1603
|
+
...rowStyle,
|
|
1604
|
+
...columnStyle,
|
|
1605
|
+
...cell
|
|
1606
|
+
} : {
|
|
1607
|
+
...baseline,
|
|
1608
|
+
...columnStyle,
|
|
1609
|
+
...rowStyle,
|
|
1610
|
+
...cell
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
function tableBorder(project, value) {
|
|
1614
|
+
const none = {
|
|
1615
|
+
color: "FFFFFF",
|
|
1616
|
+
pt: 0
|
|
1617
|
+
};
|
|
1618
|
+
const one = (item) => {
|
|
1619
|
+
const parsed = border(project, item);
|
|
1620
|
+
return parsed === void 0 ? none : {
|
|
1621
|
+
color: parsed.color,
|
|
1622
|
+
pt: parsed.width,
|
|
1623
|
+
...parsed.dash === void 0 ? {} : { dash: parsed.dash }
|
|
1624
|
+
};
|
|
1625
|
+
};
|
|
1626
|
+
if (value === null) return [
|
|
1627
|
+
none,
|
|
1628
|
+
none,
|
|
1629
|
+
none,
|
|
1630
|
+
none
|
|
1631
|
+
];
|
|
1632
|
+
if (Array.isArray(value)) {
|
|
1633
|
+
if (value.length === 2) return [
|
|
1634
|
+
one(value[0]),
|
|
1635
|
+
one(value[1]),
|
|
1636
|
+
one(value[0]),
|
|
1637
|
+
one(value[1])
|
|
1638
|
+
];
|
|
1639
|
+
if (value.length === 4) return [
|
|
1640
|
+
one(value[0]),
|
|
1641
|
+
one(value[1]),
|
|
1642
|
+
one(value[2]),
|
|
1643
|
+
one(value[3])
|
|
1644
|
+
];
|
|
1645
|
+
}
|
|
1646
|
+
return record(value) === void 0 ? void 0 : one(value);
|
|
1647
|
+
}
|
|
1648
|
+
function renderTable(project, slide, element) {
|
|
1649
|
+
const rows = element.rows;
|
|
1650
|
+
const rowCount = rows.length;
|
|
1651
|
+
const columnCount = Array.isArray(element.columnWidths) ? element.columnWidths.length : rows[0]?.length ?? 0;
|
|
1652
|
+
const tableRows = rows.map((row, rowIndex) => row.map((rawCell, columnIndex) => {
|
|
1653
|
+
const cell = record(rawCell) ?? { text: typeof rawCell === "string" || typeof rawCell === "number" ? String(rawCell) : "" };
|
|
1654
|
+
const style = {
|
|
1655
|
+
...record(resolveThemeReference(cell.textStyle, themeMap(project, "textStyles"))) ?? {},
|
|
1656
|
+
...mergeCellStyle(project, element, rowIndex, columnIndex, rowCount, columnCount, cell)
|
|
1657
|
+
};
|
|
1658
|
+
const align = Array.isArray(style.align) ? style.align : [];
|
|
1659
|
+
const horizontal = horizontalAlign(align[0], "center");
|
|
1660
|
+
const vertical = verticalAlign(align[1], "middle");
|
|
1661
|
+
const fill = solidFill(project, style.fill);
|
|
1662
|
+
const cellBorder = tableBorder(project, style.border);
|
|
1663
|
+
const rowSpan = number(cell.rowSpan);
|
|
1664
|
+
const colSpan = number(cell.colSpan);
|
|
1665
|
+
const options = {
|
|
1666
|
+
fontFace: fontFace(style.fontFamily, "Arial", string(cell.text) ?? ""),
|
|
1667
|
+
fontSize: number(style.fontSize) ?? 10,
|
|
1668
|
+
color: colorOptions(project, style.color).color,
|
|
1669
|
+
bold: boolean(style.bold) ?? false,
|
|
1670
|
+
italic: boolean(style.italic) ?? false,
|
|
1671
|
+
align: horizontal,
|
|
1672
|
+
valign: vertical,
|
|
1673
|
+
margin: .03,
|
|
1674
|
+
...rowSpan === void 0 ? {} : { rowspan: rowSpan },
|
|
1675
|
+
...colSpan === void 0 ? {} : { colspan: colSpan },
|
|
1676
|
+
...fill === void 0 ? {} : { fill },
|
|
1677
|
+
...cellBorder === void 0 ? {} : { border: cellBorder }
|
|
1678
|
+
};
|
|
1679
|
+
return {
|
|
1680
|
+
text: plainText(string(cell.text) ?? ""),
|
|
1681
|
+
options
|
|
1682
|
+
};
|
|
1683
|
+
}));
|
|
1684
|
+
const bounds = frame(element);
|
|
1685
|
+
const colRatios = Array.isArray(element.columnWidths) ? element.columnWidths.map(number).filter((item) => item !== void 0) : [];
|
|
1686
|
+
const rowRatios = Array.isArray(element.rowHeights) ? element.rowHeights.map(number).filter((item) => item !== void 0) : [];
|
|
1687
|
+
const colW = colRatios.length === columnCount ? colRatios.map((value) => value * bounds.w) : void 0;
|
|
1688
|
+
const rowH = rowRatios.length === rowCount ? rowRatios.map((value) => value * bounds.h) : void 0;
|
|
1689
|
+
const objectName = string(element.elementId);
|
|
1690
|
+
slide.addTable(tableRows, {
|
|
1691
|
+
...bounds,
|
|
1692
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1693
|
+
autoPage: false,
|
|
1694
|
+
...colW === void 0 ? {} : { colW },
|
|
1695
|
+
...rowH === void 0 ? {} : { rowH },
|
|
1696
|
+
border: {
|
|
1697
|
+
color: "FFFFFF",
|
|
1698
|
+
pt: 0
|
|
1699
|
+
},
|
|
1700
|
+
margin: 0
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
function legendPosition(value) {
|
|
1704
|
+
return {
|
|
1705
|
+
bottom: "b",
|
|
1706
|
+
left: "l",
|
|
1707
|
+
right: "r",
|
|
1708
|
+
top: "t",
|
|
1709
|
+
topRight: "tr"
|
|
1710
|
+
}[string(value) ?? ""] ?? "r";
|
|
1711
|
+
}
|
|
1712
|
+
function valueAxisOptions(project, axis) {
|
|
1713
|
+
const label = record(axis.label) ?? {};
|
|
1714
|
+
const grid = record(axis.gridLine);
|
|
1715
|
+
const minimum = number(axis.min);
|
|
1716
|
+
const maximum = number(axis.max);
|
|
1717
|
+
const fontSize = number(label.fontSize);
|
|
1718
|
+
const title = string(axis.title);
|
|
1719
|
+
return {
|
|
1720
|
+
...minimum === void 0 ? {} : { valAxisMinVal: minimum },
|
|
1721
|
+
...maximum === void 0 ? {} : { valAxisMaxVal: maximum },
|
|
1722
|
+
...fontSize === void 0 ? {} : { valAxisLabelFontSize: fontSize },
|
|
1723
|
+
...title === void 0 ? {} : {
|
|
1724
|
+
showValAxisTitle: true,
|
|
1725
|
+
valAxisTitle: title
|
|
1726
|
+
},
|
|
1727
|
+
...axis.gridLine === false ? { valGridLine: { style: "none" } } : grid === void 0 ? {} : { valGridLine: {
|
|
1728
|
+
color: colorOptions(project, grid.color, 1).color,
|
|
1729
|
+
style: dash(grid.style)
|
|
1730
|
+
} }
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
function chartAxisOptions(project, element, horizontal = false) {
|
|
1734
|
+
const xAxis = Array.isArray(element.xAxis) ? record(element.xAxis[0]) ?? {} : record(element.xAxis) ?? {};
|
|
1735
|
+
const yAxis = (Array.isArray(element.yAxis) ? element.yAxis.map(record).filter((item) => item !== void 0) : [record(element.yAxis) ?? {}])[0] ?? {};
|
|
1736
|
+
const valueAxis = horizontal ? xAxis : yAxis;
|
|
1737
|
+
const categoryAxis = horizontal ? yAxis : xAxis;
|
|
1738
|
+
const categoryLabel = record(categoryAxis.label) ?? {};
|
|
1739
|
+
return {
|
|
1740
|
+
...valueAxisOptions(project, valueAxis),
|
|
1741
|
+
...number(categoryLabel.fontSize) === void 0 ? {} : { catAxisLabelFontSize: number(categoryLabel.fontSize) },
|
|
1742
|
+
...number(categoryLabel.rotate) === void 0 ? {} : { catAxisLabelRotate: number(categoryLabel.rotate) },
|
|
1743
|
+
...categoryAxis.gridLine === false ? { catGridLine: { style: "none" } } : {}
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
function renderChart(project, pptx, slide, element, foregroundColor) {
|
|
1747
|
+
const data = record(element.data) ?? {};
|
|
1748
|
+
const columns = (Array.isArray(data.cols) ? data.cols : []).map((value) => String(value));
|
|
1749
|
+
const rows = Array.isArray(data.rows) ? data.rows : [];
|
|
1750
|
+
const columnIsNumeric = (column) => {
|
|
1751
|
+
if (column === void 0) return false;
|
|
1752
|
+
const columnIndex = columns.indexOf(column);
|
|
1753
|
+
if (columnIndex < 0) return false;
|
|
1754
|
+
const values = rows.map((row) => row[columnIndex]).filter((value) => value !== void 0 && value !== null && value !== "");
|
|
1755
|
+
return values.length > 0 && values.every((value) => {
|
|
1756
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1757
|
+
return typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value));
|
|
1758
|
+
});
|
|
1759
|
+
};
|
|
1760
|
+
const seriesDefaults = record(element.seriesDefaults) ?? {};
|
|
1761
|
+
const xAxis = Array.isArray(element.xAxis) ? record(element.xAxis[0]) ?? {} : record(element.xAxis) ?? {};
|
|
1762
|
+
const yAxes = Array.isArray(element.yAxis) ? element.yAxis.map(record).filter((item) => item !== void 0) : [record(element.yAxis) ?? {}];
|
|
1763
|
+
const mergeSeries = (raw) => {
|
|
1764
|
+
const defaults = record(seriesDefaults[string(raw.type) ?? ""]) ?? {};
|
|
1765
|
+
const merged = {
|
|
1766
|
+
...defaults,
|
|
1767
|
+
...raw
|
|
1768
|
+
};
|
|
1769
|
+
for (const key of [
|
|
1770
|
+
"marker",
|
|
1771
|
+
"dataLabels",
|
|
1772
|
+
"border",
|
|
1773
|
+
"upBars",
|
|
1774
|
+
"downBars",
|
|
1775
|
+
"totalBars",
|
|
1776
|
+
"increaseBars",
|
|
1777
|
+
"decreaseBars"
|
|
1778
|
+
]) {
|
|
1779
|
+
const base = record(defaults[key]);
|
|
1780
|
+
const specific = record(raw[key]);
|
|
1781
|
+
if (base !== void 0 || specific !== void 0) merged[key] = {
|
|
1782
|
+
...base ?? {},
|
|
1783
|
+
...specific ?? {}
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
return merged;
|
|
1787
|
+
};
|
|
1788
|
+
const series = (Array.isArray(element.series) ? element.series : []).map(record).filter((item) => item !== void 0).map(mergeSeries);
|
|
1789
|
+
const types = [];
|
|
1790
|
+
const mergeableGroups = /* @__PURE__ */ new Map();
|
|
1791
|
+
for (const [index, item] of series.entries()) {
|
|
1792
|
+
const encode = record(item.encode) ?? {};
|
|
1793
|
+
const chartTypeName = string(item.type) ?? "bar";
|
|
1794
|
+
const xColumn = string(encode.x);
|
|
1795
|
+
const yColumn = string(encode.y);
|
|
1796
|
+
const horizontal = chartTypeName === "bar" && columnIsNumeric(xColumn) && !columnIsNumeric(yColumn);
|
|
1797
|
+
const categoryColumn = string(chartTypeName === "pie" || chartTypeName === "radar" ? encode.category : horizontal ? encode.y : encode.x) ?? columns[0] ?? "category";
|
|
1798
|
+
const valueColumn = string(chartTypeName === "pie" ? encode.value : horizontal ? encode.x : encode.y) ?? columns[1] ?? "value";
|
|
1799
|
+
const categoryIndex = columns.indexOf(categoryColumn);
|
|
1800
|
+
const valueIndex = columns.indexOf(valueColumn);
|
|
1801
|
+
const selectedRows = (() => {
|
|
1802
|
+
const filter = record(item.dataFilter);
|
|
1803
|
+
const filterColumn = string(filter?.col);
|
|
1804
|
+
if (filterColumn === void 0) return rows;
|
|
1805
|
+
const filterIndex = columns.indexOf(filterColumn);
|
|
1806
|
+
return rows.filter((row) => row[filterIndex] === filter?.value);
|
|
1807
|
+
})();
|
|
1808
|
+
const filteredRows = horizontal ? [...selectedRows].reverse() : selectedRows;
|
|
1809
|
+
const filteredLabels = filteredRows.map((row) => {
|
|
1810
|
+
const value = row[categoryIndex];
|
|
1811
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : "";
|
|
1812
|
+
});
|
|
1813
|
+
const values = filteredRows.map((row) => Number(row[valueIndex] ?? 0));
|
|
1814
|
+
const type = chartTypeName === "line" ? pptx.ChartType.line : chartTypeName === "area" ? pptx.ChartType.area : chartTypeName === "scatter" ? pptx.ChartType.scatter : chartTypeName === "bubble" ? pptx.ChartType.bubble : chartTypeName === "radar" ? pptx.ChartType.radar : chartTypeName === "pie" && (number(item.innerRadius) ?? 0) > 0 ? pptx.ChartType.doughnut : chartTypeName === "pie" ? pptx.ChartType.pie : pptx.ChartType.bar;
|
|
1815
|
+
const chartColors = (resolvePptdChartSeriesColors(project, chartTypeName === "line" || chartTypeName === "area" || chartTypeName === "radar" ? item.lineColor ?? item.areaColor : item.fill) ?? [PPTD_CHART_SERIES_PALETTE[index % PPTD_CHART_SERIES_PALETTE.length] ?? "#2563EB"]).map((value) => colorOptions(project, value, 1).color);
|
|
1816
|
+
const labelsConfig = record(item.dataLabels);
|
|
1817
|
+
const showLabels = boolean(labelsConfig?.show) === true;
|
|
1818
|
+
const showPercent = labelsConfig?.content === "percentage";
|
|
1819
|
+
const dataLabelFontSize = number(labelsConfig?.fontSize);
|
|
1820
|
+
const dataLabelColor = labelsConfig?.color === void 0 ? foregroundColor : colorOptions(project, labelsConfig.color).color;
|
|
1821
|
+
const axisIndex = Math.max(0, Math.trunc(number(item.yAxisIndex) ?? 0));
|
|
1822
|
+
const valueAxis = horizontal ? xAxis : yAxes[axisIndex] ?? yAxes[0] ?? {};
|
|
1823
|
+
const valueAxisLabel = record(valueAxis.label) ?? {};
|
|
1824
|
+
const valueAxisGrid = record(valueAxis.gridLine);
|
|
1825
|
+
const valueAxisMin = number(valueAxis.min);
|
|
1826
|
+
const valueAxisMax = number(valueAxis.max);
|
|
1827
|
+
const valueAxisLabelFontSize = number(valueAxisLabel.fontSize);
|
|
1828
|
+
const valueAxisTitle = string(valueAxis.title);
|
|
1829
|
+
const groupOptions = {
|
|
1830
|
+
...horizontal ? { barDir: "bar" } : chartTypeName === "bar" ? { barDir: "col" } : {},
|
|
1831
|
+
...axisIndex === 1 ? { secondaryValAxis: true } : {},
|
|
1832
|
+
...valueAxisMin === void 0 ? {} : { valAxisMinVal: valueAxisMin },
|
|
1833
|
+
...valueAxisMax === void 0 ? {} : { valAxisMaxVal: valueAxisMax },
|
|
1834
|
+
...valueAxisLabelFontSize === void 0 ? {} : { valAxisLabelFontSize: valueAxisLabelFontSize },
|
|
1835
|
+
...valueAxisTitle === void 0 ? {} : {
|
|
1836
|
+
showValAxisTitle: true,
|
|
1837
|
+
valAxisTitle: valueAxisTitle
|
|
1838
|
+
},
|
|
1839
|
+
...valueAxis.gridLine === false ? { valGridLine: { style: "none" } } : valueAxisGrid === void 0 ? {} : { valGridLine: {
|
|
1840
|
+
color: colorOptions(project, valueAxisGrid.color, 1).color,
|
|
1841
|
+
style: dash(valueAxisGrid.style)
|
|
1842
|
+
} },
|
|
1843
|
+
...showLabels && !showPercent ? { showValue: true } : {},
|
|
1844
|
+
...showLabels && showPercent ? { showPercent: true } : {},
|
|
1845
|
+
dataLabelColor,
|
|
1846
|
+
...dataLabelFontSize === void 0 ? {} : {
|
|
1847
|
+
dataLabelFontSize,
|
|
1848
|
+
dataLabelPosition: "outEnd"
|
|
1849
|
+
},
|
|
1850
|
+
...chartTypeName === "line" ? {
|
|
1851
|
+
lineSize: number(item.width) ?? 2,
|
|
1852
|
+
lineDataSymbol: "circle"
|
|
1853
|
+
} : {},
|
|
1854
|
+
...chartTypeName === "area" ? { lineSize: number(item.width) ?? 2 } : {},
|
|
1855
|
+
...chartTypeName === "radar" ? { radarStyle: item.areaColor === void 0 ? "marker" : "filled" } : {},
|
|
1856
|
+
...(chartTypeName === "line" || chartTypeName === "area") && item.marker === false ? { lineDataSymbol: "none" } : {},
|
|
1857
|
+
...(chartTypeName === "line" || chartTypeName === "area") && record(item.marker) !== void 0 ? {
|
|
1858
|
+
lineDataSymbol: record(item.marker)?.shape === "rect" ? "square" : string(record(item.marker)?.shape) ?? "circle",
|
|
1859
|
+
lineDataSymbolSize: number(record(item.marker)?.size) ?? 6
|
|
1860
|
+
} : {},
|
|
1861
|
+
...item.stack === "percent" ? { barGrouping: "percentStacked" } : item.stack === "value" || item.stack === "stream" ? { barGrouping: "stacked" } : chartTypeName === "bar" ? { barGrouping: "clustered" } : {},
|
|
1862
|
+
...item.nullHandling === "gap" ? { displayBlanksAs: "gap" } : { displayBlanksAs: "span" },
|
|
1863
|
+
...type === pptx.ChartType.doughnut ? { holeSize: Math.round((number(item.innerRadius) ?? .5) * 100) } : {}
|
|
1864
|
+
};
|
|
1865
|
+
const dataSeries = {
|
|
1866
|
+
name: string(item.name) ?? valueColumn ?? `Series ${index + 1}`,
|
|
1867
|
+
labels: filteredLabels,
|
|
1868
|
+
values,
|
|
1869
|
+
...chartTypeName === "bubble" ? { sizes: filteredRows.map((row) => Number(row[columns.indexOf(string(encode.size) ?? "")] ?? 0)) } : {}
|
|
1870
|
+
};
|
|
1871
|
+
const mergeable = type !== pptx.ChartType.pie && type !== pptx.ChartType.doughnut;
|
|
1872
|
+
const groupKey = chartTypeName === "bar" ? `${type}:${horizontal ? "horizontal" : "vertical"}:${axisIndex}:${String(groupOptions.barGrouping)}` : `${type}:${JSON.stringify(groupOptions)}`;
|
|
1873
|
+
const existing = mergeable ? mergeableGroups.get(groupKey) : void 0;
|
|
1874
|
+
if (existing === void 0) {
|
|
1875
|
+
const chartGroup = {
|
|
1876
|
+
type,
|
|
1877
|
+
data: [dataSeries],
|
|
1878
|
+
options: {
|
|
1879
|
+
...groupOptions,
|
|
1880
|
+
chartColors
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
types.push(chartGroup);
|
|
1884
|
+
if (mergeable) mergeableGroups.set(groupKey, chartGroup);
|
|
1885
|
+
continue;
|
|
1886
|
+
}
|
|
1887
|
+
existing.data.push(dataSeries);
|
|
1888
|
+
existing.options.chartColors = [...existing.options.chartColors ?? [], ...chartColors];
|
|
1889
|
+
if (groupOptions.showValue === true) existing.options.showValue = true;
|
|
1890
|
+
if (groupOptions.showPercent === true) existing.options.showPercent = true;
|
|
1891
|
+
if (existing.options.dataLabelFontSize === void 0 && groupOptions.dataLabelFontSize !== void 0) existing.options.dataLabelFontSize = groupOptions.dataLabelFontSize;
|
|
1892
|
+
}
|
|
1893
|
+
const legend = typeof element.legend === "boolean" ? { show: element.legend } : record(element.legend) ?? {};
|
|
1894
|
+
const font = fontFace(element.fontFamily, "Arial", JSON.stringify(types.map(group => group.data)));
|
|
1895
|
+
const objectName = string(element.elementId);
|
|
1896
|
+
const title = typeof element.title === "string" ? { text: element.title } : record(element.title) ?? {};
|
|
1897
|
+
const chartFill = solidFill(project, element.fill);
|
|
1898
|
+
const chartBorder = border(project, element.border);
|
|
1899
|
+
const legendFontSize = number(legend.fontSize);
|
|
1900
|
+
const titleFontSize = number(title.fontSize);
|
|
1901
|
+
const titleText = string(title.text);
|
|
1902
|
+
const barGap = number(element.barGap);
|
|
1903
|
+
const valAxes = series.some((item) => Math.trunc(number(item.yAxisIndex) ?? 0) === 1) ? [valueAxisOptions(project, yAxes[0] ?? {}), valueAxisOptions(project, yAxes[1] ?? {})] : void 0;
|
|
1904
|
+
const common = {
|
|
1905
|
+
...frame(element),
|
|
1906
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1907
|
+
showLegend: boolean(legend.show) ?? series.some((item) => ![
|
|
1908
|
+
"waterfall",
|
|
1909
|
+
"heatmap",
|
|
1910
|
+
"treemap",
|
|
1911
|
+
"sunburst",
|
|
1912
|
+
"sankey"
|
|
1913
|
+
].includes(string(item.type) ?? "")),
|
|
1914
|
+
legendPos: legendPosition(legend.position),
|
|
1915
|
+
legendFontFace: fontFace(legend.fontFamily, font),
|
|
1916
|
+
legendColor: colorOptions(project, legend.color ?? `#${foregroundColor}`).color,
|
|
1917
|
+
...legendFontSize === void 0 ? {} : { legendFontSize },
|
|
1918
|
+
catAxisLabelFontFace: font,
|
|
1919
|
+
catAxisLabelColor: foregroundColor,
|
|
1920
|
+
catAxisLineColor: foregroundColor,
|
|
1921
|
+
valAxisLabelFontFace: font,
|
|
1922
|
+
valAxisLabelColor: foregroundColor,
|
|
1923
|
+
valAxisLineColor: foregroundColor,
|
|
1924
|
+
showTitle: titleText !== void 0,
|
|
1925
|
+
...titleText === void 0 ? {} : { title: titleText },
|
|
1926
|
+
titleFontFace: fontFace(title.fontFamily, font),
|
|
1927
|
+
titleColor: colorOptions(project, title.color ?? `#${foregroundColor}`).color,
|
|
1928
|
+
...titleFontSize === void 0 ? {} : { titleFontSize },
|
|
1929
|
+
showValue: false,
|
|
1930
|
+
chartArea: {
|
|
1931
|
+
border: chartBorder === void 0 ? {
|
|
1932
|
+
color: "FFFFFF",
|
|
1933
|
+
pt: 0
|
|
1934
|
+
} : {
|
|
1935
|
+
color: chartBorder.color,
|
|
1936
|
+
pt: chartBorder.width
|
|
1937
|
+
},
|
|
1938
|
+
fill: chartFill ?? {
|
|
1939
|
+
color: "FFFFFF",
|
|
1940
|
+
transparency: 100
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1943
|
+
plotArea: {
|
|
1944
|
+
border: {
|
|
1945
|
+
color: "FFFFFF",
|
|
1946
|
+
pt: 0
|
|
1947
|
+
},
|
|
1948
|
+
fill: {
|
|
1949
|
+
color: "FFFFFF",
|
|
1950
|
+
transparency: 100
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
...barGap === void 0 ? {} : { barGapWidthPct: Math.round(barGap * 100) },
|
|
1954
|
+
...valAxes === void 0 ? {} : { valAxes },
|
|
1955
|
+
...chartAxisOptions(project, element, series.some((item) => {
|
|
1956
|
+
const encode = record(item.encode) ?? {};
|
|
1957
|
+
return string(item.type) === "bar" && columnIsNumeric(string(encode.x)) && !columnIsNumeric(string(encode.y));
|
|
1958
|
+
}))
|
|
1959
|
+
};
|
|
1960
|
+
slide.addChart(types, common);
|
|
1961
|
+
}
|
|
1962
|
+
function renderIcon(project, slide, element) {
|
|
1963
|
+
const color = colorOptions(project, element.color).color;
|
|
1964
|
+
const objectName = string(element.elementId);
|
|
1965
|
+
slide.addText(string(element.iconName) ?? "●", {
|
|
1966
|
+
...frame(element),
|
|
1967
|
+
...objectName === void 0 ? {} : { objectName },
|
|
1968
|
+
fontFace: "Arial",
|
|
1969
|
+
fontSize: 18,
|
|
1970
|
+
color,
|
|
1971
|
+
margin: 0,
|
|
1972
|
+
align: "center",
|
|
1973
|
+
valign: "middle",
|
|
1974
|
+
fit: "shrink"
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
function renderElement(project, pptx, slide, element, foregroundColor) {
|
|
1978
|
+
if (element.elementType === "text") {
|
|
1979
|
+
renderText(project, slide, element);
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
if (element.elementType === "shape") {
|
|
1983
|
+
renderShape(project, pptx, slide, element);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
if (element.elementType === "line") {
|
|
1987
|
+
renderLine(project, pptx, slide, element);
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
if (element.elementType === "image") {
|
|
1991
|
+
renderImage(project, slide, element);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if (element.elementType === "table") {
|
|
1995
|
+
renderTable(project, slide, element);
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
if (element.elementType === "chart") {
|
|
1999
|
+
renderChart(project, pptx, slide, element, foregroundColor);
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
if (element.elementType === "icon") {
|
|
2003
|
+
renderIcon(project, slide, element);
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
/** Render one checked PPTD AST to editable native PowerPoint objects. */
|
|
2008
|
+
async function renderPptdProject(project) {
|
|
2009
|
+
const check = checkPptdProject(project);
|
|
2010
|
+
if (check.status === "fail") {
|
|
2011
|
+
const details = check.issues.filter((issue) => issue.severity === "error").slice(0, 3).map((issue) => `${issue.code}${issue.elementId === void 0 ? "" : `(${issue.elementId})`}: ${issue.message}`).join("; ");
|
|
2012
|
+
throw new Error(`PPTD rendering requires a passing check; received ${check.errorCount} errors${details === "" ? "" : `: ${details}`}`);
|
|
2013
|
+
}
|
|
2014
|
+
const pptx = new PptxGenJS();
|
|
2015
|
+
const layoutName = `DSH_PPTD_${project.width}x${project.height}`;
|
|
2016
|
+
pptx.defineLayout({
|
|
2017
|
+
name: layoutName,
|
|
2018
|
+
width: inches(project.width),
|
|
2019
|
+
height: inches(project.height)
|
|
2020
|
+
});
|
|
2021
|
+
pptx.layout = layoutName;
|
|
2022
|
+
pptx.author = "DSH PPTD";
|
|
2023
|
+
pptx.company = "DeepSeek Harness";
|
|
2024
|
+
pptx.subject = "Local PPTD v2 editable rendering";
|
|
2025
|
+
pptx.title = project.title;
|
|
2026
|
+
const textStyles = themeMap(project, "textStyles");
|
|
2027
|
+
const titleStyle = record(textStyles.title) ?? {};
|
|
2028
|
+
const bodyStyle = record(textStyles.body) ?? {};
|
|
2029
|
+
pptx.theme = {
|
|
2030
|
+
headFontFace: fontFace(titleStyle.fontFamily, "Arial"),
|
|
2031
|
+
bodyFontFace: fontFace(bodyStyle.fontFamily, "Arial")
|
|
2032
|
+
};
|
|
2033
|
+
for (const page of project.pages) {
|
|
2034
|
+
const slide = pptx.addSlide();
|
|
2035
|
+
const background = solidFill(project, page.background);
|
|
2036
|
+
slide.background = background ?? { color: "FFFFFF" };
|
|
2037
|
+
const foregroundColor = readableForeground(background?.color ?? "FFFFFF");
|
|
2038
|
+
for (const element of page.elements) renderElement(project, pptx, slide, element, foregroundColor);
|
|
2039
|
+
if (page.notes.trim() !== "") slide.addNotes(page.notes);
|
|
2040
|
+
}
|
|
2041
|
+
const output = await pptx.write({
|
|
2042
|
+
outputType: "nodebuffer",
|
|
2043
|
+
compression: true
|
|
2044
|
+
});
|
|
2045
|
+
return {
|
|
2046
|
+
bytes: new Uint8Array(output),
|
|
2047
|
+
nativeObjectCount: check.nativeObjectCount,
|
|
2048
|
+
check
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
function mediaType(file, bytes) {
|
|
2052
|
+
const extension = path.extname(file).toLowerCase();
|
|
2053
|
+
if (extension === ".png" && bytes[0] === 137 && bytes[1] === 80) return "image/png";
|
|
2054
|
+
if ((extension === ".jpg" || extension === ".jpeg") && bytes[0] === 255 && bytes[1] === 216) return "image/jpeg";
|
|
2055
|
+
if (extension === ".gif" && Buffer.from(bytes.subarray(0, 3)).toString("ascii") === "GIF") return "image/gif";
|
|
2056
|
+
if (extension === ".webp" && Buffer.from(bytes.subarray(8, 12)).toString("ascii") === "WEBP") return "image/webp";
|
|
2057
|
+
if (extension === ".svg" && Buffer.from(bytes.subarray(0, 512)).toString("utf8").includes("<svg")) return "image/svg+xml";
|
|
2058
|
+
}
|
|
2059
|
+
async function confinedFile(root, relative, maximumBytes) {
|
|
2060
|
+
const safe = safeProjectPath(relative);
|
|
2061
|
+
if (safe === void 0) throw new Error(`PPTD path is not project-relative: ${relative}`);
|
|
2062
|
+
const target = await realpath(path.join(root, ...safe.split("/")));
|
|
2063
|
+
if (target !== root && !target.startsWith(`${root}${path.sep}`)) throw new Error(`PPTD path escapes the project root: ${relative}`);
|
|
2064
|
+
const metadata = await lstat(target);
|
|
2065
|
+
if (!metadata.isFile() || metadata.size > maximumBytes) throw new Error(`PPTD file is invalid or too large: ${relative}`);
|
|
2066
|
+
return {
|
|
2067
|
+
real: target,
|
|
2068
|
+
bytes: await readFile(target)
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
/** Resolve a PPTD entry file from either the entry itself or its project directory. */
|
|
2072
|
+
async function resolvePptdEntry(inputPath) {
|
|
2073
|
+
const input = await realpath(path.resolve(inputPath));
|
|
2074
|
+
const metadata = await lstat(input);
|
|
2075
|
+
if (metadata.isFile()) {
|
|
2076
|
+
if (!input.endsWith(".pptd")) throw new Error("PPTD entry must use the .pptd extension");
|
|
2077
|
+
return input;
|
|
2078
|
+
}
|
|
2079
|
+
if (!metadata.isDirectory()) throw new Error("PPTD input must be an entry file or project directory");
|
|
2080
|
+
const entries = (await readdir(input, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".pptd")).map((entry) => entry.name).sort();
|
|
2081
|
+
if (entries.length !== 1) throw new Error(`PPTD project directory must contain exactly one .pptd entry; found ${entries.length}`);
|
|
2082
|
+
const entry = entries.at(0);
|
|
2083
|
+
if (entry === void 0) throw new Error("PPTD project directory must contain one .pptd entry");
|
|
2084
|
+
return path.join(input, entry);
|
|
2085
|
+
}
|
|
2086
|
+
/** Load a confined local PPTD project. Network resources stay disabled. */
|
|
2087
|
+
async function loadPptdProject(entryPath) {
|
|
2088
|
+
const entry = await resolvePptdEntry(entryPath);
|
|
2089
|
+
const metadata = await lstat(entry);
|
|
2090
|
+
if (!metadata.isFile() || metadata.size > MAX_MANIFEST_BYTES) throw new Error("PPTD entry is invalid or too large");
|
|
2091
|
+
const root = path.dirname(entry);
|
|
2092
|
+
const manifest = await readFile(entry, "utf8");
|
|
2093
|
+
const raw = parseYaml(manifest, path.basename(entry), []);
|
|
2094
|
+
if (raw === void 0) return parsePptdProject({
|
|
2095
|
+
entryName: path.basename(entry),
|
|
2096
|
+
manifest,
|
|
2097
|
+
pages: /* @__PURE__ */ new Map(),
|
|
2098
|
+
assets: /* @__PURE__ */ new Map()
|
|
2099
|
+
});
|
|
2100
|
+
const refs = Array.isArray(raw.pages) ? raw.pages.slice(0, MAX_PAGES) : [];
|
|
2101
|
+
const pages = /* @__PURE__ */ new Map();
|
|
2102
|
+
const loadIssues = [];
|
|
2103
|
+
for (const rawRef of refs) {
|
|
2104
|
+
const ref = typeof rawRef === "string" ? safeProjectPath(rawRef) : void 0;
|
|
2105
|
+
if (ref === void 0) continue;
|
|
2106
|
+
try {
|
|
2107
|
+
const file = await confinedFile(root, ref, MAX_PAGE_BYTES);
|
|
2108
|
+
pages.set(ref, Buffer.from(file.bytes).toString("utf8"));
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
loadIssues.push({
|
|
2111
|
+
code: "file-read",
|
|
2112
|
+
severity: "error",
|
|
2113
|
+
file: ref,
|
|
2114
|
+
message: `无法读取页面文件 ${ref}:${error instanceof Error ? error.message : String(error)}`
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
const partial = parsePptdProject({
|
|
2119
|
+
entryName: path.basename(entry),
|
|
2120
|
+
manifest,
|
|
2121
|
+
pages,
|
|
2122
|
+
assets: /* @__PURE__ */ new Map(),
|
|
2123
|
+
issues: loadIssues
|
|
2124
|
+
});
|
|
2125
|
+
const refsToLoad = /* @__PURE__ */ new Set();
|
|
2126
|
+
for (const page of partial.pages) for (const element of page.elements) if (element.elementType === "image") {
|
|
2127
|
+
const ref = localAssetPath(element.src);
|
|
2128
|
+
if (ref !== void 0) refsToLoad.add(ref);
|
|
2129
|
+
}
|
|
2130
|
+
const assets = /* @__PURE__ */ new Map();
|
|
2131
|
+
let totalBytes = 0;
|
|
2132
|
+
for (const ref of refsToLoad) {
|
|
2133
|
+
let file;
|
|
2134
|
+
try {
|
|
2135
|
+
file = await confinedFile(root, ref, MAX_ASSET_BYTES);
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
loadIssues.push({
|
|
2138
|
+
code: "file-read",
|
|
2139
|
+
severity: "error",
|
|
2140
|
+
file: ref,
|
|
2141
|
+
message: `无法读取资源 ${ref}:${error instanceof Error ? error.message : String(error)}`
|
|
2142
|
+
});
|
|
2143
|
+
continue;
|
|
2144
|
+
}
|
|
2145
|
+
totalBytes += file.bytes.byteLength;
|
|
2146
|
+
if (totalBytes > MAX_TOTAL_ASSET_BYTES) throw new Error("PPTD image resources exceed the aggregate byte limit");
|
|
2147
|
+
const type = mediaType(ref, file.bytes);
|
|
2148
|
+
if (type === void 0) {
|
|
2149
|
+
loadIssues.push({
|
|
2150
|
+
code: "invalid-resource",
|
|
2151
|
+
severity: "error",
|
|
2152
|
+
file: ref,
|
|
2153
|
+
message: `图片资源格式或内容无效:${ref}`
|
|
2154
|
+
});
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
assets.set(ref, {
|
|
2158
|
+
path: ref,
|
|
2159
|
+
mediaType: type,
|
|
2160
|
+
bytes: file.bytes,
|
|
2161
|
+
sha256: createHash("sha256").update(file.bytes).digest("hex")
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
return parsePptdProject({
|
|
2165
|
+
entryName: path.basename(entry),
|
|
2166
|
+
manifest,
|
|
2167
|
+
pages,
|
|
2168
|
+
assets,
|
|
2169
|
+
issues: loadIssues
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
//#endregion
|
|
2173
|
+
export { resolvePptdEntry as a, renderPptdProject as i, loadPptdProject as n, recommendedTextCapacity as o, parsePptdProject as r, checkPptdProject as t };
|