latextify 0.3.2__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. {latextify-0.3.2 → latextify-0.3.3}/.github/workflows/ci.yml +34 -3
  2. {latextify-0.3.2 → latextify-0.3.3}/.github/workflows/release.yml +77 -6
  3. {latextify-0.3.2 → latextify-0.3.3}/PKG-INFO +24 -3
  4. {latextify-0.3.2 → latextify-0.3.3}/README.md +23 -2
  5. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_kit.py +6 -0
  6. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/README-OFFLINE.md +4 -0
  7. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/build.py +10 -4
  8. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/install_template.py +8 -0
  9. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/target.py +2 -0
  10. latextify-0.3.3/packaging/README-WINDOWS-OFFLINE-REPO.txt +55 -0
  11. {latextify-0.3.2 → latextify-0.3.3}/pyproject.toml +1 -1
  12. latextify-0.3.3/startup-online.bat +5 -0
  13. {latextify-0.3.2 → latextify-0.3.3}/startup.bat +46 -7
  14. {latextify-0.3.2 → latextify-0.3.3}/tests/test_kit_build.py +28 -0
  15. latextify-0.3.3/tests/test_offline_repo.py +53 -0
  16. latextify-0.3.3/tools/build_windows_offline_repo.py +164 -0
  17. {latextify-0.3.2 → latextify-0.3.3}/uv.lock +1 -1
  18. {latextify-0.3.2 → latextify-0.3.3}/.dockerignore +0 -0
  19. {latextify-0.3.2 → latextify-0.3.3}/.gitattributes +0 -0
  20. {latextify-0.3.2 → latextify-0.3.3}/.github/dependabot.yml +0 -0
  21. {latextify-0.3.2 → latextify-0.3.3}/.github/workflows/codeql.yml +0 -0
  22. {latextify-0.3.2 → latextify-0.3.3}/.github/workflows/dependency-audit.yml +0 -0
  23. {latextify-0.3.2 → latextify-0.3.3}/.github/workflows/publish.yml +0 -0
  24. {latextify-0.3.2 → latextify-0.3.3}/.gitignore +0 -0
  25. {latextify-0.3.2 → latextify-0.3.3}/.python-version +0 -0
  26. {latextify-0.3.2 → latextify-0.3.3}/CONTRIBUTING.md +0 -0
  27. {latextify-0.3.2 → latextify-0.3.3}/LICENSE +0 -0
  28. {latextify-0.3.2 → latextify-0.3.3}/NOTICE +0 -0
  29. {latextify-0.3.2 → latextify-0.3.3}/SECURITY.md +0 -0
  30. {latextify-0.3.2 → latextify-0.3.3}/deploy/hf-space/DEPLOY.md +0 -0
  31. {latextify-0.3.2 → latextify-0.3.3}/deploy/hf-space/Dockerfile +0 -0
  32. {latextify-0.3.2 → latextify-0.3.3}/deploy/hf-space/README.md +0 -0
  33. {latextify-0.3.2 → latextify-0.3.3}/deploy/hf-space/warm_cache.py +0 -0
  34. {latextify-0.3.2 → latextify-0.3.3}/deploy/render/DEPLOY.md +0 -0
  35. {latextify-0.3.2 → latextify-0.3.3}/deploy/render/Dockerfile +0 -0
  36. {latextify-0.3.2 → latextify-0.3.3}/examples/01-all-embedded/.gitignore +0 -0
  37. {latextify-0.3.2 → latextify-0.3.3}/examples/01-all-embedded/README.md +0 -0
  38. {latextify-0.3.2 → latextify-0.3.3}/examples/01-all-embedded/make_manuscript.py +0 -0
  39. {latextify-0.3.2 → latextify-0.3.3}/examples/01-all-embedded/run.py +0 -0
  40. {latextify-0.3.2 → latextify-0.3.3}/examples/02-word-plus-figures/.gitignore +0 -0
  41. {latextify-0.3.2 → latextify-0.3.3}/examples/02-word-plus-figures/README.md +0 -0
  42. {latextify-0.3.2 → latextify-0.3.3}/examples/02-word-plus-figures/make_manuscript.py +0 -0
  43. {latextify-0.3.2 → latextify-0.3.3}/examples/02-word-plus-figures/run.py +0 -0
  44. {latextify-0.3.2 → latextify-0.3.3}/examples/03-multipart-refmanager/.gitignore +0 -0
  45. {latextify-0.3.2 → latextify-0.3.3}/examples/03-multipart-refmanager/README.md +0 -0
  46. {latextify-0.3.2 → latextify-0.3.3}/examples/03-multipart-refmanager/make_manuscript.py +0 -0
  47. {latextify-0.3.2 → latextify-0.3.3}/examples/03-multipart-refmanager/run.py +0 -0
  48. {latextify-0.3.2 → latextify-0.3.3}/examples/README.md +0 -0
  49. {latextify-0.3.2 → latextify-0.3.3}/latextify/__init__.py +0 -0
  50. {latextify-0.3.2 → latextify-0.3.3}/latextify/__main__.py +0 -0
  51. {latextify-0.3.2 → latextify-0.3.3}/latextify/audit/__init__.py +0 -0
  52. {latextify-0.3.2 → latextify-0.3.3}/latextify/audit/equations.py +0 -0
  53. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/__init__.py +0 -0
  54. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/authoryear_index.py +0 -0
  55. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/bib.py +0 -0
  56. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/bibmatch.py +0 -0
  57. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/bibtex_in.py +0 -0
  58. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/body_markers.py +0 -0
  59. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/corrections.py +0 -0
  60. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/crossref.py +0 -0
  61. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/csl_json_in.py +0 -0
  62. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/endnote.py +0 -0
  63. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/endnote_xml_in.py +0 -0
  64. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/fields.py +0 -0
  65. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/mendeley.py +0 -0
  66. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/merge.py +0 -0
  67. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/nbib_in.py +0 -0
  68. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/plaintext.py +0 -0
  69. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/reconcile.py +0 -0
  70. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/reflist_nondocx.py +0 -0
  71. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/refs_import.py +0 -0
  72. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/validate.py +0 -0
  73. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/wordnative.py +0 -0
  74. {latextify-0.3.2 → latextify-0.3.3}/latextify/citations/zotero.py +0 -0
  75. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli.py +0 -0
  76. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_batch.py +0 -0
  77. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_convert.py +0 -0
  78. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_equations.py +0 -0
  79. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_export.py +0 -0
  80. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_figures.py +0 -0
  81. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_privacy.py +0 -0
  82. {latextify-0.3.2 → latextify-0.3.3}/latextify/cli_review.py +0 -0
  83. {latextify-0.3.2 → latextify-0.3.3}/latextify/compile/__init__.py +0 -0
  84. {latextify-0.3.2 → latextify-0.3.3}/latextify/compile/logs.py +0 -0
  85. {latextify-0.3.2 → latextify-0.3.3}/latextify/compile/pdf.py +0 -0
  86. {latextify-0.3.2 → latextify-0.3.3}/latextify/compile/tectonic.py +0 -0
  87. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/__init__.py +0 -0
  88. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/alt_formats.py +0 -0
  89. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/alt_formats_render.py +0 -0
  90. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/anchors.py +0 -0
  91. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/bibliography.py +0 -0
  92. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/citation_resolution.py +0 -0
  93. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/figures_copy.py +0 -0
  94. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/figures_stage.py +0 -0
  95. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/inline_supplement.py +0 -0
  96. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/metadata.py +0 -0
  97. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/output_dir.py +0 -0
  98. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/project.py +0 -0
  99. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/submission.py +0 -0
  100. {latextify-0.3.2 → latextify-0.3.3}/latextify/emit/supplement.py +0 -0
  101. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/__init__.py +0 -0
  102. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/caption_gaps.py +0 -0
  103. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/convert.py +0 -0
  104. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/crop.py +0 -0
  105. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/extract.py +0 -0
  106. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/gap_fill.py +0 -0
  107. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/inventory.py +0 -0
  108. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/outcome.py +0 -0
  109. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/override.py +0 -0
  110. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/placement.py +0 -0
  111. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/raster.py +0 -0
  112. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/scrub.py +0 -0
  113. {latextify-0.3.2 → latextify-0.3.3}/latextify/figures/vector.py +0 -0
  114. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/__init__.py +0 -0
  115. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/convert_inputs.py +0 -0
  116. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/convert_routes.py +0 -0
  117. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/demo.py +0 -0
  118. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/downloads.py +0 -0
  119. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/exporting.py +0 -0
  120. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/figures_view.py +0 -0
  121. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/folder_picker.py +0 -0
  122. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/guard.py +0 -0
  123. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/lifecycle.py +0 -0
  124. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/schemas.py +0 -0
  125. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/server.py +0 -0
  126. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/alt-export.js +0 -0
  127. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/app.js +0 -0
  128. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/clean.js +0 -0
  129. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/export.js +0 -0
  130. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/figure-placement.js +0 -0
  131. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/figures.js +0 -0
  132. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/index.html +0 -0
  133. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/lifecycle.js +0 -0
  134. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/results.js +0 -0
  135. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/review.js +0 -0
  136. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/static/style.css +0 -0
  137. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/upload_utils.py +0 -0
  138. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/uploads_routes.py +0 -0
  139. {latextify-0.3.2 → latextify-0.3.3}/latextify/gui/validation_view.py +0 -0
  140. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/__init__.py +0 -0
  141. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/_xml.py +0 -0
  142. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/archive_guard.py +0 -0
  143. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/citation_sentinels.py +0 -0
  144. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/docx_clean.py +0 -0
  145. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/filters.py +0 -0
  146. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/formats.py +0 -0
  147. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/frontmatter.py +0 -0
  148. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/headings.py +0 -0
  149. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_authors.py +0 -0
  150. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_body.py +0 -0
  151. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_guess.py +0 -0
  152. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_guess_nondocx.py +0 -0
  153. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_markers.py +0 -0
  154. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_paragraphs.py +0 -0
  155. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/metadata_schema.py +0 -0
  156. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/pandoc.py +0 -0
  157. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/portable_anchors.py +0 -0
  158. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/preflight.py +0 -0
  159. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/preflight_privacy.py +0 -0
  160. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/tables.py +0 -0
  161. {latextify-0.3.2 → latextify-0.3.3}/latextify/ingest/tables_degraded.py +0 -0
  162. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/__init__.py +0 -0
  163. {latextify-0.3.2 → latextify-0.3.3}/latextify/kit/tex_cache.py +0 -0
  164. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/__init__.py +0 -0
  165. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/_compat.py +0 -0
  166. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/body.py +0 -0
  167. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/compile.py +0 -0
  168. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/emit.py +0 -0
  169. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/equations.py +0 -0
  170. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/figure.py +0 -0
  171. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/meta.py +0 -0
  172. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/preflight.py +0 -0
  173. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/reconcile.py +0 -0
  174. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/refs.py +0 -0
  175. {latextify-0.3.2 → latextify-0.3.3}/latextify/model/validate.py +0 -0
  176. {latextify-0.3.2 → latextify-0.3.3}/latextify/portable.py +0 -0
  177. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/__init__.py +0 -0
  178. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/docx_adapter.py +0 -0
  179. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/images.py +0 -0
  180. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/ole.py +0 -0
  181. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/opc.py +0 -0
  182. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/pdf.py +0 -0
  183. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/pdf_content.py +0 -0
  184. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/pptx.py +0 -0
  185. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/registry.py +0 -0
  186. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/report.py +0 -0
  187. {latextify-0.3.2 → latextify-0.3.3}/latextify/privacy/xlsx.py +0 -0
  188. {latextify-0.3.2 → latextify-0.3.3}/latextify/report/__init__.py +0 -0
  189. {latextify-0.3.2 → latextify-0.3.3}/latextify/report/render.py +0 -0
  190. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/__init__.py +0 -0
  191. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/authors.py +0 -0
  192. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/.gitkeep +0 -0
  193. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/achemso/manifest.yaml +0 -0
  194. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/achemso/metadata.tex.j2 +0 -0
  195. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/achemso/preamble.tex.j2 +0 -0
  196. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aip-advances/manifest.yaml +0 -0
  197. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aip-apl/manifest.yaml +0 -0
  198. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aip-jap/manifest.yaml +0 -0
  199. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aps-prapplied/manifest.yaml +0 -0
  200. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aps-prl/manifest.yaml +0 -0
  201. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aps-prx/manifest.yaml +0 -0
  202. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/aps-rmp/manifest.yaml +0 -0
  203. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/elsarticle/manifest.yaml +0 -0
  204. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/elsarticle/metadata.tex.j2 +0 -0
  205. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/elsarticle/preamble.tex.j2 +0 -0
  206. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/elsarticle/vendor/elsarticle.cls +0 -0
  207. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/ieeetran/manifest.yaml +0 -0
  208. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/ieeetran/metadata.tex.j2 +0 -0
  209. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/ieeetran/preamble.tex.j2 +0 -0
  210. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/VENDOR_LICENSE.txt +0 -0
  211. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/manifest.yaml +0 -0
  212. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/metadata.tex.j2 +0 -0
  213. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/preamble.tex.j2 +0 -0
  214. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/vendor/.gitattributes +0 -0
  215. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/vendor/iopart.cls +0 -0
  216. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/vendor/iopart10.clo +0 -0
  217. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/iopart/vendor/iopart12.clo +0 -0
  218. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/revtex4-2/manifest.yaml +0 -0
  219. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/revtex4-2/metadata.tex.j2 +0 -0
  220. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/revtex4-2/preamble.tex.j2 +0 -0
  221. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/VENDOR_LICENSE.txt +0 -0
  222. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/manifest.yaml +0 -0
  223. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/metadata.tex.j2 +0 -0
  224. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/preamble.tex.j2 +0 -0
  225. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/vendor/.gitattributes +0 -0
  226. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/vendor/sn-jnl.cls +0 -0
  227. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/vendor/sn-mathphys-ay.bst +0 -0
  228. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/sn-jnl/vendor/sn-mathphys-num.bst +0 -0
  229. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/wiley/manifest.yaml +0 -0
  230. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/wiley/metadata.tex.j2 +0 -0
  231. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/journals/wiley/preamble.tex.j2 +0 -0
  232. {latextify-0.3.2 → latextify-0.3.3}/latextify/templates/loader.py +0 -0
  233. {latextify-0.3.2 → latextify-0.3.3}/packaging/README-WINDOWS-PORTABLE.txt +0 -0
  234. {latextify-0.3.2 → latextify-0.3.3}/plans/FORMATS_AND_PRIVACY_PLAN.md +0 -0
  235. {latextify-0.3.2 → latextify-0.3.3}/plans/GUI_MULTIFILE_PLAN.md +0 -0
  236. {latextify-0.3.2 → latextify-0.3.3}/plans/GUI_OPTIONS_FORMATS_PLAN.md +0 -0
  237. {latextify-0.3.2 → latextify-0.3.3}/plans/METADATA_PRIVACY_PLAN.md +0 -0
  238. {latextify-0.3.2 → latextify-0.3.3}/plans/OFFLINE_DISTRIBUTION_PLAN.md +0 -0
  239. {latextify-0.3.2 → latextify-0.3.3}/plans/VECTOR_FIGURES_PLAN.md +0 -0
  240. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/GUI_JOURNALS_EXPORT_PLAN.md +0 -0
  241. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/LATEXTIFY_PLAN.md +0 -0
  242. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/MANUSCRIPT_FIDELITY_PLAN.md +0 -0
  243. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/OFFLINE_PORTABILITY_PLAN.md +0 -0
  244. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/REPO_HEALTH_PLAN.md +0 -0
  245. {latextify-0.3.2 → latextify-0.3.3}/plans/archive/SECURITY_RELIABILITY_AUDIT_PLAN.md +0 -0
  246. {latextify-0.3.2 → latextify-0.3.3}/render.yaml +0 -0
  247. {latextify-0.3.2 → latextify-0.3.3}/startup.sh +0 -0
  248. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/README.md +0 -0
  249. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/bracket_cited.docx +0 -0
  250. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/clean.docx +0 -0
  251. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/clean_black_figure.pdf +0 -0
  252. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/cropped_figure.docx +0 -0
  253. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/endnote_cited.docx +0 -0
  254. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/equations.docx +0 -0
  255. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/figures.docx +0 -0
  256. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/achemso_metadata.tex +0 -0
  257. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/achemso_preamble.tex +0 -0
  258. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/elsarticle_metadata.tex +0 -0
  259. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/elsarticle_preamble_authoryear.tex +0 -0
  260. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/elsarticle_preamble_numeric.tex +0 -0
  261. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/ieeetran_metadata.tex +0 -0
  262. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/ieeetran_preamble.tex +0 -0
  263. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/iopart_metadata.tex +0 -0
  264. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/iopart_preamble.tex +0 -0
  265. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/revtex4-2_metadata.tex +0 -0
  266. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/revtex4-2_preamble.tex +0 -0
  267. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/sn-jnl_metadata.tex +0 -0
  268. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/sn-jnl_preamble.tex +0 -0
  269. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/sn-jnl_preamble_authoryear.tex +0 -0
  270. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/wiley_metadata.tex +0 -0
  271. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/wiley_preamble_authoryear.tex +0 -0
  272. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/golden/wiley_preamble_numeric.tex +0 -0
  273. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/hand_cited.docx +0 -0
  274. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/leaky_book.xlsx +0 -0
  275. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/leaky_deck.pptx +0 -0
  276. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/leaky_doc.pdf +0 -0
  277. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/leaky_files.truth.json +0 -0
  278. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/leaky_photo.jpg +0 -0
  279. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_bracket_cited.py +0 -0
  280. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_clean.py +0 -0
  281. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_cropped_figure.py +0 -0
  282. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_endnote_cited.py +0 -0
  283. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_equations.py +0 -0
  284. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_figures.py +0 -0
  285. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_hand_cited.py +0 -0
  286. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_leaky_files.py +0 -0
  287. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_metadata_titlepage.py +0 -0
  288. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_supplement.py +0 -0
  289. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_tables.py +0 -0
  290. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_unsupported.py +0 -0
  291. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_wordnative_cited.py +0 -0
  292. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/make_zotero_cited.py +0 -0
  293. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/metadata_titlepage.docx +0 -0
  294. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/paper.yaml +0 -0
  295. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/supplement.docx +0 -0
  296. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/tables.docx +0 -0
  297. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/unsupported.docx +0 -0
  298. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/wordnative_cited.docx +0 -0
  299. {latextify-0.3.2 → latextify-0.3.3}/tests/fixtures/zotero_cited.docx +0 -0
  300. {latextify-0.3.2 → latextify-0.3.3}/tests/test_archive_guard.py +0 -0
  301. {latextify-0.3.2 → latextify-0.3.3}/tests/test_audit_equations.py +0 -0
  302. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citation_sentinels.py +0 -0
  303. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_bib.py +0 -0
  304. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_bibmatch.py +0 -0
  305. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_bibtex_in.py +0 -0
  306. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_bracket_refs.py +0 -0
  307. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_compile_stub.py +0 -0
  308. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_corrections.py +0 -0
  309. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_crossref.py +0 -0
  310. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_csl_json_in.py +0 -0
  311. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_degradation.py +0 -0
  312. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_endnote.py +0 -0
  313. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_endnote_compile.py +0 -0
  314. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_endnote_xml_in.py +0 -0
  315. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_extract.py +0 -0
  316. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_extract_endnote.py +0 -0
  317. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_extract_wordnative.py +0 -0
  318. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_fields.py +0 -0
  319. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_merge.py +0 -0
  320. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_nbib_in.py +0 -0
  321. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_parsers.py +0 -0
  322. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_plaintext.py +0 -0
  323. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_reconstruct.py +0 -0
  324. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_refs_import.py +0 -0
  325. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_stress.py +0 -0
  326. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_validate.py +0 -0
  327. {latextify-0.3.2 → latextify-0.3.3}/tests/test_citations_wordnative.py +0 -0
  328. {latextify-0.3.2 → latextify-0.3.3}/tests/test_cli.py +0 -0
  329. {latextify-0.3.2 → latextify-0.3.3}/tests/test_cli_figures.py +0 -0
  330. {latextify-0.3.2 → latextify-0.3.3}/tests/test_cli_review.py +0 -0
  331. {latextify-0.3.2 → latextify-0.3.3}/tests/test_compat_strenum.py +0 -0
  332. {latextify-0.3.2 → latextify-0.3.3}/tests/test_compile_logs.py +0 -0
  333. {latextify-0.3.2 → latextify-0.3.3}/tests/test_compile_tectonic.py +0 -0
  334. {latextify-0.3.2 → latextify-0.3.3}/tests/test_docx_clean.py +0 -0
  335. {latextify-0.3.2 → latextify-0.3.3}/tests/test_emit.py +0 -0
  336. {latextify-0.3.2 → latextify-0.3.3}/tests/test_emit_submission.py +0 -0
  337. {latextify-0.3.2 → latextify-0.3.3}/tests/test_examples.py +0 -0
  338. {latextify-0.3.2 → latextify-0.3.3}/tests/test_export_formats.py +0 -0
  339. {latextify-0.3.2 → latextify-0.3.3}/tests/test_fidelity_captions_reflist.py +0 -0
  340. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figure_placement.py +0 -0
  341. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures.py +0 -0
  342. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_caption_gaps.py +0 -0
  343. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_convert.py +0 -0
  344. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_crop.py +0 -0
  345. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_inventory.py +0 -0
  346. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_scrub.py +0 -0
  347. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_stage2.py +0 -0
  348. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_tiff.py +0 -0
  349. {latextify-0.3.2 → latextify-0.3.3}/tests/test_figures_wide.py +0 -0
  350. {latextify-0.3.2 → latextify-0.3.3}/tests/test_filters.py +0 -0
  351. {latextify-0.3.2 → latextify-0.3.3}/tests/test_filters_headings.py +0 -0
  352. {latextify-0.3.2 → latextify-0.3.3}/tests/test_frontmatter.py +0 -0
  353. {latextify-0.3.2 → latextify-0.3.3}/tests/test_gui.py +0 -0
  354. {latextify-0.3.2 → latextify-0.3.3}/tests/test_gui_demo.py +0 -0
  355. {latextify-0.3.2 → latextify-0.3.3}/tests/test_inline_citations.py +0 -0
  356. {latextify-0.3.2 → latextify-0.3.3}/tests/test_inline_supplement.py +0 -0
  357. {latextify-0.3.2 → latextify-0.3.3}/tests/test_journal_pdf_layout.py +0 -0
  358. {latextify-0.3.2 → latextify-0.3.3}/tests/test_manuscript_formats.py +0 -0
  359. {latextify-0.3.2 → latextify-0.3.3}/tests/test_metadata_guess.py +0 -0
  360. {latextify-0.3.2 → latextify-0.3.3}/tests/test_package.py +0 -0
  361. {latextify-0.3.2 → latextify-0.3.3}/tests/test_pandoc.py +0 -0
  362. {latextify-0.3.2 → latextify-0.3.3}/tests/test_pdf_combine.py +0 -0
  363. {latextify-0.3.2 → latextify-0.3.3}/tests/test_portable.py +0 -0
  364. {latextify-0.3.2 → latextify-0.3.3}/tests/test_preflight.py +0 -0
  365. {latextify-0.3.2 → latextify-0.3.3}/tests/test_preflight_privacy.py +0 -0
  366. {latextify-0.3.2 → latextify-0.3.3}/tests/test_privacy.py +0 -0
  367. {latextify-0.3.2 → latextify-0.3.3}/tests/test_repo_integrity.py +0 -0
  368. {latextify-0.3.2 → latextify-0.3.3}/tests/test_report.py +0 -0
  369. {latextify-0.3.2 → latextify-0.3.3}/tests/test_supplement.py +0 -0
  370. {latextify-0.3.2 → latextify-0.3.3}/tests/test_tables.py +0 -0
  371. {latextify-0.3.2 → latextify-0.3.3}/tests/test_tectonic_integrity.py +0 -0
  372. {latextify-0.3.2 → latextify-0.3.3}/tests/test_templates.py +0 -0
  373. {latextify-0.3.2 → latextify-0.3.3}/tests/test_templates_item22.py +0 -0
  374. {latextify-0.3.2 → latextify-0.3.3}/tests/test_templates_sn_jnl.py +0 -0
  375. {latextify-0.3.2 → latextify-0.3.3}/tests/test_xml_hardening.py +0 -0
  376. {latextify-0.3.2 → latextify-0.3.3}/tools/build_windows_portable.py +0 -0
