texsmith 0.0.2.dev0__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.
Files changed (252) hide show
  1. texsmith/__init__.py +107 -0
  2. texsmith/_alias.py +59 -0
  3. texsmith/adapters/__init__.py +6 -0
  4. texsmith/adapters/docker.py +258 -0
  5. texsmith/adapters/handlers/__init__.py +3 -0
  6. texsmith/adapters/handlers/_assets.py +363 -0
  7. texsmith/adapters/handlers/_helpers.py +62 -0
  8. texsmith/adapters/handlers/_mermaid.py +122 -0
  9. texsmith/adapters/handlers/admonitions.py +267 -0
  10. texsmith/adapters/handlers/basic.py +228 -0
  11. texsmith/adapters/handlers/blocks.py +851 -0
  12. texsmith/adapters/handlers/code.py +309 -0
  13. texsmith/adapters/handlers/inline.py +937 -0
  14. texsmith/adapters/handlers/links.py +239 -0
  15. texsmith/adapters/handlers/media.py +380 -0
  16. texsmith/adapters/latex/__init__.py +10 -0
  17. texsmith/adapters/latex/engines/__init__.py +726 -0
  18. texsmith/adapters/latex/engines/latex/__init__.py +26 -0
  19. texsmith/adapters/latex/engines/latex/log.py +720 -0
  20. texsmith/adapters/latex/engines/latex/runner.py +28 -0
  21. texsmith/adapters/latex/engines/tectonic/__init__.py +38 -0
  22. texsmith/adapters/latex/formatter.py +297 -0
  23. texsmith/adapters/latex/latexmk.py +148 -0
  24. texsmith/adapters/latex/partials/acronym.tex +1 -0
  25. texsmith/adapters/latex/partials/add.tex +1 -0
  26. texsmith/adapters/latex/partials/addition.tex +1 -0
  27. texsmith/adapters/latex/partials/blockquote.tex +3 -0
  28. texsmith/adapters/latex/partials/callout.tex +3 -0
  29. texsmith/adapters/latex/partials/choices.tex +5 -0
  30. texsmith/adapters/latex/partials/citation.tex +1 -0
  31. texsmith/adapters/latex/partials/codeblock.tex +7 -0
  32. texsmith/adapters/latex/partials/codeblock_listings.tex +5 -0
  33. texsmith/adapters/latex/partials/codeblock_pygments.tex +5 -0
  34. texsmith/adapters/latex/partials/codeblock_verbatim.tex +12 -0
  35. texsmith/adapters/latex/partials/codeinline.tex +1 -0
  36. texsmith/adapters/latex/partials/codeinlinett.tex +1 -0
  37. texsmith/adapters/latex/partials/comment.tex +1 -0
  38. texsmith/adapters/latex/partials/del.tex +1 -0
  39. texsmith/adapters/latex/partials/deletion.tex +1 -0
  40. texsmith/adapters/latex/partials/description_list.tex +5 -0
  41. texsmith/adapters/latex/partials/enquote.tex +1 -0
  42. texsmith/adapters/latex/partials/epigraph.tex +1 -0
  43. texsmith/adapters/latex/partials/exercises_solutions.tex +7 -0
  44. texsmith/adapters/latex/partials/figure.tex +33 -0
  45. texsmith/adapters/latex/partials/figure_tcolorbox.tex +15 -0
  46. texsmith/adapters/latex/partials/footnote.tex +3 -0
  47. texsmith/adapters/latex/partials/glossary.tex +1 -0
  48. texsmith/adapters/latex/partials/heading.tex +14 -0
  49. texsmith/adapters/latex/partials/highlight.tex +25 -0
  50. texsmith/adapters/latex/partials/horizontal_rule.tex +1 -0
  51. texsmith/adapters/latex/partials/href.tex +1 -0
  52. texsmith/adapters/latex/partials/icon.tex +1 -0
  53. texsmith/adapters/latex/partials/include.tex +1 -0
  54. texsmith/adapters/latex/partials/index.tex +1 -0
  55. texsmith/adapters/latex/partials/italic.tex +1 -0
  56. texsmith/adapters/latex/partials/keystroke.tex +46 -0
  57. texsmith/adapters/latex/partials/label.tex +1 -0
  58. texsmith/adapters/latex/partials/list_acronyms.tex +5 -0
  59. texsmith/adapters/latex/partials/list_glossary.tex +8 -0
  60. texsmith/adapters/latex/partials/multicolumn.tex +3 -0
  61. texsmith/adapters/latex/partials/ordered_list.tex +5 -0
  62. texsmith/adapters/latex/partials/pagestyle.tex +1 -0
  63. texsmith/adapters/latex/partials/ref.tex +1 -0
  64. texsmith/adapters/latex/partials/regex.tex +1 -0
  65. texsmith/adapters/latex/partials/smallcaps.tex +1 -0
  66. texsmith/adapters/latex/partials/strikethrough.tex +1 -0
  67. texsmith/adapters/latex/partials/strong.tex +1 -0
  68. texsmith/adapters/latex/partials/subscript.tex +1 -0
  69. texsmith/adapters/latex/partials/substitution.tex +1 -0
  70. texsmith/adapters/latex/partials/superscript.tex +1 -0
  71. texsmith/adapters/latex/partials/tabbed.tex +3 -0
  72. texsmith/adapters/latex/partials/table.tex +48 -0
  73. texsmith/adapters/latex/partials/underline.tex +1 -0
  74. texsmith/adapters/latex/partials/unordered_list.tex +5 -0
  75. texsmith/adapters/latex/partials/url.tex +1 -0
  76. texsmith/adapters/latex/pygments.py +98 -0
  77. texsmith/adapters/latex/pyxindy.py +64 -0
  78. texsmith/adapters/latex/renderer.py +220 -0
  79. texsmith/adapters/latex/tectonic.py +366 -0
  80. texsmith/adapters/latex/utils.py +86 -0
  81. texsmith/adapters/markdown/__init__.py +342 -0
  82. texsmith/adapters/plugins/__init__.py +8 -0
  83. texsmith/adapters/plugins/material.py +174 -0
  84. texsmith/adapters/plugins/snippet.py +1734 -0
  85. texsmith/adapters/transformers/__init__.py +122 -0
  86. texsmith/adapters/transformers/base.py +140 -0
  87. texsmith/adapters/transformers/strategies.py +1456 -0
  88. texsmith/adapters/transformers/utils.py +59 -0
  89. texsmith/api/__init__.py +80 -0
  90. texsmith/api/_utils.py +56 -0
  91. texsmith/api/document.py +645 -0
  92. texsmith/api/pipeline.py +229 -0
  93. texsmith/api/service.py +648 -0
  94. texsmith/api/templates.py +287 -0
  95. texsmith/core/__init__.py +6 -0
  96. texsmith/core/bibliography/__init__.py +57 -0
  97. texsmith/core/bibliography/collection.py +354 -0
  98. texsmith/core/bibliography/doi.py +194 -0
  99. texsmith/core/bibliography/issues.py +15 -0
  100. texsmith/core/bibliography/parsing.py +45 -0
  101. texsmith/core/callouts.py +99 -0
  102. texsmith/core/config.py +191 -0
  103. texsmith/core/context.py +242 -0
  104. texsmith/core/conversion/__init__.py +103 -0
  105. texsmith/core/conversion/core.py +780 -0
  106. texsmith/core/conversion/debug.py +87 -0
  107. texsmith/core/conversion/inputs.py +474 -0
  108. texsmith/core/conversion/renderer.py +578 -0
  109. texsmith/core/conversion/templates.py +646 -0
  110. texsmith/core/conversion_contexts.py +95 -0
  111. texsmith/core/diagnostics.py +118 -0
  112. texsmith/core/exceptions.py +41 -0
  113. texsmith/core/fonts/__init__.py +3 -0
  114. texsmith/core/fragments/__init__.py +716 -0
  115. texsmith/core/fragments/base.py +117 -0
  116. texsmith/core/metadata.py +280 -0
  117. texsmith/core/mustache.py +86 -0
  118. texsmith/core/partials.py +20 -0
  119. texsmith/core/rules.py +394 -0
  120. texsmith/core/templates/__init__.py +58 -0
  121. texsmith/core/templates/base.py +343 -0
  122. texsmith/core/templates/builtins.py +68 -0
  123. texsmith/core/templates/context_usage.py +137 -0
  124. texsmith/core/templates/loader.py +303 -0
  125. texsmith/core/templates/manifest.py +861 -0
  126. texsmith/core/templates/runtime.py +347 -0
  127. texsmith/core/templates/text.py +14 -0
  128. texsmith/core/templates/wrapper.py +340 -0
  129. texsmith/core/user_dir.py +179 -0
  130. texsmith/devtools.py +28 -0
  131. texsmith/extensions/__init__.py +162 -0
  132. texsmith/extensions/index/__init__.py +21 -0
  133. texsmith/extensions/index/markdown.py +94 -0
  134. texsmith/extensions/index/mkdocs_plugin.py +136 -0
  135. texsmith/extensions/index/registry.py +57 -0
  136. texsmith/extensions/index/renderer.py +183 -0
  137. texsmith/extensions/index/templates/index.tex +1 -0
  138. texsmith/extensions/latex_raw.py +115 -0
  139. texsmith/extensions/latex_text.py +117 -0
  140. texsmith/extensions/mermaid.py +267 -0
  141. texsmith/extensions/missing_footnotes.py +125 -0
  142. texsmith/extensions/multi_citations.py +54 -0
  143. texsmith/extensions/progressbar/__init__.py +9 -0
  144. texsmith/extensions/progressbar/markdown.py +212 -0
  145. texsmith/extensions/progressbar/renderer.py +117 -0
  146. texsmith/extensions/smallcaps.py +48 -0
  147. texsmith/extensions/texlogos/__init__.py +10 -0
  148. texsmith/extensions/texlogos/markdown.py +236 -0
  149. texsmith/extensions/texlogos/renderer.py +81 -0
  150. texsmith/extensions/texlogos/specs.py +66 -0
  151. texsmith/fonts/__init__.py +57 -0
  152. texsmith/fonts/cache.py +46 -0
  153. texsmith/fonts/constants.py +60 -0
  154. texsmith/fonts/coverage.py +275 -0
  155. texsmith/fonts/downloader.py +103 -0
  156. texsmith/fonts/fallback.py +438 -0
  157. texsmith/fonts/html_scripts.py +168 -0
  158. texsmith/fonts/logging.py +127 -0
  159. texsmith/fonts/pipeline.py +338 -0
  160. texsmith/fonts/scripts.py +493 -0
  161. texsmith/fonts/ucharclasses.py +164 -0
  162. texsmith/fragments/__init__.py +11 -0
  163. texsmith/fragments/bibliography/__init__.py +65 -0
  164. texsmith/fragments/bibliography/fragment.toml +3 -0
  165. texsmith/fragments/bibliography/ts-bibliography-backmatter.jinja.tex +35 -0
  166. texsmith/fragments/bibliography/ts-bibliography.jinja.tex +10 -0
  167. texsmith/fragments/callouts/__init__.py +88 -0
  168. texsmith/fragments/callouts/fragment.toml +3 -0
  169. texsmith/fragments/callouts/ts-callouts.jinja.sty +129 -0
  170. texsmith/fragments/code/__init__.py +82 -0
  171. texsmith/fragments/code/fragment.toml +3 -0
  172. texsmith/fragments/code/ts-code.jinja.sty +140 -0
  173. texsmith/fragments/extra/__init__.py +180 -0
  174. texsmith/fragments/extra/fragment.toml +3 -0
  175. texsmith/fragments/extra/ts-extra.jinja.tex +13 -0
  176. texsmith/fragments/fonts/__init__.py +880 -0
  177. texsmith/fragments/fonts/fragment.toml +3 -0
  178. texsmith/fragments/fonts/ts-fonts.jinja.sty +292 -0
  179. texsmith/fragments/frame/__init__.py +170 -0
  180. texsmith/fragments/frame/fragment.toml +3 -0
  181. texsmith/fragments/frame/ts-frame.tex.jinja +49 -0
  182. texsmith/fragments/geometry/__init__.py +169 -0
  183. texsmith/fragments/geometry/fragment.toml +3 -0
  184. texsmith/fragments/geometry/paper.py +526 -0
  185. texsmith/fragments/geometry/ts_geometry.tex.jinja +53 -0
  186. texsmith/fragments/glossary/__init__.py +67 -0
  187. texsmith/fragments/glossary/fragment.toml +3 -0
  188. texsmith/fragments/glossary/ts-glossary-backmatter.jinja.tex +5 -0
  189. texsmith/fragments/glossary/ts-glossary.jinja.sty +28 -0
  190. texsmith/fragments/index/__init__.py +66 -0
  191. texsmith/fragments/index/fragment.toml +3 -0
  192. texsmith/fragments/index/ts-index-backmatter.jinja.tex +3 -0
  193. texsmith/fragments/index/ts-index.jinja.sty +12 -0
  194. texsmith/fragments/keystrokes/__init__.py +69 -0
  195. texsmith/fragments/keystrokes/fragment.toml +3 -0
  196. texsmith/fragments/keystrokes/ts-keystrokes.jinja.sty +20 -0
  197. texsmith/fragments/todolist/__init__.py +71 -0
  198. texsmith/fragments/todolist/fragment.toml +3 -0
  199. texsmith/fragments/todolist/ts-todolist.jinja.sty +21 -0
  200. texsmith/fragments/typesetting/__init__.py +214 -0
  201. texsmith/fragments/typesetting/fragment.toml +3 -0
  202. texsmith/fragments/typesetting/ts-typesetting.tex.jinja +81 -0
  203. texsmith/index.py +26 -0
  204. texsmith/plugins/__init__.py +14 -0
  205. texsmith/progressbar.py +8 -0
  206. texsmith/quotes.py +40 -0
  207. texsmith/smart_dashes.py +66 -0
  208. texsmith/templates/__init__.py +3 -0
  209. texsmith/templates/article/README.md +33 -0
  210. texsmith/templates/article/__init__.py +281 -0
  211. texsmith/templates/article/template/manifest.toml +125 -0
  212. texsmith/templates/article/template/mermaid-config.json +18 -0
  213. texsmith/templates/article/template/template.tex +74 -0
  214. texsmith/templates/book/README.md +26 -0
  215. texsmith/templates/book/__init__.py +75 -0
  216. texsmith/templates/book/overrides/codeblock.tex +7 -0
  217. texsmith/templates/book/overrides/codeinline.tex +1 -0
  218. texsmith/templates/book/template/fixtoc.sty +81 -0
  219. texsmith/templates/book/template/manifest.toml +198 -0
  220. texsmith/templates/book/template/template.tex +314 -0
  221. texsmith/templates/common/__init__.py +1 -0
  222. texsmith/templates/common/latexmkrc +46 -0
  223. texsmith/templates/letter/README.md +59 -0
  224. texsmith/templates/letter/__init__.py +495 -0
  225. texsmith/templates/letter/demo.md +31 -0
  226. texsmith/templates/letter/fonts/modernline bold.otf +0 -0
  227. texsmith/templates/letter/fonts/modernline.otf +0 -0
  228. texsmith/templates/letter/manifest.toml +197 -0
  229. texsmith/templates/letter/template/callouts.jinja.sty +290 -0
  230. texsmith/templates/letter/template/template.tex +123 -0
  231. texsmith/templates/snippet/README.md +21 -0
  232. texsmith/templates/snippet/__init__.py +80 -0
  233. texsmith/templates/snippet/template/manifest.toml +66 -0
  234. texsmith/templates/snippet/template/template.tex +47 -0
  235. texsmith/texlogos.py +15 -0
  236. texsmith/ui/__init__.py +6 -0
  237. texsmith/ui/cli/__init__.py +22 -0
  238. texsmith/ui/cli/_options.py +338 -0
  239. texsmith/ui/cli/app.py +65 -0
  240. texsmith/ui/cli/bibliography.py +300 -0
  241. texsmith/ui/cli/commands/__init__.py +14 -0
  242. texsmith/ui/cli/commands/render.py +1128 -0
  243. texsmith/ui/cli/commands/templates.py +397 -0
  244. texsmith/ui/cli/diagnostics.py +36 -0
  245. texsmith/ui/cli/presenter.py +663 -0
  246. texsmith/ui/cli/state.py +263 -0
  247. texsmith/ui/cli/utils.py +235 -0
  248. texsmith-0.0.2.dev0.dist-info/METADATA +187 -0
  249. texsmith-0.0.2.dev0.dist-info/RECORD +252 -0
  250. texsmith-0.0.2.dev0.dist-info/WHEEL +4 -0
  251. texsmith-0.0.2.dev0.dist-info/entry_points.txt +22 -0
  252. texsmith-0.0.2.dev0.dist-info/licenses/LICENSE.md +21 -0
