simplex-web 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. simplex/README.md +32 -0
  2. simplex/cli/README.md +13 -0
  3. simplex/cli/__init__.py +5 -0
  4. simplex/cli/commands.py +384 -0
  5. simplex/deck/README.md +19 -0
  6. simplex/deck/__init__.py +7 -0
  7. simplex/deck/_template/assets/.gitkeep +0 -0
  8. simplex/deck/_template/assets/code/.gitkeep +0 -0
  9. simplex/deck/_template/assets/figures/.gitkeep +0 -0
  10. simplex/deck/_template/deck.toml +11 -0
  11. simplex/deck/_template/manim.cfg +3 -0
  12. simplex/deck/_template/notes.md +27 -0
  13. simplex/deck/_template/refs.bib +12 -0
  14. simplex/deck/_template/slides/__init__.py +7 -0
  15. simplex/deck/_template/slides/intro.py +21 -0
  16. simplex/deck/config.py +207 -0
  17. simplex/deck/registry.py +110 -0
  18. simplex/deck/scaffold.py +86 -0
  19. simplex/deck/section.py +40 -0
  20. simplex/engine/README.md +9 -0
  21. simplex/render/README.md +46 -0
  22. simplex/render/__init__.py +1 -0
  23. simplex/render/html.py +132 -0
  24. simplex/render/pdf.py +32 -0
  25. simplex/render/pptx.py +32 -0
  26. simplex/render/reconcile.py +350 -0
  27. simplex/render/runner.py +116 -0
  28. simplex/render/thumbnail.py +374 -0
  29. simplex/slides/README.md +9 -0
  30. simplex/slides/components/README.md +9 -0
  31. simplex/theme/README.md +9 -0
  32. simplex/web/README.md +33 -0
  33. simplex/web/__init__.py +1 -0
  34. simplex/web/bibliography.py +248 -0
  35. simplex/web/bibtex.py +129 -0
  36. simplex/web/builder.py +321 -0
  37. simplex/web/callouts.py +134 -0
  38. simplex/web/citations.py +118 -0
  39. simplex/web/equations.py +79 -0
  40. simplex/web/notes.py +135 -0
  41. simplex/web/refs.py +60 -0
  42. simplex/web/sidenotes.py +76 -0
  43. simplex/web/site_config.py +71 -0
  44. simplex/web/slide_ref.py +54 -0
  45. simplex/web/static/.gitkeep +0 -0
  46. simplex/web/static/README.md +23 -0
  47. simplex/web/static/fonts/lato/lato-latin-400-italic.woff2 +0 -0
  48. simplex/web/static/fonts/lato/lato-latin-400-normal.woff2 +0 -0
  49. simplex/web/static/fonts/lato/lato-latin-700-italic.woff2 +0 -0
  50. simplex/web/static/fonts/lato/lato-latin-700-normal.woff2 +0 -0
  51. simplex/web/static/fonts/lato/lato-latin-900-normal.woff2 +0 -0
  52. simplex/web/static/fonts/merriweather/merriweather-latin-400-italic.woff2 +0 -0
  53. simplex/web/static/fonts/merriweather/merriweather-latin-400-normal.woff2 +0 -0
  54. simplex/web/static/fonts/merriweather/merriweather-latin-700-italic.woff2 +0 -0
  55. simplex/web/static/fonts/merriweather/merriweather-latin-700-normal.woff2 +0 -0
  56. simplex/web/static/fonts/merriweather/merriweather-latin-900-normal.woff2 +0 -0
  57. simplex/web/static/htmx.min.js +1 -0
  58. simplex/web/static/katex/auto-render.min.js +1 -0
  59. simplex/web/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  60. simplex/web/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  61. simplex/web/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  62. simplex/web/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  63. simplex/web/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  64. simplex/web/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  65. simplex/web/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  66. simplex/web/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  67. simplex/web/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  68. simplex/web/static/katex/katex.min.css +1 -0
  69. simplex/web/static/katex/katex.min.js +1 -0
  70. simplex/web/static/lucide/README.md +7 -0
  71. simplex/web/static/lucide/lucide.min.js +12 -0
  72. simplex/web/static/notes.js +68 -0
  73. simplex/web/static/reveal.js/reset.css +30 -0
  74. simplex/web/static/reveal.js/reveal.css +8 -0
  75. simplex/web/static/reveal.js/reveal.js +9 -0
  76. simplex/web/static/simplex.css +1870 -0
  77. simplex/web/static/tailwind.js +64 -0
  78. simplex/web/static/viewer.js +428 -0
  79. simplex/web/templates/README.md +19 -0
  80. simplex/web/templates/_carousel.html +117 -0
  81. simplex/web/templates/base.html +110 -0
  82. simplex/web/templates/deck.html +149 -0
  83. simplex/web/templates/index.html +20 -0
  84. simplex/web/templates/revealjs.html.j2 +374 -0
  85. simplex/web/templates/section.html +74 -0
  86. simplex/web/vendor.py +148 -0
  87. simplex_web-0.2.0.dist-info/METADATA +166 -0
  88. simplex_web-0.2.0.dist-info/RECORD +91 -0
  89. simplex_web-0.2.0.dist-info/WHEEL +4 -0
  90. simplex_web-0.2.0.dist-info/entry_points.txt +2 -0
  91. simplex_web-0.2.0.dist-info/licenses/LICENSE +21 -0
