simplex-web 0.2.0__tar.gz → 0.2.2__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 (127) hide show
  1. {simplex_web-0.2.0 → simplex_web-0.2.2}/.gitignore +2 -2
  2. simplex_web-0.2.2/PKG-INFO +279 -0
  3. simplex_web-0.2.2/README.md +234 -0
  4. {simplex_web-0.2.0 → simplex_web-0.2.2}/pyproject.toml +4 -4
  5. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/deck.toml +5 -6
  6. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/builder.py +30 -4
  7. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/sidenotes.py +3 -1
  8. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/site_config.py +12 -0
  9. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/README.md +8 -3
  10. simplex_web-0.2.2/src/simplex/web/static/notes.js +183 -0
  11. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/simplex.css +145 -15
  12. simplex_web-0.2.2/src/simplex/web/static/tailwind.input.css +16 -0
  13. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/viewer.js +1 -1
  14. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/base.html +27 -12
  15. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/deck.html +1 -1
  16. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/revealjs.html.j2 +11 -6
  17. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/vendor.py +106 -7
  18. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_builder.py +35 -1
  19. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_sidenotes.py +2 -0
  20. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_site_config.py +8 -0
  21. simplex_web-0.2.0/PKG-INFO +0 -166
  22. simplex_web-0.2.0/README.md +0 -121
  23. simplex_web-0.2.0/src/simplex/web/static/notes.js +0 -68
  24. simplex_web-0.2.0/src/simplex/web/static/tailwind.js +0 -64
  25. {simplex_web-0.2.0 → simplex_web-0.2.2}/LICENSE +0 -0
  26. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/README.md +0 -0
  27. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/cli/README.md +0 -0
  28. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/cli/__init__.py +0 -0
  29. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/cli/commands.py +0 -0
  30. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/README.md +0 -0
  31. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/__init__.py +0 -0
  32. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/assets/.gitkeep +0 -0
  33. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/assets/code/.gitkeep +0 -0
  34. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/assets/figures/.gitkeep +0 -0
  35. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/manim.cfg +0 -0
  36. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/notes.md +0 -0
  37. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/refs.bib +0 -0
  38. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/slides/__init__.py +0 -0
  39. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/_template/slides/intro.py +0 -0
  40. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/config.py +0 -0
  41. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/registry.py +0 -0
  42. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/scaffold.py +0 -0
  43. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/deck/section.py +0 -0
  44. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/engine/README.md +0 -0
  45. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/README.md +0 -0
  46. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/__init__.py +0 -0
  47. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/html.py +0 -0
  48. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/pdf.py +0 -0
  49. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/pptx.py +0 -0
  50. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/reconcile.py +0 -0
  51. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/runner.py +0 -0
  52. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/render/thumbnail.py +0 -0
  53. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/slides/README.md +0 -0
  54. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/slides/components/README.md +0 -0
  55. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/theme/README.md +0 -0
  56. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/README.md +0 -0
  57. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/__init__.py +0 -0
  58. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/bibliography.py +0 -0
  59. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/bibtex.py +0 -0
  60. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/callouts.py +0 -0
  61. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/citations.py +0 -0
  62. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/equations.py +0 -0
  63. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/notes.py +0 -0
  64. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/refs.py +0 -0
  65. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/slide_ref.py +0 -0
  66. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/.gitkeep +0 -0
  67. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/lato/lato-latin-400-italic.woff2 +0 -0
  68. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/lato/lato-latin-400-normal.woff2 +0 -0
  69. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/lato/lato-latin-700-italic.woff2 +0 -0
  70. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/lato/lato-latin-700-normal.woff2 +0 -0
  71. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/lato/lato-latin-900-normal.woff2 +0 -0
  72. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/merriweather/merriweather-latin-400-italic.woff2 +0 -0
  73. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/merriweather/merriweather-latin-400-normal.woff2 +0 -0
  74. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/merriweather/merriweather-latin-700-italic.woff2 +0 -0
  75. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/merriweather/merriweather-latin-700-normal.woff2 +0 -0
  76. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/fonts/merriweather/merriweather-latin-900-normal.woff2 +0 -0
  77. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/htmx.min.js +0 -0
  78. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/auto-render.min.js +0 -0
  79. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  80. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  81. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  82. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  83. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  84. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  85. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  86. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  87. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  88. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/katex.min.css +0 -0
  89. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/katex/katex.min.js +0 -0
  90. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/lucide/README.md +0 -0
  91. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/lucide/lucide.min.js +0 -0
  92. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/reveal.js/reset.css +0 -0
  93. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/reveal.js/reveal.css +0 -0
  94. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/static/reveal.js/reveal.js +0 -0
  95. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/README.md +0 -0
  96. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/_carousel.html +0 -0
  97. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/index.html +0 -0
  98. {simplex_web-0.2.0 → simplex_web-0.2.2}/src/simplex/web/templates/section.html +0 -0
  99. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/README.md +0 -0
  100. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/__init__.py +0 -0
  101. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/cli/README.md +0 -0
  102. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/cli/__init__.py +0 -0
  103. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/cli/test_help.py +0 -0
  104. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/cli/test_new.py +0 -0
  105. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/cli/test_render.py +0 -0
  106. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/README.md +0 -0
  107. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/__init__.py +0 -0
  108. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/test_config.py +0 -0
  109. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/test_registry.py +0 -0
  110. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/test_scaffold.py +0 -0
  111. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/deck/test_section.py +0 -0
  112. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/engine/README.md +0 -0
  113. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/README.md +0 -0
  114. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/__init__.py +0 -0
  115. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/test_html.py +0 -0
  116. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/test_reconcile.py +0 -0
  117. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/test_runner.py +0 -0
  118. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/render/test_thumbnail.py +0 -0
  119. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/theme/README.md +0 -0
  120. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/README.md +0 -0
  121. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/__init__.py +0 -0
  122. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_bibliography.py +0 -0
  123. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_callouts.py +0 -0
  124. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_citations.py +0 -0
  125. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_equations.py +0 -0
  126. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_notes.py +0 -0
  127. {simplex_web-0.2.0 → simplex_web-0.2.2}/tests/web/test_slide_ref.py +0 -0