@@ -0,0 +1,66 @@
1
+ [compat]
2
+ texsmith = ">=0.1,<1.0"
3
+
4
+ [latex.template]
5
+ name = "snippet"
6
+ version = "0.1.4"
7
+ entrypoint = "template/template.tex"
8
+ engine = "lualatex"
9
+ shell_escape = false
10
+ texlive_year = 2023
11
+ tlmgr_packages = [
12
+ "babel",
13
+ "fontspec",
14
+ "hyphenat",
15
+ "microtype",
16
+ "tcolorbox",
17
+ "fontawesome5",
18
+ "csquotes",
19
+ "hyperref",
20
+ "tikz",
21
+ "glossaries",
22
+ "biblatex",
23
+ ]
24
+ fragments = [
25
+ "ts-fonts",
26
+ "ts-extra",
27
+ "ts-keystrokes",
28
+ "ts-callouts",
29
+ "ts-code",
30
+ "ts-glossary",
31
+ "ts-bibliography",
32
+ "ts-todolist",
33
+ ]
34
+
35
+ [latex.template.attributes.language]
36
+ default = "english"
37
+ type = "string"
38
+ allow_empty = false
39
+ sources = ["language"]
40
+
41
+ [latex.template.attributes.width]
42
+ default = "12cm"
43
+ type = "string"
44
+ allow_empty = false
45
+ sources = ["width"]
46
+
47
+ [latex.template.attributes.margin]
48
+ default = "6mm"
49
+ type = "string"
50
+ allow_empty = false
51
+ sources = ["margin"]
52
+
53
+ [latex.template.attributes.dogear]
54
+ default = "5mm"
55
+ type = "string"
56
+ allow_empty = false
57
+ sources = ["dogear"]
58
+
59
+ [latex.template.attributes.border]
60
+ default = true
61
+ type = "boolean"
62
+ sources = ["border"]
63
+
64
+ [latex.template.slots.mainmatter]
65
+ default = true
66
+ depth = "section"
@@ -0,0 +1,47 @@
1
+ \documentclass{standalone}
2
+ \usepackage{fontspec}
3
+ \usepackage{xcolor}
4
+ \definecolor{texsmithLinkColor}{HTML}{0B3D91}
5
+ \VAR{extra_packages}
6
+
7
+ \usepackage[colorlinks=true, linkcolor=texsmithLinkColor, urlcolor=texsmithLinkColor, citecolor=texsmithLinkColor]{hyperref}
8
+ \usepackage{tikz}
9
+ \usetikzlibrary{calc}
10
+
11
+ \begin{document}
12
+ \BLOCK{ if border }
13
+ \begin{tikzpicture}
14
+ \def\textwidthbox{\VAR{width}}
15
+ \def\margin{\VAR{margin}}
16
+ \def\e{\VAR{dogear}}
17
+
18
+ \node[inner sep=\margin, outer sep=0pt] (content) {%
19
+ \begin{minipage}{\textwidthbox}
20
+ \BLOCK{ endif }
21
+ \VAR{mainmatter}
22
+ \VAR{fragment_backmatter}
23
+ \BLOCK{ if border }
24
+ \end{minipage}
25
+ };
26
+
27
+ \coordinate (A) at (content.north west);
28
+ \coordinate (B) at (content.north east);
29
+ \coordinate (C) at (content.south east);
30
+ \coordinate (D) at (content.south west);
31
+
32
+ \coordinate (Bdown) at ($(B)-(0,\e)$);
33
+ \coordinate (Bleft) at ($(B)-(\e,0)$);
34
+ \coordinate (Corner) at ($(B)-(\e,\e)$);
35
+
36
+ \draw (A) -- (Bleft) -- (Bdown) -- (C) -- (D) -- cycle;
37
+ \BLOCK{ if dogear }
38
+ \draw (Corner)
39
+ .. controls ($(Corner)+(0.3*\e,0.1*\e)$) and ($(Bdown)+(-0.3*\e,-0.1*\e)$) ..
40
+ (Bdown);
41
+ \draw (Bleft)
42
+ .. controls ($(Bleft)+(0.1*\e,-0.3*\e)$) and ($(Corner)+(-0.1*\e,0.3*\e)$) ..
43
+ (Corner);
44
+ \BLOCK{ endif }
45
+ \BLOCK{ endif }
46
+ \end{tikzpicture}
47
+ \end{document}
texsmith/texlogos.py ADDED
@@ -0,0 +1,15 @@
1
+ """Public entry points for the TeX logo Markdown and LaTeX extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .extensions.texlogos import LogoSpec, iter_specs, register_renderer
6
+ from .extensions.texlogos.markdown import TexLogosExtension, makeExtension
7
+
8
+
9
+ __all__ = [
10
+ "LogoSpec",
11
+ "TexLogosExtension",
12
+ "iter_specs",
13
+ "makeExtension",
14
+ "register_renderer",
15
+ ]
@@ -0,0 +1,6 @@
1
+ """User-facing interfaces for TexSmith."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ __all__ = []
@@ -0,0 +1,22 @@
1
+ """Public CLI exports for TexSmith."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from texsmith.adapters.markdown import DEFAULT_MARKDOWN_EXTENSIONS
6
+
7
+ from .app import app, main
8
+ from .commands import render
9
+ from .state import debug_enabled, emit_error, emit_warning, ensure_rich_compat, get_cli_state
10
+
11
+
12
+ __all__ = [
13
+ "DEFAULT_MARKDOWN_EXTENSIONS",
14
+ "app",
15
+ "debug_enabled",
16
+ "emit_error",
17
+ "emit_warning",
18
+ "ensure_rich_compat",
19
+ "get_cli_state",
20
+ "main",
21
+ "render",
22
+ ]
@@ -0,0 +1,338 @@
1
+ """Shared Typer option definitions for CLI commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Annotated
7
+
8
+ import typer
9
+
10
+
11
+ INPUTS_PANEL = "Input Handling"
12
+ STRUCTURE_PANEL = "Structure"
13
+ RENDERING_PANEL = "Rendering"
14
+ OUTPUT_PANEL = "Output"
15
+ TEMPLATE_PANEL = "Template"
16
+ DIAGNOSTICS_PANEL = "Diagnostics"
17
+
18
+ InputPathArgument = Annotated[
19
+ list[Path] | None,
20
+ typer.Argument(
21
+ metavar="INPUT...",
22
+ help=(
23
+ "Conversion inputs such as Markdown (.md) or HTML (.html) source documents. "
24
+ "Optionally, BibTeX files (.bib) for citation processing."
25
+ ),
26
+ exists=True,
27
+ file_okay=True,
28
+ dir_okay=False,
29
+ readable=True,
30
+ resolve_path=True,
31
+ rich_help_panel=INPUTS_PANEL,
32
+ ),
33
+ ]
34
+
35
+ SelectorOption = Annotated[
36
+ str,
37
+ typer.Option(
38
+ "--selector",
39
+ help="CSS selector to extract the MkDocs article content.",
40
+ rich_help_panel=INPUTS_PANEL,
41
+ ),
42
+ ]
43
+
44
+ FullDocumentOption = Annotated[
45
+ bool,
46
+ typer.Option(
47
+ "--full-document",
48
+ help="Disable article extraction and render the entire HTML file.",
49
+ rich_help_panel=INPUTS_PANEL,
50
+ ),
51
+ ]
52
+
53
+ BaseLevelOption = Annotated[
54
+ str,
55
+ typer.Option(
56
+ "--base-level",
57
+ help="Base heading level relative to the template (e.g. 1 or 'section').",
58
+ rich_help_panel=STRUCTURE_PANEL,
59
+ ),
60
+ ]
61
+
62
+ StripHeadingOption = Annotated[
63
+ bool,
64
+ typer.Option(
65
+ "--strip-heading",
66
+ help="Drop the first document heading from the rendered content.",
67
+ rich_help_panel=STRUCTURE_PANEL,
68
+ ),
69
+ ]
70
+
71
+ NoPromoteTitleOption = Annotated[
72
+ bool,
73
+ typer.Option(
74
+ "--no-promote-title",
75
+ help="Disable promotion of the first heading to document title.",
76
+ rich_help_panel=TEMPLATE_PANEL,
77
+ ),
78
+ ]
79
+
80
+ NoTitleOption = Annotated[
81
+ bool,
82
+ typer.Option(
83
+ "--no-title",
84
+ help="Disable title generation even when metadata provides one.",
85
+ rich_help_panel=TEMPLATE_PANEL,
86
+ ),
87
+ ]
88
+
89
+ ParserOption = Annotated[
90
+ str | None,
91
+ typer.Option(
92
+ "--parser",
93
+ help='BeautifulSoup parser backend to use (defaults to "html.parser").',
94
+ rich_help_panel=INPUTS_PANEL,
95
+ ),
96
+ ]
97
+
98
+ DisableFallbackOption = Annotated[
99
+ bool,
100
+ typer.Option(
101
+ "--no-fallback-converters",
102
+ help="Disable registration of placeholder converters when Docker is unavailable.",
103
+ rich_help_panel=RENDERING_PANEL,
104
+ ),
105
+ ]
106
+
107
+ NoCopyAssetsOption = Annotated[
108
+ bool,
109
+ typer.Option(
110
+ "--no-copy-assets",
111
+ "-C",
112
+ help="Disable copying of remote assets to the output directory.",
113
+ rich_help_panel=RENDERING_PANEL,
114
+ ),
115
+ ]
116
+
117
+ ConvertAssetsOption = Annotated[
118
+ bool,
119
+ typer.Option(
120
+ "--convert-assets",
121
+ help=(
122
+ "Convert bitmap assets (PNG/JPEG) to PDF even when LaTeX supports the original format."
123
+ ),
124
+ rich_help_panel=RENDERING_PANEL,
125
+ ),
126
+ ]
127
+
128
+ HashAssetsOption = Annotated[
129
+ bool,
130
+ typer.Option(
131
+ "--hash-assets",
132
+ help="Hash stored asset filenames instead of preserving their original names.",
133
+ rich_help_panel=RENDERING_PANEL,
134
+ ),
135
+ ]
136
+
137
+ TemplateAttributeOption = Annotated[
138
+ list[str] | None,
139
+ typer.Option(
140
+ "--attribute",
141
+ "-a",
142
+ help="Override template attributes as key=value pairs (e.g. -a emoji=color).",
143
+ show_default=False,
144
+ rich_help_panel=TEMPLATE_PANEL,
145
+ ),
146
+ ]
147
+
148
+ EnableFragmentOption = Annotated[
149
+ list[str] | None,
150
+ typer.Option(
151
+ "--enable-fragment",
152
+ "-f",
153
+ help="Enable an additional fragment by name (can be repeated).",
154
+ show_default=False,
155
+ rich_help_panel=TEMPLATE_PANEL,
156
+ ),
157
+ ]
158
+
159
+ DisableFragmentOption = Annotated[
160
+ list[str] | None,
161
+ typer.Option(
162
+ "--disable-fragment",
163
+ "-F",
164
+ help="Disable a fragment by name (can be repeated).",
165
+ show_default=False,
166
+ rich_help_panel=TEMPLATE_PANEL,
167
+ ),
168
+ ]
169
+
170
+ ManifestOption = Annotated[
171
+ bool,
172
+ typer.Option(
173
+ "--manifest",
174
+ help="Generate a manifest.json file alongside the LaTeX output.",
175
+ rich_help_panel=RENDERING_PANEL,
176
+ ),
177
+ ]
178
+
179
+ ManifestOptionWithShort = Annotated[
180
+ bool,
181
+ typer.Option(
182
+ "--manifest",
183
+ "-m",
184
+ help="Generate a manifest.json file alongside the LaTeX output.",
185
+ rich_help_panel=RENDERING_PANEL,
186
+ ),
187
+ ]
188
+
189
+ MakefileDepsOption = Annotated[
190
+ bool,
191
+ typer.Option(
192
+ "--makefile-deps",
193
+ "-M",
194
+ help="Emit a Makefile-compatible .d dependency file when building PDFs.",
195
+ rich_help_panel=OUTPUT_PANEL,
196
+ ),
197
+ ]
198
+
199
+ DebugHtmlOption = Annotated[
200
+ bool | None,
201
+ typer.Option(
202
+ "--debug-html",
203
+ help="Persist intermediate HTML snapshots (inherits from --debug when omitted).",
204
+ rich_help_panel=DIAGNOSTICS_PANEL,
205
+ ),
206
+ ]
207
+
208
+ DebugRulesOption = Annotated[
209
+ bool,
210
+ typer.Option(
211
+ "--debug-rules",
212
+ help="Display the ordered list of registered render rules.",
213
+ rich_help_panel=DIAGNOSTICS_PANEL,
214
+ ),
215
+ ]
216
+
217
+ TemplateInfoOption = Annotated[
218
+ bool,
219
+ typer.Option(
220
+ "--template-info",
221
+ help="Display template metadata and exit.",
222
+ rich_help_panel=DIAGNOSTICS_PANEL,
223
+ ),
224
+ ]
225
+
226
+ HtmlOnlyOption = Annotated[
227
+ bool,
228
+ typer.Option(
229
+ "--html",
230
+ help="Output intermediate HTML instead of LaTeX/PDF.",
231
+ rich_help_panel=OUTPUT_PANEL,
232
+ ),
233
+ ]
234
+
235
+ LanguageOption = Annotated[
236
+ str | None,
237
+ typer.Option(
238
+ "-l",
239
+ "--language",
240
+ help="Language code passed to babel (defaults to metadata or english).",
241
+ rich_help_panel=RENDERING_PANEL,
242
+ ),
243
+ ]
244
+
245
+ SlotsOption = Annotated[
246
+ list[str] | None,
247
+ typer.Option(
248
+ "--slot",
249
+ "-s",
250
+ help=(
251
+ "Inject a document section into a template slot using 'slot:Section'. Repeat to map "
252
+ "multiple sections."
253
+ ),
254
+ show_default=False,
255
+ rich_help_panel=TEMPLATE_PANEL,
256
+ ),
257
+ ]
258
+
259
+ MarkdownExtensionsOption = Annotated[
260
+ list[str] | None,
261
+ typer.Option(
262
+ "--enable-extension",
263
+ "-x",
264
+ help=(
265
+ "Additional Markdown extensions to enable (comma or space separated values are accepted)."
266
+ ),
267
+ show_default=False,
268
+ rich_help_panel=RENDERING_PANEL,
269
+ ),
270
+ ]
271
+
272
+ DisableMarkdownExtensionsOption = Annotated[
273
+ list[str] | None,
274
+ typer.Option(
275
+ "--disable-extension",
276
+ "-X",
277
+ help=(
278
+ "Markdown extensions to disable. Provide a comma separated list or repeat the option "
279
+ "multiple times. Use --list-extensions to see the extensions enabled by default."
280
+ ),
281
+ show_default=False,
282
+ rich_help_panel=RENDERING_PANEL,
283
+ ),
284
+ ]
285
+
286
+ OutputPathOption = Annotated[
287
+ Path | None,
288
+ typer.Option(
289
+ "--output",
290
+ "-o",
291
+ "--output-dir",
292
+ help="Output file or directory. Defaults to stdout unless a template is used.",
293
+ show_default=False,
294
+ rich_help_panel=OUTPUT_PANEL,
295
+ ),
296
+ ]
297
+
298
+ OutputDirOption = Annotated[
299
+ Path,
300
+ typer.Option(
301
+ "--output-dir",
302
+ "-o",
303
+ help="Directory used to collect generated assets (if any).",
304
+ resolve_path=True,
305
+ rich_help_panel=OUTPUT_PANEL,
306
+ ),
307
+ ]
308
+
309
+ TemplateOption = Annotated[
310
+ str | None,
311
+ typer.Option(
312
+ "--template",
313
+ "-t",
314
+ help=(
315
+ "Select a LaTeX template to use during conversion. Accepts a local path, entry point, "
316
+ "or built-in slug such as 'article' or 'letter'."
317
+ ),
318
+ rich_help_panel=TEMPLATE_PANEL,
319
+ ),
320
+ ]
321
+
322
+ FontsInfoOption = Annotated[
323
+ bool,
324
+ typer.Option(
325
+ "--fonts-info",
326
+ help="Display a summary of fallback fonts detected during rendering.",
327
+ rich_help_panel=DIAGNOSTICS_PANEL,
328
+ ),
329
+ ]
330
+
331
+ OpenLogOption = Annotated[
332
+ bool,
333
+ typer.Option(
334
+ "--open-log",
335
+ help="Open the latexmk log with the system viewer when compilation fails.",
336
+ rich_help_panel=DIAGNOSTICS_PANEL,
337
+ ),
338
+ ]
texsmith/ui/cli/app.py ADDED
@@ -0,0 +1,65 @@
1
+ """Typer application wiring for the TeXSmith CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+ import typer
7
+ from typer.core import TyperCommand
8
+
9
+ from texsmith.ui.cli.commands.render import render
10
+
11
+ from .state import debug_enabled, emit_error
12
+
13
+
14
+ class HelpOnEmptyCommand(TyperCommand):
15
+ """Typer command that disables positional argument enforcement."""
16
+
17
+ def __init__(self, *args: object, **kwargs: object) -> None: # type: ignore[override]
18
+ super().__init__(*args, **kwargs)
19
+ for param in self.params:
20
+ if isinstance(param, click.Argument):
21
+ param.required = False
22
+
23
+
24
+ app = typer.Typer(
25
+ help="Convert MkDocs HTML fragments into LaTeX.",
26
+ context_settings={"help_option_names": ["--help"]},
27
+ )
28
+
29
+
30
+ app.command(cls=HelpOnEmptyCommand)(render)
31
+
32
+
33
+ def main() -> None:
34
+ """Entry point compatible with console scripts."""
35
+ try:
36
+ app()
37
+ except typer.Exit:
38
+ raise
39
+ except KeyboardInterrupt as exc:
40
+ if debug_enabled():
41
+ raise
42
+ emit_error("Operation cancelled by user.", exception=exc)
43
+ raise typer.Exit(code=1) from exc
44
+ except SystemExit:
45
+ raise
46
+ except Exception as exc: # pragma: no cover - defensive catch-all
47
+ from .state import get_cli_state
48
+
49
+ state = get_cli_state()
50
+ if state.show_tracebacks:
51
+ from rich.traceback import Traceback
52
+
53
+ tb = Traceback.from_exception(
54
+ type(exc),
55
+ exc,
56
+ exc.__traceback__,
57
+ show_locals=state.verbosity >= 2,
58
+ )
59
+ state.err_console.print(tb)
60
+ else:
61
+ emit_error(str(exc), exception=exc)
62
+ raise typer.Exit(code=1) from exc
63
+
64
+
65
+ __all__ = ["app", "main"]