@_vrsen/openswarm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/bin/openswarm.js +38 -0
- package/config.py +34 -0
- package/data_analyst_agent/.cursor/rules/data_analyst.mdc +43 -0
- package/data_analyst_agent/__init__.py +3 -0
- package/data_analyst_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/data_analyst_agent/__pycache__/data_analyst_agent.cpython-312.pyc +0 -0
- package/data_analyst_agent/data_analyst_agent.py +45 -0
- package/data_analyst_agent/instructions.md +173 -0
- package/data_analyst_agent/test_files/test_file.csv +21 -0
- package/data_analyst_agent/tools/__init__.py +6 -0
- package/deep_research/__init__.py +1 -0
- package/deep_research/__pycache__/__init__.cpython-312.pyc +0 -0
- package/deep_research/__pycache__/deep_research.cpython-312.pyc +0 -0
- package/deep_research/deep_research.py +27 -0
- package/deep_research/instructions.md +104 -0
- package/deep_research/tools/__init__.py +1 -0
- package/docs_agent/__init__.py +3 -0
- package/docs_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/__pycache__/docs_agent.cpython-312.pyc +0 -0
- package/docs_agent/docs_agent.py +61 -0
- package/docs_agent/instructions.md +418 -0
- package/docs_agent/tools/ConvertDocument.py +323 -0
- package/docs_agent/tools/CreateDocument.py +287 -0
- package/docs_agent/tools/ListDocuments.py +134 -0
- package/docs_agent/tools/ModifyDocument.py +247 -0
- package/docs_agent/tools/RestoreDocument.py +79 -0
- package/docs_agent/tools/ViewDocument.py +153 -0
- package/docs_agent/tools/__init__.py +1 -0
- package/docs_agent/tools/__pycache__/ConvertDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/CreateDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ListDocuments.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ModifyDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/RestoreDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ViewDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__init__.py +1 -0
- package/docs_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/doc_file_utils.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_blocks.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_constants.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_core.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_css.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_images.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_page.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_paragraphs.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_playwright.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_selectors.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_shared.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_validation.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/doc_file_utils.py +29 -0
- package/docs_agent/tools/utils/html_docx_blocks.py +262 -0
- package/docs_agent/tools/utils/html_docx_constants.py +78 -0
- package/docs_agent/tools/utils/html_docx_core.py +138 -0
- package/docs_agent/tools/utils/html_docx_css.py +262 -0
- package/docs_agent/tools/utils/html_docx_images.py +293 -0
- package/docs_agent/tools/utils/html_docx_page.py +185 -0
- package/docs_agent/tools/utils/html_docx_paragraphs.py +342 -0
- package/docs_agent/tools/utils/html_docx_playwright.py +184 -0
- package/docs_agent/tools/utils/html_docx_selectors.py +196 -0
- package/docs_agent/tools/utils/html_docx_shared.py +23 -0
- package/docs_agent/tools/utils/html_docx_tables.py +942 -0
- package/docs_agent/tools/utils/html_validation.py +102 -0
- package/helpers.py +59 -0
- package/image_generation_agent/__init__.py +1 -0
- package/image_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/__pycache__/image_generation_agent.cpython-312.pyc +0 -0
- package/image_generation_agent/image_generation_agent.py +31 -0
- package/image_generation_agent/instructions.md +80 -0
- package/image_generation_agent/tools/CombineImages.py +211 -0
- package/image_generation_agent/tools/EditImages.py +200 -0
- package/image_generation_agent/tools/GenerateImages.py +184 -0
- package/image_generation_agent/tools/RemoveBackground.py +108 -0
- package/image_generation_agent/tools/__init__.py +2 -0
- package/image_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/EditImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/GenerateImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/RemoveBackground.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__init__.py +2 -0
- package/image_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__pycache__/image_io.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/image_io.py +308 -0
- package/onboard.py +325 -0
- package/orchestrator/__init__.py +3 -0
- package/orchestrator/__pycache__/__init__.cpython-312.pyc +0 -0
- package/orchestrator/__pycache__/orchestrator.cpython-312.pyc +0 -0
- package/orchestrator/instructions.md +90 -0
- package/orchestrator/orchestrator.py +33 -0
- package/package.json +49 -0
- package/patches/__init__.py +1 -0
- package/patches/__pycache__/__init__.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_agency_swarm_dual_comms.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_file_attachment_refs.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_ipython_interpreter_composio.cpython-312.pyc +0 -0
- package/patches/dom-to-pptx+1.1.5.patch +133440 -0
- package/patches/patch_agency_swarm_dual_comms.py +199 -0
- package/patches/patch_file_attachment_refs.py +74 -0
- package/patches/patch_ipython_interpreter_composio.py +54 -0
- package/pyproject.toml +67 -0
- package/run.py +343 -0
- package/server.py +26 -0
- package/shared_instructions.md +119 -0
- package/shared_tools/CopyFile.py +68 -0
- package/shared_tools/ExecuteTool.py +184 -0
- package/shared_tools/FindTools.py +101 -0
- package/shared_tools/ManageConnections.py +43 -0
- package/shared_tools/SearchTools.py +44 -0
- package/shared_tools/__init__.py +7 -0
- package/shared_tools/__pycache__/CopyFile.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ExecuteTool.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/FindTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ManageConnections.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/SearchTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/.cursor/rules/slides-agent-workflow.mdc +9 -0
- package/slides_agent/__init__.py +1 -0
- package/slides_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/__pycache__/slides_agent.cpython-312.pyc +0 -0
- package/slides_agent/instructions.md +298 -0
- package/slides_agent/pptx/SKILL.md +528 -0
- package/slides_agent/pptx/html2pptx.md +625 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/slides_agent/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/slides_agent/pptx/ooxml/scripts/pack.py +169 -0
- package/slides_agent/pptx/ooxml/scripts/unpack.py +29 -0
- package/slides_agent/pptx/ooxml/scripts/validate.py +69 -0
- package/slides_agent/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/slides_agent/pptx/ooxml/scripts/validation/base.py +951 -0
- package/slides_agent/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/slides_agent/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/slides_agent/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/slides_agent/pptx/ooxml.md +427 -0
- package/slides_agent/pptx/scripts/html2pptx.js +1092 -0
- package/slides_agent/pptx/scripts/inventory.py +1020 -0
- package/slides_agent/pptx/scripts/rearrange.py +231 -0
- package/slides_agent/pptx/scripts/replace.py +385 -0
- package/slides_agent/pptx/scripts/thumbnail.py +451 -0
- package/slides_agent/slides_agent.py +109 -0
- package/slides_agent/test_deck/_theme.css +92 -0
- package/slides_agent/test_deck/assets/placeholder.svg +11 -0
- package/slides_agent/test_deck/slide_01_title.html +10 -0
- package/slides_agent/test_deck/slide_02_image_split.html +23 -0
- package/slides_agent/test_deck/slide_03_kpi.html +21 -0
- package/slides_agent/tools/ApplyPptxTextReplacements.py +91 -0
- package/slides_agent/tools/BuildPptxFromHtmlSlides.py +221 -0
- package/slides_agent/tools/CheckSlide.py +218 -0
- package/slides_agent/tools/CheckSlideCanvasOverflow.py +221 -0
- package/slides_agent/tools/CreateImageMontage.py +261 -0
- package/slides_agent/tools/CreatePptxThumbnailGrid.py +168 -0
- package/slides_agent/tools/DeleteSlide.py +78 -0
- package/slides_agent/tools/DownloadImage.py +79 -0
- package/slides_agent/tools/EnsureRasterImage.py +157 -0
- package/slides_agent/tools/ExtractPptxTextInventory.py +104 -0
- package/slides_agent/tools/GenerateImage.py +189 -0
- package/slides_agent/tools/ImageSearch.py +127 -0
- package/slides_agent/tools/InsertNewSlides.py +393 -0
- package/slides_agent/tools/ManageTheme.py +112 -0
- package/slides_agent/tools/ModifySlide.py +563 -0
- package/slides_agent/tools/ReadSlide.py +26 -0
- package/slides_agent/tools/RearrangePptxSlidesFromTemplate.py +114 -0
- package/slides_agent/tools/RestoreSnapshot.py +89 -0
- package/slides_agent/tools/SlideScreenshot.py +66 -0
- package/slides_agent/tools/__init__.py +54 -0
- package/slides_agent/tools/__pycache__/ApplyPptxTextReplacements.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/BuildPptxFromHtmlSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlideCanvasOverflow.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreateImageMontage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreatePptxThumbnailGrid.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DeleteSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DownloadImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/EnsureRasterImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ExtractPptxTextInventory.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ImageSearch.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/InsertNewSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ManageTheme.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ModifySlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ReadSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RearrangePptxSlidesFromTemplate.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RestoreSnapshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/SlideScreenshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_file_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_html_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/template_registry.cpython-312.pyc +0 -0
- package/slides_agent/tools/deck_utils.py +31 -0
- package/slides_agent/tools/html2pptx_runner.js +1183 -0
- package/slides_agent/tools/html_writer_instructions.md +149 -0
- package/slides_agent/tools/slide_file_utils.py +108 -0
- package/slides_agent/tools/slide_html_utils.py +354 -0
- package/slides_agent/tools/template_registry.py +55 -0
- package/swarm.py +82 -0
- package/video_generation_agent/__init__.py +1 -0
- package/video_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/__pycache__/video_generation_agent.cpython-312.pyc +0 -0
- package/video_generation_agent/instructions.md +178 -0
- package/video_generation_agent/tools/AddSubtitles.py +425 -0
- package/video_generation_agent/tools/CombineImages.py +166 -0
- package/video_generation_agent/tools/CombineVideos.py +113 -0
- package/video_generation_agent/tools/EditAudio.py +297 -0
- package/video_generation_agent/tools/EditImage.py +144 -0
- package/video_generation_agent/tools/EditVideoContent.py +369 -0
- package/video_generation_agent/tools/GenerateImage.py +133 -0
- package/video_generation_agent/tools/GenerateVideo.py +556 -0
- package/video_generation_agent/tools/TrimVideo.py +233 -0
- package/video_generation_agent/tools/__init__.py +1 -0
- package/video_generation_agent/tools/__pycache__/AddSubtitles.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineVideos.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditAudio.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditVideoContent.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/TrimVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__init__.py +1 -0
- package/video_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/image_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/video_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/image_utils.py +174 -0
- package/video_generation_agent/tools/utils/video_utils.py +522 -0
- package/video_generation_agent/video_generation_agent.py +26 -0
- package/virtual_assistant/__init__.py +1 -0
- package/virtual_assistant/__pycache__/__init__.cpython-312.pyc +0 -0
- package/virtual_assistant/__pycache__/virtual_assistant.cpython-312.pyc +0 -0
- package/virtual_assistant/instructions.md +206 -0
- package/virtual_assistant/tools/AddLabelToEmail.py +154 -0
- package/virtual_assistant/tools/CheckEventsForDate.py +218 -0
- package/virtual_assistant/tools/CheckUnreadSlackMessages.py +216 -0
- package/virtual_assistant/tools/CreateCalendarEvent.py +261 -0
- package/virtual_assistant/tools/DeleteCalendarEvent.py +137 -0
- package/virtual_assistant/tools/DeleteDraft.py +95 -0
- package/virtual_assistant/tools/DraftEmail.py +239 -0
- package/virtual_assistant/tools/EditFile.py +113 -0
- package/virtual_assistant/tools/FindEmails.py +330 -0
- package/virtual_assistant/tools/GetCurrentTime.py +69 -0
- package/virtual_assistant/tools/GetSlackUserInfo.py +117 -0
- package/virtual_assistant/tools/ListDirectory.py +113 -0
- package/virtual_assistant/tools/ListSkills.py +94 -0
- package/virtual_assistant/tools/ManageLabels.py +295 -0
- package/virtual_assistant/tools/ProductSearch.py +254 -0
- package/virtual_assistant/tools/ReadEmail.py +251 -0
- package/virtual_assistant/tools/ReadFile.py +108 -0
- package/virtual_assistant/tools/ReadSlackMessages.py +191 -0
- package/virtual_assistant/tools/RemoveLabelFromEmail.py +137 -0
- package/virtual_assistant/tools/RescheduleCalendarEvent.py +227 -0
- package/virtual_assistant/tools/ScholarSearch.py +216 -0
- package/virtual_assistant/tools/SendDraft.py +101 -0
- package/virtual_assistant/tools/SendSlackMessage.py +148 -0
- package/virtual_assistant/tools/WriteFile.py +95 -0
- package/virtual_assistant/tools/__init__.py +1 -0
- package/virtual_assistant/tools/__pycache__/AddLabelToEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckEventsForDate.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckUnreadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CreateCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DraftEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/EditFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/FindEmails.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetCurrentTime.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetSlackUserInfo.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListDirectory.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListSkills.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ManageLabels.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ProductSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RemoveLabelFromEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RescheduleCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ScholarSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendSlackMessage.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/WriteFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/virtual_assistant/virtual_assistant.py +52 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from typing import Dict, Optional, Tuple
|
|
2
|
+
|
|
3
|
+
from docx import Document
|
|
4
|
+
from docx.enum.section import WD_ORIENT
|
|
5
|
+
from docx.oxml import OxmlElement
|
|
6
|
+
from docx.oxml.ns import qn
|
|
7
|
+
from docx.shared import Pt
|
|
8
|
+
|
|
9
|
+
from .html_docx_constants import _PAGE_SIZES_PT
|
|
10
|
+
from .html_docx_css import _parse_background_color, _parse_float, _parse_length_to_pt
|
|
11
|
+
|
|
12
|
+
_DEFAULT_DOCX_MARGIN_PT = 72.0
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _apply_page_settings(doc: Document, html_content: str) -> None:
|
|
16
|
+
page_css = _extract_page_css(html_content)
|
|
17
|
+
margins_pt = _extract_page_margin_box_pt(page_css)
|
|
18
|
+
page_size = _extract_page_size_pt(page_css)
|
|
19
|
+
for section in doc.sections:
|
|
20
|
+
if page_size:
|
|
21
|
+
width_pt, height_pt, is_landscape = page_size
|
|
22
|
+
if is_landscape:
|
|
23
|
+
section.orientation = WD_ORIENT.LANDSCAPE
|
|
24
|
+
section.page_width = Pt(width_pt)
|
|
25
|
+
section.page_height = Pt(height_pt)
|
|
26
|
+
elif "landscape" in page_css and section.orientation != WD_ORIENT.LANDSCAPE:
|
|
27
|
+
section.orientation = WD_ORIENT.LANDSCAPE
|
|
28
|
+
section.page_width, section.page_height = (
|
|
29
|
+
section.page_height,
|
|
30
|
+
section.page_width,
|
|
31
|
+
)
|
|
32
|
+
if margins_pt is not None:
|
|
33
|
+
top, right, bottom, left = margins_pt
|
|
34
|
+
if top is not None:
|
|
35
|
+
section.top_margin = Pt(top)
|
|
36
|
+
if right is not None:
|
|
37
|
+
section.right_margin = Pt(right)
|
|
38
|
+
if bottom is not None:
|
|
39
|
+
section.bottom_margin = Pt(bottom)
|
|
40
|
+
if left is not None:
|
|
41
|
+
section.left_margin = Pt(left)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _apply_page_background(doc: Document, body_style: Dict[str, str]) -> None:
|
|
45
|
+
bg_color = _parse_background_color(body_style)
|
|
46
|
+
if not bg_color:
|
|
47
|
+
return
|
|
48
|
+
doc_element = doc._part._element
|
|
49
|
+
background = doc_element.find(qn("w:background"))
|
|
50
|
+
if background is None:
|
|
51
|
+
background = OxmlElement("w:background")
|
|
52
|
+
doc_element.insert(0, background)
|
|
53
|
+
background.set(qn("w:color"), bg_color)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _ensure_display_background_shape(doc: Document) -> None:
|
|
57
|
+
settings = doc._part.settings._element
|
|
58
|
+
existing = settings.find(qn("w:displayBackgroundShape"))
|
|
59
|
+
if existing is None:
|
|
60
|
+
elem = OxmlElement("w:displayBackgroundShape")
|
|
61
|
+
settings.append(elem)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _extract_page_css(html_content: str) -> str:
|
|
65
|
+
lower = html_content.lower()
|
|
66
|
+
start = lower.find("@page")
|
|
67
|
+
if start == -1:
|
|
68
|
+
return ""
|
|
69
|
+
block_start = lower.find("{", start)
|
|
70
|
+
if block_start == -1:
|
|
71
|
+
return ""
|
|
72
|
+
block_end = lower.find("}", block_start)
|
|
73
|
+
if block_end == -1:
|
|
74
|
+
return ""
|
|
75
|
+
return lower[block_start + 1:block_end]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _extract_page_margin_box_pt(
|
|
79
|
+
page_css: str,
|
|
80
|
+
) -> Optional[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]:
|
|
81
|
+
top = right = bottom = left = None
|
|
82
|
+
shorthand: Optional[Tuple[float, float, float, float]] = None
|
|
83
|
+
|
|
84
|
+
for rule in page_css.split(";"):
|
|
85
|
+
if ":" not in rule:
|
|
86
|
+
continue
|
|
87
|
+
key, value = [part.strip() for part in rule.split(":", 1)]
|
|
88
|
+
if key == "margin":
|
|
89
|
+
shorthand = _parse_margin_shorthand_pt(value)
|
|
90
|
+
elif key == "margin-top":
|
|
91
|
+
top = _parse_length_to_pt(value)
|
|
92
|
+
elif key == "margin-right":
|
|
93
|
+
right = _parse_length_to_pt(value)
|
|
94
|
+
elif key == "margin-bottom":
|
|
95
|
+
bottom = _parse_length_to_pt(value)
|
|
96
|
+
elif key == "margin-left":
|
|
97
|
+
left = _parse_length_to_pt(value)
|
|
98
|
+
|
|
99
|
+
if shorthand is not None:
|
|
100
|
+
s_top, s_right, s_bottom, s_left = shorthand
|
|
101
|
+
top = top if top is not None else s_top
|
|
102
|
+
right = right if right is not None else s_right
|
|
103
|
+
bottom = bottom if bottom is not None else s_bottom
|
|
104
|
+
left = left if left is not None else s_left
|
|
105
|
+
|
|
106
|
+
if all(value is None for value in (top, right, bottom, left)):
|
|
107
|
+
return None
|
|
108
|
+
return top, right, bottom, left
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _parse_margin_shorthand_pt(value: str) -> Optional[Tuple[float, float, float, float]]:
|
|
112
|
+
parts = [part for part in value.replace(",", " ").split() if part]
|
|
113
|
+
if not parts:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
parsed = [_parse_length_to_pt(part) for part in parts]
|
|
117
|
+
if any(part is None for part in parsed):
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
values = [float(part) for part in parsed if part is not None]
|
|
121
|
+
if len(values) == 1:
|
|
122
|
+
return values[0], values[0], values[0], values[0]
|
|
123
|
+
if len(values) == 2:
|
|
124
|
+
return values[0], values[1], values[0], values[1]
|
|
125
|
+
if len(values) == 3:
|
|
126
|
+
return values[0], values[1], values[2], values[1]
|
|
127
|
+
return values[0], values[1], values[2], values[3]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _extract_page_geometry_pt(
|
|
131
|
+
html_content: str,
|
|
132
|
+
) -> Tuple[float, float, float, float, float, float]:
|
|
133
|
+
page_css = _extract_page_css(html_content)
|
|
134
|
+
page_size = _extract_page_size_pt(page_css)
|
|
135
|
+
if page_size is None:
|
|
136
|
+
width_pt, height_pt = _PAGE_SIZES_PT["a4"]
|
|
137
|
+
else:
|
|
138
|
+
width_pt, height_pt, _is_landscape = page_size
|
|
139
|
+
|
|
140
|
+
margins_pt = _extract_page_margin_box_pt(page_css)
|
|
141
|
+
if margins_pt is None:
|
|
142
|
+
return (
|
|
143
|
+
width_pt,
|
|
144
|
+
height_pt,
|
|
145
|
+
_DEFAULT_DOCX_MARGIN_PT,
|
|
146
|
+
_DEFAULT_DOCX_MARGIN_PT,
|
|
147
|
+
_DEFAULT_DOCX_MARGIN_PT,
|
|
148
|
+
_DEFAULT_DOCX_MARGIN_PT,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
top, right, bottom, left = margins_pt
|
|
152
|
+
return (
|
|
153
|
+
width_pt,
|
|
154
|
+
height_pt,
|
|
155
|
+
top if top is not None else _DEFAULT_DOCX_MARGIN_PT,
|
|
156
|
+
right if right is not None else _DEFAULT_DOCX_MARGIN_PT,
|
|
157
|
+
bottom if bottom is not None else _DEFAULT_DOCX_MARGIN_PT,
|
|
158
|
+
left if left is not None else _DEFAULT_DOCX_MARGIN_PT,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _extract_page_size_pt(page_css: str) -> Optional[Tuple[float, float, bool]]:
|
|
163
|
+
if "size" not in page_css:
|
|
164
|
+
return None
|
|
165
|
+
for rule in page_css.split(";"):
|
|
166
|
+
if "size" not in rule:
|
|
167
|
+
continue
|
|
168
|
+
key, value = [part.strip() for part in rule.split(":", 1)]
|
|
169
|
+
if key != "size":
|
|
170
|
+
continue
|
|
171
|
+
tokens = [token.strip().lower() for token in value.split() if token.strip()]
|
|
172
|
+
if not tokens:
|
|
173
|
+
return None
|
|
174
|
+
size_token = next((token for token in tokens if token in _PAGE_SIZES_PT), None)
|
|
175
|
+
if not size_token:
|
|
176
|
+
return None
|
|
177
|
+
width_pt, height_pt = _PAGE_SIZES_PT[size_token]
|
|
178
|
+
is_landscape = "landscape" in tokens
|
|
179
|
+
is_portrait = "portrait" in tokens
|
|
180
|
+
if is_landscape and width_pt < height_pt:
|
|
181
|
+
width_pt, height_pt = height_pt, width_pt
|
|
182
|
+
if is_portrait and width_pt > height_pt:
|
|
183
|
+
width_pt, height_pt = height_pt, width_pt
|
|
184
|
+
return width_pt, height_pt, is_landscape
|
|
185
|
+
return None
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
|
4
|
+
from docx.oxml import OxmlElement
|
|
5
|
+
from docx.oxml.ns import qn
|
|
6
|
+
from docx.shared import Pt
|
|
7
|
+
|
|
8
|
+
from .html_docx_constants import (
|
|
9
|
+
_LIST_BASE_HANGING_TWIPS,
|
|
10
|
+
_LIST_BASE_LEFT_TWIPS,
|
|
11
|
+
_NAMED_COLORS,
|
|
12
|
+
_PADDING_SCALE,
|
|
13
|
+
)
|
|
14
|
+
from .html_docx_css import (
|
|
15
|
+
_border_sz,
|
|
16
|
+
_normalize_font_family,
|
|
17
|
+
_parse_background_color,
|
|
18
|
+
_parse_border_left,
|
|
19
|
+
_parse_box_values,
|
|
20
|
+
_parse_color,
|
|
21
|
+
_parse_float,
|
|
22
|
+
_parse_font_size_pt,
|
|
23
|
+
_parse_length_to_pt,
|
|
24
|
+
_parse_padding,
|
|
25
|
+
_parse_px_to_pt,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _apply_paragraph_style(paragraph, style_map: Dict[str, str]) -> None:
|
|
30
|
+
alignment = style_map.get("text-align", "").lower()
|
|
31
|
+
if alignment == "left":
|
|
32
|
+
paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
|
33
|
+
elif alignment == "center":
|
|
34
|
+
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
35
|
+
elif alignment == "right":
|
|
36
|
+
paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
37
|
+
elif alignment == "justify":
|
|
38
|
+
paragraph.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
|
|
39
|
+
|
|
40
|
+
_apply_paragraph_spacing(paragraph, style_map)
|
|
41
|
+
_apply_paragraph_borders(paragraph, style_map)
|
|
42
|
+
_apply_paragraph_container_styles(paragraph, style_map)
|
|
43
|
+
_apply_paragraph_line_height(paragraph, style_map)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _apply_run_style(run, style_map: Dict[str, str]) -> None:
|
|
47
|
+
font_family = style_map.get("font-family")
|
|
48
|
+
if font_family:
|
|
49
|
+
_set_run_font(run, _normalize_font_family(font_family))
|
|
50
|
+
|
|
51
|
+
font_size = style_map.get("font-size")
|
|
52
|
+
if font_size:
|
|
53
|
+
size_pt = _parse_font_size_pt(font_size)
|
|
54
|
+
if size_pt is not None:
|
|
55
|
+
run.font.size = Pt(size_pt)
|
|
56
|
+
|
|
57
|
+
color = style_map.get("color")
|
|
58
|
+
if color:
|
|
59
|
+
rgb = _parse_color(color)
|
|
60
|
+
if rgb is not None:
|
|
61
|
+
run.font.color.rgb = rgb
|
|
62
|
+
|
|
63
|
+
font_weight = style_map.get("font-weight", "").strip().lower()
|
|
64
|
+
if font_weight in {"bold", "bolder"}:
|
|
65
|
+
run.font.bold = True
|
|
66
|
+
else:
|
|
67
|
+
weight = _parse_float(font_weight) if font_weight else None
|
|
68
|
+
if weight is not None and weight >= 600:
|
|
69
|
+
run.font.bold = True
|
|
70
|
+
|
|
71
|
+
font_style = style_map.get("font-style", "").strip().lower()
|
|
72
|
+
if font_style == "italic":
|
|
73
|
+
run.font.italic = True
|
|
74
|
+
|
|
75
|
+
text_decoration = style_map.get("text-decoration", "").strip().lower()
|
|
76
|
+
if "underline" in text_decoration:
|
|
77
|
+
run.font.underline = True
|
|
78
|
+
|
|
79
|
+
letter_spacing = style_map.get("letter-spacing", "").strip().lower()
|
|
80
|
+
if letter_spacing:
|
|
81
|
+
spacing_pt = _parse_px_to_pt(letter_spacing)
|
|
82
|
+
if spacing_pt is not None:
|
|
83
|
+
r_pr = run._r.get_or_add_rPr()
|
|
84
|
+
spacing = r_pr.find(qn("w:spacing"))
|
|
85
|
+
if spacing is None:
|
|
86
|
+
spacing = OxmlElement("w:spacing")
|
|
87
|
+
r_pr.append(spacing)
|
|
88
|
+
spacing.set(qn("w:val"), str(int(spacing_pt * 20)))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _set_run_font(run, font_name: str) -> None:
|
|
92
|
+
run.font.name = font_name
|
|
93
|
+
r_pr = run._r.get_or_add_rPr()
|
|
94
|
+
r_fonts = r_pr.find(qn("w:rFonts"))
|
|
95
|
+
if r_fonts is None:
|
|
96
|
+
r_fonts = OxmlElement("w:rFonts")
|
|
97
|
+
r_pr.append(r_fonts)
|
|
98
|
+
r_fonts.set(qn("w:ascii"), font_name)
|
|
99
|
+
r_fonts.set(qn("w:hAnsi"), font_name)
|
|
100
|
+
r_fonts.set(qn("w:cs"), font_name)
|
|
101
|
+
r_fonts.set(qn("w:eastAsia"), font_name)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _apply_paragraph_container_styles(paragraph, style_map: Dict[str, str]) -> None:
|
|
105
|
+
bg_color = _parse_background_color(style_map)
|
|
106
|
+
if bg_color:
|
|
107
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
108
|
+
shd = p_pr.find(qn("w:shd"))
|
|
109
|
+
if shd is None:
|
|
110
|
+
shd = OxmlElement("w:shd")
|
|
111
|
+
p_pr.append(shd)
|
|
112
|
+
shd.set(qn("w:val"), "clear")
|
|
113
|
+
shd.set(qn("w:color"), "auto")
|
|
114
|
+
shd.set(qn("w:fill"), bg_color)
|
|
115
|
+
|
|
116
|
+
border_left = _parse_border_left(style_map)
|
|
117
|
+
if border_left:
|
|
118
|
+
width_pt, color = border_left
|
|
119
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
120
|
+
p_bdr = p_pr.find(qn("w:pBdr"))
|
|
121
|
+
if p_bdr is None:
|
|
122
|
+
p_bdr = OxmlElement("w:pBdr")
|
|
123
|
+
p_pr.append(p_bdr)
|
|
124
|
+
left = OxmlElement("w:left")
|
|
125
|
+
left.set(qn("w:val"), "single")
|
|
126
|
+
left.set(qn("w:sz"), _border_sz(width_pt))
|
|
127
|
+
left.set(qn("w:color"), color)
|
|
128
|
+
p_bdr.append(left)
|
|
129
|
+
|
|
130
|
+
padding = _parse_padding(style_map.get("padding", ""))
|
|
131
|
+
if padding:
|
|
132
|
+
top, right, bottom, left = padding
|
|
133
|
+
if left:
|
|
134
|
+
paragraph.paragraph_format.left_indent = Pt(left * _PADDING_SCALE)
|
|
135
|
+
if right:
|
|
136
|
+
paragraph.paragraph_format.right_indent = Pt(right * _PADDING_SCALE)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _apply_paragraph_line_height(paragraph, style_map: Dict[str, str]) -> None:
|
|
140
|
+
line_height = style_map.get("line-height", "").strip().lower()
|
|
141
|
+
if not line_height:
|
|
142
|
+
# Spacer-div pattern: <div style="height: Xpt"> with no other content.
|
|
143
|
+
# Convert the height to an exact line-height so the paragraph takes up
|
|
144
|
+
# the intended vertical space. Zero values are the divider pattern
|
|
145
|
+
# (<div style="height: 0pt; border-top: ...">), which should keep default
|
|
146
|
+
# spacing so the border renders correctly.
|
|
147
|
+
height_str = style_map.get("height", "").strip().lower()
|
|
148
|
+
if height_str:
|
|
149
|
+
height_pt = _parse_length_to_pt(height_str)
|
|
150
|
+
if height_pt:
|
|
151
|
+
paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
|
|
152
|
+
paragraph.paragraph_format.line_spacing = Pt(height_pt)
|
|
153
|
+
return
|
|
154
|
+
paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE
|
|
155
|
+
paragraph.paragraph_format.line_spacing = 1
|
|
156
|
+
return
|
|
157
|
+
if line_height.endswith("px") or line_height.endswith("pt"):
|
|
158
|
+
height_pt = _parse_px_to_pt(line_height)
|
|
159
|
+
if height_pt is not None:
|
|
160
|
+
paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
|
|
161
|
+
paragraph.paragraph_format.line_spacing = Pt(height_pt)
|
|
162
|
+
return
|
|
163
|
+
value = _parse_float(line_height)
|
|
164
|
+
if value:
|
|
165
|
+
paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
|
|
166
|
+
paragraph.paragraph_format.line_spacing = value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _apply_paragraph_spacing(paragraph, style_map: Dict[str, str]) -> None:
|
|
170
|
+
paragraph.paragraph_format.space_before = Pt(0)
|
|
171
|
+
paragraph.paragraph_format.space_after = Pt(0)
|
|
172
|
+
|
|
173
|
+
margin = _parse_box_values(style_map.get("margin", ""))
|
|
174
|
+
margin_top = _parse_px_to_pt(style_map.get("margin-top", ""))
|
|
175
|
+
margin_bottom = _parse_px_to_pt(style_map.get("margin-bottom", ""))
|
|
176
|
+
|
|
177
|
+
if margin:
|
|
178
|
+
top, _, bottom, _ = margin
|
|
179
|
+
if top is not None:
|
|
180
|
+
paragraph.paragraph_format.space_before = Pt(top)
|
|
181
|
+
if bottom is not None:
|
|
182
|
+
paragraph.paragraph_format.space_after = Pt(bottom)
|
|
183
|
+
|
|
184
|
+
if margin_top is not None:
|
|
185
|
+
paragraph.paragraph_format.space_before = Pt(margin_top)
|
|
186
|
+
if margin_bottom is not None:
|
|
187
|
+
paragraph.paragraph_format.space_after = Pt(margin_bottom)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _apply_paragraph_borders(paragraph, style_map: Dict[str, str]) -> None:
|
|
191
|
+
top = _parse_paragraph_border(style_map.get("border-top", ""))
|
|
192
|
+
bottom = _parse_paragraph_border(style_map.get("border-bottom", ""))
|
|
193
|
+
if not top and not bottom:
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
197
|
+
p_bdr = p_pr.find(qn("w:pBdr"))
|
|
198
|
+
if p_bdr is None:
|
|
199
|
+
p_bdr = OxmlElement("w:pBdr")
|
|
200
|
+
p_pr.append(p_bdr)
|
|
201
|
+
|
|
202
|
+
if top:
|
|
203
|
+
width_pt, color = top
|
|
204
|
+
elem = OxmlElement("w:top")
|
|
205
|
+
elem.set(qn("w:val"), "single")
|
|
206
|
+
elem.set(qn("w:sz"), _border_sz(width_pt))
|
|
207
|
+
elem.set(qn("w:color"), color)
|
|
208
|
+
p_bdr.append(elem)
|
|
209
|
+
if bottom:
|
|
210
|
+
width_pt, color = bottom
|
|
211
|
+
elem = OxmlElement("w:bottom")
|
|
212
|
+
elem.set(qn("w:val"), "single")
|
|
213
|
+
elem.set(qn("w:sz"), _border_sz(width_pt))
|
|
214
|
+
elem.set(qn("w:color"), color)
|
|
215
|
+
p_bdr.append(elem)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _parse_paragraph_border(border: str):
|
|
219
|
+
if not border:
|
|
220
|
+
return None
|
|
221
|
+
parts = border.split()
|
|
222
|
+
width_pt = _parse_px_to_pt(parts[0]) if parts else None
|
|
223
|
+
color = None
|
|
224
|
+
for part in parts:
|
|
225
|
+
if part.startswith("#"):
|
|
226
|
+
color = part[1:].upper()
|
|
227
|
+
break
|
|
228
|
+
if part.lower() in _NAMED_COLORS:
|
|
229
|
+
r, g, b = _NAMED_COLORS[part.lower()]
|
|
230
|
+
color = f"{r:02X}{g:02X}{b:02X}"
|
|
231
|
+
break
|
|
232
|
+
if width_pt and color:
|
|
233
|
+
return width_pt, color
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _add_paragraph_spacing(paragraph, before_pt: float = 0, after_pt: float = 0) -> None:
|
|
238
|
+
pf = paragraph.paragraph_format
|
|
239
|
+
if before_pt:
|
|
240
|
+
current = pf.space_before.pt if pf.space_before else 0
|
|
241
|
+
pf.space_before = Pt(current + before_pt)
|
|
242
|
+
if after_pt:
|
|
243
|
+
current = pf.space_after.pt if pf.space_after else 0
|
|
244
|
+
pf.space_after = Pt(current + after_pt)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _add_paragraph_indent(paragraph, left_pt: float = 0, right_pt: float = 0) -> None:
|
|
248
|
+
pf = paragraph.paragraph_format
|
|
249
|
+
if left_pt:
|
|
250
|
+
current = pf.left_indent.pt if pf.left_indent else 0
|
|
251
|
+
pf.left_indent = Pt(current + left_pt)
|
|
252
|
+
if right_pt:
|
|
253
|
+
current = pf.right_indent.pt if pf.right_indent else 0
|
|
254
|
+
pf.right_indent = Pt(current + right_pt)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _add_list_indent_padding(paragraph, left_pt: float = 0, right_pt: float = 0) -> None:
|
|
258
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
259
|
+
ind = p_pr.find(qn("w:ind"))
|
|
260
|
+
if ind is None:
|
|
261
|
+
ind = OxmlElement("w:ind")
|
|
262
|
+
p_pr.append(ind)
|
|
263
|
+
if left_pt:
|
|
264
|
+
current = ind.get(qn("w:left"))
|
|
265
|
+
if current and current.isdigit():
|
|
266
|
+
current_val = int(current)
|
|
267
|
+
else:
|
|
268
|
+
current_val = _LIST_BASE_LEFT_TWIPS
|
|
269
|
+
ind.set(qn("w:left"), str(current_val + int(left_pt * 20)))
|
|
270
|
+
if ind.get(qn("w:hanging")) is None:
|
|
271
|
+
ind.set(qn("w:hanging"), str(_LIST_BASE_HANGING_TWIPS))
|
|
272
|
+
if right_pt:
|
|
273
|
+
current = ind.get(qn("w:right"))
|
|
274
|
+
current_val = int(current) if current and current.isdigit() else 0
|
|
275
|
+
ind.set(qn("w:right"), str(current_val + int(right_pt * 20)))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _apply_ul_margin_indent(paragraph, additional_left_pt: float) -> None:
|
|
279
|
+
"""Shift a list item right by the parent <ul> margin-left.
|
|
280
|
+
|
|
281
|
+
When a <ul> has margin-left: X, the entire list must shift X pt to the right.
|
|
282
|
+
We add X to w:ind w:left while explicitly preserving w:hanging so the bullet
|
|
283
|
+
character and text-start gap are kept intact (paragraph-level w:ind overrides
|
|
284
|
+
the abstractNum's w:ind entirely, so we must carry both values).
|
|
285
|
+
"""
|
|
286
|
+
if not additional_left_pt:
|
|
287
|
+
return
|
|
288
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
289
|
+
ind = p_pr.find(qn("w:ind"))
|
|
290
|
+
if ind is None:
|
|
291
|
+
ind = OxmlElement("w:ind")
|
|
292
|
+
p_pr.append(ind)
|
|
293
|
+
current_left = ind.get(qn("w:left"))
|
|
294
|
+
base_left = int(current_left) if current_left and current_left.isdigit() else _LIST_BASE_LEFT_TWIPS
|
|
295
|
+
current_hanging = ind.get(qn("w:hanging"))
|
|
296
|
+
base_hanging = int(current_hanging) if current_hanging and current_hanging.isdigit() else _LIST_BASE_HANGING_TWIPS
|
|
297
|
+
ind.set(qn("w:left"), str(base_left + int(additional_left_pt * 20)))
|
|
298
|
+
ind.set(qn("w:hanging"), str(base_hanging))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _apply_list_indent(paragraph, indent_pt: float) -> None:
|
|
302
|
+
_set_list_indent_xml(paragraph, indent_pt)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _set_list_indent_xml(paragraph, indent_pt: float) -> None:
|
|
306
|
+
p_pr = paragraph._p.get_or_add_pPr()
|
|
307
|
+
ind = p_pr.find(qn("w:ind"))
|
|
308
|
+
if ind is None:
|
|
309
|
+
ind = OxmlElement("w:ind")
|
|
310
|
+
p_pr.append(ind)
|
|
311
|
+
left_twips = ind.get(qn("w:left"))
|
|
312
|
+
if left_twips and left_twips.isdigit():
|
|
313
|
+
base_left = int(left_twips)
|
|
314
|
+
else:
|
|
315
|
+
base_left = _LIST_BASE_LEFT_TWIPS
|
|
316
|
+
ind.set(qn("w:left"), str(base_left + int(indent_pt * 20)))
|
|
317
|
+
if ind.get(qn("w:hanging")) is None:
|
|
318
|
+
ind.set(qn("w:hanging"), str(_LIST_BASE_HANGING_TWIPS))
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _resolve_list_indent_pt(parent_style: Dict[str, str]) -> float:
|
|
322
|
+
margin_left = parent_style.get("margin-left", "")
|
|
323
|
+
if margin_left:
|
|
324
|
+
value = _parse_length_to_pt(margin_left)
|
|
325
|
+
if value:
|
|
326
|
+
return value
|
|
327
|
+
margin = parent_style.get("margin", "")
|
|
328
|
+
if margin:
|
|
329
|
+
values = _parse_padding(margin)
|
|
330
|
+
if values:
|
|
331
|
+
return values[3]
|
|
332
|
+
padding_left = parent_style.get("padding-left", "")
|
|
333
|
+
if padding_left:
|
|
334
|
+
value = _parse_length_to_pt(padding_left)
|
|
335
|
+
if value:
|
|
336
|
+
return value
|
|
337
|
+
padding = parent_style.get("padding", "")
|
|
338
|
+
if padding:
|
|
339
|
+
values = _parse_padding(padding)
|
|
340
|
+
if values:
|
|
341
|
+
return values[3]
|
|
342
|
+
return 0.0
|