@@ -147,14 +147,18 @@ jobs:
147
147
  with:
148
148
  python-version: "3.13"
149
149
  - name: Install
150
- run: uv sync --locked --group portable
150
+ run: |
151
+ uv sync --locked --group portable
152
+ # setup-uv may select GitHub's hosted interpreter, which is not a
153
+ # relocatable payload. Install an explicit uv-managed copy to bundle.
154
+ uv python install 3.13 --no-bin --no-registry
151
155
  - name: Set up the TARGET Python 3.10 (what install.py will run under)
152
156
  id: py310
153
157
  uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
154
158
  with:
155
159
  python-version: "3.10"
156
- - name: Build a GUI kit covering Python 3.10 and warm one journal (network ON)
157
- run: uv run latextify make-kit --target current --python-versions 3.10 --with-gui --journals revtex4-2 --output build
160
+ - name: Build a GUI/dev kit covering Python 3.10 and 3.13 (network ON)
161
+ run: uv run latextify make-kit --target current --python-versions 3.10 --python-versions 3.13 --with-gui --with-dev --journals revtex4-2 --output build
158
162
  - name: Install the kit under Python 3.10, network POISONED
159
163
  env:
160
164
  # Same reasoning as the Linux offline-kit job: uppercase only, one
@@ -220,3 +224,30 @@ jobs:
220
224
  } finally {
221
225
  if (-not $process.HasExited) { Stop-Process -Id $process.Id -Force }
222
226
  }