@@ -27,8 +27,8 @@ decks/**/slides/files/
27
27
  # Generated site
28
28
  site/
29
29
 
30
- # Vendored web runtime assets (fetched on demand by simplex.web.vendor)
31
- src/simplex/web/static/tailwind.js
30
+ # Vendored web runtime assets (fetched/compiled on demand by simplex.web.vendor)
31
+ src/simplex/web/static/tailwind.css
32
32
  src/simplex/web/static/htmx.min.js
33
33
  src/simplex/web/static/katex/
34
34
  src/simplex/web/static/reveal.js/
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: simplex-web
3
+ Version: 0.2.2
4
+ Summary: Manim-slides presentation framework with a generated web portal.
5
+ Project-URL: Homepage, https://github.com/shlomi-perles/simplex
6
+ Project-URL: Issues, https://github.com/shlomi-perles/simplex/issues
7
+ Project-URL: Repository, https://github.com/shlomi-perles/simplex
8
+ Author: Shlomi Perles
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: animation,computer-science,education,lecture,manim,manim-slides,math,presentation,static-site
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Education
22
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Classifier: Topic :: Multimedia :: Video
25
+ Classifier: Topic :: Scientific/Engineering :: Visualization
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.13
28
+ Requires-Dist: av>=15.0
29
+ Requires-Dist: jinja2>=3.1
30
+ Requires-Dist: manim-simplex>=0.2.1
31
+ Requires-Dist: manim-slides>=5.1.7
32
+ Requires-Dist: markdown-it-py>=3.0
33
+ Requires-Dist: mdit-py-plugins>=0.4
34
+ Requires-Dist: pillow>=11.0
35
+ Requires-Dist: platformdirs>=4.2
36
+ Requires-Dist: pydantic-settings>=2.3
37
+ Requires-Dist: pydantic>=2.7
38
+ Requires-Dist: pygments>=2.18
39
+ Requires-Dist: rich>=13.7
40
+ Requires-Dist: structlog>=24.1
41
+ Requires-Dist: tomli-w>=1.0
42
+ Requires-Dist: typer>=0.12
43
+ Requires-Dist: watchfiles>=0.24
44
+ Description-Content-Type: text/markdown
45
+
46
+ # Simplex
47
+
48
+ [![PyPI version](https://img.shields.io/pypi/v/simplex-web.svg)](https://pypi.org/project/simplex-web/)
49
+ [![Python](https://img.shields.io/pypi/pyversions/simplex-web.svg)](https://pypi.org/project/simplex-web/)
50
+ [![CI](https://github.com/shlomi-perles/simplex/actions/workflows/ci.yml/badge.svg)](https://github.com/shlomi-perles/simplex/actions/workflows/ci.yml)
51
+ [![License](https://img.shields.io/pypi/l/simplex-web.svg)](https://github.com/shlomi-perles/simplex/blob/main/LICENSE)
52
+
53
+ Simplex is a Manim presentation workflow with a generated static web portal.
54
+ Write normal Manim scenes, mark slide boundaries with `manim-slides`, and let
55
+ Simplex render the deck, reconcile slide metadata, build thumbnails, render
56
+ notes, and publish a browsable site.
57
+
58
+ The PyPI distribution is `simplex-web`. It installs the `simplex` command and
59
+ imports into the `simplex.*` namespace.
60
+
61
+ ## Why Simplex?
62
+
63
+ - Author scenes with standard Manim objects such as `MathTex`, `VGroup`,
64
+ `Axes`, and `Animation`.
65
+ - Use `manim-simplex` slide bases, layout regions, theme tokens, and mobjects.
66
+ - Organize decks under `decks/` with a small `deck.toml`.
67
+ - Build a static portal with deck pages, notes, citations, math rendering,
68
+ thumbnails, RevealJS playback, and optional live reload.
69
+ - Keep the rendered video frames clean; navigation chrome lives in the web
70
+ viewer where it can be changed without re-rendering videos.
71
+
72
+ ## Packages
73
+
74
+ Simplex is split into a small toolkit:
75
+
76
+ | Repository | PyPI | Purpose |
77
+ |---|---|---|
78
+ | [`manim-simplex`](https://github.com/shlomi-perles/manim-simplex) | `manim-simplex` | Manim plugin, themes, slide bases, mobjects, and manifest schema. |
79
+ | [`simplex`](https://github.com/shlomi-perles/simplex) | `simplex-web` | CLI, deck discovery, render orchestration, static portal builder. |
80
+ | [`simplex-lectures-template`](https://github.com/shlomi-perles/simplex-lectures-template) | - | Starter lectures repository. |
81
+
82
+ The import namespace is still `simplex`. Both `manim-simplex` and
83
+ `simplex-web` intentionally use a PEP 420 namespace package so their modules
84
+ merge at runtime.
85
+
86
+ ## Requirements
87
+
88
+ - Python 3.13 or newer
89
+ - FFmpeg
90
+ - A LaTeX distribution
91
+ - Manim's native dependencies, including Cairo and Pango on Linux
92
+
93
+ Typical system packages:
94
+
95
+ ```bash
96
+ # Ubuntu / Debian
97
+ sudo apt-get install texlive-latex-extra texlive-fonts-recommended ffmpeg \
98
+ libcairo2-dev libpango1.0-dev
99
+ ```
100
+
101
+ ```powershell
102
+ # Windows
103
+ winget install MiKTeX.MiKTeX
104
+ winget install Gyan.FFmpeg
105
+ ```
106
+
107
+ After installation, run:
108
+
109
+ ```bash
110
+ simplex doctor
111
+ ```
112
+
113
+ ## Install
114
+
115
+ ```bash
116
+ pip install simplex-web
117
+ ```
118
+
119
+ With `uv`:
120
+
121
+ ```bash
122
+ uv add simplex-web
123
+ ```
124
+
125
+ For local development in this repository:
126
+
127
+ ```bash
128
+ uv sync
129
+ uv run simplex doctor
130
+ ```
131
+
132
+ ## Quick Start
133
+
134
+ Create a new deck in an existing project:
135
+
136
+ ```bash
137
+ simplex new algorithms/hash-tables
138
+ simplex render hash-tables
139
+ simplex build
140
+ simplex serve
141
+ ```
142
+
143
+ Then open:
144
+
145
+ ```text
146
+ http://localhost:8000
147
+ ```
148
+
149
+ To start from the GitHub template instead:
150
+
151
+ ```bash
152
+ simplex init my-lectures
153
+ cd my-lectures
154
+ simplex new first-deck
155
+ simplex build
156
+ simplex serve
157
+ ```
158
+
159
+ ## Deck Layout
160
+
161
+ `simplex new hash-tables` creates a deck like this:
162
+
163
+ ```text
164
+ decks/hash-tables/
165
+ |-- deck.toml
166
+ |-- manim.cfg
167
+ |-- notes.md
168
+ |-- refs.bib
169
+ |-- assets/
170
+ `-- slides/
171
+ |-- __init__.py
172
+ `-- intro.py
173
+ ```
174
+
175
+ The important fields in `deck.toml` are:
176
+
177
+ ```toml
178
+ slug = "hash-tables"
179
+ title = "Hash Tables"
180
+ summary = "A one-line deck summary."
181
+ theme = "dastimator_dark"
182
+ quality = "high_quality"
183
+ entrypoints = ["slides.intro:Intro"]
184
+ ```
185
+
186
+ `entrypoints` points to scene classes inside the deck directory. Legacy
187
+ `scenes = ["Intro"]` is still accepted for single-file `slides.py` decks, but
188
+ `entrypoints` is the preferred layout.
189
+
190
+ ## Authoring Slides
191
+
192
+ A scene is ordinary Manim plus the Simplex slide base:
193
+
194
+ ```python
195
+ from manim import DOWN, ORIGIN, Tex, Write
196
+
197
+ from simplex.slides import BaseSlide
198
+ from simplex.theme.context import get_active_theme
199
+
200
+
201
+ class Intro(BaseSlide):
202
+ def construct(self) -> None:
203
+ theme = get_active_theme()
204
+
205
+ title = Tex("Hello, Simplex", font_size=theme.typography.h1)
206
+ self.region.place(title, ORIGIN)
207
+
208
+ subtitle = Tex(r"$e^{i\pi} + 1 = 0$", font_size=theme.typography.h2)
209
+ subtitle.next_to(title, DOWN, buff=0.4)
210
+
211
+ self.play(Write(title), Write(subtitle))
212
+ self.next_slide()
213
+ ```
214
+
215
+ `BaseSlide` comes from `manim-simplex`. It provides the default theme,
216
+ bounded layout region, slide lifecycle helpers, and section metadata used by
217
+ the web builder.
218
+
219
+ ## Notes And Site
220
+
221
+ Each deck can include `notes.md`. The site builder renders Markdown notes,
222
+ inline math, display math, citations from `refs.bib`, and slide references.
223
+
224
+ Site-wide options live in `site.toml`:
225
+
226
+ ```toml
227
+ brand = "Simplex"
228
+ tagline = "Manim presentations, rendered."
229
+
230
+ nav = [
231
+ { label = "Decks", href = "/" },
232
+ { label = "GitHub", href = "https://github.com/shlomi-perles/simplex" },
233
+ ]
234
+ ```
235
+
236
+ Links labeled `GitHub` are shown as the footer GitHub icon, next to the
237
+ `Built with Simplex` mark.
238
+
239
+ Deployment-only settings are read from environment variables:
240
+
241
+ - `SIMPLEX_BASE_URL`
242
+ - `SIMPLEX_GA_TAG`
243
+ - `SIMPLEX_BRAND`
244
+ - `SIMPLEX_PREVIEW`
245
+
246
+ ## CLI
247
+
248
+ | Command | Purpose |
249
+ |---|---|
250
+ | `simplex new <slug>` | Create `decks/<slug>/` from the bundled template. |
251
+ | `simplex new <section>/<slug>` | Create a deck inside a named section. |
252
+ | `simplex init [dir]` | Create a lectures repo from the GitHub template. |
253
+ | `simplex render <slug>` | Render one deck into `site/decks/<slug>/`. |
254
+ | `simplex render <slug>::<Scene>` | Render one scene from a deck. |
255
+ | `simplex build` | Render decks and build the static portal under `site/`. |
256
+ | `simplex build --no-render` | Rebuild portal HTML from existing render output. |
257
+ | `simplex serve [--watch]` | Serve `site/` locally, optionally with live reload. |
258
+ | `simplex test` | Smoke-render decks by rendering only the first animation. |
259
+ | `simplex clean` | Remove generated `site/` and `media/` output. |
260
+ | `simplex doctor` | Check required binaries on `PATH`. |
261
+
262
+ ## Development
263
+
264
+ ```bash
265
+ git clone https://github.com/shlomi-perles/simplex.git
266
+ cd simplex
267
+ uv sync
268
+ uv run ruff check .
269
+ uv run ruff format --check .
270
+ uv run basedpyright
271
+ uv run pytest -q
272
+ ```
273
+
274
+ The release workflow uses GitHub Actions Trusted Publishing to upload
275
+ `simplex-web` to PyPI. No PyPI API token is stored in the repository.
276
+
277
+ ## License
278
+
279
+ MIT.
@@ -0,0 +1,234 @@
1
+ # Simplex
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/simplex-web.svg)](https://pypi.org/project/simplex-web/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/simplex-web.svg)](https://pypi.org/project/simplex-web/)
5
+ [![CI](https://github.com/shlomi-perles/simplex/actions/workflows/ci.yml/badge.svg)](https://github.com/shlomi-perles/simplex/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/pypi/l/simplex-web.svg)](https://github.com/shlomi-perles/simplex/blob/main/LICENSE)
7
+
8
+ Simplex is a Manim presentation workflow with a generated static web portal.
9
+ Write normal Manim scenes, mark slide boundaries with `manim-slides`, and let
10
+ Simplex render the deck, reconcile slide metadata, build thumbnails, render
11
+ notes, and publish a browsable site.
12
+
13
+ The PyPI distribution is `simplex-web`. It installs the `simplex` command and
14
+ imports into the `simplex.*` namespace.
15
+
16
+ ## Why Simplex?
17
+
18
+ - Author scenes with standard Manim objects such as `MathTex`, `VGroup`,
19
+ `Axes`, and `Animation`.
20
+ - Use `manim-simplex` slide bases, layout regions, theme tokens, and mobjects.
21
+ - Organize decks under `decks/` with a small `deck.toml`.
22
+ - Build a static portal with deck pages, notes, citations, math rendering,
23
+ thumbnails, RevealJS playback, and optional live reload.
24
+ - Keep the rendered video frames clean; navigation chrome lives in the web
25
+ viewer where it can be changed without re-rendering videos.
26
+
27
+ ## Packages
28
+
29
+ Simplex is split into a small toolkit:
30
+
31
+ | Repository | PyPI | Purpose |
32
+ |---|---|---|
33
+ | [`manim-simplex`](https://github.com/shlomi-perles/manim-simplex) | `manim-simplex` | Manim plugin, themes, slide bases, mobjects, and manifest schema. |
34
+ | [`simplex`](https://github.com/shlomi-perles/simplex) | `simplex-web` | CLI, deck discovery, render orchestration, static portal builder. |
35
+ | [`simplex-lectures-template`](https://github.com/shlomi-perles/simplex-lectures-template) | - | Starter lectures repository. |
36
+
37
+ The import namespace is still `simplex`. Both `manim-simplex` and
38
+ `simplex-web` intentionally use a PEP 420 namespace package so their modules
39
+ merge at runtime.
40
+
41
+ ## Requirements
42
+
43
+ - Python 3.13 or newer
44
+ - FFmpeg
45
+ - A LaTeX distribution
46
+ - Manim's native dependencies, including Cairo and Pango on Linux
47
+
48
+ Typical system packages:
49
+
50
+ ```bash
51
+ # Ubuntu / Debian
52
+ sudo apt-get install texlive-latex-extra texlive-fonts-recommended ffmpeg \
53
+ libcairo2-dev libpango1.0-dev
54
+ ```
55
+
56
+ ```powershell
57
+ # Windows
58
+ winget install MiKTeX.MiKTeX
59
+ winget install Gyan.FFmpeg
60
+ ```
61
+
62
+ After installation, run:
63
+
64
+ ```bash
65
+ simplex doctor
66
+ ```
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ pip install simplex-web
72
+ ```
73
+
74
+ With `uv`:
75
+
76
+ ```bash
77
+ uv add simplex-web
78
+ ```
79
+
80
+ For local development in this repository:
81
+
82
+ ```bash
83
+ uv sync
84
+ uv run simplex doctor
85
+ ```
86
+
87
+ ## Quick Start
88
+
89
+ Create a new deck in an existing project:
90
+
91
+ ```bash
92
+ simplex new algorithms/hash-tables
93
+ simplex render hash-tables
94
+ simplex build
95
+ simplex serve
96
+ ```
97
+
98
+ Then open:
99
+
100
+ ```text
101
+ http://localhost:8000
102
+ ```
103
+
104
+ To start from the GitHub template instead:
105
+
106
+ ```bash
107
+ simplex init my-lectures
108
+ cd my-lectures
109
+ simplex new first-deck
110
+ simplex build
111
+ simplex serve
112
+ ```
113
+
114
+ ## Deck Layout
115
+
116
+ `simplex new hash-tables` creates a deck like this:
117
+
118
+ ```text
119
+ decks/hash-tables/
120
+ |-- deck.toml
121
+ |-- manim.cfg
122
+ |-- notes.md
123
+ |-- refs.bib
124
+ |-- assets/
125
+ `-- slides/
126
+ |-- __init__.py
127
+ `-- intro.py
128
+ ```
129
+
130
+ The important fields in `deck.toml` are:
131
+
132
+ ```toml
133
+ slug = "hash-tables"
134
+ title = "Hash Tables"
135
+ summary = "A one-line deck summary."
136
+ theme = "dastimator_dark"
137
+ quality = "high_quality"
138
+ entrypoints = ["slides.intro:Intro"]
139
+ ```
140
+
141
+ `entrypoints` points to scene classes inside the deck directory. Legacy
142
+ `scenes = ["Intro"]` is still accepted for single-file `slides.py` decks, but
143
+ `entrypoints` is the preferred layout.
144
+
145
+ ## Authoring Slides
146
+
147
+ A scene is ordinary Manim plus the Simplex slide base:
148
+
149
+ ```python
150
+ from manim import DOWN, ORIGIN, Tex, Write
151
+
152
+ from simplex.slides import BaseSlide
153
+ from simplex.theme.context import get_active_theme
154
+
155
+
156
+ class Intro(BaseSlide):
157
+ def construct(self) -> None:
158
+ theme = get_active_theme()
159
+
160
+ title = Tex("Hello, Simplex", font_size=theme.typography.h1)
161
+ self.region.place(title, ORIGIN)
162
+
163
+ subtitle = Tex(r"$e^{i\pi} + 1 = 0$", font_size=theme.typography.h2)
164
+ subtitle.next_to(title, DOWN, buff=0.4)
165
+
166
+ self.play(Write(title), Write(subtitle))
167
+ self.next_slide()
168
+ ```
169
+
170
+ `BaseSlide` comes from `manim-simplex`. It provides the default theme,
171
+ bounded layout region, slide lifecycle helpers, and section metadata used by
172
+ the web builder.
173
+
174
+ ## Notes And Site
175
+
176
+ Each deck can include `notes.md`. The site builder renders Markdown notes,
177
+ inline math, display math, citations from `refs.bib`, and slide references.
178
+
179
+ Site-wide options live in `site.toml`:
180
+
181
+ ```toml
182
+ brand = "Simplex"
183
+ tagline = "Manim presentations, rendered."
184
+
185
+ nav = [
186
+ { label = "Decks", href = "/" },
187
+ { label = "GitHub", href = "https://github.com/shlomi-perles/simplex" },
188
+ ]
189
+ ```
190
+
191
+ Links labeled `GitHub` are shown as the footer GitHub icon, next to the
192
+ `Built with Simplex` mark.
193
+
194
+ Deployment-only settings are read from environment variables:
195
+
196
+ - `SIMPLEX_BASE_URL`
197
+ - `SIMPLEX_GA_TAG`
198
+ - `SIMPLEX_BRAND`
199
+ - `SIMPLEX_PREVIEW`
200
+
201
+ ## CLI
202
+
203
+ | Command | Purpose |
204
+ |---|---|
205
+ | `simplex new <slug>` | Create `decks/<slug>/` from the bundled template. |
206
+ | `simplex new <section>/<slug>` | Create a deck inside a named section. |
207
+ | `simplex init [dir]` | Create a lectures repo from the GitHub template. |
208
+ | `simplex render <slug>` | Render one deck into `site/decks/<slug>/`. |
209
+ | `simplex render <slug>::<Scene>` | Render one scene from a deck. |
210
+ | `simplex build` | Render decks and build the static portal under `site/`. |
211
+ | `simplex build --no-render` | Rebuild portal HTML from existing render output. |
212
+ | `simplex serve [--watch]` | Serve `site/` locally, optionally with live reload. |
213
+ | `simplex test` | Smoke-render decks by rendering only the first animation. |
214
+ | `simplex clean` | Remove generated `site/` and `media/` output. |
215
+ | `simplex doctor` | Check required binaries on `PATH`. |
216
+
217
+ ## Development
218
+
219
+ ```bash
220
+ git clone https://github.com/shlomi-perles/simplex.git
221
+ cd simplex
222
+ uv sync
223
+ uv run ruff check .
224
+ uv run ruff format --check .
225
+ uv run basedpyright
226
+ uv run pytest -q
227
+ ```
228
+
229
+ The release workflow uses GitHub Actions Trusted Publishing to upload
230
+ `simplex-web` to PyPI. No PyPI API token is stored in the repository.
231
+
232
+ ## License
233
+
234
+ MIT.
@@ -32,7 +32,7 @@ classifiers = [
32
32
  "Typing :: Typed",
33
33
  ]
34
34
  dependencies = [
35
- "manim-simplex>=0.2.0",
35
+ "manim-simplex>=0.2.1",
36
36
  "manim-slides>=5.1.7",
37
37
  "pydantic>=2.7",
38
38
  "pydantic-settings>=2.3",
@@ -72,7 +72,7 @@ license-files = ["LICENSE"]
72
72
  name = "simplex-web"
73
73
  readme = "README.md"
74
74
  requires-python = ">=3.13"
75
- version = "0.2.0"
75
+ version = "0.2.2"
76
76
 
77
77
  [project.scripts]
78
78
  simplex = "simplex.cli.commands:app"
@@ -109,7 +109,7 @@ artifacts = [
109
109
  "src/simplex/web/static/htmx.min.js",
110
110
  "src/simplex/web/static/katex/**/*",
111
111
  "src/simplex/web/static/reveal.js/**/*",
112
- "src/simplex/web/static/tailwind.js",
112
+ "src/simplex/web/static/tailwind.input.css",
113
113
  ]
114
114
  only-include = [
115
115
  "LICENSE",
@@ -130,7 +130,7 @@ artifacts = [
130
130
  "src/simplex/web/static/htmx.min.js",
131
131
  "src/simplex/web/static/katex/**/*",
132
132
  "src/simplex/web/static/reveal.js/**/*",
133
- "src/simplex/web/static/tailwind.js",
133
+ "src/simplex/web/static/tailwind.input.css",
134
134
  ]
135
135
  packages = ["src/simplex"]
136
136
 
@@ -1,11 +1,10 @@
1
+ created_at = "__CREATED_AT__"
2
+ entrypoints = ["slides.intro:Intro"]
3
+ order = 1000
4
+ quality = "high_quality"
1
5
  slug = "__SLUG__"
2
- title = "__TITLE__"
3
6
  summary = "Replace with a one-line description."
4
7
  tags = []
5
8
  theme = "dastimator_dark"
6
- quality = "high_quality"
9
+ title = "__TITLE__"
7
10
  voiceover = false
8
- category = ""
9
- created_at = "__CREATED_AT__"
10
- order = 1000
11
- entrypoints = ["slides.intro:Intro"]
@@ -18,6 +18,7 @@ No render cache. Manim's per-animation cache + ``save_sections=True``
18
18
  """
19
19
 
20
20
  import contextlib
21
+ import hashlib
21
22
  import shutil
22
23
  import subprocess
23
24
  from datetime import UTC, datetime, time
@@ -36,6 +37,28 @@ from simplex.web.bibliography import Bibliography
36
37
  from simplex.web.site_config import SiteConfig
37
38
 
38
39
 
40
+ def _static_source_dir() -> Path:
41
+ return Path(__file__).parent / "static"
42
+
43
+
44
+ def _file_version(path: Path) -> str:
45
+ """Short content hash for cache-busting generated asset URLs."""
46
+ if not path.exists() or not path.is_file():
47
+ return ""
48
+ digest = hashlib.blake2s(digest_size=6)
49
+ with path.open("rb") as f:
50
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
51
+ digest.update(chunk)
52
+ return digest.hexdigest()
53
+
54
+
55
+ def _with_version(url: str, version: str) -> str:
56
+ if not version:
57
+ return url
58
+ separator = "&" if "?" in url else "?"
59
+ return f"{url}{separator}v={version}"
60
+
61
+
39
62
  def _jinja(site_cfg: SiteConfig) -> Environment:
40
63
  env = Environment(
41
64
  loader=PackageLoader("simplex.web", "templates"),
@@ -45,7 +68,9 @@ def _jinja(site_cfg: SiteConfig) -> Environment:
45
68
  )
46
69
 
47
70
  def static(path: str) -> str:
48
- return site_cfg.url("static/" + path.lstrip("/"))
71
+ clean = path.lstrip("/")
72
+ url = site_cfg.url("static/" + clean)
73
+ return _with_version(url, _file_version(_static_source_dir() / clean))
49
74
 
50
75
  globals_: dict[str, Any] = cast(dict[str, Any], env.globals)
51
76
  globals_["static"] = static
@@ -55,13 +80,13 @@ def _jinja(site_cfg: SiteConfig) -> Environment:
55
80
 
56
81
  def _copy_static(site_dir: Path) -> None:
57
82
  """Copy bundled static assets into ``site/static/``."""
58
- src = Path(__file__).parent / "static"
83
+ src = _static_source_dir()
59
84
  src.mkdir(parents=True, exist_ok=True)
60
85
  vendor.ensure(src)
61
86
  dst = site_dir / "static"
62
87
  dst.mkdir(parents=True, exist_ok=True)
63
88
  for entry in src.iterdir():
64
- if entry.name in {"README.md", ".gitkeep"}:
89
+ if entry.name in {"README.md", ".gitkeep", "tailwind.input.css"}:
65
90
  continue
66
91
  target = dst / entry.name
67
92
  if entry.is_dir():
@@ -178,7 +203,7 @@ def _build_deck(
178
203
  for main in manifest.main_slides
179
204
  )
180
205
 
181
- html.render_html(
206
+ slides_html = html.render_html(
182
207
  deck,
183
208
  manifest.model_copy(update={"main_slides": enriched}),
184
209
  output_dir=deck_out,
@@ -206,6 +231,7 @@ def _build_deck(
206
231
  has_notes_pdf=_has_notes_pdf(deck_out),
207
232
  notes_html=notes_html,
208
233
  palette_css=render_web_css(deck.resolved_web_palette()),
234
+ slides_version=_file_version(slides_html),
209
235
  )
210
236
  (deck_out / "index.html").write_text(page, encoding="utf-8")
211
237
  return (
@@ -69,7 +69,9 @@ def _extract_bodies(html: str) -> dict[str, str]:
69
69
 
70
70
  def _render_sidenote(n: str, num: str, body: str) -> str:
71
71
  return (
72
- f'<label for="sn-toggle-{n}" class="sidenote-ref" id="snref-{n}">{num}</label>'
72
+ f'<label for="sn-toggle-{n}" class="sidenote-ref" id="snref-{n}" '
73
+ f'role="button" tabindex="0" aria-controls="sn-{n}" aria-expanded="false">'
74
+ f"{num}</label>"
73
75
  f'<input type="checkbox" id="sn-toggle-{n}" class="sidenote-toggle" '
74
76
  'aria-hidden="true" />'
75
77
  f'<aside class="sidenote" id="sn-{n}" role="note">{body}</aside>'
@@ -9,6 +9,7 @@ import os
9
9
  import tomllib
10
10
  from pathlib import Path
11
11
  from typing import Any, Self
12
+ from urllib.parse import urlparse
12
13
 
13
14
  from pydantic import BaseModel, ConfigDict
14
15
 
@@ -44,6 +45,17 @@ class SiteConfig(BaseModel):
44
45
  return f"/{clean}"
45
46
  return f"{base}/{clean}"
46
47
 
48
+ def is_external_url(self, href: str) -> bool:
49
+ """Return true for links that should not be prefixed by `base_url`."""
50
+ parsed = urlparse(href)
51
+ return bool(parsed.scheme or parsed.netloc)
52
+
53
+ def nav_url(self, href: str) -> str:
54
+ """Resolve a committed nav href without breaking external links."""
55
+ if self.is_external_url(href) or href.startswith("#"):
56
+ return href
57
+ return self.url(href)
58
+
47
59
  @classmethod
48
60
  def load(cls, repo_root: Path | None = None) -> Self:
49
61
  repo_root = repo_root or Path.cwd()