simplex/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # src/simplex
2
+
3
+ The `simplex` distribution's half of the namespace: deck discovery,
4
+ render pipeline, web portal, and CLI. The plugin half (`simplex.plugin`,
5
+ `simplex.engine`, `simplex.theme`, `simplex.slides`) is shipped by the
6
+ `manim-simplex` distribution and merged into this same namespace at
7
+ import time via PEP 420.
8
+
9
+ This directory ships **no** `__init__.py`. Don't add one -- it would
10
+ shadow the implicit namespace and break the `manim-simplex` half.
11
+
12
+ ## Public surface (this distribution)
13
+
14
+ - `simplex.deck` -- `DeckConfig`, `discover`, `scaffold`
15
+ - `simplex.render` -- `runner`, `reconcile`, `html`, `pdf`, `pptx`, `thumbnail`
16
+ - `simplex.web` -- `builder`, `notes`, bibliography stack, templates, static, SSE reload
17
+ - `simplex.cli.commands:app` -- the Typer app behind `uv run simplex`
18
+
19
+ ## Public surface (re-exported from `manim-simplex`)
20
+
21
+ - `simplex.plugin:activate` -- the `manim.plugins` entry-point (set `plugins = simplex` in your `manim.cfg`)
22
+ - `simplex.slides` -- `BaseSlide`, `make_chrome`
23
+ - `simplex.theme` -- `Theme`, `WebPalette`, `active_theme`, `get_active_theme`, `presets`, `render_web_css`
24
+ - `simplex.engine` -- `apply_theme_defaults`, `Region`, `Remove`, `register_exit`, `set_exit_animation`, `clear_scene`, `SimplexSectionType`
25
+
26
+ ## Don't
27
+
28
+ - Don't add `src/simplex/__init__.py`. The namespace must stay implicit.
29
+ - Don't import `manim_editor`. The legacy package is deprecated and not a dependency.
30
+ - Don't wrap Manim constructors. Authors write vanilla Manim; the framework configures defaults underneath via `Mobject.set_default(...)`.
31
+ - Don't add a custom quality enum. Use `manim.constants.QUALITIES` keys directly.
32
+ - Don't bypass the plugin entry-point by setting `manim.config` from your scene. The plugin runs once at import time; that's the correct seam.
simplex/cli/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # cli/
2
+
3
+ Typer-based command surface exposed as `simplex` via `[project.scripts]`.
4
+
5
+ ## Public surface
6
+
7
+ - `app` -- Typer application (entry point: `simplex.cli.commands:app`)
8
+ - Commands: `new`, `render`, `build`, `serve`, `clean`, `doctor`
9
+
10
+ ## Don't
11
+
12
+ - Don't add business logic here. Commands are thin shells over `deck`, `render`, and `web`.
13
+ - Don't `os.chdir` outside of `serve`; that command intentionally enters `site/` to use `http.server`.
@@ -0,0 +1,5 @@
1
+ """CLI entry point."""
2
+
3
+ from simplex.cli.commands import app
4
+
5
+ __all__ = ["app"]
@@ -0,0 +1,384 @@
1
+ """Simplex CLI -- new | init | render | build | serve | test | clean | doctor."""
2
+
3
+ import asyncio
4
+ import contextlib
5
+ import http.server
6
+ import shutil
7
+ import socketserver
8
+ import subprocess
9
+ import sys
10
+ import threading
11
+ from collections.abc import Iterable
12
+ from pathlib import Path
13
+ from typing import Annotated
14
+
15
+ import typer
16
+ from rich.console import Console
17
+
18
+ from simplex.deck.registry import discover
19
+ from simplex.deck.scaffold import scaffold as deck_scaffold
20
+ from simplex.render import pdf, runner
21
+ from simplex.web.builder import build as build_site
22
+ from simplex.web.site_config import SiteConfig
23
+
24
+ app = typer.Typer(help="Simplex -- Manim-slides framework with a generated portal.")
25
+ console = Console()
26
+
27
+ _DECKS = Path("decks")
28
+ _SITE = Path("site")
29
+
30
+ # Reload signaling for `simplex serve --watch`.
31
+ _RELOAD_EVENT = threading.Event()
32
+
33
+
34
+ @app.command()
35
+ def new(target: str) -> None:
36
+ """Scaffold a new deck.
37
+
38
+ ``simplex new <slug>`` creates ``decks/<slug>/`` (featured section).
39
+ ``simplex new <section>/<slug>`` creates ``decks/<section>/<slug>/``.
40
+ """
41
+ dest = deck_scaffold(target, _DECKS)
42
+ console.print(f"[green]Created[/green] {dest}")
43
+
44
+
45
+ @app.command()
46
+ def init(
47
+ target_dir: Annotated[
48
+ Path | None,
49
+ typer.Argument(
50
+ help="Directory to create. Default: prompt + git clone the template.",
51
+ ),
52
+ ] = None,
53
+ ) -> None:
54
+ """Scaffold a new lectures repo from ``shlomi-perles/simplex-lectures-template``.
55
+
56
+ Requires the ``gh`` CLI for full template integration; falls back to
57
+ ``git clone`` of the public template otherwise.
58
+ """
59
+ template_repo = "shlomi-perles/simplex-lectures-template"
60
+ if target_dir is None:
61
+ target_dir = Path(typer.prompt("New repo directory name"))
62
+ if target_dir.exists():
63
+ raise typer.BadParameter(f"{target_dir} already exists")
64
+
65
+ gh_path = shutil.which("gh")
66
+ if gh_path is not None:
67
+ repo_name = typer.prompt(
68
+ f"GitHub repo to create (default: {target_dir.name})",
69
+ default=target_dir.name,
70
+ )
71
+ subprocess.run(
72
+ [
73
+ gh_path,
74
+ "repo",
75
+ "create",
76
+ repo_name,
77
+ "--template",
78
+ template_repo,
79
+ "--clone",
80
+ "--private",
81
+ ],
82
+ check=True,
83
+ )
84
+ console.print(f"[green]Created[/green] {repo_name} from {template_repo}")
85
+ return
86
+
87
+ git_path = shutil.which("git")
88
+ if git_path is None:
89
+ raise typer.BadParameter("neither `gh` nor `git` is available on PATH")
90
+ subprocess.run(
91
+ [git_path, "clone", f"https://github.com/{template_repo}.git", str(target_dir)],
92
+ check=True,
93
+ )
94
+ shutil.rmtree(target_dir / ".git", ignore_errors=True)
95
+ console.print(
96
+ f"[green]Cloned[/green] template into {target_dir}. "
97
+ "Run `git init && git add . && git commit -m initial` inside it next."
98
+ )
99
+
100
+
101
+ @app.command()
102
+ def render(
103
+ target: str,
104
+ scene: Annotated[
105
+ list[str] | None,
106
+ typer.Option(
107
+ "--scene",
108
+ help="Re-render only this scene class. Repeatable.",
109
+ ),
110
+ ] = None,
111
+ ) -> None:
112
+ """Render a single deck.
113
+
114
+ Triple-syntax targets accepted:
115
+
116
+ - ``slug`` full deck
117
+ - ``slug::SceneClass`` one scene (alias for ``--scene SceneClass``)
118
+ - ``slug::SceneClass::MainName`` reserved; for now renders the whole scene
119
+ """
120
+ slug, _, scene_spec = target.partition("::")
121
+ site_cfg = SiteConfig.load()
122
+ registry = discover(_DECKS, default_section_order=site_cfg.default_section_order)
123
+ deck = registry.find_deck(slug)
124
+ if deck is None:
125
+ raise typer.BadParameter(f"unknown deck: {slug}")
126
+
127
+ scene_filter: tuple[str, ...] = tuple(scene or ())
128
+ if scene_spec:
129
+ scene_name, _, _main = scene_spec.partition("::")
130
+ scene_filter = (*scene_filter, scene_name)
131
+
132
+ out = _SITE / "decks" / deck.slug
133
+ out.mkdir(parents=True, exist_ok=True)
134
+
135
+ try:
136
+ runner.render(deck, output_dir=out, scenes=scene_filter)
137
+ except ValueError as exc:
138
+ raise typer.BadParameter(str(exc)) from exc
139
+
140
+ with contextlib.suppress(subprocess.SubprocessError, FileNotFoundError, ImportError):
141
+ pdf.export(deck, output_dir=out)
142
+
143
+ console.print(f"[green]Rendered[/green] {deck.slug} -> {out}")
144
+
145
+
146
+ @app.command()
147
+ def build(
148
+ only: Annotated[
149
+ list[str] | None,
150
+ typer.Option("--only", help="Only build this deck slug. Repeatable."),
151
+ ] = None,
152
+ scene: Annotated[
153
+ list[str] | None,
154
+ typer.Option(
155
+ "--scene",
156
+ help="Re-render only this scene class on every selected deck.",
157
+ ),
158
+ ] = None,
159
+ no_render: Annotated[
160
+ bool,
161
+ typer.Option("--no-render", help="Skip rendering; only rebuild HTML/portal."),
162
+ ] = False,
163
+ ) -> None:
164
+ """Build the full static portal under ``site/``."""
165
+ build_site(
166
+ _DECKS,
167
+ _SITE,
168
+ render=not no_render,
169
+ only=tuple(only or ()),
170
+ scenes=tuple(scene or ()),
171
+ )
172
+ console.print(f"[green]Built[/green] {_SITE}")
173
+
174
+
175
+ @app.command()
176
+ def test(
177
+ only: Annotated[
178
+ list[str] | None,
179
+ typer.Option("--only", help="Only test this deck slug. Repeatable."),
180
+ ] = None,
181
+ ) -> None:
182
+ """Smoke-render every deck by rendering only the first animation.
183
+
184
+ Used in CI: catches scene-construction errors without paying for full
185
+ video encoding. Exits non-zero on the first deck that fails to render.
186
+ """
187
+ site_cfg = SiteConfig.load()
188
+ registry = discover(_DECKS, default_section_order=site_cfg.default_section_order)
189
+ only_set = set(only or ())
190
+
191
+ failures: list[tuple[str, str]] = []
192
+ for section in registry.sections:
193
+ for deck in section.decks:
194
+ if only_set and deck.slug not in only_set:
195
+ continue
196
+ out = _SITE / "decks" / deck.slug
197
+ try:
198
+ runner.render(deck, output_dir=out, write_last_frame=True)
199
+ console.print(f"[green]ok[/green] {deck.slug}")
200
+ except (subprocess.CalledProcessError, ValueError) as exc:
201
+ failures.append((deck.slug, str(exc)))
202
+ console.print(f"[red]FAIL[/red] {deck.slug}: {exc}")
203
+
204
+ if failures:
205
+ raise typer.Exit(code=1)
206
+
207
+
208
+ @app.command()
209
+ def serve(
210
+ port: Annotated[int, typer.Option(help="Port to serve on.")] = 8000,
211
+ watch: Annotated[
212
+ bool,
213
+ typer.Option(
214
+ "--watch/--no-watch",
215
+ help="Watch decks/ + src/ and reload the browser on save.",
216
+ ),
217
+ ] = False,
218
+ ) -> None:
219
+ """Serve ``site/`` via the stdlib HTTP server.
220
+
221
+ With ``--watch``, also runs a watchfiles loop that re-runs the build on
222
+ every save and pushes an SSE event so open browser tabs reload.
223
+ """
224
+ if not _SITE.exists():
225
+ raise typer.BadParameter("site/ does not exist -- run `simplex build` first")
226
+
227
+ handler_cls = _make_handler(_SITE)
228
+ server = _SimplexTCPServer(("", port), handler_cls)
229
+ console.print(f"Serving http://localhost:{port}")
230
+
231
+ server_thread: threading.Thread | None = None
232
+ try:
233
+ if watch:
234
+ server_thread = threading.Thread(target=server.serve_forever, daemon=True)
235
+ server_thread.start()
236
+ asyncio.run(_watch_loop())
237
+ else:
238
+ server.serve_forever(poll_interval=0.2)
239
+ except KeyboardInterrupt:
240
+ console.print("\n[yellow]stopping[/yellow]")
241
+ finally:
242
+ if server_thread is not None:
243
+ server.shutdown()
244
+ server_thread.join(timeout=2)
245
+ server.server_close()
246
+
247
+
248
+ class _SimplexTCPServer(socketserver.ThreadingTCPServer):
249
+ allow_reuse_address = True
250
+ daemon_threads = True
251
+
252
+
253
+ def _make_handler(site_dir: Path) -> type[http.server.BaseHTTPRequestHandler]:
254
+ """Return a request handler that serves files + an SSE endpoint."""
255
+
256
+ class _Handler(http.server.SimpleHTTPRequestHandler):
257
+ def __init__(self, *args: object, **kwargs: object) -> None:
258
+ super().__init__(*args, directory=str(site_dir), **kwargs) # type: ignore[arg-type]
259
+
260
+ def do_GET(self) -> None:
261
+ if self.path == "/_simplex/events":
262
+ self.send_response(200)
263
+ self.send_header("Content-Type", "text/event-stream")
264
+ self.send_header("Cache-Control", "no-cache")
265
+ self.send_header("Connection", "keep-alive")
266
+ self.end_headers()
267
+ self._stream_events()
268
+ return
269
+ super().do_GET()
270
+
271
+ def _stream_events(self) -> None:
272
+ try:
273
+ while True:
274
+ if _RELOAD_EVENT.wait(timeout=30):
275
+ _RELOAD_EVENT.clear()
276
+ self.wfile.write(b"event: reload\ndata: 1\n\n")
277
+ else:
278
+ self.wfile.write(b": keepalive\n\n")
279
+ self.wfile.flush()
280
+ except BrokenPipeError:
281
+ pass
282
+ except ConnectionResetError:
283
+ pass
284
+
285
+ def log_message(self, format: str, *args: object) -> None:
286
+ pass # quiet by default
287
+
288
+ return _Handler
289
+
290
+
291
+ async def _watch_loop() -> None:
292
+ """Watchfiles + rebuild + broadcast SSE reload on every change."""
293
+ try:
294
+ from watchfiles import awatch
295
+ except ImportError as exc:
296
+ raise typer.BadParameter(
297
+ "watchfiles is required for --watch; install with `uv sync`"
298
+ ) from exc
299
+
300
+ targets = [p for p in (Path("decks"), Path("src") / "simplex") if p.exists()]
301
+ console.print(f"[yellow]watching[/yellow] {', '.join(str(t) for t in targets)}")
302
+
303
+ async for changes in awatch(*targets, debounce=200):
304
+ affected = sorted(_affected_deck_slugs(changes))
305
+ if not affected:
306
+ continue
307
+ console.print(f"[yellow]reload[/yellow] {', '.join(affected)}")
308
+ try:
309
+ build_site(_DECKS, _SITE, only=tuple(affected), watch=True)
310
+ _RELOAD_EVENT.set()
311
+ except Exception as exc:
312
+ console.print(f"[red]build failed[/red]: {exc}")
313
+
314
+
315
+ def _affected_deck_slugs(changes: Iterable[tuple[object, str]]) -> set[str]:
316
+ """Map watchfiles change paths to the deck slugs they affect.
317
+
318
+ A change under ``decks/<slug>/...`` affects that slug. A change under
319
+ ``src/simplex/...`` affects every deck (return empty -> caller rebuilds all).
320
+ """
321
+ slugs: set[str] = set()
322
+ src_changed = False
323
+ for _, path in changes:
324
+ parts = Path(path).resolve().relative_to(Path.cwd().resolve(), walk_up=True).parts
325
+ if len(parts) >= 2 and parts[0] == "decks":
326
+ slugs.add(parts[1])
327
+ elif len(parts) >= 2 and parts[0] == "src":
328
+ src_changed = True
329
+ if src_changed:
330
+ slugs = set() # signals "rebuild everything"
331
+ return slugs
332
+
333
+
334
+ @app.command()
335
+ def clean(
336
+ deck: Annotated[
337
+ list[str] | None,
338
+ typer.Option(
339
+ "--deck",
340
+ help="Only clean these deck slugs (site/decks/<slug>/).",
341
+ ),
342
+ ] = None,
343
+ ) -> None:
344
+ """Remove ``site/`` and ``media/``.
345
+
346
+ With ``--deck <slug>`` (repeatable), only that deck's output is removed.
347
+ """
348
+ if deck:
349
+ site_cfg = SiteConfig.load()
350
+ registry = discover(_DECKS, default_section_order=site_cfg.default_section_order)
351
+ for slug in deck:
352
+ d = registry.find_deck(slug)
353
+ if d is None:
354
+ raise typer.BadParameter(f"unknown deck: {slug}")
355
+ site_deck = _SITE / "decks" / d.slug
356
+ if site_deck.exists():
357
+ shutil.rmtree(site_deck)
358
+ console.print(f"removed {site_deck}")
359
+ console.print(f"[green]Cleaned[/green] {d.slug}")
360
+ return
361
+
362
+ for target in (_SITE, Path("media")):
363
+ if target.exists():
364
+ shutil.rmtree(target)
365
+ console.print(f"removed {target}")
366
+
367
+
368
+ @app.command()
369
+ def doctor() -> None:
370
+ """Verify required system binaries are reachable on PATH."""
371
+ required = ("latex", "ffmpeg", "manim", "manim-slides")
372
+ ok = True
373
+ for tool in required:
374
+ found = shutil.which(tool)
375
+ if found:
376
+ console.print(f"[green]ok[/green] {tool} -> {found}")
377
+ else:
378
+ console.print(f"[red]MISSING[/red] {tool}")
379
+ ok = False
380
+ sys.exit(0 if ok else 1)
381
+
382
+
383
+ if __name__ == "__main__":
384
+ app()
simplex/deck/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # deck/
2
+
3
+ Per-deck configuration, sectioned discovery, scaffolding.
4
+
5
+ ## Public surface
6
+
7
+ - `DeckConfig` -- frozen pydantic model loaded from `deck.toml`.
8
+ - `SectionConfig` -- frozen model loaded from `decks/<dir>/_section.toml`.
9
+ - `discover(decks_dir)` -- returns a `SectionedRegistry` (sections -> decks).
10
+ - `scaffold(target, decks_dir)` -- copies `_template/` into either
11
+ `decks/<slug>/` (featured) or `decks/<section>/<slug>/`.
12
+
13
+ ## Don't
14
+
15
+ - Don't load `deck.toml` outside of `DeckConfig.load`.
16
+ - Don't recurse deeper than one level -- decks live at `decks/<slug>/` or
17
+ `decks/<section>/<slug>/`, never below.
18
+ - Don't add fields without a default. Older decks must keep validating
19
+ after a config bump.
@@ -0,0 +1,7 @@
1
+ """Deck configuration, discovery, and scaffolder."""
2
+
3
+ from simplex.deck.config import DeckConfig
4
+ from simplex.deck.registry import discover
5
+ from simplex.deck.scaffold import scaffold
6
+
7
+ __all__ = ["DeckConfig", "discover", "scaffold"]
File without changes
File without changes
File without changes
@@ -0,0 +1,11 @@
1
+ slug = "__SLUG__"
2
+ title = "__TITLE__"
3
+ summary = "Replace with a one-line description."
4
+ tags = []
5
+ theme = "dastimator_dark"
6
+ quality = "high_quality"
7
+ voiceover = false
8
+ category = ""
9
+ created_at = "__CREATED_AT__"
10
+ order = 1000
11
+ entrypoints = ["slides.intro:Intro"]
@@ -0,0 +1,3 @@
1
+ [CLI]
2
+ plugins = simplex
3
+ save_sections = True
@@ -0,0 +1,27 @@
1
+ # Notes
2
+
3
+ This deck is a starting point. Replace the contents of `slides/` and fill these notes.
4
+
5
+ Refer to a slide inline with `[slide:1]` to add a clickable jump link.
6
+
7
+ Add a sidenote with the `^[...]` syntax — it floats into the right margin on wide screens.^[Sidenotes are Tufte-style: a numbered reference inline, the body in the gutter; narrow viewports collapse to an inline reveal on click.]
8
+
9
+ Inline math: $a^2 + b^2 = c^2$. Display math:
10
+
11
+ $$
12
+ \int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}
13
+ $$
14
+
15
+ Theorem / definition / lemma / remark / proof blockquotes turn into colour-coded callouts and get anchored ids automatically:
16
+
17
+ > **Theorem 1.1.** Every blockquote whose first paragraph begins with `**Theorem N.N.**` (or `Lemma`, `Definition`, `Remark`, `Proof`, …) becomes a referenceable callout.
18
+
19
+ Reference one by id with `\ref{theorem-1-1}` — the renderer resolves it to **Theorem 1.1** automatically.
20
+
21
+ Cite the bundled `refs.bib` with `\cite{key}` — the build appends a References section automatically (`\cite{KB15}` here).
22
+
23
+ Code blocks render with Pygments:
24
+
25
+ ```python
26
+ print("hello, simplex")
27
+ ```
@@ -0,0 +1,12 @@
1
+ % Bibliography for the deck. Cite in notes.md with `\cite{key}`.
2
+ % The web build (`simplex build`) parses this file, generates biblatex-style
3
+ % alpha labels (e.g. `[KB15]`), and appends a References section to the
4
+ % rendered notes page.
5
+
6
+ @inproceedings{KB15,
7
+ author = {Kingma, Diederik P. and Ba, Jimmy},
8
+ title = {Adam: A Method for Stochastic Optimization},
9
+ booktitle = {International Conference on Learning Representations (ICLR)},
10
+ year = {2015},
11
+ url = {http://arxiv.org/abs/1412.6980},
12
+ }
@@ -0,0 +1,7 @@
1
+ """Scene modules live alongside this file (e.g. `intro.py`).
2
+
3
+ Reference them from `deck.toml` as `entrypoints = ["slides.intro:Intro", ...]`;
4
+ the runner loads each entrypoint module directly. Don't re-export scene
5
+ classes here -- manim's discovery filters by `__module__`, so re-exports
6
+ are silently dropped.
7
+ """
@@ -0,0 +1,21 @@
1
+ """Template intro scene -- one slide with a title + subtitle."""
2
+
3
+ from manim import DOWN, ORIGIN, Tex, Write
4
+
5
+ from simplex.slides import BaseSlide
6
+ from simplex.theme.context import get_active_theme
7
+
8
+
9
+ class Intro(BaseSlide):
10
+ title: str = "Hello, Simplex"
11
+ subtitle: str = r"$f(x) = e^{i\pi} + 1 = 0$"
12
+
13
+ def construct(self) -> None:
14
+ theme = get_active_theme()
15
+ title_mob = Tex(self.title, font_size=theme.typography.h1)
16
+ self.region.place(title_mob, ORIGIN)
17
+
18
+ sub = Tex(self.subtitle, font_size=theme.typography.h2)
19
+ sub.next_to(title_mob, DOWN, buff=0.4)
20
+ self.play(Write(title_mob), Write(sub))
21
+ self.next_slide()