227
+ - name: Build and exercise the editable offline repository
228
+ env:
229
+ HTTPS_PROXY: http://127.0.0.1:9
230
+ HTTP_PROXY: http://127.0.0.1:9
231
+ ALL_PROXY: http://127.0.0.1:9
232
+ run: |
233
+ $python = uv python find --managed-python 3.13
234
+ uv run --group portable python tools/build_windows_offline_repo.py `
235
+ --kit build/latextify-offline-windows-x64 `
236
+ --python-runtime $python `
237
+ --output build/offline-repo
238
+ if ($LASTEXITCODE -ne 0) { throw "offline repository build failed" }
239
+ $repo = Resolve-Path "build/offline-repo/LaTeXtify-Windows-Offline-Repo"
240
+ & "$repo/.runtime/python/python.exe" "$repo/.offline-kit/install.py" --dir $repo
241
+ if ($LASTEXITCODE -ne 0) { throw "offline repository setup failed" }
242
+ Push-Location $repo
243
+ try {
244
+ $env:LATEXTIFY_OFFLINE = "1"
245
+ $env:PATH = "$repo/.offline-kit/tectonic;$env:PATH"
246
+ $env:TECTONIC_CACHE_DIR = "$repo/.offline-kit/tex-bundle-cache"
247
+ & ".venv/Scripts/python.exe" -c "import pathlib, latextify; assert pathlib.Path(latextify.__file__).resolve().is_relative_to(pathlib.Path.cwd())"
248
+ if ($LASTEXITCODE -ne 0) { throw "offline repository did not import editable source" }
249
+ & ".venv/Scripts/python.exe" -m pytest -q tests/test_portable.py
250
+ if ($LASTEXITCODE -ne 0) { throw "offline repository test command failed" }
251
+ } finally {
252
+ Pop-Location
253
+ }
@@ -45,11 +45,15 @@ jobs:
45
45
  ~/.cache/Tectonic
46
46
  key: tectonic-${{ runner.os }}-v1
47
47
  - name: Install build dependencies
48
- run: uv sync --locked --no-dev --extra gui --group portable
48
+ run: |
49
+ uv sync --locked --no-dev --extra gui --group portable
50
+ # The hosted interpreter is runner infrastructure, not a portable
51
+ # payload. Install a complete uv-managed runtime for the repo bundle.
52
+ uv python install 3.13 --no-bin --no-registry
49
53
  - name: Build native Windows runtime assets
50
54
  env:
51
55
  GITHUB_TOKEN: ${{ github.token }}
52
- run: uv run latextify make-kit --target current --python-versions 3.13 --with-gui -o build
56
+ run: uv run latextify make-kit --target current --python-versions 3.13 --with-gui --with-dev -o build
53
57
  - name: Freeze and verify the portable application
54
58
  env:
55
59
  HTTPS_PROXY: http://127.0.0.1:9
@@ -63,10 +67,70 @@ jobs:
63
67
  Get-Content "build/portable/LaTeXtify-Windows-Portable/latextify-startup.log" -ErrorAction SilentlyContinue
64
68
  throw "portable self-test failed (exit $($selfTest.ExitCode))"
65
69
  }
70
+
71
+ - name: Build and verify the editable offline repository
72
+ env:
73
+ HTTPS_PROXY: http://127.0.0.1:9
74
+ HTTP_PROXY: http://127.0.0.1:9
75
+ ALL_PROXY: http://127.0.0.1:9
76
+ run: |
77
+ # setup-uv already installed this managed interpreter. Copying its
78
+ # complete runtime makes the target independent of system Python.
79
+ $python = uv python find --managed-python 3.13
80
+ uv run --group portable python tools/build_windows_offline_repo.py `
81
+ --kit build/latextify-offline-windows-x64 `
82
+ --python-runtime $python `
83
+ --output build/offline-repo
84
+ if ($LASTEXITCODE -ne 0) { throw "offline repository build failed" }
85
+
86
+ $repo = Resolve-Path "build/offline-repo/LaTeXtify-Windows-Offline-Repo"
87
+ $process = Start-Process -FilePath "cmd.exe" `
88
+ -ArgumentList "/c", "startup.bat --port 8766 --no-browser --keep-alive" `
89
+ -WorkingDirectory $repo -PassThru -WindowStyle Hidden
90
+ try {
91
+ $ready = $false
92
+ for ($i = 0; $i -lt 60; $i++) {
93
+ Start-Sleep -Seconds 1
94
+ try {
95
+ $response = Invoke-WebRequest "http://127.0.0.1:8766/" -UseBasicParsing -NoProxy
96
+ if ($response.StatusCode -eq 200 -and $response.Content -match "LaTeXtify") {
97
+ $ready = $true
98
+ break
99
+ }
100
+ } catch {}
101
+ if ($process.HasExited) { break }
102
+ }
103
+ if (-not $ready) {
104
+ Get-Content "$repo/latextify-startup.log" -ErrorAction SilentlyContinue
105
+ throw "offline repository GUI did not start"
106
+ }
107
+ } finally {
108
+ if (-not $process.HasExited) { Stop-Process -Id $process.Id -Force }
109
+ }
110
+
111
+ Push-Location $repo
112
+ try {
113
+ $env:LATEXTIFY_OFFLINE = "1"
114
+ $env:PATH = "$repo/.offline-kit/tectonic;$env:PATH"
115
+ $env:TECTONIC_CACHE_DIR = "$repo/.offline-kit/tex-bundle-cache"
116
+ & ".venv/Scripts/python.exe" -c "import pathlib, latextify; assert pathlib.Path(latextify.__file__).resolve().is_relative_to(pathlib.Path.cwd())"
117
+ if ($LASTEXITCODE -ne 0) { throw "offline repository did not import editable source" }
118
+ & ".venv/Scripts/python.exe" -m pytest -q tests/test_portable.py
119
+ if ($LASTEXITCODE -ne 0) { throw "offline repository test command failed" }
120
+ & ".venv/Scripts/python.exe" -m latextify convert tests/fixtures/clean.docx -j revtex4-2 -o offline-repo-demo --pdf
121
+ if ($LASTEXITCODE -ne 0) { throw "offline repository conversion failed" }
122
+ if (-not (Test-Path "offline-repo-demo/revtex4-2/main.pdf")) {
123
+ throw "offline repository did not compile a PDF"
124
+ }
125
+ } finally {
126
+ Pop-Location
127
+ }
66
128
  - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
