decktile 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 (46) hide show
  1. decktile/__init__.py +5 -0
  2. decktile/__main__.py +3 -0
  3. decktile/assets/lucide/VENDORED.md +21 -0
  4. decktile/assets/lucide/lucide.css +2193 -0
  5. decktile/assets/lucide/lucide.ttf +3 -0
  6. decktile/cli.py +269 -0
  7. decktile/parser/__init__.py +0 -0
  8. decktile/parser/ast.py +191 -0
  9. decktile/parser/markdown.py +289 -0
  10. decktile/plugins/cards.py +25 -0
  11. decktile/plugins/directives.py +56 -0
  12. decktile/plugins/fenced.py +228 -0
  13. decktile/plugins/layouts.py +69 -0
  14. decktile/plugins/lucide.py +65 -0
  15. decktile/plugins/lucide_font.py +26 -0
  16. decktile/render/__init__.py +0 -0
  17. decktile/render/html.py +287 -0
  18. decktile/render/ir.py +536 -0
  19. decktile/render/odf/__init__.py +4 -0
  20. decktile/render/odf/geometry.py +510 -0
  21. decktile/render/odf/shapes.py +719 -0
  22. decktile/render/odf/styles.py +828 -0
  23. decktile/render/odp.py +380 -0
  24. decktile/render/pdf.py +17 -0
  25. decktile/render/pdg.py +72 -0
  26. decktile/render/seaborn.py +155 -0
  27. decktile/render/templates/base.css +741 -0
  28. decktile/render/templates/decktile.js +371 -0
  29. decktile/render/templates/shell.html +106 -0
  30. decktile/seaborn_styles/__init__.py +5 -0
  31. decktile/seaborn_styles/ossummit_europe.py +30 -0
  32. decktile/theme/__init__.py +0 -0
  33. decktile/theme/colors.py +53 -0
  34. decktile/theme/loader.py +22 -0
  35. decktile/theme/parser.py +264 -0
  36. decktile/themes/default.css +146 -0
  37. decktile/themes/kcd_vietnam.css +159 -0
  38. decktile/themes/kubecon_china.css +213 -0
  39. decktile/themes/kubecon_japan.css +156 -0
  40. decktile/themes/ossummit_europe.css +196 -0
  41. decktile/themes/ossummit_korea.css +196 -0
  42. decktile-0.2.0.dist-info/METADATA +625 -0
  43. decktile-0.2.0.dist-info/RECORD +46 -0
  44. decktile-0.2.0.dist-info/WHEEL +4 -0
  45. decktile-0.2.0.dist-info/entry_points.txt +6 -0
  46. decktile-0.2.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,625 @@
