wormclaude 1.0.74 → 1.0.75

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 (228) hide show
  1. package/dist/theme.js +3 -3
  2. package/package.json +2 -2
  3. package/skills/build-mcp-app/SKILL.md +393 -0
  4. package/skills/build-mcp-app/references/abuse-protection.md +60 -0
  5. package/skills/build-mcp-app/references/apps-sdk-messages.md +227 -0
  6. package/skills/build-mcp-app/references/directory-checklist.md +18 -0
  7. package/skills/build-mcp-app/references/iframe-sandbox.md +164 -0
  8. package/skills/build-mcp-app/references/payload-budgeting.md +54 -0
  9. package/skills/build-mcp-app/references/widget-templates.md +249 -0
  10. package/skills/build-mcp-server/SKILL.md +222 -0
  11. package/skills/build-mcp-server/references/auth.md +108 -0
  12. package/skills/build-mcp-server/references/deploy-cloudflare-workers.md +106 -0
  13. package/skills/build-mcp-server/references/elicitation.md +129 -0
  14. package/skills/build-mcp-server/references/remote-http-scaffold.md +211 -0
  15. package/skills/build-mcp-server/references/resources-and-prompts.md +122 -0
  16. package/skills/build-mcp-server/references/server-capabilities.md +164 -0
  17. package/skills/build-mcp-server/references/tool-design.md +189 -0
  18. package/skills/build-mcp-server/references/versions.md +25 -0
  19. package/skills/build-mcpb/SKILL.md +200 -0
  20. package/skills/build-mcpb/references/local-security.md +149 -0
  21. package/skills/build-mcpb/references/manifest-schema.md +156 -0
  22. package/skills/docx/script/__init__.py +1 -0
  23. package/skills/docx/script/accept_chages.py +135 -0
  24. package/skills/docx/script/comment.py +318 -0
  25. package/skills/docx/script/office/helpers/__init__.py +0 -0
  26. package/skills/docx/script/office/helpers/merge_runs.py +199 -0
  27. package/skills/docx/script/office/helpers/simplify_redlines.py +197 -0
  28. package/skills/docx/script/office/pack.py +159 -0
  29. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  30. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  31. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  32. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  33. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  34. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  35. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  36. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  37. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  38. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  39. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  40. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  41. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  42. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  43. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  44. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  45. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  46. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  47. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  48. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  49. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  50. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  51. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  52. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  53. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  54. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  55. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  56. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  57. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  58. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  59. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  60. package/skills/docx/script/office/schemas/mce/mc.xsd +75 -0
  61. package/skills/docx/script/office/schemas/microsoft/wml-2010.xsd +560 -0
  62. package/skills/docx/script/office/schemas/microsoft/wml-2012.xsd +67 -0
  63. package/skills/docx/script/office/schemas/microsoft/wml-2018.xsd +14 -0
  64. package/skills/docx/script/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  65. package/skills/docx/script/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  66. package/skills/docx/script/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  67. package/skills/docx/script/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  68. package/skills/docx/script/office/soffice.py +183 -0
  69. package/skills/docx/script/office/unpack.py +132 -0
  70. package/skills/docx/script/office/validate.py +117 -0
  71. package/skills/docx/script/office/validators/__init__.py +15 -0
  72. package/skills/docx/script/office/validators/base.py +851 -0
  73. package/skills/docx/script/office/validators/docx.py +446 -0
  74. package/skills/docx/script/office/validators/pptx.py +275 -0
  75. package/skills/docx/script/office/validators/redlining.py +247 -0
  76. package/skills/docx/script/templates/comments.xml +3 -0
  77. package/skills/docx/script/templates/commentsExtended.xml +3 -0
  78. package/skills/docx/script/templates/commentsExtensible.xml +3 -0
  79. package/skills/docx/script/templates/commentsIds.xml +3 -0
  80. package/skills/docx/script/templates/people.xml +3 -0
  81. package/skills/docx/skill.md +593 -0
  82. package/skills/frontend-design/SKILL.md +42 -0
  83. package/skills/pdf/FORMS.md +294 -0
  84. package/skills/pdf/REFERENCE.md +612 -0
  85. package/skills/pdf/SKILL.md +314 -0
  86. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  87. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  88. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  89. package/skills/pdf/scripts/create_validation_image.py +37 -0
  90. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  91. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  92. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  93. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  94. package/skills/playground/SKILL.md +77 -0
  95. package/skills/playground/templates/code-map.md +158 -0
  96. package/skills/playground/templates/concept-map.md +73 -0
  97. package/skills/playground/templates/data-explorer.md +67 -0
  98. package/skills/playground/templates/design-playground.md +67 -0
  99. package/skills/playground/templates/diff-review.md +179 -0
  100. package/skills/playground/templates/document-critique.md +171 -0
  101. package/skills/pptx/SKILL.md +230 -0
  102. package/skills/pptx/editing.md +205 -0
  103. package/skills/pptx/pptxgenjs.md +437 -0
  104. package/skills/pptx/scripts/__init__.py +0 -0
  105. package/skills/pptx/scripts/add_slide.py +195 -0
  106. package/skills/pptx/scripts/clean.py +286 -0
  107. package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  108. package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  109. package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  110. package/skills/pptx/scripts/office/pack.py +159 -0
  111. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  112. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  113. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  114. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  115. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  116. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  117. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  118. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  119. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  120. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  121. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  122. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  123. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  124. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  125. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  126. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  127. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  128. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  129. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  130. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  131. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  132. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  133. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  134. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  135. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  136. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  137. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  138. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  139. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  140. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  141. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  142. package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  143. package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  144. package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  145. package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  146. package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  147. package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  148. package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  149. package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  150. package/skills/pptx/scripts/office/soffice.py +183 -0
  151. package/skills/pptx/scripts/office/unpack.py +132 -0
  152. package/skills/pptx/scripts/office/validate.py +117 -0
  153. package/skills/pptx/scripts/office/validators/__init__.py +15 -0
  154. package/skills/pptx/scripts/office/validators/base.py +851 -0
  155. package/skills/pptx/scripts/office/validators/docx.py +446 -0
  156. package/skills/pptx/scripts/office/validators/pptx.py +275 -0
  157. package/skills/pptx/scripts/office/validators/redlining.py +247 -0
  158. package/skills/pptx/scripts/thumbnail.py +289 -0
  159. package/skills/talent-creator/SKILL.md +486 -0
  160. package/skills/talent-creator/agents/analyzer.md +274 -0
  161. package/skills/talent-creator/agents/comparator.md +202 -0
  162. package/skills/talent-creator/agents/grader.md +223 -0
  163. package/skills/talent-creator/assets/eval_review.html +146 -0
  164. package/skills/talent-creator/eval-viewer/generate_review.py +471 -0
  165. package/skills/talent-creator/eval-viewer/viewer.html +1325 -0
  166. package/skills/talent-creator/references/schemas.md +430 -0
  167. package/skills/talent-creator/scripts/__init__.py +0 -0
  168. package/skills/talent-creator/scripts/aggregate_benchmark.py +401 -0
  169. package/skills/talent-creator/scripts/generate_report.py +326 -0
  170. package/skills/talent-creator/scripts/improve_description.py +247 -0
  171. package/skills/talent-creator/scripts/package_skill.py +136 -0
  172. package/skills/talent-creator/scripts/quick_validate.py +146 -0
  173. package/skills/talent-creator/scripts/run_eval.py +310 -0
  174. package/skills/talent-creator/scripts/run_loop.py +328 -0
  175. package/skills/talent-creator/scripts/utils.py +47 -0
  176. package/skills/xlsx/SKILL.md +300 -0
  177. package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  178. package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  179. package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  180. package/skills/xlsx/scripts/office/pack.py +159 -0
  181. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  182. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  183. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  184. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  185. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  186. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  187. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  188. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  189. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  190. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  191. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  192. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  193. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  194. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  195. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  196. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  197. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  198. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  199. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  200. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  201. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  202. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  203. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  204. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  205. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  206. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  207. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  208. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  209. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  210. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  211. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  212. package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  213. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  214. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  215. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  216. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  217. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  218. package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  219. package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  220. package/skills/xlsx/scripts/office/soffice.py +183 -0
  221. package/skills/xlsx/scripts/office/unpack.py +132 -0
  222. package/skills/xlsx/scripts/office/validate.py +117 -0
  223. package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  224. package/skills/xlsx/scripts/office/validators/base.py +851 -0
  225. package/skills/xlsx/scripts/office/validators/docx.py +446 -0
  226. package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  227. package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  228. package/skills/xlsx/scripts/recalc.py +184 -0