67
129
  with:
68
- name: windows-portable
69
- path: build/portable/LaTeXtify-Windows-Portable.zip
130
+ name: windows-offline-packages
131
+ path: |
132
+ build/portable/LaTeXtify-Windows-Portable.zip
133
+ build/offline-repo/LaTeXtify-Windows-Offline-Repo.zip
70
134
  if-no-files-found: error
71
135
 
72
136
  build-kits:
@@ -161,13 +225,19 @@ jobs:
161
225
 
162
226
  - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
163
227
  with:
164
- name: windows-portable
228
+ name: windows-offline-packages
165
229
  path: build
166
230
 
167
- - name: Add the portable Windows application to checksums
231
+ - name: Add the Windows packages to checksums
168
232
  run: |
169
233
  cd build
234
+ # upload-artifact preserves the common `build/`-relative directory
235
+ # structure when given files from two different subdirectories.
236
+ # Flatten those two known assets before checksumming/uploading them.
237
+ mv portable/LaTeXtify-Windows-Portable.zip .
238
+ mv offline-repo/LaTeXtify-Windows-Offline-Repo.zip .
170
239
  sha256sum LaTeXtify-Windows-Portable.zip >> SHA256SUMS.txt
240
+ sha256sum LaTeXtify-Windows-Offline-Repo.zip >> SHA256SUMS.txt
171
241
  cat SHA256SUMS.txt