1
+ Metadata-Version: 2.1
2
+ Name: decktile
3
+ Version: 0.2.0
4
+ Summary: Markdown to designed slides: HTML, PDF and editable ODP
5
+ Author-Email: Reza Jelveh <git@jelveh.me>
6
+ License: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/decktile/decktile
8
+ Project-URL: Source, https://github.com/decktile/decktile
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: markdown-it-py>=4.2.0
11
+ Requires-Dist: python-frontmatter>=1.3.0
12
+ Requires-Dist: weasyprint<70,>=69.0
13
+ Requires-Dist: jinja2>=3.1.6
14
+ Requires-Dist: typer>=0.26.8
15
+ Requires-Dist: linkify-it-py>=2.1.0
16
+ Requires-Dist: mdit-py-plugins>=0.6.1
17
+ Requires-Dist: python-pptx>=1.0.2
18
+ Requires-Dist: tinycss2>=1.5.1
19
+ Requires-Dist: pygments-styles>=0.3.0
20
+ Requires-Dist: odfdo>=3.27.2
21
+ Requires-Dist: mermaidx>=0.8.1
22
+ Requires-Dist: python-lucide>=0.5.0
23
+ Requires-Dist: predictygraph>=0.1.0
24
+ Provides-Extra: docs
25
+ Requires-Dist: sphinx>=9.0.4; extra == "docs"
26
+ Requires-Dist: furo>=2025.12.19; extra == "docs"
27
+ Requires-Dist: myst-parser>=5.1.0; extra == "docs"
28
+ Description-Content-Type: text/markdown
29
+
30
+ # Decktile
31
+
32
+ Markdown to styled HTML slides with PDF and ODP output.
33
+
34
+ ## Why
35
+
36
+ Building a slide deck in PowerPoint is tedious. Writing markdown is fast.
37
+ Decktile turns markdown into slides. Content lives in markdown files. Styling
38
+ lives in CSS. No `.pptx` editing, no inline HTML, no copy-paste hell.
39
+
40
+ Other tools (Marp, Slidev) mix HTML and CSS into the markdown. This breaks
41
+ separation of concerns - your slides become `<div>` soup that's hard to
42
+ change later. Decktile keeps them apart: directives (`@kicker`, `@layout`,
43
+ `::: card`) are semantic; all styling is in theme CSS files.
44
+
45
+ Decktile is opinionated. It ships with card layouts, accent-colored quotes,
46
+ `▸` bullet markers, dark mode. You theme it via CSS variables. If you want
47
+ pixel-level custom layouts, use raw HTML. If you want a deck that looks
48
+ good out of the box with minimal markup, this is the one.
49
+
50
+ ## Frontmatter
51
+
52
+ ```yaml
53
+ ---
54
+ title: My Talk
55
+ theme: dynamia # theme name (default, dynamia, kubecon_japan)
56
+ variant: dark # global dark mode
57
+ logo: assets/logo.svg # top-right logo (replaces theme default)
58
+ logo_dark: assets/logo-white.png # dark mode variant of logo
59
+ watermark: assets/brand-mark.svg # bottom-right watermark (conference logo); none = off
60
+ background: none # slide background image (replaces theme default); none = off
61
+ style: | # raw CSS override
62
+ :root { --color-accent: #e91e63; }
63
+ ---
64
+ ```
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ pip install decktile # or: uv tool install decktile
70
+ ```
71
+
72
+ HTML output works everywhere. PDF and ODP output use WeasyPrint, which needs Pango
73
+ from your system:
74
+
75
+ - **Linux:** usually present; otherwise `apt install libpango-1.0-0 libpangoft2-1.0-0`
76
+ - **macOS:** `brew install pango`
77
+ - **Windows:** install [MSYS2](https://www.msys2.org/), run
78
+ `pacman -S mingw-w64-ucrt-x86_64-pango`, then set
79
+ `WEASYPRINT_DLL_DIRECTORIES=C:\msys64\ucrt64\bin`
80
+
81
+ See [WeasyPrint's install guide](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html)
82
+ for details. From a checkout:
83
+
84
+ ```bash
85
+ pdm install # core + HTML/PDF/ODP
86
+ pdm install -G plot # + seaborn/matplotlib for inline charts
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ ```bash
92
+ pdm run decktile slides.md # HTML + presenter view
93
+ pdm run decktile slides.md --odp # + ODP (programmatic)
94
+ pdm run decktile slides.md --image-odp # + ODP (screenshots from PDF)
95
+ pdm run decktile slides.md --pdf # + PDF
96
+ pdm run decktile -w slides.md # watch and rebuild on changes
97
+ pdm run decktile --odp -w slides.md # watch + ODP
98
+ pdm run decktile --dist slides.md # HTML + all referenced assets as zip
99
+ pdm run decktile a.md b.md --pdf # several decks
100
+ pdm run decktile '**/*.md' --pdf # quoted globs are expanded (recursive with **)
101
+ ```
102
+
103
+ A missing or failing file is reported (`error: <file>: <reason>`) and skipped;
104
+ the rest still build, and the exit code is 1 if any file failed.
105
+
106
+ ## Assets
107
+
108
+ Image paths in markdown are relative to the source file. On build, decktile
109
+ symlinks all directories from the source folder into `dist/`. Files and
110
+ `.md` files are skipped, only directories are linked.
111
+
112
+ ```bash
113
+ pdm run decktile slides.md # symlinks slides/assets/ → slides/dist/assets/
114
+ ```
115
+
116
+ `--odp` is the production-ready ODP path: a native ODF renderer (styles.py,
117
+ geometry.py, shapes.py) that reproduces the HTML layout, colors, fonts, and
118
+ positioning directly in `content.xml`/`styles.xml` - no PDF roundtrip. Cards,
119
+ grids, tables, lists, quotes, speaker notes (from `<!-- comment -->` blocks),
120
+ and lucide icons (rendered as colored glyphs from an embedded icon font) all
121
+ map to real ODF shapes and styles, editable in LibreOffice Impress.
122
+
123
+ `--image-odp` remains available as a pixel-perfect fallback (PDF screenshot
124
+ per slide embedded as an image) for decks the native renderer doesn't handle
125
+ well yet.
126
+
127
+ If you know the ODF spec or `odfdo` internals, contributions to the native ODP
128
+ renderer are welcome. See `src/decktile/render/odp.py` and `src/decktile/render/odf/`.
129
+
130
+ An Obsidian plugin that renders `.md` slides to HTML in-pane would be great.
131
+ The Python CLI makes distribution tricky; if you've tackled that problem
132
+ before, reach out.
133
+
134
+ Most of this codebase is AI-generated. I've kept it DRY where I could, but
135
+ there's still duplication - the lucide icon rendering appears in a couple
136
+ places that should be one. PRs that consolidate repeated logic are welcome.
137
+
138
+ ## Viewer controls
139
+
140
+ | Action | Key / Mouse |
141
+ |--------|-------------|
142
+ | Next slide | Left click (on slide area), Right arrow, Down arrow, PgDn, Space |
143
+ | Previous slide | Right click, Left arrow, Up arrow, PgUp, Backspace |
144
+ | First slide | Home |
145
+ | Last slide | End |
146
+ | Toggle fullscreen | `f` |
147
+ | Open presenter view | Presenter button, `p` |
148
+ | Close presenter | `q` |
149
+
150
+ ## Slide directives
151
+
152
+ ```
153
+ @kicker text # title slide eyebrow
154
+ @subtitle text # title slide subtitle
155
+ @speaker name=X role=Y<br> github=Z twitter=Z email=Z # title slide attribution with social links
156
+ @layout name # apply a slide layout
157
+ @col # explicit column break in two-col / compare layouts
158
+ @row # horizontal row within a column (side by side)
159
+ @tiny text # small annotation below content
160
+ @variant dark # switch to dark mode for this slide
161
+ @hidden # exclude slide from output (alias: @hide)
162
+ {icon:star} # inline lucide icon, e.g. {icon:heart stroke=#d05a39}
163
+ ```
164
+
165
+ ## Layouts
166
+
167
+ | Layout | Usage |
168
+ |--------|-------|
169
+ | `@layout two-col` | Heading full-width, content split 50/50. Use `@col` for explicit break. |
170
+ | `@layout image-right` | Heading full-width, text left, image right |
171
+ | `@layout image-left` | Heading full-width, image left, text right |
172
+ | `@layout compare` | Two cards side-by-side with an arrow connector, conclusion notes below |
173
+ | `@layout metrics-N` | 2-4 metric cards, auto-detected from `::: card {metric}` blocks |
174
+ | `@layout ecosystem` | Compact stacked layout for partner/device logos. Small labels, uniform image sizing |
175
+ | `@layout timeline-vertical` | Cards alternate left/right of a central vertical spine, connected by stubs |
176
+ | `@layout timeline-horizontal` | Cards alternate above/below a central horizontal spine |
177
+ | Custom | `@layout <name>` adds CSS class `layout-<name>`, style via frontmatter `style:` block |
178
+
179
+ ### Compare layout
180
+
181
+ ```markdown
182
+ @layout compare
183
+
184
+ ## Before & After
185
+
186
+ ::: card{ tag="red" }
187
+ ### Without HAMi
188
+
189
+ GPU utilization at 65%, manual bin-packing required.
190
+ :::
191
+
192
+ ::: arrow
193
+
194
+ :::
195
+
196
+ ::: card{ tag="green" }
197
+ ### With HAMi
198
+
199
+ GPU utilization at 92%, zero manual intervention.
200
+ :::
201
+
202
+ ::: notes{ tag="green" }
203
+ > HAMi is the only CNCF project providing hardware-level GPU sharing.
204
+ :::
205
+ ```
206
+
207
+ ### Metrics layout
208
+
209
+ Key metrics display 2-4 cards with a large number and supporting label. Auto-detected from consecutive `::: card {metric}` blocks. No explicit `@layout` needed.
210
+
211
+ ```markdown
212
+ ## Key Metrics
213
+
214
+ ::: card {metric}
215
+ 10x
216
+ Operational cost improvement
217
+ :::
218
+
219
+ ::: card {metric}
220
+ 50%
221
+ GPU utilization
222
+ :::
223
+
224
+ ::: card {metric}
225
+ 10x
226
+ Density improvement
227
+ :::
228
+ ```
229
+
230
+ Each `::: card {metric}` block: first line is the big number, following lines are the label.
231
+ Styled with accent-colored large text (`2.8em`, bold) and dimmed label text.
232
+ Grid auto-centers vertically in the slide body.
233
+
234
+ ### Timeline layouts
235
+
236
+ Cards alternate sides of a central spine -- left/right for `timeline-vertical`,
237
+ above/below for `timeline-horizontal`. The spine spans the full available
238
+ height/width; `justify-content: space-evenly` auto-spaces however many
239
+ milestones there are along it -- no per-count tuning needed for a
240
+ *reasonable* number of items. `::: card {focus}` scales an item up and
241
+ highlights it. `::: card {label="Q1"}` prints a short name directly on the
242
+ spine next to that card's own dot, separate from the card's fuller `###`
243
+ header. No explicit `cols=` needed beyond matching the layout's flow
244
+ (`cols=1` for vertical, one row for horizontal).
245
+
246
+ **No automatic collision avoidance beyond that.** `space-evenly` only
247
+ distributes *leftover* space -- once total card height (vertical) or width
248
+ (horizontal) exceeds the spine's fixed length, flexbox has no fallback
249
+ to shrink text, and cards start genuinely overlapping. Keep it to
250
+ **5-6 items per timeline slide**; past that, split into multiple slides.
251
+
252
+ ```markdown
253
+ @layout timeline-vertical
254
+
255
+ ## Project Roadmap
256
+
257
+ ::: grid {cols=1}
258
+ ::: card
259
+ ### Q1 2026
260
+ Kickoff and planning.
261
+ :::
262
+ ::: card {focus}
263
+ ### Q2 2026
264
+ Major public launch.
265
+ :::
266
+ ::: card
267
+ ### Q3 2026
268
+ Scale and iterate.
269
+ :::
270
+ ::: card
271
+ ### Q4 2026
272
+ Expand to new markets.
273
+ :::
274
+ :::
275
+ ```
276
+
277
+ ```markdown
278
+ @layout timeline-horizontal
279
+
280
+ ## Release Timeline
281
+
282
+ ::: grid {cols=4}
283
+ ::: card
284
+ ### v1.0
285
+ Initial release
286
+ :::
287
+ ::: card {focus}
288
+ ### v2.0
289
+ Big feature launch
290
+ :::
291
+ ::: card
292
+ ### v2.1
293
+ Bugfixes
294
+ :::
295
+ ::: card
296
+ ### v3.0
297
+ Next major
298
+ :::
299
+ :::
300
+ ```
301
+
302
+ `timeline-horizontal` cards have a fixed height (keep text brief -- a
303
+ title plus one short line) so the connector stub reliably reaches the
304
+ spine regardless of content length.
305
+
306
+ ### Section labels and side images
307
+
308
+ Ecosystem slides use `####` (h4) for section labels above grids, and
309
+ `::: card {side-image}` for images beside content without affecting row sizing:
310
+
311
+ ```markdown
312
+ #### Ecosystem & Device Support
313
+
314
+ ::: grid {cols=2}
315
+ ::: card
316
+ ![NVIDIA](devices/nvidia.png) ...
317
+ :::
318
+ ::: card {side-image}
319
+ ![GitHub QR](ecosystem/github-qr.png)
320
+ :::
321
+ :::
322
+ ```
323
+
324
+ ### Compare layout
325
+
326
+ The arrow block accepts text or images:
327
+ ```
328
+ ::: arrow
329
+ ⚠
330
+ :::
331
+
332
+ ::: arrow
333
+ {icon:arrow-right size=24}
334
+ :::
335
+ ```
336
+
337
+ ## Fenced blocks
338
+
339
+ ```
340
+ ::: grid {cols=2} # responsive grid
341
+ ::: grid {cols=3} # 3-column grid
342
+ ::: card # basic card
343
+ ::: card{ tag="green" } # colored left border + background
344
+ ::: card{ tag="quote" } # accent left border, italic, no fill
345
+ ::: card {metric} # big number + label card (first line = value, rest = label)
346
+ ::: card {blueprint} # blue engineering-drawing panel; a ```dot inside uses the blueprint palette
347
+ ::: arrow # connector for compare layout
348
+ ::: notes{ tag="green" } # full-width conclusion card
349
+ > quote text # blockquote, renders as .quote div
350
+ | col1 | col2 | # pipe table
351
+ `inline code` # inline code
352
+ ```language # fenced code block with syntax highlighting
353
+ ```mermaid # Mermaid diagram, inline SVG
354
+ ```seaborn # Seaborn chart, inline SVG
355
+ ```pdg # predictygraph diagram, inline SVG
356
+ ```
357
+
358
+ Card/grid `{...}` attributes: bare words are CSS classes (`{metric}` → `.card.metric`),
359
+ `k=v` pairs produce `k-v` classes (`{tag=green}` → `.card.tag-green`). Grid
360
+ also accepts `cols=N` and `class=name` literally.
361
+
362
+ ## Lucide icons
363
+
364
+ Use `{icon:star}` anywhere inline - headings, paragraphs, tables, card
365
+ headers, card body, arrows, notes. Powered by `python-lucide`.
366
+
367
+ ```
368
+ {icon:star} # default 1em height
369
+ {icon:check cls=accent-primary} # uses theme accent via CSS class
370
+ {icon:heart cls=accent-secondary size=1.2em} # class + explicit size
371
+ {icon:arrow-right size=32} # pixel size
372
+ ```
373
+
374
+ Parameters: `stroke`, `fill`, `width`, `height`, `size` (sets both), `cls` (CSS classes).
375
+
376
+ Use `cls=accent-primary`, `cls=accent-secondary`, or `cls=accent-contrast`
377
+ to match theme accent colors instead of hardcoded hex values.
378
+
379
+ No configuration needed - `pdm install` includes the dependency.
380
+
381
+ ## Diagrams (predictygraph)
382
+
383
+ ` ```pdg ` blocks are drawn with [predictygraph](https://pypi.org/project/predictygraph/),
384
+ installed with decktile. Nothing else to install. Node classes match decktile tag colours:
385
+
386
+ ````markdown
387
+ ```pdg
388
+ graph dir=LR
389
+ app "Application" .green
390
+ db "Database" .cyan shape=cylinder
391
+ app -> db
392
+ ```
393
+ ````
394
+
395
+ Available node classes: `green`, `cyan`, `yellow`, `red`. The diagram uses the
396
+ slide's body font. Put a `css { … }` block inside the diagram to change anything.
397
+ See predictygraph's [spec](https://github.com/predictygraph/predictygraph/blob/master/docs/SPEC.md)
398
+ for the full syntax.
399
+
400
+ Wrap a diagram in `::: card {blueprint}` for an engineering-drawing look:
401
+ blue grid card, light lines and text, translucent node fills, with the
402
+ node classes as brighter accents. Works the same under any theme.
403
+
404
+ ````markdown
405
+ ::: card {blueprint}
406
+ ```pdg
407
+ graph dir=LR
408
+ pod "Pod" .red
409
+ sched "hami-scheduler" .cyan
410
+ pod -> sched
411
+ ```
412
+ :::
413
+ ````
414
+
415
+ ## Mermaid diagrams
416
+
417
+ ````markdown
418
+ ```mermaid
419
+ graph LR
420
+ User((User)) --> API[API Gateway]
421
+ API --> DB[(Database)]
422
+ ```
423
+ ````
424
+
425
+ Renders inline SVG in HTML, PDF in ODP. Uses the `mermaidx` Python package.
426
+
427
+ ## Seaborn charts
428
+
429
+ ````markdown
430
+ ```seaborn
431
+ tips = sns.load_dataset("tips")
432
+ sns.scatterplot(data=tips, x="total_bill", y="tip", hue="day")
433
+ ```
434
+ ````
435
+
436
+ Runs Python in-process, renders inline SVG. Requires `pdm install -G plot`.
437
+ Pre-imported: `sns`, `plt`, `pd`, `np`. Set `seaborn_theme: kcd_vietnam` in
438
+ frontmatter to apply brand colors from the matching CSS theme file. Colors
439
+ and fonts are parsed from the `:root` block. Falls
440
+ back to seaborn palette names (`Paired` is the default, also `deep`,
441
+ `muted`, `pastel`, etc.) if no theme CSS matches.
442
+
443
+ ### Custom charts with matplotlib
444
+
445
+ For charts beyond seaborn's high-level API, drop into matplotlib directly.
446
+ Theme colors are available through rcParams -- no hex codes needed:
447
+
448
+ | rcParam | Maps to |
449
+ |---------|---------|
450
+ | `plt.rcParams["axes.facecolor"]` | Card background |
451
+ | `plt.rcParams["axes.edgecolor"]` | Primary accent |
452
+ | `plt.rcParams["text.color"]` | Foreground text |
453
+ | `plt.rcParams["xtick.color"]` | Dimmed/muted text |
454
+ | `"C0"` … `"C5"` | Theme palette (Paired-derived brand colors) |
455
+
456
+ For a slide-style figure background with a clear plot area:
457
+
458
+ ```python
459
+ fig, ax = plt.subplots()
460
+ ax.set_facecolor("none")
461
+ fig.patch.set_facecolor(plt.rcParams["axes.facecolor"])
462
+ ```
463
+
464
+ For semantic colors use `plt.get_cmap("Paired")` and sample by position:
465
+
466
+ ```python
467
+ cmap = plt.get_cmap("Paired")
468
+ danger = cmap(4.5 / 12) # muted red
469
+ ok = cmap(2.5 / 12) # green
470
+ neutral = cmap(0.5 / 12) # light blue
471
+ ```
472
+
473
+ See `examples/seaborn_demo.md` for the memory oversubscription chart
474
+ (horizontal stacked bars, limit markers, annotations).
475
+
476
+ ## Layout caveats
477
+
478
+ `@col` overrides auto-detection in all layouts. Use it when auto-split
479
+ puts content in the wrong column.
480
+
481
+ ## Slide transitions
482
+
483
+ Set a default in frontmatter or override per-slide:
484
+
485
+ ```yaml
486
+ ---
487
+ transition: fade
488
+ ---
489
+ ```
490
+
491
+ ```markdown
492
+ @transition slide
493
+ ```
494
+
495
+ | Transition | Effect |
496
+ |------------|--------|
497
+ | `fade` | Opacity 0 → 1 |
498
+ | `slide` | TranslateX + fade |
499
+ | `zoom` | Scale .85 + fade |
500
+ | `wipe` | Clip-path reveal |
501
+
502
+ All transitions run 0.4s, forwards-only (incoming slide animates).
503
+
504
+ ## Dark mode
505
+
506
+ Set `variant: dark` in frontmatter for all slides, or `@variant dark`
507
+ per slide:
508
+
509
+ ```yaml
510
+ ---
511
+ title: My Talk
512
+ variant: dark
513
+ ---
514
+ ```
515
+
516
+ ```markdown
517
+ @variant dark
518
+
519
+ ## This slide uses the dark theme
520
+ ```
521
+
522
+ Per-slide overrides work as slideshow transitions: `@variant light` switches
523
+ back to light mode on the next slide.
524
+
525
+ ## Theming
526
+
527
+ Colors, borders, and spacing use CSS custom properties. Override them via
528
+ frontmatter `style:` block or a custom theme file. See [THEMING.md](docs/THEMING.md)
529
+ for the full variable reference.
530
+
531
+ ```yaml
532
+ ---
533
+ style: |
534
+ :root {
535
+ --color-accent: #e91e63;
536
+ }
537
+ ---
538
+ ```
539
+
540
+ ## Speaker notes
541
+
542
+ Any HTML comment in a slide becomes speaker notes in the presenter view:
543
+
544
+ ```markdown
545
+ ---
546
+
547
+ <!--
548
+ These are speaker notes.
549
+ They appear in the presenter view.
550
+ -->
551
+ ```
552
+
553
+ ## Demo
554
+
555
+ `examples/features_demo.md` is a 17-slide deck exercising every feature:
556
+ title slides, `@layout two-col`, `@layout image-right`, `@layout compare`,
557
+ grids with tagged cards, tables, fenced code blocks, mermaid diagrams,
558
+ seaborn charts, predictygraph diagrams, lucide icons, blockquotes, speaker notes,
559
+ and all directives.
560
+
561
+ See also: `examples/mermaid_demo.md`, `examples/seaborn_demo.md`,
562
+ `examples/pdg_demo.md`, `examples/lucide_demo.md`, `examples/timeline_demo.md`.
563
+
564
+ ## Pipeline
565
+
566
+ ```
567
+ slides.md
568
+ → markdown-it-py (parse)
569
+ → Document AST (headings, paragraphs, grids, cards, tables)
570
+ → build_ir() (resolve theme styles via tinycss2)
571
+ → SlideIR (font_size, color, accent, SVG/PDF, + rendered HTML)
572
+ ↙ ↘ ↘
573
+ html.py odp.py pdf.py
574
+ (_render_elem) (_render_elem) (weasyprint)
575
+ ↘
576
+ image_odp (pdftoppm → PNG → ODP)
577
+ ```
578
+
579
+ The IR is the single source of truth between renderers. Each `Elem` carries
580
+ pre-rendered inline HTML (for the browser) plus resolved style properties
581
+ (font_size, color, accent, muted) for the ODP renderer. Seaborn and mermaid
582
+ SVGs are generated at IR build time and shared across renderers.
583
+
584
+ ## Architecture decisions
585
+
586
+ ### Why Python over Go
587
+
588
+ Go has no ODP library. No weasyprint equivalent. Goldmark works but its
589
+ plugin ecosystem is thin, and Go's compile cycle adds friction to design
590
+ work where you rebuild after every 2px change.
591
+
592
+ ### Why Python over Rust
593
+
594
+ Rust comes closer than Go: pulldown-cmark handles markdown well, `cssparser`
595
+ does proper CSS parsing. But no odfdo equivalent, no weasyprint. Syntect
596
+ covers fewer languages than Pygments for syntax highlighting.
597
+
598
+ ### Why not Node
599
+
600
+ Node slide tools ship a dev server, a bundler, a hot-reload daemon, and
601
+ usually Electron just to render markdown to `<div>` tags. Every dependency
602
+ adds a maintenance burden and expands the security surface. Python needs
603
+ weasyprint and odfdo. That's the stack.
604
+
605
+ ### The Python sweet spot
606
+
607
+ - **odfdo**: ODP generation via OpenDocument XML
608
+ - **weasyprint**: HTML/CSS to PDF via embedded layout engine, no browser dependency
609
+ - **markdown-it-py**: same parser as the JS ecosystem, GFM support
610
+ - **Pygments**: comprehensive syntax highlighting for code blocks
611
+ - **Jinja2**: mature templating, CSS injection, template includes
612
+
613
+ Deployable as a single binary via PyInstaller or Nuitka when needed.
614
+
615
+ ## License
616
+
617
+ GPL-3.0-or-later. See [LICENSE](LICENSE).
618
+
619
+ ## Related projects
620
+
621
+ - [Marp](https://marp.app/) - Markdown to slides, JS/Electron, extensive ecosystem
622
+ - [Slidev](https://sli.dev/) - Markdown to slides, Vue/Node, presenter mode, rich theming
623
+ - [reveal.js](https://revealjs.com/) - HTML presentation framework, JS
624
+ - [landslide](https://github.com/adamzap/landslide) - Markdown to slides, Python, dormant
625
+ - [lookatme](https://github.com/d0c-s4vage/lookatme) - Terminal markdown presentations, Python
@@ -0,0 +1,46 @@
1
+ decktile-0.2.0.dist-info/METADATA,sha256=dOBTErA4G_rV3qN6jo4Lq5gVZkNzBgzsM9gNEliDthc,19489
2
+ decktile-0.2.0.dist-info/WHEEL,sha256=IuIk3wbaEnFH1cx75iM2R1CmliRI3dFLXTbRakgvgFQ,90
3
+ decktile-0.2.0.dist-info/entry_points.txt,sha256=tNyeH9mA_5Htys6B8ZtsWcm9JSIqf9lpftOWwjtWPJs,87
4
+ decktile-0.2.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
5
+ decktile/__init__.py,sha256=hyjZADKHKhDNNcNez13PYByRQOunC_Ch1ZMWmIXRy24,98
6
+ decktile/__main__.py,sha256=Klpm1GwP4RXspy3JuDvb7gziJ8maDjxNNn85_luZTUw,56
7
+ decktile/assets/lucide/VENDORED.md,sha256=VC3qj6SzLunB7CzKisOrQZW3YHmy5K0dfLrwlW0EHeU,917
8
+ decktile/assets/lucide/lucide.css,sha256=pF9IoI61Bcwk_iPVnsXBg3UXzs0H8jGk-QY3Cg2tUw8,105919
9
+ decktile/assets/lucide/lucide.ttf,sha256=TzS_7Rorp0BNzP6LHmtOXwVPjlO7wSBxWM-kr2Ag8Gw,131
10
+ decktile/cli.py,sha256=7n-eC-Cs4CD8_oCdmCNTvH0wrXows7p7G6W3MQ_AgWw,10501
11
+ decktile/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ decktile/parser/ast.py,sha256=OXJWvJDfYxuGAx_d2vk-PljcFnFt2ZAAqr0RMXOYTPY,3852
13
+ decktile/parser/markdown.py,sha256=GpHWdcEDY50vq_3u1cURexKwkFxxNzyhxTmSlBVx_nA,10704
14
+ decktile/plugins/cards.py,sha256=p86Uunoti59B5krZE50P-Flq738Dt14p9eZvx1-pVks,759
15
+ decktile/plugins/directives.py,sha256=efCCcnT2WqjFjcS2oirjV6KzG0oHcVpz8i6cnksfQL0,2025
16
+ decktile/plugins/fenced.py,sha256=sCz0RojZUS8Al-Bc64pTyT7NlLwTtLAzdC8nge4YNTI,7841
17
+ decktile/plugins/layouts.py,sha256=L_VNS-U_tJ9AmNIUxSFbxtorGv8IC8udYVvp4hkCSq4,2707
18
+ decktile/plugins/lucide.py,sha256=q5m-8u4qft1j-fdrhA7U75N_zxYwAToGyN1FCglWUF0,2023
19
+ decktile/plugins/lucide_font.py,sha256=wWGS64_hoqvH7O0bmXN-5OA-JS_1cOgKfqtePnKfNdE,853
20
+ decktile/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ decktile/render/html.py,sha256=rx36vr1dRuSlAxBocpo_D-K16HAi20FwyUVaxvPbBSg,11497
22
+ decktile/render/ir.py,sha256=yP31cCXOr47blhd4yjfjLN9AirjhGhJh-6kjeBZdzPY,21897
23
+ decktile/render/odf/__init__.py,sha256=Q-UVlWlrN6gxwf6xb0PNNIC6D6TG2PZSXx4wFrdoEgc,169
24
+ decktile/render/odf/geometry.py,sha256=xYsCjYS_yVIF9yXW3Y-mMZwqmXi5m19l7dUIV5qN1f4,21362
25
+ decktile/render/odf/shapes.py,sha256=c78HdIWUdvVvlIUWN2jeW2YzcUx8pm-gMjEAyt5M97g,28360
26
+ decktile/render/odf/styles.py,sha256=CQ9VzOaAcaAhtS7Nps4GsciMbdTs6aUOz2TUXzQeVzI,39041
27
+ decktile/render/odp.py,sha256=bBVhf9tRW1-9tmtwk023-FAlyndmr-pG0wFo5x4fO1c,17152
28
+ decktile/render/pdf.py,sha256=cFqOfOzZZIzTdyS4SAQd9-RahRgCkbcsRjhRO7Ofic0,525
29
+ decktile/render/pdg.py,sha256=vqQ0rY792BuNqTun8HDROi5-JwRCUD43jVgym-p856w,2740
30
+ decktile/render/seaborn.py,sha256=VZmzFpa0egGNA0VKm90s1mzfWGE630jA7LVeNH1NG6Y,5106
31
+ decktile/render/templates/base.css,sha256=iLI9wW56YsrVpgcyaCnpeezZot_Q8KGsDhBOQlEk42I,22150
32
+ decktile/render/templates/decktile.js,sha256=0maEoTxFLBZGDF0bsDsUlWL7tbBBlMV7BLsUXDwJAMA,13949
33
+ decktile/render/templates/shell.html,sha256=AhLiJYijXItgPojl4MSmPtqkt26-P7zveUktbytOljs,4264
34
+ decktile/seaborn_styles/__init__.py,sha256=ABL9O8LVQt6xh_orer9sb1rsw0f9M2UKG74rwlUtP_I,201
35
+ decktile/seaborn_styles/ossummit_europe.py,sha256=KlhBVaYnCvtpERL-YX89um0p2b1Wr5ayDffsW85I7zk,905
36
+ decktile/theme/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ decktile/theme/colors.py,sha256=MBnFPSYVzu_-B2P9MvK-mE5sAUB7YYQtRvjdG4YSU2w,1629
38
+ decktile/theme/loader.py,sha256=TJM54P0vKtoL6DkrBi6QfbNEAln4A4i2AHsrO2pyZCY,580
39
+ decktile/theme/parser.py,sha256=NUOJTXmcUaGPyRzTZV0SRcFHTQVe5iPtdxUKxZZ1tSs,10441
40
+ decktile/themes/default.css,sha256=F2TjCeZ80j9Lbd7V4s5rzYaDb0wTQ4rmfZ1ORg-pjsM,4906
41
+ decktile/themes/kcd_vietnam.css,sha256=50WHD0baQ3mJrqHX502wEsjZblK39q8iVEq2TIMluwQ,5341
42
+ decktile/themes/kubecon_china.css,sha256=yQuU250ms1eLhaOwP9dz_pvVAl9AksUn8KYvC2WnwlY,6902
43
+ decktile/themes/kubecon_japan.css,sha256=oywP8TmyMtg6pqwC20pr9hzbXvIdFW--WmmQBJEZeGY,5298
44
+ decktile/themes/ossummit_europe.css,sha256=ShpSEnmuO76xhgctRRoo72UVXiEhFFMX9yX5_0-5WAA,6329
45
+ decktile/themes/ossummit_korea.css,sha256=cb-EvD-TkpZzzW98bNTT9b89vqf_TmTfBk0myBfd-AA,6222
46
+ decktile-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: pdm-backend (2.5.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ decktile = decktile.cli:app
3
+ slidr = decktile.cli:app
4
+
5
+ [gui_scripts]
6
+