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.
- decktile/__init__.py +5 -0
- decktile/__main__.py +3 -0
- decktile/assets/lucide/VENDORED.md +21 -0
- decktile/assets/lucide/lucide.css +2193 -0
- decktile/assets/lucide/lucide.ttf +3 -0
- decktile/cli.py +269 -0
- decktile/parser/__init__.py +0 -0
- decktile/parser/ast.py +191 -0
- decktile/parser/markdown.py +289 -0
- decktile/plugins/cards.py +25 -0
- decktile/plugins/directives.py +56 -0
- decktile/plugins/fenced.py +228 -0
- decktile/plugins/layouts.py +69 -0
- decktile/plugins/lucide.py +65 -0
- decktile/plugins/lucide_font.py +26 -0
- decktile/render/__init__.py +0 -0
- decktile/render/html.py +287 -0
- decktile/render/ir.py +536 -0
- decktile/render/odf/__init__.py +4 -0
- decktile/render/odf/geometry.py +510 -0
- decktile/render/odf/shapes.py +719 -0
- decktile/render/odf/styles.py +828 -0
- decktile/render/odp.py +380 -0
- decktile/render/pdf.py +17 -0
- decktile/render/pdg.py +72 -0
- decktile/render/seaborn.py +155 -0
- decktile/render/templates/base.css +741 -0
- decktile/render/templates/decktile.js +371 -0
- decktile/render/templates/shell.html +106 -0
- decktile/seaborn_styles/__init__.py +5 -0
- decktile/seaborn_styles/ossummit_europe.py +30 -0
- decktile/theme/__init__.py +0 -0
- decktile/theme/colors.py +53 -0
- decktile/theme/loader.py +22 -0
- decktile/theme/parser.py +264 -0
- decktile/themes/default.css +146 -0
- decktile/themes/kcd_vietnam.css +159 -0
- decktile/themes/kubecon_china.css +213 -0
- decktile/themes/kubecon_japan.css +156 -0
- decktile/themes/ossummit_europe.css +196 -0
- decktile/themes/ossummit_korea.css +196 -0
- decktile-0.2.0.dist-info/METADATA +625 -0
- decktile-0.2.0.dist-info/RECORD +46 -0
- decktile-0.2.0.dist-info/WHEEL +4 -0
- decktile-0.2.0.dist-info/entry_points.txt +6 -0
- 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
|
+
 ...
|
|
317
|
+
:::
|
|
318
|
+
::: card {side-image}
|
|
319
|
+

|
|
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,,
|