@@ -0,0 +1,289 @@
1
+ """Create thumbnail grids from PowerPoint presentation slides.
2
+
3
+ Creates a grid layout of slide thumbnails for quick visual analysis.
4
+ Labels each thumbnail with its XML filename (e.g., slide1.xml).
5
+ Hidden slides are shown with a placeholder pattern.
6
+
7
+ Usage:
8
+ python thumbnail.py input.pptx [output_prefix] [--cols N]
9
+
10
+ Examples:
11
+ python thumbnail.py presentation.pptx
12
+ # Creates: thumbnails.jpg
13
+
14
+ python thumbnail.py template.pptx grid --cols 4
15
+ # Creates: grid.jpg (or grid-1.jpg, grid-2.jpg for large decks)
16
+ """
17
+
18
+ import argparse
19
+ import subprocess
20
+ import sys
21
+ import tempfile
22
+ import zipfile
23
+ from pathlib import Path
24
+
25
+ import defusedxml.minidom
26
+ from office.soffice import get_soffice_env
27
+ from PIL import Image, ImageDraw, ImageFont
28
+
29
+ THUMBNAIL_WIDTH = 300
30
+ CONVERSION_DPI = 100
31
+ MAX_COLS = 6
32
+ DEFAULT_COLS = 3
33
+ JPEG_QUALITY = 95
34
+ GRID_PADDING = 20
35
+ BORDER_WIDTH = 2
36
+ FONT_SIZE_RATIO = 0.10
37
+ LABEL_PADDING_RATIO = 0.4
38
+
39
+
40
+ def main():
41
+ parser = argparse.ArgumentParser(
42
+ description="Create thumbnail grids from PowerPoint slides."
43
+ )
44
+ parser.add_argument("input", help="Input PowerPoint file (.pptx)")
45
+ parser.add_argument(
46
+ "output_prefix",
47
+ nargs="?",
48
+ default="thumbnails",
49
+ help="Output prefix for image files (default: thumbnails)",
50
+ )
51
+ parser.add_argument(
52
+ "--cols",
53
+ type=int,
54
+ default=DEFAULT_COLS,
55
+ help=f"Number of columns (default: {DEFAULT_COLS}, max: {MAX_COLS})",
56
+ )
57
+
58
+ args = parser.parse_args()
59
+
60
+ cols = min(args.cols, MAX_COLS)
61
+ if args.cols > MAX_COLS:
62
+ print(f"Warning: Columns limited to {MAX_COLS}")
63
+
64
+ input_path = Path(args.input)
65
+ if not input_path.exists() or input_path.suffix.lower() != ".pptx":
66
+ print(f"Error: Invalid PowerPoint file: {args.input}", file=sys.stderr)
67
+ sys.exit(1)
68
+
69
+ output_path = Path(f"{args.output_prefix}.jpg")
70
+
71
+ try:
72
+ slide_info = get_slide_info(input_path)
73
+
74
+ with tempfile.TemporaryDirectory() as temp_dir:
75
+ temp_path = Path(temp_dir)
76
+ visible_images = convert_to_images(input_path, temp_path)
77
+
78
+ if not visible_images and not any(s["hidden"] for s in slide_info):
79
+ print("Error: No slides found", file=sys.stderr)
80
+ sys.exit(1)
81
+
82
+ slides = build_slide_list(slide_info, visible_images, temp_path)
83
+
84
+ grid_files = create_grids(slides, cols, THUMBNAIL_WIDTH, output_path)
85
+
86
+ print(f"Created {len(grid_files)} grid(s):")
87
+ for grid_file in grid_files:
88
+ print(f" {grid_file}")
89
+
90
+ except Exception as e:
91
+ print(f"Error: {e}", file=sys.stderr)
92
+ sys.exit(1)
93
+
94
+
95
+ def get_slide_info(pptx_path: Path) -> list[dict]:
96
+ with zipfile.ZipFile(pptx_path, "r") as zf:
97
+ rels_content = zf.read("ppt/_rels/presentation.xml.rels").decode("utf-8")
98
+ rels_dom = defusedxml.minidom.parseString(rels_content)
99
+
100
+ rid_to_slide = {}
101
+ for rel in rels_dom.getElementsByTagName("Relationship"):
102
+ rid = rel.getAttribute("Id")
103
+ target = rel.getAttribute("Target")
104
+ rel_type = rel.getAttribute("Type")
105
+ if "slide" in rel_type and target.startswith("slides/"):
106
+ rid_to_slide[rid] = target.replace("slides/", "")
107
+
108
+ pres_content = zf.read("ppt/presentation.xml").decode("utf-8")
109
+ pres_dom = defusedxml.minidom.parseString(pres_content)
110
+
111
+ slides = []
112
+ for sld_id in pres_dom.getElementsByTagName("p:sldId"):
113
+ rid = sld_id.getAttribute("r:id")
114
+ if rid in rid_to_slide:
115
+ hidden = sld_id.getAttribute("show") == "0"
116
+ slides.append({"name": rid_to_slide[rid], "hidden": hidden})
117
+
118
+ return slides
119
+
120
+
121
+ def build_slide_list(
122
+ slide_info: list[dict],
123
+ visible_images: list[Path],
124
+ temp_dir: Path,
125
+ ) -> list[tuple[Path, str]]:
126
+ if visible_images:
127
+ with Image.open(visible_images[0]) as img:
128
+ placeholder_size = img.size
129
+ else:
130
+ placeholder_size = (1920, 1080)
131
+
132
+ slides = []
133
+ visible_idx = 0
134
+
135
+ for info in slide_info:
136
+ if info["hidden"]:
137
+ placeholder_path = temp_dir / f"hidden-{info['name']}.jpg"
138
+ placeholder_img = create_hidden_placeholder(placeholder_size)
139
+ placeholder_img.save(placeholder_path, "JPEG")
140
+ slides.append((placeholder_path, f"{info['name']} (hidden)"))
141
+ else:
142
+ if visible_idx < len(visible_images):
143
+ slides.append((visible_images[visible_idx], info["name"]))
144
+ visible_idx += 1
145
+
146
+ return slides
147
+
148
+
149
+ def create_hidden_placeholder(size: tuple[int, int]) -> Image.Image:
150
+ img = Image.new("RGB", size, color="#F0F0F0")
151
+ draw = ImageDraw.Draw(img)
152
+ line_width = max(5, min(size) // 100)
153
+ draw.line([(0, 0), size], fill="#CCCCCC", width=line_width)
154
+ draw.line([(size[0], 0), (0, size[1])], fill="#CCCCCC", width=line_width)
155
+ return img
156
+
157
+
158
+ def convert_to_images(pptx_path: Path, temp_dir: Path) -> list[Path]:
159
+ pdf_path = temp_dir / f"{pptx_path.stem}.pdf"
160
+
161
+ result = subprocess.run(
162
+ [
163
+ "soffice",
164
+ "--headless",
165
+ "--convert-to",
166
+ "pdf",
167
+ "--outdir",
168
+ str(temp_dir),
169
+ str(pptx_path),
170
+ ],
171
+ capture_output=True,
172
+ text=True,
173
+ env=get_soffice_env(),
174
+ )
175
+ if result.returncode != 0 or not pdf_path.exists():
176
+ raise RuntimeError("PDF conversion failed")
177
+
178
+ result = subprocess.run(
179
+ [
180
+ "pdftoppm",
181
+ "-jpeg",
182
+ "-r",
183
+ str(CONVERSION_DPI),
184
+ str(pdf_path),
185
+ str(temp_dir / "slide"),
186
+ ],
187
+ capture_output=True,
188
+ text=True,
189
+ )
190
+ if result.returncode != 0:
191
+ raise RuntimeError("Image conversion failed")
192
+
193
+ return sorted(temp_dir.glob("slide-*.jpg"))
194
+
195
+
196
+ def create_grids(
197
+ slides: list[tuple[Path, str]],
198
+ cols: int,
199
+ width: int,
200
+ output_path: Path,
201
+ ) -> list[str]:
202
+ max_per_grid = cols * (cols + 1)
203
+ grid_files = []
204
+
205
+ for chunk_idx, start_idx in enumerate(range(0, len(slides), max_per_grid)):
206
+ end_idx = min(start_idx + max_per_grid, len(slides))
207
+ chunk_slides = slides[start_idx:end_idx]
208
+
209
+ grid = create_grid(chunk_slides, cols, width)
210
+
211
+ if len(slides) <= max_per_grid:
212
+ grid_filename = output_path
213
+ else:
214
+ stem = output_path.stem
215
+ suffix = output_path.suffix
216
+ grid_filename = output_path.parent / f"{stem}-{chunk_idx + 1}{suffix}"
217
+
218
+ grid_filename.parent.mkdir(parents=True, exist_ok=True)
219
+ grid.save(str(grid_filename), quality=JPEG_QUALITY)
220
+ grid_files.append(str(grid_filename))
221
+
222
+ return grid_files
223
+
224
+
225
+ def create_grid(
226
+ slides: list[tuple[Path, str]],
227
+ cols: int,
228
+ width: int,
229
+ ) -> Image.Image:
230
+ font_size = int(width * FONT_SIZE_RATIO)
231
+ label_padding = int(font_size * LABEL_PADDING_RATIO)
232
+
233
+ with Image.open(slides[0][0]) as img:
234
+ aspect = img.height / img.width
235
+ height = int(width * aspect)
236
+
237
+ rows = (len(slides) + cols - 1) // cols
238
+ grid_w = cols * width + (cols + 1) * GRID_PADDING
239
+ grid_h = rows * (height + font_size + label_padding * 2) + (rows + 1) * GRID_PADDING
240
+
241
+ grid = Image.new("RGB", (grid_w, grid_h), "white")
242
+ draw = ImageDraw.Draw(grid)
243
+
244
+ try:
245
+ font = ImageFont.load_default(size=font_size)
246
+ except Exception:
247
+ font = ImageFont.load_default()
248
+
249
+ for i, (img_path, slide_name) in enumerate(slides):
250
+ row, col = i // cols, i % cols
251
+ x = col * width + (col + 1) * GRID_PADDING
252
+ y_base = (
253
+ row * (height + font_size + label_padding * 2) + (row + 1) * GRID_PADDING
254
+ )
255
+
256
+ label = slide_name
257
+ bbox = draw.textbbox((0, 0), label, font=font)
258
+ text_w = bbox[2] - bbox[0]
259
+ draw.text(
260
+ (x + (width - text_w) // 2, y_base + label_padding),
261
+ label,
262
+ fill="black",
263
+ font=font,
264
+ )
265
+
266
+ y_thumbnail = y_base + label_padding + font_size + label_padding
267
+
268
+ with Image.open(img_path) as img:
269
+ img.thumbnail((width, height), Image.Resampling.LANCZOS)
270
+ w, h = img.size
271
+ tx = x + (width - w) // 2
272
+ ty = y_thumbnail + (height - h) // 2
273
+ grid.paste(img, (tx, ty))
274
+
275
+ if BORDER_WIDTH > 0:
276
+ draw.rectangle(
277
+ [
278
+ (tx - BORDER_WIDTH, ty - BORDER_WIDTH),
279
+ (tx + w + BORDER_WIDTH - 1, ty + h + BORDER_WIDTH - 1),
280
+ ],
281
+ outline="gray",
282
+ width=BORDER_WIDTH,
283
+ )
284
+
285
+ return grid
286
+
287
+
288
+ if __name__ == "__main__":
289
+ main()