codewave-openclaw-installer 2.1.13 → 2.2.1

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