172
242
 
173
243
  - name: Create release (if missing) and upload assets
@@ -181,6 +251,7 @@ jobs:
181
251
  latextify-offline-linux-x64.zip
182
252
  latextify-offline-macos-arm64.zip
183
253
  LaTeXtify-Windows-Portable.zip
254
+ LaTeXtify-Windows-Offline-Repo.zip
184
255
  latextify-source.zip
185
256
  SHA256SUMS.txt
186
257
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: latextify
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Convert Word manuscripts to journal-ready LaTeX projects and PDFs
5
5
  Project-URL: Homepage, https://github.com/pquarterman17/LaTeXtify
6
6
  Project-URL: Repository, https://github.com/pquarterman17/LaTeXtify
@@ -331,8 +331,10 @@ To start the web GUI without touching a command line, double-click
331
331
  **`startup.bat`** (Windows) or run **`./startup.sh`** (macOS/Linux). It installs
332
332
  dependencies on first run, opens the GUI in your browser, and — if anything
333
333
  goes wrong — writes an easy-to-find `latextify-startup.log` next to the script
334
- and prints it for you to copy. This source-tree launcher needs internet access
335
- on its first run.
334
+ and prints it for you to copy. On Windows, `startup.bat` is offline-first and
335
+ never downloads missing software. A connected source checkout can explicitly
336
+ opt into the old online bootstrap by running **`startup-online.bat`** instead.
337
+ The macOS/Linux source launcher still needs internet access on its first run.
336
338
 
337
339
  ### Offline / firewalled Windows computer
338
340
 
@@ -360,6 +362,25 @@ Python already present on the work computer. Embedded EMF/WMF figures work,
360
362
  but preserving them as vectors requires an offline installation of LibreOffice
361
363
  or Inkscape; otherwise Windows attempts a 600-DPI raster fallback.
362
364
 
