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.
Files changed (211) hide show
  1. package/bin/check.mjs +0 -0
  2. package/bin/install.mjs +0 -0
  3. package/package.json +2 -1
  4. package/skills/ppt-master/.env.example +35 -0
  5. package/skills/ppt-master/README.md +90 -0
  6. package/skills/ppt-master/SKILL.md +390 -17
  7. package/skills/ppt-master/audit_report_model_perf.md +213 -0
  8. package/skills/ppt-master/audit_report_portability.md +90 -0
  9. package/skills/ppt-master/audit_report_robustness.md +192 -0
  10. package/skills/ppt-master/audit_report_workflow.md +233 -0
  11. package/skills/ppt-master/fix_report_bid.md +51 -0
  12. package/skills/ppt-master/fix_report_scripts.md +41 -0
  13. package/skills/ppt-master/references/bid-content-example-netease.md +815 -0
  14. package/skills/ppt-master/references/bid-content-template.md +390 -0
  15. package/skills/ppt-master/references/bid-example-netease.md +339 -0
  16. package/skills/ppt-master/references/bid-presentation.md +384 -0
  17. package/skills/ppt-master/references/bid-svgs/03_toc.svg +38 -0
  18. package/skills/ppt-master/references/bid-svgs/04_chapter_company.svg +7 -0
  19. package/skills/ppt-master/references/bid-svgs/05_company_overview.svg +28 -0
  20. package/skills/ppt-master/references/bid-svgs/06_vision_business.svg +45 -0
  21. package/skills/ppt-master/references/bid-svgs/07_product_system.svg +52 -0
  22. package/skills/ppt-master/references/bid-svgs/08_codewave_timeline.svg +29 -0
  23. package/skills/ppt-master/references/bid-svgs/09_certifications.svg +33 -0
  24. package/skills/ppt-master/references/bid-svgs/10_client_logos.svg +25 -0
  25. package/skills/ppt-master/references/bid-svgs/11_brand_mission.svg +14 -0
  26. package/skills/ppt-master/references/bid-svgs/12_chapter_demand.svg +7 -0
  27. package/skills/ppt-master/references/bid-svgs/16_chapter_advantages.svg +7 -0
  28. package/skills/ppt-master/references/bid-svgs/18_platform_arch.svg +24 -0
  29. package/skills/ppt-master/references/bid-svgs/19_function_matrix.svg +35 -0
  30. package/skills/ppt-master/references/bid-svgs/20_dev_arch.svg +29 -0
  31. package/skills/ppt-master/references/bid-svgs/21_source_code.svg +25 -0
  32. package/skills/ppt-master/references/bid-svgs/22_ai_dev.svg +27 -0
  33. package/skills/ppt-master/references/bid-svgs/23_asset_market.svg +33 -0
  34. package/skills/ppt-master/references/bid-svgs/24_multi_org.svg +28 -0
  35. package/skills/ppt-master/references/bid-svgs/25_i18n.svg +22 -0
  36. package/skills/ppt-master/references/bid-svgs/26_multi_tenant.svg +25 -0
  37. package/skills/ppt-master/references/bid-svgs/27_integration.svg +27 -0
  38. package/skills/ppt-master/references/bid-svgs/28_permission.svg +31 -0
  39. package/skills/ppt-master/references/bid-svgs/29_security.svg +25 -0
  40. package/skills/ppt-master/references/bid-svgs/30_ai_capabilities.svg +21 -0
  41. package/skills/ppt-master/references/bid-svgs/31_product_combo.svg +25 -0
  42. package/skills/ppt-master/references/bid-svgs/32_digital_transform.svg +28 -0
  43. package/skills/ppt-master/references/bid-svgs/33_chapter_cases.svg +7 -0
  44. package/skills/ppt-master/references/bid-svgs/34_cummins_case.svg +23 -0
  45. package/skills/ppt-master/references/bid-svgs/35_cpq_cost.svg +22 -0
  46. package/skills/ppt-master/references/bid-svgs/36_tech_platform.svg +18 -0
  47. package/skills/ppt-master/references/bid-svgs/37_platform_value.svg +24 -0
  48. package/skills/ppt-master/references/bid-svgs/38_operation_plan.svg +25 -0
  49. package/skills/ppt-master/references/bid-svgs/39_zpmc_case.svg +21 -0
  50. package/skills/ppt-master/references/bid-svgs/40_zpmc_apps.svg +23 -0
  51. package/skills/ppt-master/references/bid-svgs/41_chapter_service.svg +7 -0
  52. package/skills/ppt-master/references/bid-svgs/42_tech_support.svg +24 -0
  53. package/skills/ppt-master/references/bid-svgs/43_knowledge_transfer.svg +25 -0
  54. package/skills/ppt-master/references/bid-svgs/44_operation_promotion.svg +20 -0
  55. package/skills/ppt-master/references/bid-svgs/45_custom_dev_guide.svg +24 -0
  56. package/skills/ppt-master/references/bid-svgs/46_sla_guarantee.svg +18 -0
  57. package/skills/ppt-master/references/bid-svgs/47_strategic_cooperation.svg +25 -0
  58. package/skills/ppt-master/references/bid-svgs/48_ecosystem.svg +24 -0
  59. package/skills/ppt-master/references/bid-svgs/49_chapter_delivery.svg +23 -0
  60. package/skills/ppt-master/references/bid-svgs/53_pm_intro.svg +24 -0
  61. package/skills/ppt-master/references/bid-svgs/54_after_sales.svg +27 -0
  62. package/skills/ppt-master/references/bid-svgs/55_ending.svg +8 -0
  63. package/skills/ppt-master/references/company_intro.md +93 -0
  64. package/skills/ppt-master/references/company_intro_images/slide1_img00.png +0 -0
  65. package/skills/ppt-master/references/company_intro_images/slide1_img01.png +0 -0
  66. package/skills/ppt-master/references/company_intro_images/slide1_img02.png +0 -0
  67. package/skills/ppt-master/references/company_intro_images/slide1_img03.png +0 -0
  68. package/skills/ppt-master/references/company_intro_images/slide1_img04.png +0 -0
  69. package/skills/ppt-master/references/company_intro_images/slide1_img05.png +0 -0
  70. package/skills/ppt-master/references/company_intro_images/slide1_img06.png +0 -0
  71. package/skills/ppt-master/references/company_intro_images/slide1_img07.png +0 -0
  72. package/skills/ppt-master/references/company_intro_images/slide1_img08.png +0 -0
  73. package/skills/ppt-master/references/company_intro_images/slide1_img09.png +0 -0
  74. package/skills/ppt-master/references/company_intro_images/slide1_img10.png +0 -0
  75. package/skills/ppt-master/references/company_intro_images/slide1_img11.png +0 -0
  76. package/skills/ppt-master/references/company_intro_images/slide1_img12.png +0 -0
  77. package/skills/ppt-master/references/company_intro_images/slide1_img13.png +0 -0
  78. package/skills/ppt-master/references/company_intro_images/slide1_img14.png +0 -0
  79. package/skills/ppt-master/references/company_intro_images/slide1_img15.png +0 -0
  80. package/skills/ppt-master/references/company_intro_images/slide1_img16.png +0 -0
  81. package/skills/ppt-master/references/company_intro_images/slide1_img17.png +0 -0
  82. package/skills/ppt-master/references/company_intro_images/slide1_img18.png +0 -0
  83. package/skills/ppt-master/references/company_intro_images/slide1_img19.png +0 -0
  84. package/skills/ppt-master/references/company_intro_images/slide1_img20.png +0 -0
  85. package/skills/ppt-master/references/company_intro_images/slide1_img21.png +0 -0
  86. package/skills/ppt-master/references/company_intro_images/slide1_img22.png +0 -0
  87. package/skills/ppt-master/references/company_intro_images/slide1_img23.png +0 -0
  88. package/skills/ppt-master/references/company_intro_images/slide1_img24.png +0 -0
  89. package/skills/ppt-master/references/company_intro_images/slide1_img25.png +0 -0
  90. package/skills/ppt-master/references/company_intro_images/slide1_img26.png +0 -0
  91. package/skills/ppt-master/references/company_intro_images/slide1_img27.png +0 -0
  92. package/skills/ppt-master/references/company_intro_images/slide1_img28.png +0 -0
  93. package/skills/ppt-master/references/company_intro_images/slide1_img29.png +0 -0
  94. package/skills/ppt-master/references/company_intro_images/slide2_img30.png +0 -0
  95. package/skills/ppt-master/references/company_intro_images/slide2_img31.jpg +0 -0
  96. package/skills/ppt-master/references/company_intro_images/slide3_img32.png +0 -0
  97. package/skills/ppt-master/references/company_intro_images/slide3_img33.png +0 -0
  98. package/skills/ppt-master/references/drawio-integration.md +144 -0
  99. package/skills/ppt-master/references/executor-base.md +3 -4
  100. package/skills/ppt-master/references/image-generator.md +93 -6
  101. package/skills/ppt-master/references/strategist.md +8 -5
  102. package/skills/ppt-master/requirements.txt +19 -0
  103. package/skills/ppt-master/resources/backgrounds/chapter_bg.png +0 -0
  104. package/skills/ppt-master/resources/backgrounds/company_bg.png +0 -0
  105. package/skills/ppt-master/resources/backgrounds/cover_bg.png +0 -0
  106. package/skills/ppt-master/resources/backgrounds/index.json +40 -0
  107. package/skills/ppt-master/resources/backgrounds/tech_bg.png +0 -0
  108. package/skills/ppt-master/scripts/README.md +1 -1
  109. package/skills/ppt-master/scripts/__pycache__/config.cpython-314.pyc +0 -0
  110. package/skills/ppt-master/scripts/__pycache__/error_helper.cpython-314.pyc +0 -0
  111. package/skills/ppt-master/scripts/__pycache__/pptx_animations.cpython-314.pyc +0 -0
  112. package/skills/ppt-master/scripts/__pycache__/project_utils.cpython-314.pyc +0 -0
  113. package/skills/ppt-master/scripts/arch_diagram.py +370 -0
  114. package/skills/ppt-master/scripts/bid_init.py +79 -0
  115. package/skills/ppt-master/scripts/drawio_gen.py +366 -0
  116. package/skills/ppt-master/scripts/drawio_to_svg.py +458 -0
  117. package/skills/ppt-master/scripts/finalize_svg.py +14 -0
  118. package/skills/ppt-master/scripts/image_backends/__pycache__/__init__.cpython-314.pyc +0 -0
  119. package/skills/ppt-master/scripts/image_backends/__pycache__/backend_aigw_gemini.cpython-314.pyc +0 -0
  120. package/skills/ppt-master/scripts/image_backends/__pycache__/backend_common.cpython-314.pyc +0 -0
  121. package/skills/ppt-master/scripts/svg_finalize/__pycache__/__init__.cpython-314.pyc +0 -0
  122. package/skills/ppt-master/scripts/svg_finalize/__pycache__/crop_images.cpython-314.pyc +0 -0
  123. package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_icons.cpython-314.pyc +0 -0
  124. package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_images.cpython-314.pyc +0 -0
  125. package/skills/ppt-master/scripts/svg_finalize/__pycache__/fix_image_aspect.cpython-314.pyc +0 -0
  126. package/skills/ppt-master/scripts/svg_finalize/__pycache__/flatten_tspan.cpython-314.pyc +0 -0
  127. package/skills/ppt-master/scripts/svg_finalize/__pycache__/svg_rect_to_path.cpython-314.pyc +0 -0
  128. package/skills/ppt-master/scripts/svg_page_gen.py +871 -0
  129. package/skills/ppt-master/scripts/svg_quality_checker.py +169 -8
  130. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/__init__.cpython-314.pyc +0 -0
  131. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_context.cpython-314.pyc +0 -0
  132. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_converter.cpython-314.pyc +0 -0
  133. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_elements.cpython-314.pyc +0 -0
  134. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_paths.cpython-314.pyc +0 -0
  135. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_styles.cpython-314.pyc +0 -0
  136. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_utils.cpython-314.pyc +0 -0
  137. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_builder.cpython-314.pyc +0 -0
  138. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_cli.cpython-314.pyc +0 -0
  139. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_dimensions.cpython-314.pyc +0 -0
  140. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_discovery.cpython-314.pyc +0 -0
  141. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_media.cpython-314.pyc +0 -0
  142. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_notes.cpython-314.pyc +0 -0
  143. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_slide_xml.cpython-314.pyc +0 -0
  144. package/skills/ppt-master/scripts/svg_to_pptx/pptx_builder.py +7 -1
  145. package/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py +14 -0
  146. package/skills/ppt-master/projects/.gitkeep +0 -0
  147. /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
  148. /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
  149. /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
  150. /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
  151. /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
  152. /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
  153. /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
  154. /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
  155. /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
  156. /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
  157. /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
  158. /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
  159. /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
  160. /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
  161. /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
  162. /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
  163. /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
  164. /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
  165. /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
  166. /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
  167. /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
  168. /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
  169. /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
  170. /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
  171. /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
  172. /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
  173. /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
  174. /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
  175. /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
  176. /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
  177. /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
  178. /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
  179. /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
  180. /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
  181. /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
  182. /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
  183. /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
  184. /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
  185. /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
  186. /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
  187. /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
  188. /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
  189. /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
  190. /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
  191. /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
  192. /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
  193. /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
  194. /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
  195. /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
  196. /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
  197. /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
  198. /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
  199. /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
  200. /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
  201. /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
  202. /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
  203. /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
  204. /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
  205. /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
  206. /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
  207. /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
  208. /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
  209. /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
  210. /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
  211. /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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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("&", "&amp;").replace("<", "&lt;")
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,