codewave-openclaw-installer 2.1.10 → 2.1.12
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/bin/check.mjs +0 -0
- package/bin/install.mjs +0 -0
- package/package.json +2 -1
- package/skills/ppt-master/.env.example +35 -0
- package/skills/ppt-master/README.md +90 -0
- package/skills/ppt-master/SKILL.md +390 -17
- package/skills/ppt-master/audit_report_model_perf.md +213 -0
- package/skills/ppt-master/audit_report_portability.md +90 -0
- package/skills/ppt-master/audit_report_robustness.md +192 -0
- package/skills/ppt-master/audit_report_workflow.md +233 -0
- package/skills/ppt-master/fix_report_bid.md +51 -0
- package/skills/ppt-master/fix_report_scripts.md +41 -0
- package/skills/ppt-master/references/bid-content-example-netease.md +815 -0
- package/skills/ppt-master/references/bid-content-template.md +390 -0
- package/skills/ppt-master/references/bid-example-netease.md +339 -0
- package/skills/ppt-master/references/bid-presentation.md +384 -0
- package/skills/ppt-master/references/bid-svgs/03_toc.svg +38 -0
- package/skills/ppt-master/references/bid-svgs/04_chapter_company.svg +7 -0
- package/skills/ppt-master/references/bid-svgs/05_company_overview.svg +28 -0
- package/skills/ppt-master/references/bid-svgs/06_vision_business.svg +45 -0
- package/skills/ppt-master/references/bid-svgs/07_product_system.svg +52 -0
- package/skills/ppt-master/references/bid-svgs/08_codewave_timeline.svg +29 -0
- package/skills/ppt-master/references/bid-svgs/09_certifications.svg +33 -0
- package/skills/ppt-master/references/bid-svgs/10_client_logos.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/11_brand_mission.svg +14 -0
- package/skills/ppt-master/references/bid-svgs/12_chapter_demand.svg +7 -0
- package/skills/ppt-master/references/bid-svgs/16_chapter_advantages.svg +7 -0
- package/skills/ppt-master/references/bid-svgs/18_platform_arch.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/19_function_matrix.svg +35 -0
- package/skills/ppt-master/references/bid-svgs/20_dev_arch.svg +29 -0
- package/skills/ppt-master/references/bid-svgs/21_source_code.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/22_ai_dev.svg +27 -0
- package/skills/ppt-master/references/bid-svgs/23_asset_market.svg +33 -0
- package/skills/ppt-master/references/bid-svgs/24_multi_org.svg +28 -0
- package/skills/ppt-master/references/bid-svgs/25_i18n.svg +22 -0
- package/skills/ppt-master/references/bid-svgs/26_multi_tenant.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/27_integration.svg +27 -0
- package/skills/ppt-master/references/bid-svgs/28_permission.svg +31 -0
- package/skills/ppt-master/references/bid-svgs/29_security.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/30_ai_capabilities.svg +21 -0
- package/skills/ppt-master/references/bid-svgs/31_product_combo.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/32_digital_transform.svg +28 -0
- package/skills/ppt-master/references/bid-svgs/33_chapter_cases.svg +7 -0
- package/skills/ppt-master/references/bid-svgs/34_cummins_case.svg +23 -0
- package/skills/ppt-master/references/bid-svgs/35_cpq_cost.svg +22 -0
- package/skills/ppt-master/references/bid-svgs/36_tech_platform.svg +18 -0
- package/skills/ppt-master/references/bid-svgs/37_platform_value.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/38_operation_plan.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/39_zpmc_case.svg +21 -0
- package/skills/ppt-master/references/bid-svgs/40_zpmc_apps.svg +23 -0
- package/skills/ppt-master/references/bid-svgs/41_chapter_service.svg +7 -0
- package/skills/ppt-master/references/bid-svgs/42_tech_support.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/43_knowledge_transfer.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/44_operation_promotion.svg +20 -0
- package/skills/ppt-master/references/bid-svgs/45_custom_dev_guide.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/46_sla_guarantee.svg +18 -0
- package/skills/ppt-master/references/bid-svgs/47_strategic_cooperation.svg +25 -0
- package/skills/ppt-master/references/bid-svgs/48_ecosystem.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/49_chapter_delivery.svg +23 -0
- package/skills/ppt-master/references/bid-svgs/53_pm_intro.svg +24 -0
- package/skills/ppt-master/references/bid-svgs/54_after_sales.svg +27 -0
- package/skills/ppt-master/references/bid-svgs/55_ending.svg +8 -0
- package/skills/ppt-master/references/company_intro.md +93 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img00.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img01.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img02.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img03.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img04.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img05.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img06.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img07.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img08.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img09.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img10.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img11.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img12.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img13.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img14.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img15.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img16.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img17.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img18.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img19.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img20.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img21.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img22.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img23.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img24.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img25.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img26.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img27.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img28.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img29.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide2_img30.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide2_img31.jpg +0 -0
- package/skills/ppt-master/references/company_intro_images/slide3_img32.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide3_img33.png +0 -0
- package/skills/ppt-master/references/drawio-integration.md +144 -0
- package/skills/ppt-master/references/executor-base.md +3 -4
- package/skills/ppt-master/references/image-generator.md +93 -6
- package/skills/ppt-master/references/strategist.md +8 -5
- package/skills/ppt-master/requirements.txt +19 -0
- package/skills/ppt-master/resources/backgrounds/chapter_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/company_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/cover_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/index.json +40 -0
- package/skills/ppt-master/resources/backgrounds/tech_bg.png +0 -0
- package/skills/ppt-master/scripts/README.md +1 -1
- package/skills/ppt-master/scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/error_helper.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/pptx_animations.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/project_utils.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/arch_diagram.py +370 -0
- package/skills/ppt-master/scripts/bid_init.py +79 -0
- package/skills/ppt-master/scripts/drawio_gen.py +366 -0
- package/skills/ppt-master/scripts/drawio_to_svg.py +458 -0
- package/skills/ppt-master/scripts/finalize_svg.py +14 -0
- package/skills/ppt-master/scripts/image_backends/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/image_backends/__pycache__/backend_aigw_gemini.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/image_backends/__pycache__/backend_common.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/crop_images.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_icons.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_images.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/fix_image_aspect.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/flatten_tspan.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/svg_rect_to_path.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_page_gen.py +871 -0
- package/skills/ppt-master/scripts/svg_quality_checker.py +169 -8
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_context.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_converter.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_elements.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_paths.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_styles.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_utils.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_builder.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_cli.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_dimensions.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_discovery.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_media.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_notes.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_slide_xml.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/pptx_builder.py +7 -1
- package/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py +14 -0
- package/skills/ppt-master/projects/.gitkeep +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/01_cover.svg" → Technology Blue Business/01_cover.svg} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/02_chapter.svg" → Technology Blue Business/02_chapter.svg} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/02_toc.svg" → Technology Blue Business/02_toc.svg} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/03_content.svg" → Technology Blue Business/03_content.svg} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/04_ending.svg" → Technology Blue Business/04_ending.svg} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246/design_spec.md" → Technology Blue Business/design_spec.md} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246//351/207/215/345/272/206/345/244/247/345/255/246logo.png" → Technology Blue Business//316/230c/314/247i/314/200/317/203/342/225/221a/314/212/317/203n/314/203/302/272/317/203/302/241/302/252logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts/{/351/207/215/345/272/206/345/244/247/345/255/246//351/207/215/345/272/206/345/244/247/345/255/246logo2.png" → Technology Blue Business//316/230c/314/247i/314/200/317/203/342/225/221a/314/212/317/203n/314/203/302/272/317/203/302/241/302/252logo2.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214 → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{346/213/233/345/225/206/351/223/266/350/241/214//346/213/233/345/225/206/351/223/266/350/241/214/345/205/254/345/217/270/351/207/221/350/236/215.png" → 302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202//302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202/317/203a/314/200/302/274/317/203A/314/212/342/225/225/316/230c/314/247/303/246/316/246/342/202/247i/314/200.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241//345/217/263/344/270/212/350/247/222 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241//345/244/247/345/236/213 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204//345/217/263/344/270/212/350/247/222 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204//345/244/247/345/236/213 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243//345/217/263/344/270/212/350/247/222 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243//345/244/247/345/236/213 logo.png" → 316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//346/260/264/347/224/265/344/270/211/345/261/200logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//302/265/342/226/221/342/224/244/317/204o/314/210/342/225/241/316/243/342/225/225e/314/210/317/203/342/226/222C/314/247logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//344/270/255/345/233/275/346/260/264/345/212/241logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//316/243/342/225/225/302/241/317/203/302/242/342/225/234/302/265/342/226/221/342/224/244/317/203e/314/200i/314/201logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//345/215/216/344/270/234/351/231/242logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203i/314/200A/314/210/316/243/342/225/225/302/243/316/230O/314/210o/314/201logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//347/224/265/345/273/272logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243 → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201}/design_spec.md" +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//346/260/264/347/224/265/344/270/211/345/261/200logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//302/265/342/226/221/342/224/244/317/204o/314/210/342/225/241/316/243/342/225/225e/314/210/317/203/342/226/222C/314/247logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//344/270/255/345/233/275/346/260/264/345/212/241logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//316/243/342/225/225/302/241/317/203/302/242/342/225/234/302/265/342/226/221/342/224/244/317/203e/314/200i/314/201logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//345/215/216/344/270/234/351/231/242logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203i/314/200A/314/210/316/243/342/225/225/302/243/316/230O/314/210o/314/201logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//347/224/265/345/273/272logo.png" → 316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221logo.png"} +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/01_cover.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/02_chapter.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/02_toc.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/03_content.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/04_ending.svg" +0 -0
- /package/skills/ppt-master/templates/layouts//{347/247/221/346/212/200/350/223/235/345/225/206/345/212/241 → 317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201}/design_spec.md" +0 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
drawio_to_svg.py - Convert draw.io XML to PPT Master compatible SVG.
|
|
4
|
+
|
|
5
|
+
Parses a .drawio file and renders it as a standalone SVG at 1280x720
|
|
6
|
+
(16:9 PPT format), compatible with the PPT Master pipeline.
|
|
7
|
+
|
|
8
|
+
This does NOT require draw.io Desktop installed. It directly parses
|
|
9
|
+
the mxGraph XML and renders nodes/edges as SVG elements.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python drawio_to_svg.py <input.drawio> -o <output.svg>
|
|
13
|
+
python drawio_to_svg.py <input.drawio> -o <output.svg> --width 1280 --height 720
|
|
14
|
+
python drawio_to_svg.py <input.drawio> -o <output.svg> --title "System Architecture"
|
|
15
|
+
python drawio_to_svg.py <input.drawio> -o <output.svg> --background "#ffffff" --padding 40
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
import re
|
|
20
|
+
import argparse
|
|
21
|
+
import xml.etree.ElementTree as ET
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class NodeInfo:
|
|
28
|
+
id: str
|
|
29
|
+
label: str
|
|
30
|
+
x: float
|
|
31
|
+
y: float
|
|
32
|
+
width: float
|
|
33
|
+
height: float
|
|
34
|
+
style: dict = field(default_factory=dict)
|
|
35
|
+
parent: str = "1"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class EdgeInfo:
|
|
40
|
+
id: str
|
|
41
|
+
label: str
|
|
42
|
+
source: str
|
|
43
|
+
target: str
|
|
44
|
+
style: dict = field(default_factory=dict)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def parse_style(style_str: str) -> dict:
|
|
48
|
+
"""Parse mxGraph style string into dict."""
|
|
49
|
+
result = {}
|
|
50
|
+
if not style_str:
|
|
51
|
+
return result
|
|
52
|
+
for part in style_str.rstrip(";").split(";"):
|
|
53
|
+
if "=" in part:
|
|
54
|
+
k, v = part.split("=", 1)
|
|
55
|
+
result[k.strip()] = v.strip()
|
|
56
|
+
elif part.strip():
|
|
57
|
+
result[part.strip()] = True
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parse_drawio(filepath: str):
|
|
62
|
+
"""Parse a .drawio file and return nodes and edges."""
|
|
63
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
64
|
+
tree = ET.parse(f)
|
|
65
|
+
root = tree.getroot()
|
|
66
|
+
|
|
67
|
+
nodes = {}
|
|
68
|
+
edges = []
|
|
69
|
+
groups = {}
|
|
70
|
+
|
|
71
|
+
for diagram in root.findall(".//diagram"):
|
|
72
|
+
for cell in diagram.iter("mxCell"):
|
|
73
|
+
cell_id = cell.get("id", "")
|
|
74
|
+
if cell_id in ("0", "1"):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
parent = cell.get("parent", "1")
|
|
78
|
+
style = parse_style(cell.get("style", ""))
|
|
79
|
+
value = cell.get("value", "")
|
|
80
|
+
geo = cell.find("mxGeometry")
|
|
81
|
+
|
|
82
|
+
if cell.get("vertex") == "1":
|
|
83
|
+
x = float(geo.get("x", "0")) if geo is not None else 0
|
|
84
|
+
y = float(geo.get("y", "0")) if geo is not None else 0
|
|
85
|
+
w = float(geo.get("width", "120")) if geo is not None else 120
|
|
86
|
+
h = float(geo.get("height", "60")) if geo is not None else 60
|
|
87
|
+
|
|
88
|
+
if "container" in style:
|
|
89
|
+
groups[cell_id] = NodeInfo(cell_id, value, x, y, w, h, style, parent)
|
|
90
|
+
else:
|
|
91
|
+
nodes[cell_id] = NodeInfo(cell_id, value, x, y, w, h, style, parent)
|
|
92
|
+
|
|
93
|
+
elif cell.get("edge") == "1":
|
|
94
|
+
edges.append(EdgeInfo(cell_id, value,
|
|
95
|
+
cell.get("source", ""),
|
|
96
|
+
cell.get("target", ""),
|
|
97
|
+
style))
|
|
98
|
+
|
|
99
|
+
# Also check <object> elements (N2G format)
|
|
100
|
+
for obj in diagram.iter("object"):
|
|
101
|
+
obj_id = obj.get("id", "")
|
|
102
|
+
value = obj.get("label", "")
|
|
103
|
+
inner = obj.find("mxCell")
|
|
104
|
+
if inner is None:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
style = parse_style(inner.get("style", ""))
|
|
108
|
+
parent = inner.get("parent", "1")
|
|
109
|
+
geo = inner.find("mxGeometry")
|
|
110
|
+
|
|
111
|
+
if inner.get("vertex") == "1":
|
|
112
|
+
x = float(geo.get("x", "0")) if geo is not None else 0
|
|
113
|
+
y = float(geo.get("y", "0")) if geo is not None else 0
|
|
114
|
+
w = float(geo.get("width", "120")) if geo is not None else 120
|
|
115
|
+
h = float(geo.get("height", "60")) if geo is not None else 60
|
|
116
|
+
nodes[obj_id] = NodeInfo(obj_id, value, x, y, w, h, style, parent)
|
|
117
|
+
|
|
118
|
+
elif inner.get("edge") == "1":
|
|
119
|
+
edges.append(EdgeInfo(obj_id, value,
|
|
120
|
+
inner.get("source", obj.get("source", "")),
|
|
121
|
+
inner.get("target", obj.get("target", "")),
|
|
122
|
+
style))
|
|
123
|
+
|
|
124
|
+
# Resolve relative positions for grouped nodes
|
|
125
|
+
for nid, node in nodes.items():
|
|
126
|
+
if node.parent in groups:
|
|
127
|
+
g = groups[node.parent]
|
|
128
|
+
node.x += g.x
|
|
129
|
+
node.y += g.y
|
|
130
|
+
|
|
131
|
+
return nodes, edges, groups
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def color_from_style(style: dict, key: str, default: str) -> str:
|
|
135
|
+
return style.get(key, default)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_shape_type(style: dict) -> str:
|
|
139
|
+
"""Determine shape type from style dict."""
|
|
140
|
+
shape = style.get("shape", "")
|
|
141
|
+
if "cylinder" in shape or "cylinder3" in str(style):
|
|
142
|
+
return "cylinder"
|
|
143
|
+
if "cloud" in shape:
|
|
144
|
+
return "cloud"
|
|
145
|
+
if "rhombus" in style or "diamond" in str(style):
|
|
146
|
+
return "diamond"
|
|
147
|
+
if "ellipse" in style:
|
|
148
|
+
return "ellipse"
|
|
149
|
+
if "hexagon" in shape:
|
|
150
|
+
return "hexagon"
|
|
151
|
+
if style.get("rounded"):
|
|
152
|
+
return "rounded"
|
|
153
|
+
return "rect"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def svg_shape(node: NodeInfo, svg_lines: list):
|
|
157
|
+
"""Render a node as SVG elements."""
|
|
158
|
+
x, y, w, h = node.x, node.y, node.width, node.height
|
|
159
|
+
fill = color_from_style(node.style, "fillColor", "#dae8fc")
|
|
160
|
+
stroke = color_from_style(node.style, "strokeColor", "#6c8ebf")
|
|
161
|
+
shape = get_shape_type(node.style)
|
|
162
|
+
stroke_w = "1.5"
|
|
163
|
+
|
|
164
|
+
if shape == "cylinder":
|
|
165
|
+
# Cylinder: rect + top/bottom ellipse
|
|
166
|
+
ry = min(12, h * 0.15)
|
|
167
|
+
svg_lines.append(f' <rect x="{x}" y="{y + ry}" width="{w}" height="{h - 2*ry}" '
|
|
168
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
169
|
+
svg_lines.append(f' <ellipse cx="{x + w/2}" cy="{y + ry}" rx="{w/2}" ry="{ry}" '
|
|
170
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
171
|
+
svg_lines.append(f' <ellipse cx="{x + w/2}" cy="{y + h - ry}" rx="{w/2}" ry="{ry}" '
|
|
172
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
173
|
+
elif shape == "ellipse" or shape == "cloud":
|
|
174
|
+
svg_lines.append(f' <ellipse cx="{x + w/2}" cy="{y + h/2}" rx="{w/2}" ry="{h/2}" '
|
|
175
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
176
|
+
elif shape == "diamond":
|
|
177
|
+
cx, cy = x + w/2, y + h/2
|
|
178
|
+
points = f"{cx},{y} {x+w},{cy} {cx},{y+h} {x},{cy}"
|
|
179
|
+
svg_lines.append(f' <polygon points="{points}" '
|
|
180
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
181
|
+
elif shape == "hexagon":
|
|
182
|
+
dx = w * 0.15
|
|
183
|
+
points = f"{x+dx},{y} {x+w-dx},{y} {x+w},{y+h/2} {x+w-dx},{y+h} {x+dx},{y+h} {x},{y+h/2}"
|
|
184
|
+
svg_lines.append(f' <polygon points="{points}" '
|
|
185
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
186
|
+
elif shape == "rounded":
|
|
187
|
+
r = min(12, w * 0.1, h * 0.2)
|
|
188
|
+
svg_lines.append(f' <rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{r}" ry="{r}" '
|
|
189
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
190
|
+
else:
|
|
191
|
+
svg_lines.append(f' <rect x="{x}" y="{y}" width="{w}" height="{h}" '
|
|
192
|
+
f'fill="{fill}" stroke="{stroke}" stroke-width="{stroke_w}" />')
|
|
193
|
+
|
|
194
|
+
# Label
|
|
195
|
+
label = node.label.replace("&", "&").replace("<", "<").replace(">", ">")
|
|
196
|
+
if label:
|
|
197
|
+
font_size = 12
|
|
198
|
+
svg_lines.append(f' <text x="{x + w/2}" y="{y + h/2}" '
|
|
199
|
+
f'text-anchor="middle" dominant-baseline="central" '
|
|
200
|
+
f'font-family="Helvetica, Arial, sans-serif" font-size="{font_size}" '
|
|
201
|
+
f'fill="#333333">{label}</text>')
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def svg_edge(edge: EdgeInfo, nodes: dict, svg_lines: list):
|
|
205
|
+
"""Render an edge as orthogonal polyline for cleaner look."""
|
|
206
|
+
src = nodes.get(edge.source)
|
|
207
|
+
tgt = nodes.get(edge.target)
|
|
208
|
+
if not src or not tgt:
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
# Center points
|
|
212
|
+
sx = src.x + src.width / 2
|
|
213
|
+
sy = src.y + src.height / 2
|
|
214
|
+
tx = tgt.x + tgt.width / 2
|
|
215
|
+
ty = tgt.y + tgt.height / 2
|
|
216
|
+
|
|
217
|
+
stroke = color_from_style(edge.style, "strokeColor", "#666666")
|
|
218
|
+
dash = ""
|
|
219
|
+
if edge.style.get("dashed"):
|
|
220
|
+
pattern = edge.style.get("dashPattern", "8 8")
|
|
221
|
+
dash = f' stroke-dasharray="{pattern}"'
|
|
222
|
+
|
|
223
|
+
# Determine best connection ports (top/bottom/left/right of each node)
|
|
224
|
+
dy = ty - sy
|
|
225
|
+
dx = tx - sx
|
|
226
|
+
|
|
227
|
+
# Choose vertical-first routing (most common for TB layouts)
|
|
228
|
+
if abs(dy) > 10:
|
|
229
|
+
# Connect bottom of src to top of tgt (or top to bottom if reversed)
|
|
230
|
+
if dy > 0:
|
|
231
|
+
# src above tgt
|
|
232
|
+
p1x, p1y = sx, src.y + src.height # bottom of src
|
|
233
|
+
p4x, p4y = tx, tgt.y # top of tgt
|
|
234
|
+
else:
|
|
235
|
+
# src below tgt
|
|
236
|
+
p1x, p1y = sx, src.y # top of src
|
|
237
|
+
p4x, p4y = tx, tgt.y + tgt.height # bottom of tgt
|
|
238
|
+
|
|
239
|
+
# Midpoint Y for the horizontal segment
|
|
240
|
+
mid_y = (p1y + p4y) / 2
|
|
241
|
+
|
|
242
|
+
# Build orthogonal path: vertical → horizontal → vertical
|
|
243
|
+
if abs(p1x - p4x) < 3:
|
|
244
|
+
# Nodes are vertically aligned, straight line
|
|
245
|
+
points = f"{p1x:.1f},{p1y:.1f} {p4x:.1f},{p4y:.1f}"
|
|
246
|
+
else:
|
|
247
|
+
points = f"{p1x:.1f},{p1y:.1f} {p1x:.1f},{mid_y:.1f} {p4x:.1f},{mid_y:.1f} {p4x:.1f},{p4y:.1f}"
|
|
248
|
+
else:
|
|
249
|
+
# Horizontal connection (same layer)
|
|
250
|
+
if dx > 0:
|
|
251
|
+
p1x, p1y = src.x + src.width, sy # right of src
|
|
252
|
+
p4x, p4y = tgt.x, ty # left of tgt
|
|
253
|
+
else:
|
|
254
|
+
p1x, p1y = src.x, sy # left of src
|
|
255
|
+
p4x, p4y = tgt.x + tgt.width, ty # right of tgt
|
|
256
|
+
|
|
257
|
+
if abs(p1y - p4y) < 3:
|
|
258
|
+
points = f"{p1x:.1f},{p1y:.1f} {p4x:.1f},{p4y:.1f}"
|
|
259
|
+
else:
|
|
260
|
+
mid_x = (p1x + p4x) / 2
|
|
261
|
+
points = f"{p1x:.1f},{p1y:.1f} {mid_x:.1f},{p1y:.1f} {mid_x:.1f},{p4y:.1f} {p4x:.1f},{p4y:.1f}"
|
|
262
|
+
|
|
263
|
+
# Arrow head (small triangle at end point)
|
|
264
|
+
has_arrow = edge.style.get("endArrow", "classic") != "none"
|
|
265
|
+
arrow_svg = ""
|
|
266
|
+
if has_arrow:
|
|
267
|
+
# Parse last two points to determine arrow direction
|
|
268
|
+
pts = points.strip().split()
|
|
269
|
+
if len(pts) >= 2:
|
|
270
|
+
last = pts[-1].split(",")
|
|
271
|
+
prev = pts[-2].split(",")
|
|
272
|
+
lx, ly = float(last[0]), float(last[1])
|
|
273
|
+
px, py = float(prev[0]), float(prev[1])
|
|
274
|
+
adx, ady = lx - px, ly - py
|
|
275
|
+
length = max((adx**2 + ady**2)**0.5, 0.001)
|
|
276
|
+
# Normalize
|
|
277
|
+
ux, uy = adx/length, ady/length
|
|
278
|
+
# Arrow triangle (size 8)
|
|
279
|
+
sz = 8
|
|
280
|
+
# Perpendicular
|
|
281
|
+
rx, ry = -uy * sz * 0.5, ux * sz * 0.5
|
|
282
|
+
# Arrow base point
|
|
283
|
+
bx, by = lx - ux * sz, ly - uy * sz
|
|
284
|
+
arrow_svg = (f' <polygon points="{lx:.1f},{ly:.1f} {bx+rx:.1f},{by+ry:.1f} '
|
|
285
|
+
f'{bx-rx:.1f},{by-ry:.1f}" fill="{stroke}" />')
|
|
286
|
+
|
|
287
|
+
svg_lines.append(f' <polyline points="{points}" fill="none" '
|
|
288
|
+
f'stroke="{stroke}" stroke-width="1.5"{dash} stroke-linejoin="round" />')
|
|
289
|
+
if arrow_svg:
|
|
290
|
+
svg_lines.append(arrow_svg)
|
|
291
|
+
|
|
292
|
+
# Edge label - position at midpoint of the polyline path
|
|
293
|
+
if edge.label:
|
|
294
|
+
pts = [tuple(map(float, p.split(","))) for p in points.strip().split()]
|
|
295
|
+
if len(pts) >= 2:
|
|
296
|
+
mid_idx = len(pts) // 2
|
|
297
|
+
if len(pts) % 2 == 0:
|
|
298
|
+
lx = (pts[mid_idx-1][0] + pts[mid_idx][0]) / 2
|
|
299
|
+
ly = (pts[mid_idx-1][1] + pts[mid_idx][1]) / 2
|
|
300
|
+
else:
|
|
301
|
+
lx, ly = pts[mid_idx]
|
|
302
|
+
ly -= 8
|
|
303
|
+
label = edge.label.replace("&", "&").replace("<", "<").replace(">", ">")
|
|
304
|
+
svg_lines.append(f' <rect x="{lx-len(label)*3.5-4}" y="{ly-10}" '
|
|
305
|
+
f'width="{len(label)*7+8}" height="16" rx="3" fill="white" fill-opacity="0.85" />')
|
|
306
|
+
svg_lines.append(f' <text x="{lx}" y="{ly}" text-anchor="middle" dominant-baseline="central" '
|
|
307
|
+
f'font-family="Helvetica, Arial, sans-serif" font-size="11" '
|
|
308
|
+
f'fill="{stroke}">{label}</text>')
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def clip_to_rect(sx, sy, tx, ty, node):
|
|
312
|
+
"""Clip a line from (sx,sy) toward (tx,ty) to the boundary of node's rect."""
|
|
313
|
+
cx = node.x + node.width / 2
|
|
314
|
+
cy = node.y + node.height / 2
|
|
315
|
+
dx = tx - sx
|
|
316
|
+
dy = ty - sy
|
|
317
|
+
if abs(dx) < 0.001 and abs(dy) < 0.001:
|
|
318
|
+
return sx, sy
|
|
319
|
+
|
|
320
|
+
hw = node.width / 2
|
|
321
|
+
hh = node.height / 2
|
|
322
|
+
|
|
323
|
+
# Find intersection with rect boundary
|
|
324
|
+
t_vals = []
|
|
325
|
+
if abs(dx) > 0.001:
|
|
326
|
+
t_vals.append(hw / abs(dx))
|
|
327
|
+
if abs(dy) > 0.001:
|
|
328
|
+
t_vals.append(hh / abs(dy))
|
|
329
|
+
if t_vals:
|
|
330
|
+
t = min(t_vals)
|
|
331
|
+
return cx + dx * t * (1 if dx > 0 else -1) / max(abs(dx), 0.001) * min(hw, abs(dx) * t), \
|
|
332
|
+
cy + dy * t * (1 if dy > 0 else -1) / max(abs(dy), 0.001) * min(hh, abs(dy) * t)
|
|
333
|
+
return sx, sy
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def clip_to_rect(sx, sy, tx, ty, node: NodeInfo):
|
|
337
|
+
"""Clip point (sx, sy) to boundary of node rect, coming from direction of (tx, ty)."""
|
|
338
|
+
cx = node.x + node.width / 2
|
|
339
|
+
cy = node.y + node.height / 2
|
|
340
|
+
dx = tx - cx
|
|
341
|
+
dy = ty - cy
|
|
342
|
+
|
|
343
|
+
if abs(dx) < 0.01 and abs(dy) < 0.01:
|
|
344
|
+
return sx, sy
|
|
345
|
+
|
|
346
|
+
hw = node.width / 2
|
|
347
|
+
hh = node.height / 2
|
|
348
|
+
|
|
349
|
+
# Parametric intersection
|
|
350
|
+
scale_x = abs(dx / hw) if hw > 0 else 999
|
|
351
|
+
scale_y = abs(dy / hh) if hh > 0 else 999
|
|
352
|
+
scale = max(scale_x, scale_y)
|
|
353
|
+
|
|
354
|
+
if scale < 0.01:
|
|
355
|
+
return sx, sy
|
|
356
|
+
|
|
357
|
+
return cx + dx / scale, cy + dy / scale
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def render_svg(nodes: dict, edges: list, groups: dict,
|
|
361
|
+
width: int = 1280, height: int = 720,
|
|
362
|
+
title: str = "", bg: str = "#ffffff",
|
|
363
|
+
padding: int = 40) -> str:
|
|
364
|
+
"""Render parsed drawio data as SVG string."""
|
|
365
|
+
|
|
366
|
+
# Auto-scale to fit canvas
|
|
367
|
+
if nodes:
|
|
368
|
+
all_items = list(nodes.values()) + list(groups.values())
|
|
369
|
+
min_x = min(n.x for n in all_items)
|
|
370
|
+
min_y = min(n.y for n in all_items)
|
|
371
|
+
max_x = max(n.x + n.width for n in all_items)
|
|
372
|
+
max_y = max(n.y + n.height for n in all_items)
|
|
373
|
+
|
|
374
|
+
content_w = max_x - min_x
|
|
375
|
+
content_h = max_y - min_y
|
|
376
|
+
avail_w = width - 2 * padding
|
|
377
|
+
avail_h = height - 2 * padding - (40 if title else 0)
|
|
378
|
+
|
|
379
|
+
scale = min(avail_w / max(content_w, 1), avail_h / max(content_h, 1), 2.0)
|
|
380
|
+
|
|
381
|
+
offset_x = padding + (avail_w - content_w * scale) / 2 - min_x * scale
|
|
382
|
+
offset_y = padding + (40 if title else 0) + (avail_h - content_h * scale) / 2 - min_y * scale
|
|
383
|
+
|
|
384
|
+
# Apply transform
|
|
385
|
+
for n in nodes.values():
|
|
386
|
+
n.x = n.x * scale + offset_x
|
|
387
|
+
n.y = n.y * scale + offset_y
|
|
388
|
+
n.width *= scale
|
|
389
|
+
n.height *= scale
|
|
390
|
+
for g in groups.values():
|
|
391
|
+
g.x = g.x * scale + offset_x
|
|
392
|
+
g.y = g.y * scale + offset_y
|
|
393
|
+
g.width *= scale
|
|
394
|
+
g.height *= scale
|
|
395
|
+
|
|
396
|
+
lines = [
|
|
397
|
+
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}" width="{width}" height="{height}">',
|
|
398
|
+
f' <rect width="{width}" height="{height}" fill="{bg}" />',
|
|
399
|
+
]
|
|
400
|
+
|
|
401
|
+
# Title
|
|
402
|
+
if title:
|
|
403
|
+
lines.append(f' <text x="{width/2}" y="32" text-anchor="middle" '
|
|
404
|
+
f'font-family="Helvetica, Arial, sans-serif" font-size="20" '
|
|
405
|
+
f'font-weight="bold" fill="#333333">{title}</text>')
|
|
406
|
+
|
|
407
|
+
# Groups (background)
|
|
408
|
+
for g in groups.values():
|
|
409
|
+
r = 8
|
|
410
|
+
fill = color_from_style(g.style, "fillColor", "#f5f5f5")
|
|
411
|
+
stroke = color_from_style(g.style, "strokeColor", "#999999")
|
|
412
|
+
lines.append(f' <rect x="{g.x}" y="{g.y}" width="{g.width}" height="{g.height}" '
|
|
413
|
+
f'rx="{r}" fill="{fill}" stroke="{stroke}" stroke-width="1" '
|
|
414
|
+
f'stroke-dasharray="4 4" />')
|
|
415
|
+
if g.label:
|
|
416
|
+
label = g.label.replace("&", "&").replace("<", "<")
|
|
417
|
+
lines.append(f' <text x="{g.x + 10}" y="{g.y + 18}" '
|
|
418
|
+
f'font-family="Helvetica, Arial, sans-serif" font-size="13" '
|
|
419
|
+
f'font-weight="bold" fill="#666666">{label}</text>')
|
|
420
|
+
|
|
421
|
+
# Edges (behind nodes)
|
|
422
|
+
for e in edges:
|
|
423
|
+
svg_edge(e, nodes, lines)
|
|
424
|
+
|
|
425
|
+
# Nodes (on top)
|
|
426
|
+
for n in nodes.values():
|
|
427
|
+
svg_shape(n, lines)
|
|
428
|
+
|
|
429
|
+
lines.append('</svg>')
|
|
430
|
+
return "\n".join(lines)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def main():
|
|
434
|
+
parser = argparse.ArgumentParser(description="Convert draw.io to PPT-compatible SVG")
|
|
435
|
+
parser.add_argument("input", help="Input .drawio file")
|
|
436
|
+
parser.add_argument("-o", "--output", required=True, help="Output .svg file")
|
|
437
|
+
parser.add_argument("--width", type=int, default=1280, help="SVG width (default: 1280)")
|
|
438
|
+
parser.add_argument("--height", type=int, default=720, help="SVG height (default: 720)")
|
|
439
|
+
parser.add_argument("--title", default="", help="Diagram title")
|
|
440
|
+
parser.add_argument("--background", default="#ffffff", help="Background color")
|
|
441
|
+
parser.add_argument("--padding", type=int, default=40, help="Padding around content")
|
|
442
|
+
args = parser.parse_args()
|
|
443
|
+
|
|
444
|
+
nodes, edges, groups = parse_drawio(args.input)
|
|
445
|
+
svg = render_svg(nodes, edges, groups,
|
|
446
|
+
width=args.width, height=args.height,
|
|
447
|
+
title=args.title, bg=args.background,
|
|
448
|
+
padding=args.padding)
|
|
449
|
+
|
|
450
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
451
|
+
f.write(svg)
|
|
452
|
+
|
|
453
|
+
print(f"✅ Converted: {args.output}")
|
|
454
|
+
print(f" Canvas: {args.width}x{args.height}, Nodes: {len(nodes)}, Edges: {len(edges)}")
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
if __name__ == "__main__":
|
|
458
|
+
main()
|
|
@@ -238,6 +238,20 @@ def finalize_project(
|
|
|
238
238
|
|
|
239
239
|
def main() -> None:
|
|
240
240
|
"""Run the CLI entry point."""
|
|
241
|
+
try:
|
|
242
|
+
_main_impl()
|
|
243
|
+
except KeyboardInterrupt:
|
|
244
|
+
print("\nInterrupted by user.")
|
|
245
|
+
sys.exit(130)
|
|
246
|
+
except SystemExit:
|
|
247
|
+
raise
|
|
248
|
+
except Exception as e:
|
|
249
|
+
safe_print(f"[ERROR] Fatal error: {type(e).__name__}: {e}")
|
|
250
|
+
sys.exit(1)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _main_impl() -> None:
|
|
254
|
+
"""Internal implementation of main CLI logic."""
|
|
241
255
|
parser = argparse.ArgumentParser(
|
|
242
256
|
description='PPT Master - SVG Post-processing Tool',
|
|
243
257
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
Binary file
|
package/skills/ppt-master/scripts/image_backends/__pycache__/backend_aigw_gemini.cpython-314.pyc
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|