365
+ For an **editable source checkout** on an air-gapped Windows computer, use
366
+ **`LaTeXtify-Windows-Offline-Repo.zip`**. It contains the complete tracked
367
+ repository, a portable Python 3.13 runtime, pinned runtime and development
368
+ dependencies, Pandoc, Tectonic, and the warmed TeX cache. Extract the whole
369
+ folder and double-click `startup.bat`; its first launch creates `.venv` from
370
+ the local wheelhouse without contacting any service. Code changes under
371
+ `latextify\` are used on the next launch, and the bundled environment can run
372
+ the offline test, lint, and type-check commands described in
373
+ `README-OFFLINE-REPO.txt`.
374
+
375
+ These packages serve different purposes:
376
+
377
+ | Release asset | Intended use | Python required |
378
+ |---|---|---|
379
+ | `LaTeXtify-Windows-Portable.zip` | Normal GUI use | No |
380
+ | `LaTeXtify-Windows-Offline-Repo.zip` | Editable/debuggable/testable source | No |
381
+ | `latextify-offline-windows-x64.zip` | Inspectable installed Python package | Yes |
382
+ | GitHub/source ZIP | Connected development | Yes |
383
+
363
384
  ## Usage
364
385
 
365
386
  > Installed from source with `uv sync`? Prefix these with `uv run` (e.g.
@@ -285,8 +285,10 @@ To start the web GUI without touching a command line, double-click
285
285
  **`startup.bat`** (Windows) or run **`./startup.sh`** (macOS/Linux). It installs
286
286
  dependencies on first run, opens the GUI in your browser, and — if anything
287
287
  goes wrong — writes an easy-to-find `latextify-startup.log` next to the script
288
- and prints it for you to copy. This source-tree launcher needs internet access
289
- on its first run.
288
+ and prints it for you to copy. On Windows, `startup.bat` is offline-first and
289
+ never downloads missing software. A connected source checkout can explicitly
290
+ opt into the old online bootstrap by running **`startup-online.bat`** instead.
291
+ The macOS/Linux source launcher still needs internet access on its first run.
290
292
 
291
293
  ### Offline / firewalled Windows computer
292
294
 
@@ -314,6 +316,25 @@ Python already present on the work computer. Embedded EMF/WMF figures work,
314
316
  but preserving them as vectors requires an offline installation of LibreOffice
315
317
  or Inkscape; otherwise Windows attempts a 600-DPI raster fallback.
316
318
 
319
+ For an **editable source checkout** on an air-gapped Windows computer, use
320
+ **`LaTeXtify-Windows-Offline-Repo.zip`**. It contains the complete tracked
321
+ repository, a portable Python 3.13 runtime, pinned runtime and development
322
+ dependencies, Pandoc, Tectonic, and the warmed TeX cache. Extract the whole
323
+ folder and double-click `startup.bat`; its first launch creates `.venv` from
324
+ the local wheelhouse without contacting any service. Code changes under
325
+ `latextify\` are used on the next launch, and the bundled environment can run
326
+ the offline test, lint, and type-check commands described in
327
+ `README-OFFLINE-REPO.txt`.
328
+
329
+ These packages serve different purposes:
330
+
331
+ | Release asset | Intended use | Python required |
332
+ |---|---|---|
333
+ | `LaTeXtify-Windows-Portable.zip` | Normal GUI use | No |
334
+ | `LaTeXtify-Windows-Offline-Repo.zip` | Editable/debuggable/testable source | No |
335
+ | `latextify-offline-windows-x64.zip` | Inspectable installed Python package | Yes |
336
+ | GitHub/source ZIP | Connected development | Yes |
337
+
317
338
  ## Usage
318
339
 
319
340
  > Installed from source with `uv sync`? Prefix these with `uv run` (e.g.
@@ -43,6 +43,11 @@ def make_kit_cmd(
43
43
  with_gui: bool = typer.Option(
44
44
  False, "--with-gui", help="Also bundle the optional web-GUI dependency wheels."
45
45
  ),
46
+ with_dev: bool = typer.Option(
47
+ False,
48
+ "--with-dev",
49
+ help="Also bundle development/test dependencies for an offline source checkout.",
50
+ ),
46
51
  zip_kit: bool = typer.Option(
47
52
  False, "--zip", help="Also produce a .zip of the kit folder for distribution."
48
53
  ),
@@ -67,6 +72,7 @@ def make_kit_cmd(
67
72
  warm_tex=warm_tex,
68
73
  journals=journal_list,
69
74
  with_gui=with_gui,
75
+ with_dev=with_dev,
70
76
  make_zip=zip_kit,
71
77
  )
72
78
  except KitBuildError as exc:
@@ -19,6 +19,10 @@ Everything installs into this folder; nothing else on the system is touched.
19
19
  | `requirements.txt` | the exact pinned versions (for IT / security review) |
20
20
  | `bundle-info.json` | which OS, CPU, and Python versions this kit covers |
21
21
 
22
+ When `with_dev` is true in `bundle-info.json`, `requirements.txt` and the
23
+ wheelhouse also contain the pinned test, lint, and type-check dependencies used
24
+ by the editable offline repository release.
25
+
22
26
  ## Requirements
23
27
 
24
28
  - The OS and CPU this kit was built for — see `bundle-info.json` (the folder
@@ -84,12 +84,11 @@ def _build_project_wheel(wheelhouse: Path) -> str:
84
84
  return wheels[-1].name.split("-")[1]
85
85
 
86
86
 
87
- def _export_requirements(req: Path, *, with_gui: bool) -> None:
87
+ def _export_requirements(req: Path, *, with_gui: bool, with_dev: bool = False) -> None:
88
88
  cmd = [
89
89
  "uv",
90
90
  "export",
91
91
  "--frozen",
92
- "--no-dev",
93
92
  "--no-emit-project",
94
93
  "--no-hashes",
95
94
  "--format",
@@ -97,6 +96,10 @@ def _export_requirements(req: Path, *, with_gui: bool) -> None:
97
96
  "-o",
98
97
  str(req),
99
98
  ]
99
+ if not with_dev:
100
+ cmd += ["--no-dev"]
101
+ else:
102
+ cmd += ["--group", "dev"]
100
103
  if with_gui:
101
104
  cmd += ["--extra", "gui"]
102
105
  _run(cmd, cwd=str(REPO_ROOT))
@@ -243,13 +246,15 @@ def make_kit(
243
246
  warm_tex: bool = True,
244
247
  journals: list[str] | None = None,
245
248
  with_gui: bool = False,
249
+ with_dev: bool = False,
246
250
  make_zip: bool = False,
247
251
  ) -> Path:
248
252
  """Build an offline kit for ``target_name`` under ``output_dir``; return the kit dir.
249
253
 
250
254
  ``journals`` limits TeX-cache warming (default: every registered journal).
251
255
  ``warm_tex=False`` produces a smaller emit-only kit (no ``tex-bundle-cache/``).
252
- ``with_gui`` adds the optional GUI dependency wheels.
256
+ ``with_gui`` adds the optional GUI dependency wheels. ``with_dev`` also
257
+ bundles the development/test dependency group for an editable offline checkout.
253
258
  """
254
259
  _require_uv()
255
260
  target = resolve_target(target_name)
@@ -263,7 +268,7 @@ def make_kit(
263
268
  wheelhouse.mkdir(parents=True)
264
269
 
265
270
  version = _build_project_wheel(wheelhouse)
266
- _export_requirements(kit_dir / "requirements.txt", with_gui=with_gui)
271
+ _export_requirements(kit_dir / "requirements.txt", with_gui=with_gui, with_dev=with_dev)
267
272
  _download_deps(wheelhouse, kit_dir / "requirements.txt", python_versions, target)
268
273
 
269
274
  _fetch_tectonic(target, kit_dir / "tectonic")
@@ -282,6 +287,7 @@ def make_kit(
282
287
  list(python_versions),
283
288
  warm_tex=warm_tex,
284
289
  with_gui=with_gui,
290
+ with_dev=with_dev,
285
291
  journals=warmed,
286
292
  ),
287
293
  indent=2,
@@ -133,6 +133,13 @@ def _package_spec(info: dict) -> str:
133
133
  return "latextify[gui]" if info.get("with_gui", False) else "latextify"
134
134
 
135
135
 
136
+ def _install_requirements(base: list[str]) -> None:
137
+ """Install the exact exported dependency set before the project wheel."""
138
+ requirements = HERE / "requirements.txt"
139
+ if requirements.is_file():
140
+ _run(base + ["-r", str(requirements)])
141
+
142
+
136
143
  def _write_launchers(target: Path, venv_dir: Path, *, with_gui: bool) -> list[Path]:
137
144
  """Write LaTeXtify.bat / ./latextify that run the venv CLI with the in-kit
138
145
  Tectonic binary on PATH and TECTONIC_CACHE_DIR pointed at the warmed cache.
@@ -234,6 +241,7 @@ def main() -> None:
234
241
  subprocess.run(base + ["--upgrade", "--quiet", "pip"])
235
242
  with_gui = bool(info.get("with_gui", False))
236
243
  package = _package_spec(info)
244
+ _install_requirements(base)
237
245
  _run(base + ["--upgrade", package])
238
246
 
239
247
  imports = "import latextify"
@@ -128,6 +128,7 @@ def build_bundle_info(
128
128
  *,
129
129
  warm_tex: bool,
130
130
  with_gui: bool,
131
+ with_dev: bool = False,
131
132
  journals: list[str],
132
133
  ) -> dict:
133
134
  """Shape the ``bundle-info.json`` manifest (pure)."""
@@ -140,5 +141,6 @@ def build_bundle_info(
140
141
  "python_versions": list(py_versions),
141
142
  "warm_tex": warm_tex,
142
143
  "with_gui": with_gui,
144
+ "with_dev": with_dev,
143
145
  "warmed_journals": sorted(journals) if warm_tex else [],
144
146
  }
@@ -0,0 +1,55 @@
1
+ LaTeXtify for Windows - editable offline repository
2
+ ===================================================
3
+
4
+ This bundle is for running, editing, debugging, and testing the LaTeXtify
5
+ source code on a Windows computer that cannot reach GitHub, Astral, or PyPI.
6
+
7
+ START THE GUI
8
+ -------------
9
+ 1. Extract the entire folder to a short writable path, such as
10
+ C:\LaTeXtify-Source.
11
+ 2. Double-click startup.bat.
12
+ 3. The first launch creates .venv from the bundled wheelhouse. This can take
13
+ a few minutes, but it does not use the network or require administrator
14
+ rights. Later launches reuse that environment.
15
+
16
+ The bundle contains a portable Python 3.13 runtime, all pinned dependencies,
17
+ Pandoc, Tectonic, the warmed TeX package cache, and the complete tracked source
18
+ checkout. The GUI and command line run the files in this folder, so edits to
19
+ latextify\ take effect on the next launch.
20
+
21
+ OFFLINE DEVELOPMENT
22
+ -------------------
23
+ Run the test suite:
24
+
25
+ .venv\Scripts\python.exe -m pytest -q -m "not network"
26
+
27
+ Run lint and type checks:
28
+
29
+ .venv\Scripts\python.exe -m ruff check .
30
+ .venv\Scripts\python.exe -m mypy
31
+
32
+ Run the command line directly:
33
+
34
+ .venv\Scripts\python.exe -m latextify convert paper.docx -j revtex4-2 --pdf
35
+
36
+ NETWORK BEHAVIOR
37
+ ----------------
38
+ startup.bat never downloads missing software. The bundled launch sets
39
+ LATEXTIFY_OFFLINE=1, uses the bundled Tectonic/cache, and disables Crossref
40
+ reference checks. Other GUI features remain available.
41
+
42
+ For a normal connected source checkout, startup-online.bat explicitly enables
43
+ the historical uv/dependency download setup. Do not use it on an air-gapped
44
+ computer.
45
+
46
+ EMF/WMF figures can be rasterized without additional software. Preserving them
47
+ as vectors requires an approved offline installation of LibreOffice or
48
+ Inkscape.
49
+
50
+ TROUBLESHOOTING
51
+ ---------------
52
+ The startup log is latextify-startup.log beside startup.bat. Keep .runtime,
53
+ .offline-kit, and the source tree together. If endpoint security blocks
54
+ python.exe, pandoc.exe, or tectonic.exe, provide IT with SHA256SUMS.txt from
55
+ the release and request allow-listing.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "latextify"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Convert Word manuscripts to journal-ready LaTeX projects and PDFs"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -0,0 +1,5 @@
1
+ @echo off
2
+ REM Explicitly opt into the source launcher's network bootstrap.
3
+ set "LATEXTIFY_ALLOW_DOWNLOADS=1"
4
+ call "%~dp0startup.bat" %*
5
+ exit /b %ERRORLEVEL%
@@ -9,6 +9,8 @@ setlocal
9
9
  cd /d "%~dp0"
10
10
  set "LOG=%~dp0latextify-startup.log"
11
11
  set "VENVPY=%~dp0.venv\Scripts\python.exe"
12
+ set "BUNDLEPY=%~dp0.runtime\python\python.exe"
13
+ set "OFFLINEKIT=%~dp0.offline-kit\install.py"
12
14
 
13
15
  > "%LOG%" echo LaTeXtify startup log
14
16
  >> "%LOG%" echo Run at %DATE% %TIME%
@@ -20,10 +22,30 @@ if not exist "%VENVPY%" goto setup
20
22
  if not errorlevel 1 goto launch
21
23
 
22
24
  :setup
25
+ REM An offline repository bundle carries both of these. Prefer them before
26
+ REM looking for uv and never contact the network from this path.
27
+ if exist "%BUNDLEPY%" if exist "%OFFLINEKIT%" goto setup_offline
28
+
29
+ REM A plain source checkout is offline-first too. Downloads happen only via
30
+ REM startup-online.bat (which sets LATEXTIFY_ALLOW_DOWNLOADS=1 explicitly).
31
+ if /I not "%LATEXTIFY_ALLOW_DOWNLOADS%"=="1" goto no_offline_runtime
32
+
33
+ :setup_online
23
34
  where uv >nul 2>&1
24
35
  if errorlevel 1 goto install_uv
25
36
  goto have_uv
26
37
 
38
+ :setup_offline
39
+ echo Setting up LaTeXtify entirely from bundled files - please wait...
40
+ >> "%LOG%" echo --- offline setup from .runtime and .offline-kit ---
41
+ set "LATEXTIFY_OFFLINE=1"
42
+ "%BUNDLEPY%" "%OFFLINEKIT%" --dir "%CD%" >> "%LOG%" 2>&1
43
+ if errorlevel 1 goto fail
44
+ if not exist "%VENVPY%" goto fail
45
+ "%VENVPY%" -c "import latextify.gui.server" >> "%LOG%" 2>&1
46
+ if errorlevel 1 goto fail
47
+ goto launch
48
+
27
49
  :install_uv
28
50
  echo uv is not installed -- installing it now...
29
51
  >> "%LOG%" echo --- installing uv via official installer ---
@@ -37,10 +59,16 @@ if errorlevel 1 goto no_uv
37
59
  echo Setting up LaTeXtify - first run installs dependencies, please wait...
38
60
  >> "%LOG%" echo --- uv sync --extra gui ---
39
61
  uv sync --extra gui >> "%LOG%" 2>&1
62
+ if errorlevel 1 goto fail
40
63
 
41
64
  :launch
42
65
  if not exist "%VENVPY%" goto fail
43
- echo Starting LaTeXtify. Your browser should open at http://127.0.0.1:8501
66
+ if exist "%OFFLINEKIT%" (
67
+ set "LATEXTIFY_OFFLINE=1"
68
+ set "PATH=%~dp0.offline-kit\tectonic;%PATH%"
69
+ set "TECTONIC_CACHE_DIR=%~dp0.offline-kit\tex-bundle-cache"
70
+ )
71
+ echo Starting LaTeXtify. Your browser should open shortly.
44
72
  echo Keep this window open while you use it. Close it or press Ctrl+C to stop.
45
73
  >> "%LOG%" echo --- launch: python -m latextify gui ---
46
74
  "%VENVPY%" -m latextify gui %* >> "%LOG%" 2>&1
@@ -51,14 +79,25 @@ exit /b 0
51
79
  :no_uv
52
80
  >> "%LOG%" echo ERROR: automatic uv install failed.
53
81
  >> "%LOG%" echo.
54
- >> "%LOG%" echo This is the SOURCE launcher and its first run requires internet access.
55
- >> "%LOG%" echo For a firewalled or air-gapped computer, use the release asset named
56
- >> "%LOG%" echo LaTeXtify-Windows-Portable.zip instead of the source-code zip.
57
- >> "%LOG%" echo Transfer that zip from a connected computer, extract the whole
58
- >> "%LOG%" echo folder, then double-click LaTeXtify.exe. No Python or install is needed.
82
+ >> "%LOG%" echo startup-online.bat was selected, but its uv download failed.
83
+ >> "%LOG%" echo For a firewalled computer, use LaTeXtify-Windows-Offline-Repo.zip
84
+ >> "%LOG%" echo for editable source or LaTeXtify-Windows-Portable.zip for ordinary use.
59
85
  >> "%LOG%" echo.
60
86
  >> "%LOG%" echo On a connected computer, install uv from https://docs.astral.sh/uv/
61
- >> "%LOG%" echo and run this launcher again.
87
+ >> "%LOG%" echo and run startup-online.bat again.
88
+ goto fail
89
+
90
+ :no_offline_runtime
91
+ >> "%LOG%" echo ERROR: no working environment or bundled offline runtime was found.
92
+ >> "%LOG%" echo.
93
+ >> "%LOG%" echo This launcher does not download software unless you explicitly use
94
+ >> "%LOG%" echo startup-online.bat on a connected computer.
95
+ >> "%LOG%" echo.
96
+ >> "%LOG%" echo For an editable, air-gapped checkout, use the release asset named
97
+ >> "%LOG%" echo LaTeXtify-Windows-Offline-Repo.zip. It includes Python, the complete
98
+ >> "%LOG%" echo wheelhouse, Pandoc, Tectonic, the TeX cache, and this source tree.
99
+ >> "%LOG%" echo.
100
+ >> "%LOG%" echo For ordinary use, use LaTeXtify-Windows-Portable.zip instead.
62
101
  goto fail
63
102
 
64
103
  :fail
@@ -92,6 +92,7 @@ def test_bundle_info_manifest_shape():
92
92
  ["3.10", "3.11"],
93
93
  warm_tex=True,
94
94
  with_gui=False,
95
+ with_dev=False,
95
96
  journals=["revtex4-2", "iopart"],
96
97
  )
97
98
  assert info["name"] == "latextify"
@@ -99,6 +100,7 @@ def test_bundle_info_manifest_shape():
99
100
  assert info["target"] == "linux-x64"
100
101
  assert info["python_versions"] == ["3.10", "3.11"]
101
102
  assert info["warm_tex"] is True
103
+ assert info["with_dev"] is False
102
104
  assert info["warmed_journals"] == ["iopart", "revtex4-2"] # sorted
103
105
 
104
106
 
@@ -109,11 +111,13 @@ def test_bundle_info_emit_only_lists_no_warmed_journals():
109
111
  ["3.12"],
110
112
  warm_tex=False,
111
113
  with_gui=True,
114
+ with_dev=True,
112
115
  journals=["revtex4-2"],
113
116
  )
114
117
  assert info["warm_tex"] is False
115
118
  assert info["warmed_journals"] == []
116
119
  assert info["with_gui"] is True
120
+ assert info["with_dev"] is True
117
121
 
118
122
 
119
123
  # --------------------------------------------------------------------------- #
@@ -167,6 +171,30 @@ def test_command_line_only_kit_does_not_write_gui_launcher(tmp_path):
167
171
  assert all("gui" not in path.name.lower() for path in launchers)
168
172
 
169
173
 
174
+ def test_installer_installs_pinned_requirements(monkeypatch, tmp_path):
175
+ requirements = tmp_path / "requirements.txt"
176
+ requirements.write_text("pytest==9.0.0\n")
177
+ calls = []
178
+ monkeypatch.setattr(install_template, "HERE", tmp_path)
179
+ monkeypatch.setattr(install_template, "_run", calls.append)
180
+
181
+ install_template._install_requirements(["python", "-m", "pip", "install"])
182
+
183
+ assert calls == [["python", "-m", "pip", "install", "-r", str(requirements)]]
184
+
185
+
186
+ def test_dev_requirements_export_includes_dev_group(monkeypatch, tmp_path):
187
+ calls = []
188
+ monkeypatch.setattr(build, "_run", lambda command, **kwargs: calls.append(command))
189
+
190
+ build._export_requirements(tmp_path / "requirements.txt", with_gui=True, with_dev=True)
191
+
192
+ command = calls[0]
193
+ assert "--no-dev" not in command
194
+ assert command[command.index("--group") + 1] == "dev"
195
+ assert command[command.index("--extra") + 1] == "gui"
196
+
197
+
170
198
  def test_default_warm_journals_are_the_registered_ones():
171
199
  # Sanity: warming defaults to the live registry, not a hardcoded list.
172
200
  assert set(loader.available()) == set(loader.discover())
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ import zipfile
4
+ from pathlib import Path
5
+
6
+ from tools import build_windows_offline_repo as offline_repo
7
+
8
+
9
+ def test_copy_checkout_copies_only_reported_tracked_files(monkeypatch, tmp_path: Path) -> None:
10
+ source = tmp_path / "source"
11
+ destination = tmp_path / "destination"
12
+ (source / "package").mkdir(parents=True)
13
+ (source / "package" / "tracked.py").write_text("tracked")
14
+ (source / "ignored.txt").write_text("ignored")
15
+ monkeypatch.setattr(offline_repo, "tracked_files", lambda _repo: [Path("package/tracked.py")])
16
+
17
+ offline_repo._copy_checkout(source, destination)
18
+
19
+ assert (destination / "package" / "tracked.py").read_text() == "tracked"
20
+ assert not (destination / "ignored.txt").exists()
21
+
22
+
23
+ def test_zip_tree_has_one_top_level_bundle_directory(tmp_path: Path) -> None:
24
+ bundle = tmp_path / offline_repo.BUNDLE_NAME
25
+ (bundle / "latextify").mkdir(parents=True)
26
+ (bundle / "startup.bat").write_text("@echo off\n")
27
+ (bundle / "latextify" / "__init__.py").write_text("")
28
+ archive = tmp_path / "bundle.zip"
29
+
30
+ offline_repo._zip_tree(bundle, archive)
31
+
32
+ with zipfile.ZipFile(archive) as zf:
33
+ assert sorted(zf.namelist()) == [
34
+ f"{offline_repo.BUNDLE_NAME}/latextify/__init__.py",
35
+ f"{offline_repo.BUNDLE_NAME}/startup.bat",
36
+ ]
37
+
38
+
39
+ def test_windows_startup_is_offline_first() -> None:
40
+ source = (offline_repo.ROOT / "startup.bat").read_text(encoding="utf-8")
41
+ folded = source.casefold()
42
+ assert ".runtime\\python\\python.exe" in folded
43
+ assert ".offline-kit\\install.py" in folded
44
+ assert '--dir "%cd%"' in folded
45
+ assert '--dir "%~dp0"' not in folded
46
+ assert 'if /i not "%latextify_allow_downloads%"=="1"' in folded
47
+ assert "goto no_offline_runtime" in folded
48
+
49
+
50
+ def test_online_bootstrap_requires_explicit_wrapper() -> None:
51
+ wrapper = (offline_repo.ROOT / "startup-online.bat").read_text(encoding="utf-8")
52
+ assert 'set "LATEXTIFY_ALLOW_DOWNLOADS=1"' in wrapper
53
+ assert 'call "%~dp0startup.bat" %*' in wrapper