lhtml-site 1.0.0__py3-none-any.whl
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.
- lhtml_site/__init__.py +0 -0
- lhtml_site/browser.py +59 -0
- lhtml_site/build_output.py +80 -0
- lhtml_site/configuration.py +354 -0
- lhtml_site/configure_example.yaml +113 -0
- lhtml_site/credits.py +198 -0
- lhtml_site/deck.py +423 -0
- lhtml_site/dependencies.py +358 -0
- lhtml_site/design.py +292 -0
- lhtml_site/design_checks.py +125 -0
- lhtml_site/development.py +210 -0
- lhtml_site/docs/agents.md +250 -0
- lhtml_site/docs/development.md +98 -0
- lhtml_site/docs/reference.md +1076 -0
- lhtml_site/doctor.py +191 -0
- lhtml_site/extensions.py +151 -0
- lhtml_site/feedback.js +151 -0
- lhtml_site/feedback.py +145 -0
- lhtml_site/figures.py +117 -0
- lhtml_site/filesystem.py +182 -0
- lhtml_site/front_matter.py +36 -0
- lhtml_site/generate.py +1012 -0
- lhtml_site/generator_tool.py +159 -0
- lhtml_site/headings.py +221 -0
- lhtml_site/index_page.py +39 -0
- lhtml_site/layout/__init__.py +12 -0
- lhtml_site/layout/analysis.py +785 -0
- lhtml_site/layout/build.py +318 -0
- lhtml_site/layout/findings.py +251 -0
- lhtml_site/layout/geometry.py +28 -0
- lhtml_site/layout/measure.py +367 -0
- lhtml_site/layout/page.js +1087 -0
- lhtml_site/layout/report.py +354 -0
- lhtml_site/layout_options.py +110 -0
- lhtml_site/lint.py +322 -0
- lhtml_site/logger.py +70 -0
- lhtml_site/markdown_page.py +110 -0
- lhtml_site/menu.py +64 -0
- lhtml_site/pages.py +276 -0
- lhtml_site/pdf_export.py +227 -0
- lhtml_site/plugins/code_include.py +197 -0
- lhtml_site/plugins/pre_include.py +20 -0
- lhtml_site/project.py +136 -0
- lhtml_site/report_lock.py +80 -0
- lhtml_site/source_map.py +369 -0
- lhtml_site/source_scan.py +202 -0
- lhtml_site/starters/site/01_welcome/assets/diagram.svg +7 -0
- lhtml_site/starters/site/01_welcome/index.html.j2 +18 -0
- lhtml_site/starters/site/02_writing/index.html.j2 +20 -0
- lhtml_site/starters/slides/01_title/index.html.j2 +10 -0
- lhtml_site/starters/slides/02_text/index.html.j2 +14 -0
- lhtml_site/starters/slides/03_two_columns/index.html.j2 +18 -0
- lhtml_site/starters/slides/04_image/assets/diagram.svg +7 -0
- lhtml_site/starters/slides/04_image/index.html.j2 +14 -0
- lhtml_site/structure.py +84 -0
- lhtml_site/theme.py +112 -0
- lhtml_site/themes/slides/css/classes.css +27 -0
- lhtml_site/themes/slides/css/code.css +71 -0
- lhtml_site/themes/slides/css/font.css +227 -0
- lhtml_site/themes/slides/css/icons.css +27 -0
- lhtml_site/themes/slides/css/main.css +141 -0
- lhtml_site/themes/slides/css/pdf_export.css +6 -0
- lhtml_site/themes/slides/design.yaml +335 -0
- lhtml_site/themes/slides/fonts/source_code_pro/LICENSE.md +93 -0
- lhtml_site/themes/slides/fonts/source_code_pro/README.md +18 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-It.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Medium.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-MediumIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/LICENSE.md +93 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/README.md +18 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-It.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/slides/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides/icons/left_disabled.svg +1 -0
- lhtml_site/themes/slides/icons/left_enabled.svg +1 -0
- lhtml_site/themes/slides/icons/left_hover.svg +1 -0
- lhtml_site/themes/slides/icons/menu.svg +1 -0
- lhtml_site/themes/slides/icons/menu_hover.svg +1 -0
- lhtml_site/themes/slides/icons/right_disabled.svg +1 -0
- lhtml_site/themes/slides/icons/right_enabled.svg +1 -0
- lhtml_site/themes/slides/icons/right_hover.svg +1 -0
- lhtml_site/themes/slides/js/menu.js +63 -0
- lhtml_site/themes/slides/js/navigation.js +84 -0
- lhtml_site/themes/slides/js/presentation_resize_page.js +40 -0
- lhtml_site/themes/slides/js/readiness.js +20 -0
- lhtml_site/themes/slides/libs/katex/contrib/auto-render.min.js +1 -0
- lhtml_site/themes/slides/libs/katex/contrib/copy-tex.min.css +1 -0
- lhtml_site/themes/slides/libs/katex/contrib/copy-tex.min.js +1 -0
- lhtml_site/themes/slides/libs/katex/contrib/mathtex-script-type.min.js +1 -0
- lhtml_site/themes/slides/libs/katex/contrib/mhchem.min.js +1 -0
- lhtml_site/themes/slides/libs/katex/contrib/render-a11y-string.min.js +1 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- lhtml_site/themes/slides/libs/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- lhtml_site/themes/slides/libs/katex/katex.min.css +1 -0
- lhtml_site/themes/slides/libs/katex/katex.min.js +1 -0
- lhtml_site/themes/slides/template/base.html +80 -0
- lhtml_site/themes/slides/theme.yaml +11 -0
- lhtml_site/themes/slides-pdf/css/classes.css +27 -0
- lhtml_site/themes/slides-pdf/css/code.css +71 -0
- lhtml_site/themes/slides-pdf/css/font.css +227 -0
- lhtml_site/themes/slides-pdf/css/icons.css +27 -0
- lhtml_site/themes/slides-pdf/css/main.css +141 -0
- lhtml_site/themes/slides-pdf/css/pdf_export.css +6 -0
- lhtml_site/themes/slides-pdf/design.yaml +3 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/LICENSE.md +93 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/README.md +18 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-It.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Medium.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-MediumIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/LICENSE.md +93 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/README.md +18 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-It.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/slides-pdf/icons/left_disabled.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/left_enabled.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/left_hover.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/menu.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/menu_hover.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/right_disabled.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/right_enabled.svg +1 -0
- lhtml_site/themes/slides-pdf/icons/right_hover.svg +1 -0
- lhtml_site/themes/slides-pdf/js/menu.js +62 -0
- lhtml_site/themes/slides-pdf/js/navigation.js +84 -0
- lhtml_site/themes/slides-pdf/js/presentation_resize_page.js +40 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/auto-render.min.js +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/copy-tex.min.css +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/copy-tex.min.js +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/mathtex-script-type.min.js +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/mhchem.min.js +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/contrib/render-a11y-string.min.js +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- lhtml_site/themes/slides-pdf/libs/katex/katex.min.css +1 -0
- lhtml_site/themes/slides-pdf/libs/katex/katex.min.js +1 -0
- lhtml_site/themes/slides-pdf/template/base.html +76 -0
- lhtml_site/themes/slides-pdf/theme.yaml +13 -0
- lhtml_site/themes/webpage-frame/css/classes.css +50 -0
- lhtml_site/themes/webpage-frame/css/code.css +71 -0
- lhtml_site/themes/webpage-frame/css/font.css +227 -0
- lhtml_site/themes/webpage-frame/css/icons.css +27 -0
- lhtml_site/themes/webpage-frame/css/main.css +390 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/LICENSE.md +93 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/README.md +18 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-It.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Medium.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-MediumIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_code_pro/WOFF2/TTF/SourceCodePro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/LICENSE.md +93 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/README.md +18 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Black.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BlackIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Bold.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-BoldIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLight.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-ExtraLightIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-It.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Light.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-LightIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Regular.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-Semibold.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/fonts/source_serif_pro/WOFF2/TTF/SourceSerifPro-SemiboldIt.ttf.woff2 +0 -0
- lhtml_site/themes/webpage-frame/icons/down_enabled.svg +44 -0
- lhtml_site/themes/webpage-frame/icons/left_disabled.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/left_enabled.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/left_hover.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/menu.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/menu_hover.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/right_disabled.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/right_enabled.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/right_hover.svg +1 -0
- lhtml_site/themes/webpage-frame/icons/up_enabled.svg +44 -0
- lhtml_site/themes/webpage-frame/js/menu.js +270 -0
- lhtml_site/themes/webpage-frame/js/title_id.js +51 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/auto-render.min.js +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/copy-tex.min.css +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/copy-tex.min.js +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/mathtex-script-type.min.js +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/mhchem.min.js +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/contrib/render-a11y-string.min.js +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- lhtml_site/themes/webpage-frame/libs/katex/katex.min.css +1 -0
- lhtml_site/themes/webpage-frame/libs/katex/katex.min.js +1 -0
- lhtml_site/themes/webpage-frame/template/base.html +74 -0
- lhtml_site/themes/webpage-frame/theme.yaml +11 -0
- lhtml_site/tracking.py +34 -0
- lhtml_site/video_cache.py +124 -0
- lhtml_site/video_convert/__init__.py +0 -0
- lhtml_site/video_convert/video_convert.py +151 -0
- lhtml_site/wrap.py +91 -0
- lhtml_site-1.0.0.dist-info/METADATA +384 -0
- lhtml_site-1.0.0.dist-info/RECORD +421 -0
- lhtml_site-1.0.0.dist-info/WHEEL +5 -0
- lhtml_site-1.0.0.dist-info/entry_points.txt +2 -0
- lhtml_site-1.0.0.dist-info/licenses/LICENSE.md +21 -0
- lhtml_site-1.0.0.dist-info/top_level.txt +1 -0
lhtml_site/__init__.py
ADDED
|
File without changes
|
lhtml_site/browser.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""The headless browser of the generator: the Chromium of Playwright for
|
|
2
|
+
Python, run by the PDF export (lhtml_site/pdf_export.py), the layout report
|
|
3
|
+
and --render (lhtml_site/layout/measure.py).
|
|
4
|
+
|
|
5
|
+
Playwright comes with the extra `lhtml-site[pdf]`, its browser from
|
|
6
|
+
`python -m playwright install --only-shell chromium`; the configuration key
|
|
7
|
+
'chrome' runs another Chrome instead.
|
|
8
|
+
"""
|
|
9
|
+
import importlib.util
|
|
10
|
+
from contextlib import asynccontextmanager
|
|
11
|
+
|
|
12
|
+
from lhtml_site.doctor import playwright_browser_command, playwright_install_command
|
|
13
|
+
|
|
14
|
+
PLAYWRIGHT_MISSING = '{feature} needs {modules}: {install}, then {browser}'
|
|
15
|
+
CHROMIUM_MISSING = ('The Chromium of Playwright is not installed: {browser} '
|
|
16
|
+
'(or the configuration key chrome)')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def installed(modules=('playwright',)):
|
|
20
|
+
return all(importlib.util.find_spec(module) for module in modules)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def require(feature, modules=('playwright',)):
|
|
24
|
+
"""Raise a RuntimeError saying how to install `modules` when one of them
|
|
25
|
+
is missing (`feature`: 'The layout report', ...)."""
|
|
26
|
+
if installed(modules):
|
|
27
|
+
return
|
|
28
|
+
raise RuntimeError(missing(feature, modules))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def missing(feature, modules=('playwright',)):
|
|
32
|
+
"""What to install for `feature`, for the way lhtml-site is installed
|
|
33
|
+
(the extra lhtml-site[pdf]: playwright and pypdf, then the browser)."""
|
|
34
|
+
return PLAYWRIGHT_MISSING.format(feature=feature, modules=' and '.join(modules),
|
|
35
|
+
install=playwright_install_command(), browser=playwright_browser_command())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@asynccontextmanager
|
|
39
|
+
async def chromium(chrome_path=None, args=()):
|
|
40
|
+
"""A headless Chromium: the one of Playwright, or the executable
|
|
41
|
+
`chrome_path` (the configuration key 'chrome')."""
|
|
42
|
+
from playwright.async_api import Error, async_playwright
|
|
43
|
+
|
|
44
|
+
async with async_playwright() as playwright:
|
|
45
|
+
try:
|
|
46
|
+
browser = await playwright.chromium.launch(executable_path=chrome_path or None, args=list(args))
|
|
47
|
+
except Error as error:
|
|
48
|
+
if "Executable doesn't exist" in str(error):
|
|
49
|
+
raise RuntimeError(CHROMIUM_MISSING.format(browser=playwright_browser_command())) from None
|
|
50
|
+
raise
|
|
51
|
+
try:
|
|
52
|
+
yield browser
|
|
53
|
+
finally:
|
|
54
|
+
await browser.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def first_line(error):
|
|
58
|
+
"""The first line of what a Playwright error says (the rest is its call log)."""
|
|
59
|
+
return (str(error).strip().splitlines() or [type(error).__name__])[0]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Publish a completed build while keeping the previous output on failure."""
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import tempfile
|
|
7
|
+
import uuid
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def links_out_of(site):
|
|
11
|
+
"""The links of a site a build that writes in it (--only) would write
|
|
12
|
+
through: a symbolic link leading out of it, a file with another hard
|
|
13
|
+
link (a file of the sources, `cp -al`, `rsync -H`). As (path, what it
|
|
14
|
+
reaches)."""
|
|
15
|
+
site = Path(site)
|
|
16
|
+
if not site.is_dir():
|
|
17
|
+
return []
|
|
18
|
+
real = site.resolve()
|
|
19
|
+
found = []
|
|
20
|
+
for directory, dirs, files in os.walk(site):
|
|
21
|
+
for name in dirs + files:
|
|
22
|
+
path = Path(directory) / name
|
|
23
|
+
if path.is_symlink():
|
|
24
|
+
target = path.resolve()
|
|
25
|
+
if target != real and real not in target.parents:
|
|
26
|
+
found.append((path, target))
|
|
27
|
+
elif name in files and os.lstat(path).st_nlink > 1:
|
|
28
|
+
found.append((path, 'another hard link to the same file'))
|
|
29
|
+
return found
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def staged_site(meta, copy=False):
|
|
34
|
+
"""The build in a staging directory, published when it completes.
|
|
35
|
+
`copy`: the staging directory starts as a copy of the site (a partial
|
|
36
|
+
build that must publish nothing if it is refused: --only --strict)."""
|
|
37
|
+
target = Path(meta['site_directory']).resolve()
|
|
38
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
staging = Path(tempfile.mkdtemp(prefix='.site-build-', dir=target.parent))
|
|
40
|
+
if copy and target.is_dir():
|
|
41
|
+
shutil.copytree(target, staging, symlinks=True, dirs_exist_ok=True)
|
|
42
|
+
original = meta['site_directory']
|
|
43
|
+
meta['published_site_directory'] = target
|
|
44
|
+
meta['site_directory'] = staging
|
|
45
|
+
try:
|
|
46
|
+
yield
|
|
47
|
+
# A target may publish something else than the site it rendered: the
|
|
48
|
+
# PDF export keeps <deck>.pdf and images/, and asks for the HTML it
|
|
49
|
+
# used as an intermediate not to be published. It says so rather
|
|
50
|
+
# than deleting the directory under whatever runs next. In a project
|
|
51
|
+
# of decks or targets, the directory of the target is its own: a site
|
|
52
|
+
# published there before (--debug keeps it) goes too, it would be
|
|
53
|
+
# stale. A project of one site may have published something else
|
|
54
|
+
# there, which is kept.
|
|
55
|
+
if not meta.get('publish_site', True):
|
|
56
|
+
shutil.rmtree(staging, ignore_errors=True)
|
|
57
|
+
if meta.get('build_scope'):
|
|
58
|
+
shutil.rmtree(target, ignore_errors=True)
|
|
59
|
+
if staging.is_dir():
|
|
60
|
+
backup = target.with_name('.site-backup-' + uuid.uuid4().hex)
|
|
61
|
+
had_target = target.exists()
|
|
62
|
+
if had_target:
|
|
63
|
+
target.rename(backup)
|
|
64
|
+
try:
|
|
65
|
+
staging.rename(target)
|
|
66
|
+
except BaseException:
|
|
67
|
+
if had_target:
|
|
68
|
+
backup.rename(target)
|
|
69
|
+
raise
|
|
70
|
+
if had_target:
|
|
71
|
+
shutil.rmtree(backup)
|
|
72
|
+
except BaseException:
|
|
73
|
+
if meta['debug'] and staging.exists():
|
|
74
|
+
meta['log'].warning(f'Failed build kept for inspection: {staging}')
|
|
75
|
+
else:
|
|
76
|
+
shutil.rmtree(staging, ignore_errors=True)
|
|
77
|
+
raise
|
|
78
|
+
finally:
|
|
79
|
+
meta['site_directory'] = original
|
|
80
|
+
meta.pop('published_site_directory', None)
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""User configuration, its validation, and the meta of a build."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import asdict, dataclass, field
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
import difflib
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
from lhtml_site.filesystem import read_yaml
|
|
12
|
+
from lhtml_site.structure import PRIVATE_KEYS
|
|
13
|
+
|
|
14
|
+
GENERATOR_DIRECTORY = Path(__file__).resolve().parent # the package: its code, themes/, plugins/, example/
|
|
15
|
+
RENAMED_DIRECTORIES = {'theme_templates': 'themes', 'src_site_example': 'example'} # in 3.0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Keys of meta set by the generator during a build (never by the configuration)
|
|
19
|
+
# Plugins of the previous version that the build now runs itself: the name of
|
|
20
|
+
# the file, and what it became.
|
|
21
|
+
CORE_STAGES = {
|
|
22
|
+
'menu.py': "the theme's menu",
|
|
23
|
+
'title_submenu.py': 'the heading ids (configuration key title_id:)',
|
|
24
|
+
'auto_wrap.py': 'the wrapping of a page in the template of its theme',
|
|
25
|
+
'auto-wrap.py': 'the wrapping of a page in the template of its theme',
|
|
26
|
+
'redirection_first_page.py': 'the index of a site that has none',
|
|
27
|
+
'cache_video.py': "the transcoded videos (set 'cache_video_directory:' to ask for them)",
|
|
28
|
+
'generate_pdf.py': "the PDF export, a target now (targets: pdf: {theme: slides-pdf})",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
RUNTIME_KEYS = frozenset((
|
|
32
|
+
'args', 'log', 'plugin_paths', 'config_file', 'config_directory', 'lib_directory',
|
|
33
|
+
'published_site_directory', # the site, while the build is made in a staging directory
|
|
34
|
+
'exported', # the file an export wrote (lhtml_site/pdf_export.py)
|
|
35
|
+
'only', # --only: the build is made in the site, for some pages
|
|
36
|
+
'structure', 'built', # all the pages of the site / those generated (lhtml_site/structure.py)
|
|
37
|
+
'previous_pages', # --only: HTML of the pages before the build (restored if they fail)
|
|
38
|
+
'strict', # --strict: the check of the warnings, before the pages are kept
|
|
39
|
+
'loaded_design', 'macros', # the design (lhtml_site/design.py) and its macros for LHTML
|
|
40
|
+
'heading_ids', 'heading_summary', # the ids of each page / its headings (lhtml_site/headings.py)
|
|
41
|
+
'lint_findings', # {source path: [finding]} of the generated pages (lhtml_site/lint.py)
|
|
42
|
+
'measure', # --only: the pages the layout report measures again
|
|
43
|
+
'source_roots', # the sources read by each page (lhtml_site/tracking.py)
|
|
44
|
+
'dependency_contexts', 'dependency_store', 'dependency_measure',
|
|
45
|
+
'template_dependencies', # what each page depends on (lhtml_site/dependencies.py)
|
|
46
|
+
'_dependency_engine', '_dependency_theme_scopes', # the same for all its pages: read once
|
|
47
|
+
'analyse_layout', # --layout: the build runs the layout analysis (lhtml_site/layout/build.py)
|
|
48
|
+
'publish_site', # a target asks for its intermediate site not to be published
|
|
49
|
+
'source_map', 'feedback', # builds for development (lhtml_site/source_map.py, lhtml_site/feedback.py)
|
|
50
|
+
'planned_slides', # entries of the deck without a source yet
|
|
51
|
+
'export_name', # the name of the PDF of a deck (lhtml_site/pdf_export.py)
|
|
52
|
+
'build_scope', # <deck>/<target>: where its state is kept (lhtml_site/project.py)
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ConfigError(ValueError):
|
|
57
|
+
"""An actionable configuration error, displayed without a traceback."""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class Config:
|
|
62
|
+
# Directories: as written in the YAML, absolute Paths once load_config resolved them
|
|
63
|
+
source_directory: Path | str = 'src_site'
|
|
64
|
+
site_directory: Path | str = '.site'
|
|
65
|
+
theme: Path | str = str(GENERATOR_DIRECTORY / 'themes/webpage-frame')
|
|
66
|
+
# Extensions, and nothing else: everything a bundled theme needs to
|
|
67
|
+
# render a project written in LHTML is a stage of the build now
|
|
68
|
+
# (lhtml_site/wrap.py, lhtml_site/headings.py, lhtml_site/menu.py, lhtml_site/index_page.py), so the
|
|
69
|
+
# default is empty and a project lists only what is its own.
|
|
70
|
+
plugin: list[str] = field(default_factory=list)
|
|
71
|
+
debug: bool = False
|
|
72
|
+
title_id: bool = True
|
|
73
|
+
keywords: dict = field(default_factory=dict)
|
|
74
|
+
use_tidy: bool = False
|
|
75
|
+
line_breaks: bool = False # 'line-breaks:' in the YAML, as LHTML spells it
|
|
76
|
+
# Page settings a custom theme needs in the site, beyond the ones the
|
|
77
|
+
# bundled themes display (lhtml_site/structure.py, PUBLIC_KEYS). Everything else
|
|
78
|
+
# an author writes in a config.yaml or a deck entry stays out of the site.
|
|
79
|
+
public_page_keys: list[str] = field(default_factory=list)
|
|
80
|
+
plugin_arg: dict = field(default_factory=dict)
|
|
81
|
+
layout: dict = field(default_factory=dict) # options of the layout analysis (--layout)
|
|
82
|
+
cache_video_directory: Path | str | None = None
|
|
83
|
+
chrome: str | None = None
|
|
84
|
+
design: dict | str | None = None
|
|
85
|
+
deck: list | dict | str | None = None
|
|
86
|
+
# A project of several decks, each built to several targets (lhtml_site/project.py):
|
|
87
|
+
# 'directories' (each directory of the sources is a deck), and
|
|
88
|
+
# {name: {theme: directory}}, the themes resolved to Paths by load_config
|
|
89
|
+
decks: str | None = None
|
|
90
|
+
targets: dict | None = None
|
|
91
|
+
|
|
92
|
+
def to_meta(self):
|
|
93
|
+
values = asdict(self)
|
|
94
|
+
for key in ('cache_video_directory', 'chrome', 'design', 'deck', 'decks', 'targets'):
|
|
95
|
+
if values[key] is None:
|
|
96
|
+
del values[key]
|
|
97
|
+
return values
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class BuildContext:
|
|
102
|
+
config: Config
|
|
103
|
+
config_file: Path
|
|
104
|
+
args: Any
|
|
105
|
+
log: Any
|
|
106
|
+
plugin_paths: list[str] = field(default_factory=list)
|
|
107
|
+
meta: dict = field(init=False)
|
|
108
|
+
|
|
109
|
+
def __post_init__(self):
|
|
110
|
+
# The state of a build: a copy, which never changes the configuration
|
|
111
|
+
self.meta = self.config.to_meta()
|
|
112
|
+
self.meta.update(config_file=str(self.config_file),
|
|
113
|
+
config_directory=self.config_file.parent,
|
|
114
|
+
lib_directory=GENERATOR_DIRECTORY,
|
|
115
|
+
args=self.args, log=self.log,
|
|
116
|
+
plugin_paths=self.plugin_paths)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _file_or_inline(values, key, inline_types, inline_text, config_directory):
|
|
120
|
+
"""A key given as the path of a YAML file (resolved from the configuration
|
|
121
|
+
directory, must exist) or inline (mapping with string keys / list)."""
|
|
122
|
+
value = values.get(key)
|
|
123
|
+
if value is None:
|
|
124
|
+
return
|
|
125
|
+
if isinstance(value, str) and value.strip():
|
|
126
|
+
file = Path(value).expanduser()
|
|
127
|
+
if not file.is_absolute():
|
|
128
|
+
file = config_directory / file
|
|
129
|
+
if not file.is_file():
|
|
130
|
+
raise ConfigError(f"'{key}' file not found: '{file}'")
|
|
131
|
+
values[key] = str(file.resolve())
|
|
132
|
+
elif not isinstance(value, inline_types) or (isinstance(value, dict)
|
|
133
|
+
and any(not isinstance(k, str) for k in value)):
|
|
134
|
+
raise ConfigError(f"'{key}' must be {inline_text} or the path of a YAML file")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _chrome_executable(values, config_directory):
|
|
138
|
+
"""'chrome': the Chrome executable the browser runs (layout report,
|
|
139
|
+
PDF export, --render), instead of the Chromium of Playwright. A
|
|
140
|
+
relative path is resolved from the configuration directory; a macOS
|
|
141
|
+
bundle ('.app') is resolved to the executable it contains."""
|
|
142
|
+
value = values.get('chrome')
|
|
143
|
+
if value is None:
|
|
144
|
+
return
|
|
145
|
+
if not isinstance(value, str) or not value.strip():
|
|
146
|
+
raise ConfigError("'chrome' must be the path of the Chrome executable")
|
|
147
|
+
file = Path(value.strip()).expanduser()
|
|
148
|
+
if not file.is_absolute():
|
|
149
|
+
file = config_directory / file
|
|
150
|
+
if file.suffix == '.app' and file.is_dir(): # macOS bundle
|
|
151
|
+
inside = file / 'Contents/MacOS' / file.stem
|
|
152
|
+
if not inside.is_file():
|
|
153
|
+
candidates = sorted(p for p in (file / 'Contents/MacOS').glob('*') if p.is_file())
|
|
154
|
+
if not candidates:
|
|
155
|
+
raise ConfigError(f"'chrome': no executable in the bundle '{file}'")
|
|
156
|
+
inside = candidates[0]
|
|
157
|
+
file = inside
|
|
158
|
+
if not file.is_file():
|
|
159
|
+
raise ConfigError(f"'chrome' executable not found: '{file}'")
|
|
160
|
+
if not os.access(file, os.X_OK):
|
|
161
|
+
raise ConfigError(f"'chrome' is not executable: '{file}'")
|
|
162
|
+
values['chrome'] = str(file.resolve())
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
TARGET_NAME = re.compile(r'[A-Za-z][A-Za-z0-9_-]*$')
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def in_package(path):
|
|
169
|
+
"""A path into a checkout of the generator as the projects write it
|
|
170
|
+
(a submodule): its themes/, plugins/ and example/ were at the root of the
|
|
171
|
+
repository, and are in its package now (src/lhtml_site/). The same path
|
|
172
|
+
in the package when it exists there, else the path unchanged."""
|
|
173
|
+
path = Path(path)
|
|
174
|
+
if path.exists():
|
|
175
|
+
return path
|
|
176
|
+
for i, part in enumerate(path.parts):
|
|
177
|
+
if part in ('themes', 'plugins', 'example'):
|
|
178
|
+
moved = Path(*path.parts[:i], 'src', 'lhtml_site', *path.parts[i:])
|
|
179
|
+
if moved.exists():
|
|
180
|
+
return moved
|
|
181
|
+
return path
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def theme_directory(value, config_directory):
|
|
185
|
+
"""A theme given as a directory (relative to the configuration) or as the
|
|
186
|
+
name of a theme of the generator ('slides')."""
|
|
187
|
+
directory = Path(value).expanduser()
|
|
188
|
+
if not directory.is_absolute():
|
|
189
|
+
directory = config_directory / directory
|
|
190
|
+
if not directory.is_dir() and (GENERATOR_DIRECTORY / 'themes' / value).is_dir():
|
|
191
|
+
directory = GENERATOR_DIRECTORY / 'themes' / value
|
|
192
|
+
return in_package(directory).resolve()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _decks_and_targets(values, data, config_directory):
|
|
196
|
+
"""'decks' and 'targets' (lhtml_site/project.py), checked, the themes resolved."""
|
|
197
|
+
if values.get('decks') is not None and values['decks'] != 'directories':
|
|
198
|
+
raise ConfigError("'decks' must be 'directories' (each directory of the sources is a deck)")
|
|
199
|
+
targets = values.get('targets')
|
|
200
|
+
if targets is None:
|
|
201
|
+
return
|
|
202
|
+
if not isinstance(targets, dict) or not targets:
|
|
203
|
+
raise ConfigError("'targets' must map a name to its settings, e.g. html: {theme: slides}")
|
|
204
|
+
if 'theme' in data:
|
|
205
|
+
raise ConfigError("'theme' and 'targets' together: give the theme of each target")
|
|
206
|
+
resolved = {}
|
|
207
|
+
for name, spec in targets.items():
|
|
208
|
+
if not isinstance(name, str) or not TARGET_NAME.match(name):
|
|
209
|
+
raise ConfigError(f"'targets': invalid name {name!r} (letters, digits, - and _)")
|
|
210
|
+
if name == 'images':
|
|
211
|
+
raise ConfigError("'targets': 'images' is where the PDF export writes the slide images; "
|
|
212
|
+
"choose another name")
|
|
213
|
+
if not isinstance(spec, dict) or set(spec) != {'theme'} or not isinstance(spec['theme'], str):
|
|
214
|
+
raise ConfigError(f"'targets': {name}: expected {{theme: <theme>}}")
|
|
215
|
+
# its existence is checked by validate_paths, as the one of the
|
|
216
|
+
# sources: --clean needs neither
|
|
217
|
+
resolved[name] = {'theme': theme_directory(spec['theme'], config_directory)}
|
|
218
|
+
values['targets'] = resolved
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def load_config(filename, debug_override=None):
|
|
222
|
+
path = Path(filename).expanduser().resolve()
|
|
223
|
+
warnings = []
|
|
224
|
+
if not path.exists() and path.name == 'configure_default.yaml':
|
|
225
|
+
raise ConfigError("'configure_default.yaml' was renamed 'configure_example.yaml' (lhtml-site 1.0): "
|
|
226
|
+
"update your command")
|
|
227
|
+
if not path.exists():
|
|
228
|
+
raise ConfigError(f"Configuration not found: '{path}' (run lhtml-site in the "
|
|
229
|
+
f"directory of a project; lhtml-site init creates one)")
|
|
230
|
+
try:
|
|
231
|
+
data = read_yaml(path)
|
|
232
|
+
except ValueError as exc: # the message names the file
|
|
233
|
+
raise ConfigError(str(exc)) from None
|
|
234
|
+
if data is None:
|
|
235
|
+
data = {}
|
|
236
|
+
if not isinstance(data, dict) or any(not isinstance(k, str) for k in data):
|
|
237
|
+
raise ConfigError('Configuration must contain named key: value pairs')
|
|
238
|
+
|
|
239
|
+
if 'line-breaks' in data: # the spelling of LHTML
|
|
240
|
+
data['line_breaks'] = data.pop('line-breaks')
|
|
241
|
+
if 'plugin_post' in data:
|
|
242
|
+
raise ConfigError("'plugin_post' was removed (lhtml-site 1.0): list its plugins under 'plugin' "
|
|
243
|
+
"(all their hooks are run)")
|
|
244
|
+
if 'level_print' in data:
|
|
245
|
+
raise ConfigError("'level_print' was removed (lhtml-site 1.0): the build prints no indented log "
|
|
246
|
+
"(-v shows its stages)")
|
|
247
|
+
if 'include_head' in data:
|
|
248
|
+
raise ConfigError("'include_head' was removed (lhtml-site 1.0): it was read nowhere; a theme "
|
|
249
|
+
"places what goes in <head>")
|
|
250
|
+
|
|
251
|
+
defaults = Config().to_meta()
|
|
252
|
+
known = set(defaults) | {'cache_video_directory', 'chrome', 'design', 'deck', 'decks', 'targets'}
|
|
253
|
+
for key in sorted(set(data) - known):
|
|
254
|
+
if key in RUNTIME_KEYS:
|
|
255
|
+
raise ConfigError(f"'{key}' is runtime state and cannot be set in YAML")
|
|
256
|
+
suggestion = difflib.get_close_matches(key, sorted(known), n=1)
|
|
257
|
+
hint = f" Did you mean '{suggestion[0]}'?" if suggestion else ''
|
|
258
|
+
warnings.append(f"Unknown configuration key '{key}', ignored.{hint} "
|
|
259
|
+
f"(A plugin reads its options in 'plugin_arg'.)")
|
|
260
|
+
values = {**defaults, **{k: v for k, v in data.items() if k in known}}
|
|
261
|
+
for key in ('debug', 'title_id', 'use_tidy', 'line_breaks'):
|
|
262
|
+
if type(values[key]) is not bool:
|
|
263
|
+
raise ConfigError(f"'{key}' must be a boolean (true or false)")
|
|
264
|
+
for key in ('keywords', 'plugin_arg', 'layout'):
|
|
265
|
+
if not isinstance(values[key], dict) or any(not isinstance(k, str) for k in values[key]):
|
|
266
|
+
raise ConfigError(f"'{key}' must be a mapping with string keys")
|
|
267
|
+
for name in sorted({'params', 'page', 'credit', 'credits'} & set(values['keywords'])):
|
|
268
|
+
raise ConfigError(f"'keywords': '{name}' is reserved (Jinja variable of the generator: "
|
|
269
|
+
f"params, the deck parameters of a page; page, its metadata; credit, credits)")
|
|
270
|
+
plugins = values['plugin']
|
|
271
|
+
if isinstance(plugins, str):
|
|
272
|
+
plugins = [plugins]
|
|
273
|
+
elif plugins is None:
|
|
274
|
+
plugins = []
|
|
275
|
+
values['plugin'] = plugins
|
|
276
|
+
for key in ('plugin', 'public_page_keys'):
|
|
277
|
+
if not isinstance(values[key], list) or any(not isinstance(v, str) or not v.strip() for v in values[key]):
|
|
278
|
+
raise ConfigError(f"'{key}' must be a list of non-empty strings")
|
|
279
|
+
for name in sorted(set(values['public_page_keys']) & set(PRIVATE_KEYS)):
|
|
280
|
+
raise ConfigError(f"'public_page_keys': '{name}' is never written into the site "
|
|
281
|
+
f"(it is the path of the source on this machine)")
|
|
282
|
+
# The layout analysis is part of the generator, not a plugin: it is
|
|
283
|
+
# selected by --layout and configured by 'layout:'. A configuration of
|
|
284
|
+
# the previous version says so here, before the build, naming the fix.
|
|
285
|
+
for entry in plugins:
|
|
286
|
+
if entry.endswith('layout_report.py'):
|
|
287
|
+
raise ConfigError("'plugin': the layout report is no longer a plugin. Remove "
|
|
288
|
+
f"'{entry}' from 'plugin:' and run the build with --layout")
|
|
289
|
+
if isinstance(values['plugin_arg'], dict) and 'layout_report' in values['plugin_arg']:
|
|
290
|
+
raise ConfigError("'plugin_arg': the options of the layout report moved to the "
|
|
291
|
+
"configuration key 'layout:'. Move the content of "
|
|
292
|
+
"'plugin_arg: layout_report:' under 'layout:'")
|
|
293
|
+
# Stages the build always runs: a project does not list them, and cannot
|
|
294
|
+
# know whether its theme has a menu - the theme declares that (theme.yaml).
|
|
295
|
+
for entry in plugins:
|
|
296
|
+
name = entry.rsplit('/', 1)[-1]
|
|
297
|
+
if name in CORE_STAGES:
|
|
298
|
+
raise ConfigError(f"'plugin': {CORE_STAGES[name]} is a stage of the build, not a "
|
|
299
|
+
f"plugin. Remove '{entry}' from 'plugin:'")
|
|
300
|
+
if isinstance(values['plugin_arg'], dict) and 'menu' in values['plugin_arg']:
|
|
301
|
+
raise ConfigError("'plugin_arg': the extra page settings of the menu are declared by "
|
|
302
|
+
"the theme. Move the content of 'plugin_arg: menu: keys:' to "
|
|
303
|
+
"'toc: keys:' in the theme.yaml of your theme")
|
|
304
|
+
|
|
305
|
+
_chrome_executable(values, path.parent)
|
|
306
|
+
_decks_and_targets(values, data, path.parent)
|
|
307
|
+
|
|
308
|
+
_file_or_inline(values, 'design', dict, 'a mapping (tokens, macros)', path.parent)
|
|
309
|
+
_file_or_inline(values, 'deck', (list, dict), 'a list of slides', path.parent)
|
|
310
|
+
|
|
311
|
+
for key in ('source_directory', 'site_directory', 'theme', 'cache_video_directory'):
|
|
312
|
+
if key not in values:
|
|
313
|
+
continue
|
|
314
|
+
value = values[key]
|
|
315
|
+
if not isinstance(value, str) or not value.strip():
|
|
316
|
+
raise ConfigError(f"'{key}' must be a non-empty directory path")
|
|
317
|
+
directory = Path(value).expanduser()
|
|
318
|
+
if not directory.is_absolute():
|
|
319
|
+
directory = path.parent / directory
|
|
320
|
+
if key == 'theme': # a directory, or the name of a theme of the generator
|
|
321
|
+
directory = theme_directory(value, path.parent)
|
|
322
|
+
elif key == 'source_directory':
|
|
323
|
+
directory = in_package(directory)
|
|
324
|
+
if key in ('source_directory', 'theme') and not directory.is_dir():
|
|
325
|
+
# the new name as the configuration writes it (a checkout: found in its package)
|
|
326
|
+
renamed = Path(*[RENAMED_DIRECTORIES.get(part, part) for part in directory.parts])
|
|
327
|
+
if in_package(renamed).is_dir():
|
|
328
|
+
raise ConfigError(f"'{key}': '{directory.as_posix()}' was renamed "
|
|
329
|
+
f"'{renamed.as_posix()}' (lhtml-site 1.0): update '{key}'")
|
|
330
|
+
values[key] = directory.resolve()
|
|
331
|
+
if debug_override is not None:
|
|
332
|
+
values['debug'] = debug_override
|
|
333
|
+
return Config(**values), path, warnings
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def validate_paths(config, config_file, require_inputs=True):
|
|
337
|
+
output = Path(config.site_directory).resolve()
|
|
338
|
+
inputs = [('source_directory', config.source_directory)]
|
|
339
|
+
inputs += [(f'targets: {name}: theme', spec['theme']) for name, spec in (config.targets or {}).items()] \
|
|
340
|
+
or [('theme', config.theme)]
|
|
341
|
+
for key, directory in inputs:
|
|
342
|
+
directory = Path(directory).resolve()
|
|
343
|
+
if output == directory or output in directory.parents or directory in output.parents:
|
|
344
|
+
raise ConfigError(f"Unsafe 'site_directory': output and '{key}' must not overlap ({output}, {directory})")
|
|
345
|
+
if require_inputs and not directory.is_dir():
|
|
346
|
+
raise ConfigError(f"'{key}' directory not found: {directory}")
|
|
347
|
+
protected = [config_file.resolve(), GENERATOR_DIRECTORY.resolve()]
|
|
348
|
+
if config.cache_video_directory:
|
|
349
|
+
protected.append(Path(config.cache_video_directory).resolve())
|
|
350
|
+
for path in protected:
|
|
351
|
+
if output == path or output in path.parents:
|
|
352
|
+
raise ConfigError(f"Unsafe 'site_directory': deleting '{output}' would remove '{path}'")
|
|
353
|
+
if output.exists() and not output.is_dir():
|
|
354
|
+
raise ConfigError(f"'site_directory' is not a directory: {output}")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# configure.yaml: the configuration of a site built by lhtml-site.
|
|
2
|
+
#
|
|
3
|
+
# Run from this directory:
|
|
4
|
+
# lhtml-site build into .site/ (open .site/index.html in a browser)
|
|
5
|
+
# lhtml-site --watch build, and rebuild on each save
|
|
6
|
+
# lhtml-site --serve --watch the same, and a preview on http://127.0.0.1:8000/
|
|
7
|
+
# lhtml-site --check-config check this file, without building
|
|
8
|
+
#
|
|
9
|
+
# Relative paths are read from this directory. A line starting with # is a
|
|
10
|
+
# comment: remove the # in front of an option to use it. The options of the
|
|
11
|
+
# command line (--debug, ...) take precedence over this file. Every option is
|
|
12
|
+
# described in the section Configuration of the reference of lhtml-site:
|
|
13
|
+
# https://github.com/drohmer/lhtml-site/blob/main/src/lhtml_site/docs/reference.md
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# Essentials
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
# Where the pages are. A page is a file .html.j2 (LHTML) or .md.j2 (Markdown),
|
|
20
|
+
# usually <name>/index.html.j2 with its images and videos next to it; the
|
|
21
|
+
# pages follow the order of their paths (01_..., 02_...) unless a deck says.
|
|
22
|
+
source_directory: 'src/'
|
|
23
|
+
|
|
24
|
+
# Where the site is written. It is replaced at each build: never edit it.
|
|
25
|
+
site_directory: '.site/'
|
|
26
|
+
|
|
27
|
+
# The look of the site: a theme of the generator, or the directory of yours.
|
|
28
|
+
# webpage-frame web pages with a navigation menu
|
|
29
|
+
# slides slides shown in the browser (1920x1080)
|
|
30
|
+
# slides-pdf the same slides, exported to a PDF (needs Playwright, lhtml-site[pdf]: lhtml-site doctor)
|
|
31
|
+
theme: 'webpage-frame'
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Common options
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
# The order of the pages (of the slides of a talk): a file listing them, see
|
|
38
|
+
# the section Deck of the reference. Without it, the order of their paths.
|
|
39
|
+
# deck: 'deck.yaml'
|
|
40
|
+
|
|
41
|
+
# Values every page can show, as Jinja variables: {{ author }}, {{ year }}.
|
|
42
|
+
# keywords:
|
|
43
|
+
# author: 'Your Name'
|
|
44
|
+
# year: 2026
|
|
45
|
+
|
|
46
|
+
# Your own macros and design tokens, added to those of the theme (the slides
|
|
47
|
+
# themes have a design: .structure/design.md lists it after a build).
|
|
48
|
+
# design:
|
|
49
|
+
# tokens:
|
|
50
|
+
# font: {small: 80%}
|
|
51
|
+
# macros:
|
|
52
|
+
# note:
|
|
53
|
+
# class: note
|
|
54
|
+
# doc: 'A remark in gray: note:: ... ::'
|
|
55
|
+
# css: '.note { color: gray; }'
|
|
56
|
+
|
|
57
|
+
# The videos of the pages, converted to the formats every browser reads
|
|
58
|
+
# (needs ffmpeg, and ffpb: pip install ffpb). The converted files are kept
|
|
59
|
+
# in this directory.
|
|
60
|
+
# cache_video_directory: 'cache_video/'
|
|
61
|
+
|
|
62
|
+
# The headings get an id, so that a section can be linked, and the theme
|
|
63
|
+
# lists them. Default: true.
|
|
64
|
+
# title_id: true
|
|
65
|
+
|
|
66
|
+
# A line break in a source is a line break in the page. Default: false (the
|
|
67
|
+
# lines of a paragraph are joined).
|
|
68
|
+
# line-breaks: false
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# Several decks, in the browser and as a PDF
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
# Each directory of source_directory is a deck, built once for each target,
|
|
75
|
+
# into <site_directory>/<deck>/<target>/. Takes the place of 'theme:' above
|
|
76
|
+
# (remove that line).
|
|
77
|
+
# decks: directories
|
|
78
|
+
# targets:
|
|
79
|
+
# html: {theme: slides}
|
|
80
|
+
# pdf: {theme: slides-pdf}
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# Extensions
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
# Transformations of the pages, in the order they run. The menu, the heading
|
|
87
|
+
# ids and the template of the theme are part of the build: list only your
|
|
88
|
+
# own here. Paths are relative to this file, then to the generator, which
|
|
89
|
+
# comes with two:
|
|
90
|
+
# plugins/pre_include.py text at the top of every page (shared Jinja macros)
|
|
91
|
+
# plugins/code_include.py code of a git repository inserted in the pages
|
|
92
|
+
# plugin: ['plugins/pre_include.py']
|
|
93
|
+
# plugin_arg: # the options of each extension, by its name
|
|
94
|
+
# pre_include: ['macros.html']
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Tools
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
# The layout report (lhtml-site --layout, needs Playwright, lhtml-site[pdf]): where it is
|
|
101
|
+
# written. The theme says which part of the page it measures.
|
|
102
|
+
# layout:
|
|
103
|
+
# output: '.layout/'
|
|
104
|
+
|
|
105
|
+
# The Chrome run by the PDF export, the layout report and --render, instead of
|
|
106
|
+
# the Chromium of Playwright (a macOS .app is accepted).
|
|
107
|
+
# chrome: '/Applications/Google Chrome.app'
|
|
108
|
+
|
|
109
|
+
# Keep the intermediate files and show the tracebacks (as --debug).
|
|
110
|
+
# debug: false
|
|
111
|
+
|
|
112
|
+
# Clean the HTML of the pages with tidy (needs the tidy library).
|
|
113
|
+
# use_tidy: false
|