Turn a Markdown note into a slide deck and export it to PDF or a PNG image series, with live readability checks.
  • TypeScript 91.5%
  • JavaScript 7%
  • CSS 1.5%
Find a file
Johannes Kaindl 8b918383cb
build(lint): Inline-eslint-disable in src/ per Gate verbieten
Der Community-Store wertet ein Inline-disable einer obsidianmd/*-Regel
als Error, unabhaengig von der Begruendung. Das hat jetzt zwei reine
Wartungs-Releases gekostet (0.3.1 und 0.6.1) — die Konvention stand
zwar seit 0.3.1 im Kopf von eslint.config.mjs, aber nichts erzwang sie:
ein disable laesst `npm run lint` gruen durchlaufen und faellt erst
Tage spaeter im Store-Review auf.

scripts/check-no-inline-disables.mjs laeuft jetzt als erster Schritt
von `npm run lint` und blockt jedes `// eslint-disable*` in src/.
Ausnahmen gehen weiterhin als file-scoped Override mit Begruendung in
eslint.config.mjs — dort sind sie sichtbar und store-tauglich.

Verifiziert: kuenstlich eingefuegtes disable → exit 1, sauberer Baum
→ exit 0. src/ ist aktuell disable-frei.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 23:22:07 +02:00
.github/workflows chore: prepare 0.1.0 release — infra, README/CHANGELOG, screenshots 2026-06-27 12:36:58 +02:00
docs docs(plans): Implementierungsplan KI-Settings-QoL (9 Tasks, TDD) 2026-07-16 14:14:36 +02:00
scripts build(lint): Inline-eslint-disable in src/ per Gate verbieten 2026-07-19 23:22:07 +02:00
src fix(store-review): eslint-disables auflösen statt abschalten (0.6.1) 2026-07-19 23:13:41 +02:00
tests fix(ai-settings): Review-Findings — Statuslabel-Duplikat, fehlende Thinking-Desc, a11y-Aktiv-Marker, toter Code 2026-07-16 16:38:24 +02:00
.gitignore test(visual): Headless-Chrome-Visual-Smoke über die echte Render-Pipeline 2026-07-07 13:19:27 +02:00
AGENTS.md build(lint): Inline-eslint-disable in src/ per Gate verbieten 2026-07-19 23:22:07 +02:00
CHANGELOG.md docs(changelog): doppelten 0.6.1-Header entfernen 2026-07-19 23:14:17 +02:00
CONTRIBUTING.md docs: README (+de), AGENTS, CHANGELOG, SECURITY, CONTRIBUTING for release readiness 2026-06-25 13:51:53 +02:00
esbuild.config.mjs fix(preview): scale slides to pane width + inline KaTeX fonts 2026-06-26 10:32:56 +02:00
eslint.config.mjs build(lint): Inline-eslint-disable in src/ per Gate verbieten 2026-07-19 23:22:07 +02:00
eslint.portal.config.mjs chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00
LICENSE chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00
LICENSE-DOCS chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00
manifest.json chore(release): 0.6.1 2026-07-19 23:13:46 +02:00
package-lock.json feat(export): PNG via modern-screenshot (foreignObject) statt html2canvas 2026-06-28 07:16:37 +02:00
package.json build(lint): Inline-eslint-disable in src/ per Gate verbieten 2026-07-19 23:22:07 +02:00
README.de.md docs+fix(llm): §-Nummerierung, README-Beispiele, Deck-Prompt-Grammatik 2026-07-07 13:42:22 +02:00
README.md docs+fix(llm): §-Nummerierung, README-Beispiele, Deck-Prompt-Grammatik 2026-07-07 13:42:22 +02:00
SECURITY.md feat(export): write PNGs to the vault's configured attachment folder 2026-06-26 10:00:09 +02:00
styles.css refactor(ui): Generate-View nutzt den geteilten Status-Baustein 2026-07-16 16:04:48 +02:00
tsconfig.json chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00
tsconfig.test.json chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00
versions.json chore(release): 0.6.1 2026-07-19 23:13:46 +02:00
vitest.config.ts chore: scaffold slide-deck plugin from template 2026-06-25 12:31:23 +02:00

Slide Deck

Turn a Markdown note into a slide deck and export it to PDF or a PNG image series, with live readability checks.

License: AGPL-3.0 Release Platform: Desktop + Mobile

Slide Deck — a two-column slide with bullet list, inline math, and an image

Deutsch

Features

  • Theme isolation — slides render inside a sandboxed iframe, so the active Obsidian theme never leaks into the preview or the exports. A deck looks identical regardless of the vault theme.
  • Five built-in themes — the Nordstern set: shiro 白 (light, default), kuro 黒 (dark), sumi 墨 (true-black, high-contrast), kairo 回路 (dark, cyan), kurenai 紅 (dark, red) — selected per deck via the theme: frontmatter key; each carries a matching code-highlight and Mermaid theme. Legacy 0.4.x keys (default, dark, serif, high-contrast) still work — they resolve silently to their Nordstern successor.
  • Live theme switcher — the preview toolbar has a theme dropdown for ephemeral try-on, a source label (from frontmatter / from default / ● unsaved) that shows where the active theme comes from, and a Set button that writes theme: directly into the note's frontmatter. Frontmatter is the source of truth; the Settings default applies only to notes without a theme: key.
  • User themes — drop .css files into a configurable themes folder (default Slide-Deck-Themes/); the frontmatter theme: value is the filename without the .css extension. Each file is a --sd-* token block with optional extra CSS on top of the plugin's design system (type scale, spacing, rhythm) — a 7-token theme already looks finished; user themes inherit the shiro theme's code-highlight and Mermaid styles unless overridden. See the theming guide. The Settings tab shows all valid theme keys live.
  • Theme import/export — an Open in Finder button reveals the themes folder so you can drop files in; Export theme as .css writes any theme as an editable .css starting point; a toggle hides the themes folder in Obsidian's file explorer.
  • Nine per-slide templatesdefault, title, section, quote, image-focus, two-column, columns-3, stat, cover-image — set per slide with a layout directive (an HTML comment); columns are separated by a column directive, and in multi-column templates the leading heading spans all columns. See the layout guide.
  • Combinable density modifiers — add compact (tighter type) or code-heavy (smaller code) to any template in the same layout directive.
  • Smart layout inference — with no explicit directive, the layout is inferred from content shape: a lone heading becomes section, a lone block quote becomes quote, a lone image or diagram becomes image-focus, and column splits pick two-column / columns-3. An explicit layout directive always wins.
  • Deck slotsheader:, footer:, and paginate: frontmatter keys render as floating corner slots on every slide (pagination shows n / N).
  • Media that fills and centers — block images and Mermaid diagrams occupy the available space, horizontally and vertically centered and scaled to fit (object-fit: contain), for both Obsidian ![[embeds]] and standard ![](…) images.
  • Sparse slides compose vertically — slides with little content are vertically centered instead of clinging to the top.
  • Markdown notes → slides — separate slides with a line containing only ---; YAML frontmatter controls theme, aspect ratio, and font floor per note.
  • Live preview pane — renders the current note as a slide deck in a side panel, scaled to pane width, with a click-to-source link on overflow warnings.
  • Fit-or-warn readability — each slide auto-scales content down to a configurable legibility floor (minFontPx); slides that would need smaller text are flagged as overflowing instead of becoming unreadable.
  • Custom CSS — an optional CSS snippet in Settings is appended to the deck styles in both preview and exports, for branding or tweaks.
  • PDF export — renders all slides at their native resolution; on desktop, triggers the system print dialog (choose "Save as PDF"); on mobile (iOS/iPadOS), writes a self-contained HTML file and opens it via the OS so you can print or share to PDF from there.
  • PNG image-series export — captures each slide via modern-screenshot and writes numbered PNGs into a configurable export folder (Settings, default Slide-Deck-Export/); typographically accurate inter-word spacing.
  • Mobile support — runs on iOS/iPadOS (Obsidian Mobile); all desktop-only APIs are platform-guarded.
  • KaTeX math — inline and display math ($…$ / $$…$$) rendered by KaTeX.
  • Code highlighting — fenced code blocks highlighted by highlight.js, per-theme.
  • Accessible callouts — Obsidian-style > [!note], [!warning], [!danger], [!tip], [!info] blocks rendered with redundant coding: border color + geometric shape + visible label word (not color-only; satisfies WCAG 1.4.1).
  • Mermaid diagrams — fenced ```mermaid ``` blocks rendered as SVG, per-theme.
  • EN/DE interface — all UI strings follow Obsidian's language setting (English canonical, German supported).

Screenshots

Accessible callouts rendered with icon, shape, and label

Requirements

  • Obsidian ≥ 1.13.0 (the settings tab uses the declarative settings API introduced in 1.13.0)
  • Desktop + Mobile (isDesktopOnly: false) — runs on desktop (Windows, macOS, Linux) and on mobile (iOS/iPadOS); desktop-only APIs are platform-guarded.
  • Desktop PDF export uses the system print dialog — choose "Save as PDF" in the printer dropdown. It does not produce a PDF file directly.
  • Mobile PDF export writes a self-contained HTML file into the export folder and opens it with the OS default app; from there you can print or share to PDF. The file name is <export-folder>/<note-name>.html.
  • PNG export writes files into a configurable export folder (Settings → Slide Deck → Export folder, default Slide-Deck-Export/). PDF export on desktop goes through the system print dialog, where you choose the location.

Install

Community Plugins (intended channel)

Planned: once accepted into the Obsidian community plugin registry it will be installable via Settings → Community plugins → Browse → search "Slide Deck".

Manual install

  1. Download main.js, manifest.json, and styles.css from the latest release.
  2. Create the folder .obsidian/plugins/slide-deck/ inside your vault.
  3. Copy the three files into that folder.
  4. In Obsidian: Settings → Community plugins → Installed plugins — enable Slide Deck.

BRAT (Beta Reviewers Auto-update Tool)

  1. Install the BRAT plugin.
  2. In BRAT settings, add https://codeberg.org/jkaindl/slide-deck (or its GitHub mirror https://github.com/johannes-kaindl/slide-deck).
  3. Reload Obsidian.

Build from source

git clone https://codeberg.org/jkaindl/slide-deck.git
cd slide-deck
npm install
npm run build          # produces main.js
cp main.js manifest.json styles.css /path/to/vault/.obsidian/plugins/slide-deck/

Configuration

Plugin settings

Setting Key Default Description
Default preset defaultTheme shiro Preset used when a note has no theme frontmatter directive
Minimum body font size (px) minFontPx 24 Legibility floor — slides that would need smaller text are flagged as overflowing
Image export scale imageScale 2 Pixel multiplier for PNG export (2 = 2×, crisp on HiDPI screens)
Custom CSS customCss (empty) CSS appended to the deck styles in preview and exports, for branding or tweaks
Export folder exportFolder Slide-Deck-Export Vault folder for the PNG image-series export
Themes folder themesFolder Slide-Deck-Themes Vault folder scanned for user .css themes
Hide themes folder hideThemesFolder true Hide the themes folder in Obsidian's file explorer

Per-note frontmatter

Add a YAML frontmatter block at the top of your note to control presentation-level settings:

---
theme: kuro
aspect: 16:9
minFontPx: 24
header: My talk
footer: ACME Corp
paginate: true
---
Key Values Description
theme shiro · kuro · sumi · kairo · kurenai · user-theme-key (legacy default/dark/serif/high-contrast still resolve) Visual preset name; user theme key = the .css filename without the extension
aspect 16:9 (default), 4:3 Canvas size: 1280×720 (16:9) or 960×720 (4:3)
minFontPx any positive number Per-note legibility floor; overrides the plugin setting
header any text Floating header slot shown on every slide
footer any text Floating footer slot shown on every slide
paginate true · yes · on Show a page indicator (n / N) on every slide

Slide layout & syntax

Nine per-slide templates (default, title, section, quote, image-focus, two-column, columns-3, stat, cover-image), combinable density modifiers (compact, code-heavy), the layout and column directives, and smart layout inference are documented in the Slide layouts & syntax guide.

Slide separator

Use a line containing only --- to split slides:

---
theme: shiro
aspect: 16:9
---

# Slide 1

Content here.

---

# Slide 2

More content.

Note: the --- in the YAML frontmatter block is the standard YAML delimiter and is not a slide separator.

How it works

  1. Parsing — the active note's Markdown is split on --- lines into individual slide bodies. A YAML frontmatter block (if present) sets deck-level directives.
  2. Fixed canvas — each slide is rendered onto a fixed canvas: 1280×720 px (16:9) or 960×720 px (4:3). The canvas size does not change with window size.
  3. Theme isolation — slides render inside a sandboxed iframe with the chosen theme's styles injected directly. The active Obsidian theme does not reach inside the iframe, so the deck looks identical in preview, PDF, and PNG regardless of vault theme.
  4. Fit-or-warn — each slide's content is measured in the DOM. If it exceeds the canvas, the content is scaled down uniformly. Scaling stops at minFontPx (the legibility floor). If the content would still overflow at that scale, the slide is flagged with a warning in the preview pane rather than scaled further.
  5. Export — the same theme-isolated iframe artifact feeds all export paths: the print pipeline (PDF) and per-slide modern-screenshot (domToCanvas) capture (PNG). On desktop, PDF is printed via contentWindow.print(); on mobile, a self-contained HTML file is written to the vault and handed to the OS via openWithDefaultApp.

Network use (local AI)

The Generate presentation from note command sends note content to an OpenAI-compatible LLM endpoint that you configure (default http://localhost:1234, i.e. a local LM Studio). No cloud service is involved unless you point the endpoint at one.

  • Reachability pings and model lists are requested when you open the generation dialog or the settings tab. These are automatic requests to the configured endpoint(s).
  • Note contents are sent only when you press "Generate".
  • No telemetry, no analytics, no third-party services.

Server CORS

Streaming runs over XMLHttpRequest under the Obsidian origin, so the endpoint must allow cross-origin requests. LM Studio's CORS toggle must be on; Ollama needs OLLAMA_ORIGINS=app://obsidian.md (or *). If the endpoint answers the reachability ping but refuses the stream, the plugin automatically falls back to a non-streaming request (you lose the live token view, but the deck still generates).

License

Code: AGPL-3.0-or-later. Documentation: CC BY-SA 4.0. Author: Johannes Kaindl — https://jkaindl.de